Eclair - perpustakaan logging deklaratif Spring Java



Ada banyak pertanyaan tentang pekerjaan layanan pada tahap pengembangan, pengujian dan dukungan, dan semuanya pada pandangan pertama tidak seperti: "Apa yang terjadi?" , "Apakah ada permintaan?" , "Apa format tanggal?" , "Mengapa layanan ini tidak merespons?" dll.

Log yang dikompilasi dengan benar akan dapat menjawab secara terperinci ini dan banyak pertanyaan lainnya secara mandiri tanpa partisipasi pengembang. Untuk mencapai tujuan yang menggiurkan ini, lahirlah perpustakaan Eclair, yang dirancang untuk terlibat dalam dialog dengan semua peserta dalam proses tanpa menarik terlalu banyak selimut.

Tentang selimut dan fitur solusinya - di bawah ini.

Apa masalah logging


Jika Anda tidak terlalu tertarik untuk memahami tempat, Anda dapat segera melanjutkan ke deskripsi solusi kami.

  • Log aplikasi adalah alibinya.
    Paling sering, hanya dia yang bisa membuktikan keberhasilan aplikasi. Tidak ada keadaan dalam layanan mikro, sistem yang berdekatan bersifat mobile dan rewel. “Ulangi”, “buat ulang”, “periksa ulang” - semua ini sulit dan / atau tidak mungkin. Log harus cukup informatif untuk menjawab pertanyaan: "Apa yang terjadi?" Setiap saat . . Log harus jelas untuk semua orang: pengembang, penguji, kadang-kadang analis, kadang-kadang administrator, kadang-kadang garis dukungan pertama - apa pun terjadi.
  • Layanan microsoft adalah tentang multithreading.
    Permintaan yang datang ke layanan (atau data yang diminta oleh layanan) paling sering diproses oleh beberapa utas. Log semua utas biasanya dicampur. Apakah Anda ingin membedakan antara utas paralel dan membedakan antara utas "berurutan"? Aliran yang sama digunakan kembali untuk pemrosesan permintaan secara berurutan, berulang kali mengeksekusi logikanya sendiri untuk set data yang berbeda. "Urutan" ini mengalir dari bidang lain, tetapi batas-batasnya harus jelas bagi pembaca.
  • Log harus menyimpan format data asli.
    Jika pada kenyataannya layanan dipertukarkan oleh XML, maka log yang sesuai harus menyimpan XML. Itu tidak selalu kompak dan tidak selalu indah (tapi nyaman). Lebih mudah untuk melihat kesuksesan, lebih mudah untuk menganalisis kegagalan. Dalam beberapa kasus, log dapat digunakan untuk memutar atau memproses ulang permintaan secara manual.
  • Bagian dari data dalam log membutuhkan hubungan khusus.
    Data yang masuk (permintaan), data yang keluar (jawaban), permintaan ke sistem pihak ketiga dan tanggapan dari mereka sering diminta untuk disimpan secara terpisah. Mereka tunduk pada persyaratan khusus: berdasarkan umur simpan atau keandalan. Selain itu, data ini dapat memiliki jumlah yang mengesankan dibandingkan dengan garis log yang khas.
  • Bagian dari data bukan untuk log.
    Berikut ini biasanya dikeluarkan dari log reguler: data biner (byte array, base64, ..), data pribadi klien / mitra / individu lain dan badan hukum. Itu selalu merupakan cerita individu, tetapi sistematis dan tidak cocok untuk kontrol manual.

Kenapa tidak tangan


