Logging Javascript Super Sederhana - Dua Dekorator, dan Selesai


Apakah Anda masih bosan menulis logger.info('ServiceName.methodName.') Dan logger.info('ServiceName.methodName -> done.') Untuk setiap bersin? Mungkin Anda, seperti saya, telah berulang kali berpikir untuk mengotomatisasi bisnis ini? Pada artikel ini, kita akan berbicara tentang class-logger sebagai salah satu solusi untuk masalah hanya dengan dua dekorator.


Mengapa itu perlu?


Tidak semua perfeksionis adalah insinyur, tetapi semua insinyur perfeksionis (well, hampir). Kami menyukai abstraksi singkat yang indah. Kita melihat keindahan di set buaya, yang seseorang tidak siap dan tidak bisa membaca. Kami senang merasa seperti dewa, menciptakan alam semesta mikro yang hidup berdasarkan aturan kami. Agaknya, semua kualitas ini berasal dari kemalasan kita yang luar biasa. Tidak, insinyur itu tidak takut bekerja sebagai kelas, tetapi ia sangat membenci tindakan berulang yang rutin yang diusahakan oleh lengannya.


Setelah menulis beberapa ribu baris kode yang dimaksudkan hanya untuk penebangan, biasanya kita sampai pada penciptaan beberapa pola dan standar kita sendiri untuk menulis pesan. Sayangnya, kami masih harus menerapkan pola-pola ini secara manual. "Mengapa" utama dari kelas-logger adalah untuk menyediakan cara deklaratif, dapat dikonfigurasi untuk dengan mudah mencatat pesan templat ketika membuat kelas dan memanggil metode-metodenya.


Catur kosong!


Tanpa berkeliaran, mari kita langsung menuju kode.


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

Layanan ini akan membuat tiga entri log:


  • Sebelum membuatnya dengan daftar argumen yang diteruskan ke konstruktor.
  • Sebelum menelepon eat dengan daftar argumen.
  • Setelah menelepon eat dengan daftar argumen dan hasil eksekusi.

Dalam kata-kata kode:


 //    ServiceCats // `ServiceCats.construct. Args: [].` const serviceCats = new ServiceCats() //    `eat` // `ServiceCats.eat. Args: [milk].` serviceCats.eat('milk') //    `eat` // `ServiceCats.eat -> done. Args: [milk]. Res: purr.` 

Aduk langsung .


Daftar lengkap acara yang dapat dijanjikan:


  • Sebelum membuat kelas.
  • Sebelum memanggil metode sinkron dan asinkron dan properti fungsional, baik statis maupun non-statis.
  • Setelah memanggil metode sinkron dan asinkron dan properti fungsional, baik statis maupun non-statis.
  • Kesalahan dalam memanggil metode sinkron dan asinkron dan sifat fungsional, baik statis maupun non-statis.

Properti fungsional adalah fungsi panah yang ditetapkan ke properti ( class ServiceCats { private meow = () => null } ). Paling sering digunakan untuk mempertahankan konteks eksekusi ( this ).

Kami dijanjikan cara logging yang "dapat dikonfigurasi"


kelas-logger tiga tingkat hirarki konfigurasi:


  • Global
  • Kelas
  • Metode

Saat memanggil setiap metode, ketiga level bergabung bersama. Perpustakaan menyediakan konfigurasi default global yang masuk akal, sehingga dapat digunakan tanpa konfigurasi sebelumnya.


Konfigurasi global


Itu dibuat untuk seluruh aplikasi. Itu bisa diganti dengan setConfig .


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

Konfigurasi kelas


