NodeJS logging menjadi mudah


Berapa kali Anda menulis logger.info('ServiceName.methodName.') Dan logger.info('ServiceName.methodName -> done.') Untuk setiap dan setiap metode layanan Anda yang ingin Anda log? Apakah Anda ingin otomatis dan memiliki tanda tangan konstan yang sama di seluruh aplikasi Anda? Jika begitu, kita sangat mirip, kita telah menderita rasa sakit yang sama terlalu sering, dan sekarang kita akhirnya bisa mencoba mengatasinya. Bersama. Hadirin sekalian, izinkan saya memperkenalkan ... penebang kelas !


"The why" dari class-logger


Insinyur sering perfeksionis. Perfeksionis sampai ekstrem. Kami menyukai abstraksi yang rapi. Kami suka kode bersih. Kami melihat keindahan dalam bahasa buatan yang bahkan orang lain tidak bisa baca. Kami menyukai pembuatan alam semesta digital kecil, hidup dengan aturan yang kami tetapkan. Kami menyukai semua itu, mungkin, karena kami sangat malas. Tidak, kami tidak takut bekerja, tetapi kami benci melakukan pekerjaan apa pun yang dapat diotomatisasi.


Setelah menulis beberapa ribu baris hanya kode logging, kami biasanya datang dengan pola-pola tertentu, menstandarisasi apa yang ingin kita catat. Namun kita masih harus menerapkan pola itu secara manual. Jadi ide inti class-logger adalah untuk menyediakan cara standar deklaratif yang sangat dapat dikonfigurasi untuk mencatat pesan sebelum dan sesudah eksekusi metode kelas.


Mulai cepat


Mari kita mulai bekerja dan melihat seperti apa kode yang sebenarnya.


 import { LogClass, Log } from 'class-logger' @LogClass() class ServiceCats { @Log() eat(food: string) { return 'purr' } } 

Layanan ini akan masuk tiga kali:


  • Pada saat penciptaannya dengan daftar argumen diteruskan ke konstruktor.
  • Sebelum eat dijalankan dengan daftar argumennya.
  • Setelah eat dieksekusi dengan daftar argumen dan hasilnya.

Dalam kata-kata kode:


 // Logs before the actual call to the constructor // `ServiceCats.construct. Args: [].` const serviceCats = new ServiceCats() // Logs before the actual call to `eat` // `ServiceCats.eat. Args: [milk].` serviceCats.eat('milk') // Logs after the actual call to `eat` // `ServiceCats.eat -> done. Args: [milk]. Res: purr.` 

Demo langsung .


Apa lagi yang bisa kita login? Berikut daftar lengkap acara:


  • Sebelum konstruksi kelas.
  • Sebelum metode statis dan non-statis sinkron dan asinkron dan sifat fungsional.
  • Setelah metode statis dan non-statis sinkron dan asinkron dan sifat fungsional.
  • Kesalahan metode statis dan non-statis sinkron dan asinkron serta sifat fungsional.

Properti fungsional adalah fungsi panah yang ditetapkan ke properti ( class ServiceCats { private meow = () => null } ).

Menyesuaikannya dengan kebutuhan kita


Sejauh ini bagus, tapi kami telah dijanjikan "dapat disesuaikan", bukan? Jadi bagaimana kita bisa mengubahnya?


class-logger menyediakan tiga lapisan konfigurasi hirarkis:


  • Global
  • Kelas
  • Metode

Pada setiap pemanggilan metode, ketiganya dievaluasi dan digabungkan bersama dari atas ke bawah. Ada beberapa konfigurasi global default yang waras, sehingga Anda dapat menggunakan perpustakaan tanpa konfigurasi sama sekali.


Konfigurasi global


Ini adalah konfigurasi aplikasi-lebar. Dapat diatur dengan panggilan setConfig .


 import { setConfig } from 'class-logger' setConfig({ log: console.info, }) 

Konfigurasi kelas