Ambil org.slf4j.Logger ( org.slf4j.Logger ke sana dengan Appenders dari jenis apa pun) dan tulis semua yang diperlukan untuk log tersebut. Pintu masuk ke metode utama, keluar, jika perlu, mencerminkan kesalahan yang tertangkap, beberapa data. Apakah ini perlu? Ya tapi

  • Jumlah kode bertambah tidak masuk akal (luar biasa). Pada awalnya, ini tidak terlalu mencolok, jika Anda hanya mencatat paling dasar (dukungan sukses, omong-omong, dengan pendekatan ini).
  • Memanggil logger dengan tangan Anda dengan cepat menjadi kemalasan. Mendeklarasikan bidang static dengan logger terlalu malas (well, Lombok bisa melakukan ini untuk kita). Kami pengembang malas. Dan kita mendengarkan kemalasan kita, ini kemalasan yang mulia: ini terus mengubah dunia menjadi lebih baik.
  • Layanan Microsoft tidak bagus di semua sisi. Ya, mereka kecil dan cantik, tetapi ada sisi lain: ada banyak! Satu aplikasi dari awal hingga akhir sering ditulis oleh satu pengembang. Warisan tidak tampak di depan matanya. Senang, tidak dibebani dengan aturan yang dipaksakan, pengembang menganggap itu tugas untuk menemukan format lognya sendiri, prinsipnya dan aturannya sendiri. Kemudian, secara brilian mengimplementasikan penemuan ini. Setiap kelas berbeda. Apakah ini masalah? Kolosal.
  • Refactoring akan memecah log Anda. Bahkan Ide Mahakuasa tidak akan menyelamatkannya. Memperbarui log sama mustahilnya dengan memperbarui Javadoc. Pada saat yang sama, setidaknya Javadoc hanya dibaca oleh pengembang (tidak, tidak ada yang membaca), tetapi audiens log jauh lebih luas dan tim pengembangan tidak terbatas.
  • MDC (Konteks Diagnostik yang Dipetakan) adalah bagian integral dari aplikasi multi-utas. Pengisian MDC secara manual membutuhkan pembersihan tepat waktu di akhir pekerjaan di sungai. Jika tidak, Anda berisiko mengikat salah satu ThreadLocal ke data yang tidak terkait. Tangan dan mata untuk mengendalikan ini, saya berani katakan, tidak mungkin.

Dan inilah cara kami memecahkan masalah ini dalam aplikasi kami.

Apa itu Eclair dan apa yang bisa dilakukannya


Eclair adalah alat yang menyederhanakan penulisan kode yang dicatat. Ini membantu untuk mengumpulkan meta-informasi yang diperlukan tentang kode sumber, mengaitkannya dengan data yang terbang dalam aplikasi dalam runtime dan mengirimkannya ke repositori log yang biasa, sambil menghasilkan kode minimum.

Tujuan utamanya adalah membuat log dapat dipahami oleh semua peserta dalam proses pengembangan. Oleh karena itu, kenyamanan menulis kode, manfaat Eclair tidak berakhir, tetapi hanya dimulai.

Eclair mencatat metode dan parameter yang dijelaskan:

  • mencatat entri / keluar metode dari metode / pengecualian / argumen / nilai yang dikembalikan oleh metode
  • memfilter pengecualian untuk mencatatnya secara spesifik ke tipe: hanya jika diperlukan
  • memvariasikan "detail" log, berdasarkan pada pengaturan aplikasi untuk lokasi saat ini: misalnya, dalam kasus yang paling terperinci ia mencetak nilai argumen (semua atau beberapa), dalam versi terpendek - hanya fakta memasukkan metode
  • mencetak data sebagai JSON / XML / dalam format lain (siap bekerja dengan Jackson, JAXB di luar kotak): memahami format mana yang paling disukai untuk parameter tertentu
  • memahami SpEL (Spring Expression Language) untuk instalasi deklaratif dan pembersihan otomatis MDC
  • menulis ke N logger, "logger" dalam pemahaman Eclair adalah kacang dalam konteks yang mengimplementasikan antarmuka EclairLogger : Anda dapat menentukan logger yang harus memproses anotasi berdasarkan nama, dengan alias, atau secara default
  • memberi tahu programmer tentang beberapa kesalahan dalam menggunakan anotasi: misalnya, Eclair tahu itu berfungsi pada proksi dinamis (dengan semua fitur berikutnya), oleh karena itu, dapat menyarankan bahwa anotasi pada metode private tidak akan pernah berfungsi
  • menerima penjelasan meta (seperti Spring menyebutnya): Anda dapat menentukan anotasi untuk masuk, menggunakan beberapa anotasi dasar - untuk mengurangi kode
  • mampu menutupi "sensitif" data saat mencetak: di luar kotak XPath-shielding XML
  • menulis log dalam mode "manual", mendefinisikan penyerang dan "memperluas" argumen yang mengimplementasikan Supplier : memberikan kesempatan untuk menginisialisasi argumen "malas"