Ini unik untuk setiap kelas dan diterapkan untuk semua metode kelas ini. Dapat menimpa konfigurasi global.


 import { LogClass } from 'class-logger' setConfig({ log: console.info, }) @LogClass({ //       log: console.debug, }) class ServiceCats {} 

Konfigurasi metode


Hanya berfungsi untuk metode itu sendiri. Dibutuhkan lebih dari konfigurasi kelas dan konfigurasi global.


 import { LogClass } from 'class-logger' setConfig({ log: console.info, }) @LogClass({ //       log: console.debug, }) class ServiceCats { private energy = 100 @Log({ //        log: console.warn, }) eat(food: string) { return 'purr' } //    `console.debug`    sleep() { this.energy += 100 } } 

Aduk langsung


Apa yang bisa dikonfigurasi?


Kami memeriksa cara mengubah konfigurasi, tetapi masih belum menemukan apa sebenarnya yang bisa diubah di dalamnya.


Di sini Anda dapat menemukan banyak contoh objek konfigurasi untuk berbagai kasus .

Tautkan ke antarmuka jika Anda memahami TypeScript lebih baik. dari Rusia :)

Objek konfigurasi memiliki properti berikut:


log


Ini adalah fungsi yang sebenarnya berkaitan dengan penebangan. Dia menerima pesan yang diformat sebagai string di input. Digunakan untuk mencatat peristiwa berikut:


  • Sebelum membuat kelas.
  • Sebelum memanggil metode sinkron dan asinkron dan properti fungsional, baik statis maupun non-statis.
  • Setelah memanggil metode sinkron dan asinkron dan properti fungsional, baik statis maupun non-statis.

Secara default, console.log .


logError


Ini adalah fungsi yang juga berkaitan dengan pencatatan, tetapi hanya pesan kesalahan. Dia juga menerima pesan yang diformat sebagai string. Digunakan untuk mencatat peristiwa berikut:


  • Kesalahan dalam memanggil metode sinkron dan asinkron dan sifat fungsional, baik statis maupun non-statis.

Standarnya adalah console.error .


formatter


Ini adalah objek dengan dua metode: start dan end . Metode ini membuat pesan yang diformat sama.


start membuat pesan untuk acara berikut:


  • Sebelum membuat kelas.
  • Sebelum memanggil metode sinkron dan asinkron dan properti fungsional, baik statis maupun non-statis.

end menghasilkan pesan untuk acara berikut:


  • Setelah memanggil metode sinkron dan asinkron dan properti fungsional, baik statis maupun non-statis.
  • Kesalahan dalam memanggil metode sinkron dan asinkron dan sifat fungsional, baik statis maupun non-statis.

Secara default, new ClassLoggerFormatterService() .


termasuk


Konfigurasi apa yang harus dimasukkan dalam pesan akhir.


args

Ini bisa berupa nilai Boolean atau objek.


Jika boolean , maka itu menetapkan apakah akan menyertakan daftar argumen (ingat Args: [milk] ?) Dalam semua pesan ( start dan end ).


Jika itu adalah objek, maka ia harus memiliki dua properti Boolean: start dan end . start menentukan apakah akan menyertakan daftar argumen untuk pesan start , end untuk pesan end .


Defaultnya true .


membangun

Ini adalah boolean yang mengontrol apakah akan mencatat pembuatan kelas atau tidak.


Defaultnya true .


hasil

Ini adalah boolean yang mengontrol pencatatan nilai pengembalian dari metode. Perlu dicatat bahwa nilai kembali tidak hanya apa yang dikembalikan metode pada eksekusi yang sukses, tetapi juga kesalahan yang dilemparkannya jika eksekusi tidak berhasil. Ingat Res: purr ? Jika flag ini false , maka tidak akan ada Res: purr .


Defaultnya true .


kelasInstance

Ini bisa berupa nilai Boolean atau objek. Konsepnya sama dengan include.args .


Menambahkan tampilan instance serial dari kelas Anda ke posting. Dengan kata lain, jika kelas Anda memiliki properti apa pun, mereka akan diserialisasi dalam JSON dan ditambahkan ke log.


Tidak semua properti bisa serial. class-logger menggunakan logika berikut:


  • Kami mengambil semua properti intansa milik kami (yang tidak ada dalam prototipe).
    • Mengapa Sangat jarang, prototipe berubah secara dinamis, sehingga tidak masuk akal untuk mencatat isinya.
  • Kami membuang mereka semua dari jenis function .
    • Mengapa Lebih sering daripada tidak, sifat function tipe hanyalah fungsi panah yang tidak dibuat oleh metode konvensional untuk mempertahankan konteks ( this ). Mereka jarang memiliki sifat dinamis, sehingga tidak ada gunanya menebang mereka.
  • Kami membuang dari mereka semua benda yang bukan benda sederhana.
    • Apa benda sederhana ini? ClassLoggerFormatterService menganggap objek sederhana jika prototipenya adalah Object.prototype .
    • Mengapa Seringkali contoh kelas layanan lainnya bertindak sebagai properti. Log kami akan membengkak dengan cara yang paling buruk jika kami mulai membuat serial.
  • Serialize dalam string JSON semua 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 } } //    `Test` // 'Test.construct. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}.' const test = new Test() //    `method2` // 'Test.method2. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}.' test.method2() //    `method2` // 'Test.method2 -> done. Args: []. Class instance: {"prop1":42,"prop2":{"test":42}}. Res: 42.' 

Standarnya false .


Mengambil kendali penuh atas pemformatan pesan


Bagaimana jika Anda menyukai gagasan itu, tetapi gagasan Anda tentang si cantik bersikeras pada format baris pesan yang berbeda? Anda dapat mengambil kendali penuh atas pemformatan pesan dengan melewati pemformat Anda sendiri.


Anda dapat menulis formatter Anda sendiri dari awal. Tentu saja Tetapi opsi ini tidak akan dicakup dalam kerangka kerja ini (jika Anda tertarik pada opsi khusus ini, lihat bagian "Memformat" di README).


Cara tercepat dan termudah untuk mengganti format adalah dengan mewarisi formatter Anda dari formatter default - ClassLoggerFormatterService .


ClassLoggerFormatterService memiliki metode yang protected berikut ini yang membuat blok kecil dari pesan akhir:


  • base
    • Mengembalikan nama kelas dan nama metode. Contoh: ServiceCats.eat .
  • operation
    • Pengembalian -> done atau -> error tergantung pada apakah metode berhasil diselesaikan.
  • args
    • Mengembalikan daftar argumen serial. Contoh :. . Args: [milk] . Ini menggunakan fast-safe-stringify untuk membuat serialisasi objek di bawah tenda.
  • classInstance
    • Mengembalikan instance serial dari kelas. Contoh :. . Class instance: {"prop1":42,"prop2":{"test":42}} . Jika Anda menyertakan include.classInstance dalam konfigurasi, tetapi karena beberapa alasan instance itu sendiri tidak tersedia pada saat pencatatan (misalnya, untuk metode statis atau sebelum membuat kelas), mengembalikan N/A
  • result
    • Mengembalikan hasil eksekusi serial atau kesalahan serial. Menggunakan fast-safe-stringify untuk objek di bawah tenda. Kesalahan serial meliputi properti berikut:
    • Nama kelas (fungsi) yang digunakan untuk membuat kesalahan ( error.constructor.name ).
    • Kode ( error.code ).
    • Pesan ( error.message ).
    • Nama ( error.name ).
    • Jejak tumpukan ( error.stack ).
  • final
    • Kembali . Sederhana .

Pesan start terdiri dari:


  • base
  • args
  • classInstance
  • final

Pesan end terdiri dari:


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

Anda dapat mengganti hanya metode dasar yang Anda butuhkan.


Mari kita lihat bagaimana Anda dapat menambahkan cap waktu ke semua posting.


Saya tidak mengatakan bahwa ini harus dilakukan dalam proyek nyata. pino , winston, dan sebagian besar penebang lainnya dapat melakukan ini sendiri. Contoh ini hanya untuk tujuan pendidikan.

 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(), }) 

Aduk langsung


Kesimpulan


Ingatlah untuk mengikuti instruksi instalasi dan membiasakan diri dengan persyaratan sebelum menggunakan perpustakaan ini pada proyek Anda.


Saya harap Anda tidak membuang waktu dengan sia-sia, dan artikel itu setidaknya sedikit bermanfaat bagi Anda. Tolong tendang dan kritik. Kami akan belajar kode lebih baik bersama.

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


All Articles