Ini memiliki efek terhadap setiap metode kelas Anda. Itu bisa mengesampingkan konfigurasi global.


 import { LogClass } from 'class-logger' setConfig({ log: console.info, }) @LogClass({ // It overrides global config for this service log: console.debug, }) class ServiceCats {} 

Konfigurasi metode


Itu hanya mempengaruhi metode itu sendiri. Mengganti konfigurasi kelas dan, karenanya, konfigurasi global.


 import { LogClass } from 'class-logger' setConfig({ log: console.info, }) @LogClass({ // It overrides global config for this service log: console.debug, }) class ServiceCats { private energy = 100 @Log({ // It overrides class config for this method only log: console.warn, }) eat(food: string) { return 'purr' } // This method stil uses `console.debug` provided by class config sleep() { this.energy += 100 } } 

Demo langsung


Opsi konfigurasi


Yah, kita sudah belajar cara mengubah default, namun tidak ada salahnya untuk menutupi apa yang ada untuk mengkonfigurasi, ya?


Di sini Anda dapat menemukan banyak override konfigurasi yang berguna .

Inilah tautan ke antarmuka objek config jika Anda dapat menggunakan TypeScript lebih baik daripada bahasa Inggris :)

Objek konfigurasi memiliki properti ini:


log


Ini adalah fungsi yang melakukan pencatatan aktual pesan akhir yang diformat. Ini digunakan untuk mencatat peristiwa ini:


  • Sebelum konstruksi kelas.
  • Sebelum metode statis dan non-statis sinkron dan asinkron dan sifat fungsional.
  • Setelah metode statis dan non-statis sinkron dan asinkron dan sifat fungsional.

Default: console.log


logError


Ini adalah fungsi yang melakukan pencatatan aktual dari pesan kesalahan yang diformat akhir. Ini digunakan untuk mencatat peristiwa ini dan satu-satunya:


  • Kesalahan metode statis dan non-statis sinkron dan asinkron serta sifat fungsional.

Default: console.error


formatter


Ini objek dengan dua metode: start dan end . Ini memformat data logging ke string terakhir.


start format pesan untuk acara ini:


  • Sebelum konstruksi kelas.
  • Sebelum metode statis dan non-statis sinkron dan asinkron dan sifat fungsional.

end format pesan untuk acara ini:


  • Setelah metode statis dan non-statis sinkron dan asinkron dan sifat fungsional.
  • Kesalahan metode statis dan non-statis sinkron dan asinkron serta sifat fungsional.

Default: new ClassLoggerFormatterService()


termasuk


Konfigurasi apa yang harus dimasukkan dalam pesan.


args

Itu bisa berupa boolean atau objek.


Jika boolean, itu menetapkan apakah akan menyertakan daftar argumen (ingat bahwa Args: [milk] ?) Ke keduanya, mulai (sebelum konstruksi dan sebelum panggilan metode) dan berakhir (setelah panggilan metode, panggilan metode kesalahan), pesan.


Jika itu sebuah objek, ia harus memiliki dua properti boolean: start dan end . start termasuk / tidak termasuk daftar argumen untuk pesan awal, end melakukan hal yang sama untuk pesan akhir.


Default: true


membangun

Pengaturan boolean flag apakah akan mencatat konstruksi kelas atau tidak.


Default: true


hasil

Pengaturan boolean flag lain apakah akan memasukkan nilai balik dari pemanggilan metode atau kesalahan yang dilemparkan olehnya. Ingat Res: purr ? Jika Anda menetapkan bendera ini ke false tidak akan ada Res: purr .


Default: true


kelasInstance

Sekali lagi, baik boolean atau objek.
Jika Anda mengaktifkannya, representasi string instance kelas Anda akan ditambahkan ke log. Dengan kata lain, jika instance kelas Anda memiliki beberapa properti, mereka akan dikonversi ke string JSON dan ditambahkan ke pesan log.


Tidak semua properti akan ditambahkan. class-logger mengikuti logika ini:


  • Ambil properti sendiri (non-prototipe) dari sebuah instance.
    • Mengapa Ini adalah kasus yang jarang terjadi ketika prototipe Anda berubah secara dinamis, oleh karena itu hampir tidak masuk akal untuk mencatatnya.
  • Jatuhkan salah satu dari mereka yang memiliki tipe function .
    • Mengapa Sebagian besar properti function waktu hanyalah fungsi panah tetap yang digunakan alih-alih metode kelas biasa untuk mempertahankan konteks this . Tidak masuk akal untuk menggembungkan log Anda dengan tubuh yang dirubah dari fungsi-fungsi itu.
  • Jatuhkan salah satu dari mereka yang bukan benda biasa.
    • Benda apa yang polos? ClassLoggerFormatterService menganggap suatu objek objek sederhana jika prototipe-nya benar-benar sama dengan Object.prototype .
    • Mengapa Seringkali kita memasukkan instance dari kelas lain sebagai properti (menyuntikkannya sebagai dependensi). Log kami akan menjadi sangat gemuk jika kami menyertakan versi ketat dari dependensi ini.
  • Tentukan apa yang tersisa.

 class ServiceA {} @LogClass({ include: { classInstance: true, }, }) class Test { private serviceA = new ServiceA() private prop1 = 42 private prop2 = { test: 42 } private method1 = () => null @Log() public method2() { return 42 } } // Logs to the console before the class' construction: // 'Test.construct. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}.' const test = new Test() // Logs to the console before the method call: // 'Test.method2. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}.' test.method2() // Logs to the console after the method call: // 'Test.method2 -> done. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}. Res: 42.' 

Default: false


Mengambil kendali atas pemformatan


Jadi bagaimana jika Anda menyukai ide keseluruhan, tetapi ingin pesan Anda terlihat berbeda? Anda dapat mengambil kendali penuh atas pemformatan yang melewati pemformat kustom Anda sendiri.


Anda dapat menulis formatter Anda sendiri dari awal. Benar-benar Namun kami tidak akan membahas opsi ini di sini (jika Anda benar-benar tertarik dengan itu, alamat bagian "Memformat" dari README).


Yang tercepat dan, mungkin, hal termudah untuk dilakukan adalah dengan subkelas pemformat bawaan bawaan - ClassLoggerFormatterService .


ClassLoggerFormatterService memiliki metode yang dilindungi ini, berfungsi sebagai blok bangunan pesan terakhir:


  • base
    • Mengembalikan nama kelas dengan nama metode. Contoh: ServiceCats.eat .
  • operation
    • Mengembalikan -> done atau -> error berdasarkan apakah itu merupakan eksekusi yang sukses dari suatu metode atau yang salah.
  • args
    • Mengembalikan daftar argumen yang dirubah. Contoh :. . Args: [milk] . Ini menggunakan fast-safe-stringify untuk objek di bawah tenda.
  • classInstance
    • Mengembalikan turunan kelas stringified. Contoh :. . Class instance: {"prop1":42,"prop2":{"test":42}} . Jika Anda memilih untuk memasukkan instance kelas, tetapi itu tidak tersedia (begitulah metode statis dan konstruksi kelas), ia mengembalikan N/A
  • result
    • Mengembalikan hasil eksekusi yang diketik (bahkan jika itu adalah kesalahan). Menggunakan fast-safe-stringify untuk membuat serialisasi objek. Kesalahan yang terkomplikasi akan terdiri dari properti berikut:
    • Nama kelas (fungsi) kesalahan dibuat dengan ( error.constructor.name ).
    • Kode kesalahan ( error.code ).
    • Pesan kesalahan ( error.message ).
    • Nama kesalahan ( error.name ).
    • Jejak tumpukan ( error.stack ).
  • final
    • Kembali . Adil . .

Pesan start terdiri dari:


  • base
  • args
  • classInstance
  • final

Pesan end terdiri dari:


  • base
  • operation
  • args
  • classInstance
  • result
  • final

Anda bisa mengesampingkan salah satu metode blok bangunan tersebut. Mari kita lihat bagaimana kita bisa menambahkan stempel waktu. Saya tidak mengatakan kita harus melakukannya. pino , winston dan banyak penebang lainnya mampu menambahkan cap waktu sendiri. Jadi contohnya adalah murni mendidik.


 import { ClassLoggerFormatterService, IClassLoggerFormatterStartData, setConfig, } from 'class-logger' class ClassLoggerTimestampFormatterService extends ClassLoggerFormatterService { protected base(data: IClassLoggerFormatterStartData) { const baseSuper = super.base(data) const timestamp = Date.now() const baseWithTimestamp = `${timestamp}:${baseSuper}` return baseWithTimestamp } } setConfig({ formatter: new ClassLoggerTimestampFormatterService(), }) 

Demo langsung


Kesimpulan


Tolong, jangan lupa untuk mengikuti langkah - langkah instalasi dan berkenalan dengan persyaratan sebelum Anda memutuskan untuk menggunakan perpustakaan ini.


Semoga Anda menemukan sesuatu yang berguna untuk proyek Anda. Silakan sampaikan umpan balik Anda kepada saya! Saya sangat menghargai kritik dan pertanyaan apa pun.

Source: https://habr.com/ru/post/id447358/


All Articles