Bagaimana menghubungkan Eclair


Kode sumber diterbitkan di GitHub di bawah lisensi Apache 2.0.

Untuk terhubung, Anda memerlukan Java 8, Maven, dan Spring Boot 1.5+. Artifact yang diselenggarakan oleh Maven Central Repository:

 <dependency> <groupId>ru.tinkoff</groupId> <artifactId>eclair-spring-boot-starter</artifactId> <version>0.8.3</version> </dependency> 

Starter berisi implementasi standar EclairLogger , yang menggunakan sistem logging yang diinisialisasi oleh Spring Boot dengan beberapa set pengaturan yang terverifikasi.

Contohnya


Berikut adalah beberapa contoh penggunaan perpustakaan umum. Pertama, sebuah fragmen kode diberikan, kemudian log yang sesuai, tergantung pada ketersediaan tingkat logging tertentu. Serangkaian contoh yang lebih lengkap dapat ditemukan di Wiki proyek di bagian Contoh .

Contoh paling sederhana


Level standarnya adalah DEBUG.

 @Log void simple() { } 
Jika level tersedia... maka log akan seperti ini
TRACE
DEBUG
DEBUG [] rteeExample.simple >
DEBUG [] rteeExample.simple <
INFO
WARN
ERROR
-

Detail log tergantung pada level logging yang tersedia.


Level logging yang tersedia di lokasi saat ini memengaruhi detail log. Semakin rendah level yang tersedia (yaitu, semakin dekat ke TRACE), semakin detail log.

 @Log(INFO) boolean verbose(String s, Integer i, Double d) { return false; } 
LevelLog
TRACE
DEBUG
INFO [] rteeExample.verbose > s="s", i=4, d=5.6
INFO [] rteeExample.verbose < false
INFOINFO [] rteeExample.verbose >
INFO [] rteeExample.verbose <
WARN
ERROR
-

Menyetel penebangan pengecualian


Jenis pengecualian yang dicatat dapat difilter. Pengecualian yang dipilih dan keturunan mereka akan dijanjikan. Dalam contoh ini, NullPointerException akan dicatat di tingkat WARN, Exception di tingkat ERROR (secara default), dan Error tidak akan dicatat sama sekali (karena Error tidak termasuk dalam filter anotasi pertama @Log.error dan secara eksplisit dikecualikan dari filter anotasi kedua).

 @Log.error(level = WARN, ofType = {NullPointerException.class, IndexOutOfBoundsException.class}) @Log.error(exclude = Error.class) void filterErrors(Throwable throwable) throws Throwable { throw throwable; } //       filterErrors(new NullPointerException()); filterErrors(new Exception()); filterErrors(new Error()); 
LevelLog
TRACE
DEBUG
INFO
WARN
WARN [] rteeExample.filterErrors ! java.lang.NullPointerException
java.lang.NullPointerException: null
at rteeExampleTest.filterErrors(ExampleTest.java:0)
..
ERROR [] rteeExample.filterErrors ! java.lang.Exception
java.lang.Exception: null
at rteeExampleTest.filterErrors(ExampleTest.java:0)
..
ERRORERROR [] rteeExample.filterErrors ! java.lang.Exception
java.lang.Exception: null
at rteeExampleTest.filterErrors(ExampleTest.java:0)
..

Tetapkan setiap parameter secara terpisah


 @Log.in(INFO) void parameterLevels(@Log(INFO) Double d, @Log(DEBUG) String s, @Log(TRACE) Integer i) { } 
LevelLog
TRACEINFO [] rteeExample.parameterLevels > d=9.4, s="v", i=7
DEBUGINFO [] rteeExample.parameterLevels > d=9.4, s="v"
INFOINFO [] rteeExample.parameterLevels > 9.4
WARN
ERROR
-

Pilih dan sesuaikan format cetakan


"Printer" yang bertanggung jawab untuk format cetak dapat dikonfigurasi oleh pra dan pasca prosesor. Dalam contoh di atas, maskJaxb2Printer dikonfigurasi sehingga elemen yang cocok dengan ekspresi XPath "//s" -masking menggunakan "********" . Pada saat yang sama, jacksonPrinter mencetak Dto "apa adanya".

 @Log.out(printer = "maskJaxb2Printer") Dto printers(@Log(printer = "maskJaxb2Printer") Dto xml, @Log(printer = "jacksonPrinter") Dto json, Integer i) { return xml; } 
LevelLog
TRACE
DEBUG
DEBUG [] rteeExample.printers >
xml=<dto><i>5</i><s>********</s></dto>, json={"i":5,"s":"password"}
DEBUG [] rteeExample.printers <
<dto><i>5</i><s>********</s></dto>
INFO
WARN
ERROR
-

Banyak penebang dalam konteks


Metode ini dicatat menggunakan beberapa logger pada saat yang sama: oleh logger default (dijelaskan menggunakan @Primary ) dan auditLogger auditLogger . Anda dapat menentukan beberapa penebang jika Anda ingin memisahkan peristiwa yang dicatat bukan hanya berdasarkan level (TRACE - ERROR), tetapi juga mengirimkannya ke penyimpanan yang berbeda. Sebagai contoh, logger utama dapat menulis log ke file pada disk menggunakan slf4j, dan auditLogger dapat menulis irisan data khusus ke penyimpanan yang sangat baik (misalnya, dalam Kafka) dalam format spesifiknya sendiri.

 @Log @Log(logger = "auditLogger") void twoLoggers() { } 

Manajemen MDC


MDC yang diatur menggunakan anotasi secara otomatis dihapus setelah keluar dari metode yang dianotasi. Nilai catatan MDC dapat dihitung secara dinamis menggunakan SpEL. Berikut ini adalah contoh: string statis yang dirasakan oleh konstanta, mengevaluasi ekspresi 1 + 1 , memanggil jacksonPrinter , memanggil metode static randomUUID .
MDC dengan atribut global = true tidak dihapus setelah keluar dari metode: seperti yang Anda lihat, satu-satunya catatan yang tersisa di MDC hingga akhir log adalah sum .

 @Log void outer() { self.mdc(); } @Mdc(key = "static", value = "string") @Mdc(key = "sum", value = "1 + 1", global = true) @Mdc(key = "beanReference", value = "@jacksonPrinter.print(new ru.tinkoff.eclair.example.Dto())") @Mdc(key = "staticMethod", value = "T(java.util.UUID).randomUUID()") @Log void mdc() { self.inner(); } @Log.in void inner() { } 

Log saat menjalankan kode di atas:
DEBUG [] rteeExample.outer >
DEBUG [beanReference={"i":0,"s":null}, sum=2, static=string, staticMethod=01234567-89ab-cdef-ghij-klmnopqrstuv] rteeExample.mdc >
DEBUG [beanReference={"i":0,"s":null}, sum=2, static=string, staticMethod=01234567-89ab-cdef-ghij-klmnopqrstuv] rteeExample.inner >
DEBUG [beanReference={"i":0,"s":null}, sum=2, static=string, staticMethod=01234567-89ab-cdef-ghij-klmnopqrstuv] rteeExample.mdc <
DEBUG [sum=2] rteeExample.outer <


Instalasi MDC Berbasis Parameter


Jika Anda menentukan MDC menggunakan anotasi pada parameter, maka parameter anotasi tersedia sebagai objek root dari konteks evaluasi. Di sini "s" adalah bidang Dto kelas dengan tipe String .

 @Log.in void mdcByArgument(@Mdc(key = "dto", value = "#this") @Mdc(key = "length", value = "s.length()") Dto dto) { } 

Log saat menjalankan kode di atas:
DEBUG [length=8, dto=Dto{i=12, s='password'}] rteeExample.mdcByArgument > dto=Dto{i=12, s='password'}

Pencatatan manual


Untuk "manual" logging sudah cukup untuk mengimplementasikan implementasi ManualLogger . Argumen yang berlalu yang mengimplementasikan Supplier antarmuka akan "diperluas" hanya jika perlu.

 @Autowired private ManualLogger logger; @Log void manual() { logger.info("Eager logging: {}", Math.PI); logger.debug("Lazy logging: {}", (Supplier) () -> Math.PI); } 
LevelLog
TRACE
DEBUG
DEBUG [] rteeExample.manual >
INFO [] rteeExample.manual - Eager logging: 3.141592653589793
DEBUG [] rteeExample.manual - Lazy logging: 3.141592653589793
DEBUG [] rteeExample.manual <
INFOINFO [] rteeExample.manual - Eager logging: 3.141592653589793
WARN
ERROR
-

Apa yang tidak dilakukan Eclair


Eclair tidak tahu di mana Anda akan menyimpan log Anda, untuk berapa lama dan detail. Eclair tidak tahu bagaimana Anda berencana menggunakan log Anda. Eclair dengan hati-hati mengekstrak dari aplikasi Anda semua informasi yang Anda butuhkan dan mengarahkannya ke penyimpanan yang Anda konfigurasi.

Contoh konfigurasi EclairLogger mengarahkan log ke Logback logger dengan Appender tertentu:

 @Bean public EclairLogger eclairLogger() { LoggerFacadeFactory factory = loggerName -> { ch.qos.logback.classic.LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); ch.qos.logback.classic.Logger logger = context.getLogger(loggerName); // Appender<ILoggingEvent> appender = ? // logger.addAppender(appender); return new Slf4JLoggerFacade(logger); }; return new SimpleLogger(factory, LoggingSystem.get(SimpleLogger.class.getClassLoader())); } 

Solusi ini bukan untuk semua orang.


Sebelum Anda mulai menggunakan Eclair sebagai alat utama untuk log, Anda harus membiasakan diri dengan sejumlah fitur dari solusi ini. "Fitur" ini disebabkan oleh fakta bahwa Eclair didasarkan pada mekanisme proxy standar untuk Spring.

- Kecepatan eksekusi kode yang dibungkus dengan proxy berikutnya tidak signifikan, tetapi akan turun. Bagi kami, kerugian ini jarang signifikan. Jika muncul pertanyaan untuk mengurangi waktu tunggu, ada banyak langkah pengoptimalan yang efektif. Menolak log informatif yang nyaman dapat dianggap sebagai salah satu langkah, tetapi tidak pada awalnya.

- StackTrace "mengasapi" sedikit lagi. Jika Anda tidak terbiasa dengan tumpukan panjang proxy Spring, ini bisa menjadi gangguan bagi Anda. Untuk alasan yang sama jelasnya, debugging kelas proksi akan sulit.

- Tidak setiap kelas dan setiap metode dapat diproksi : metode private tidak dapat diproksi, Anda akan perlu sendiri untuk mencatat rantai metode dalam satu kacang, Anda tidak dapat membuat proksi apa pun yang bukan kacang, dll.

Pada akhirnya


Sangat jelas bahwa alat ini, seperti yang lain, harus dapat digunakan untuk mendapatkan manfaat darinya. Dan materi ini hanya menerangi sisi di mana kami memutuskan untuk mencari solusi yang sempurna.

Kritik, pemikiran, petunjuk, tautan - Saya dengan hangat menyambut partisipasi Anda dalam kehidupan proyek! Saya akan senang jika Anda merasa Eclair berguna untuk proyek Anda.

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


All Articles