Pisahkan metode logging di Java / logback

Bart menulis beberapa log


Tantangan


Katakanlah kita ingin mencatat setiap metode kelas Java tertentu secara berbeda:


  • Setiap metode memiliki file log sendiri,
  • ... format log Anda,
  • ... tingkat minimum penebangannya,
  • kami memperluas format log dengan % kami sendiri,
  • kemampuan untuk memperbarui konfigurasi ini dengan cepat.

Artikel ini menunjukkan cara memenuhi persyaratan ini. Untuk menjaga kesederhanaan, pemisahan penebangan hanya dilakukan dengan metode; pada kenyataannya, Anda mungkin ingin memiliki konfigurasi kualifikasi hierarkis, seperti ... Tautan ke kode sumber lengkap akan berada di bawah.


Kode klien


  class ThingService { log = LoggerFactory.getLogger(); getThing() { log.debug("getThing..."); // => one.log } listThings() { log.debug("listThings..."); // => another.log } } 

Logback


Untuk implementasinya, pustaka logging "logback" yang solid telah dipilih, yang memberikan kemungkinan menarik untuk kustomisasi:


 ch.qos.logback:logback-classic:1.2.3 

Ini dikonfigurasi baik dari konfigurasi XML dan langsung dari Jawa, pendekatan dapat digabungkan:


  public void configureLogback() throws JoranException { LoggerContext lc = LoggerFactory.getILoggerFactory(); lc.reset(); // reset prev config JoranConfigurator configurator = new JoranConfigurator(); configurator.setContext(lc); configurator.doConfigure("config.xml"); // any data source StatusPrinter.printInCaseOfErrorsOrWarnings(lc); //   : Logger root = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); root.setLevel(Level.INFO); //  } 

Secara singkat tentang pencatatan:


  1. Programmer menarik logger ,
  2. Logger menarik appenders yang ditugaskan kepadanya,
  3. Appender berpikir dan memanggil pembuat kode,
  4. Encoder memformat tepat satu baris log,
  5. Untuk melakukan ini, ia menarik rantai konverter , yang masing-masing mengungkapkan % ,
  6. Sukses

Untuk mempermudah, konfigurasi Java murni dipilih. Semuanya cukup jelas di sini jika Anda mengingat konfigurasi XML. Tugas utama adalah membuat appender / encoder Anda sendiri dan mendaftarkannya - mereka akan dipanggil oleh logback dari usus mereka. Hampir setiap objek yang Anda buat harus diingat untuk mulai menggunakan metode start() . Contoh abstrak:


  Logger rootLogger = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); LoggerContext lc = rootLogger.getLoggerContext(); lc.reset(); // reset prev config var encoder = new PatternLayoutEncoder(); encoder.setContext(lc); encoder.setPattern("%-5level %message%n"); encoder.start(); var appender = new ConsoleAppender<ILoggingEvent>(); appender.setContext(lc); appender.setEncoder(encoder); appender.start(); rootLogger.setLevel(Level.DEBUG); rootLogger.addAppender(appender); 

Pisahkan metode login dari satu sama lain


Agar logback dapat membedakan satu metode dari yang lain, sebelum memanggil metode tersebut, simpan namanya dalam Konteks Diagnostik ThreadLocal Mapped. Lebih lanjut, selama analisis, kami tidak mendapatkan nilai-nilai ini langsung dari kelas MDC , karena kode logging akan dieksekusi di utas lainnya dan data ini tidak akan ada di sana - kami mendapatkannya melalui ILoggingEvent.getMDCPropertyMap() .


Dalam kasus umum, seperti yang dicatat oleh vooft dengan benar, Anda perlu mendukung tumpukan panggilan dan tidak menimpa nilai MDC, tetapi mengembalikannya ke bingkai sebelumnya, yang dilakukan melalui pengenalan ThreadLocal baru. Contoh skematis:


  try { MDC.put(MDC_KEY_METHOD, currentMethod); // 1.  currentMethod    // 2.    // 3.       AOP, . } finally { String previousMethod = //     MDC.put(previousMethod); } 

File log sendiri untuk setiap metode


Ayo buat dan jangan lupa untuk mendaftarkan appender Anda sendiri:


  class MultiAppender extends AppenderBase<ILoggingEvent> { @Override protected void append(ILoggingEvent event) { method = event.getMDCPropertyMap().get(MDC_KEY_METHOD); Appender appender = getOrCreateAppender(method); appender.doAppend(event); } 

Dia sendiri hampir tidak melakukan apa-apa, hanya delegasi yang masuk ke paket file nyata, satu untuk setiap metode. Didelegasikan ke satu, yang paling cocok. Appenden "asli" dibuat atas permintaan, sebagai berikut:


  fileAppender = new FileAppender<ILoggingEvent>(); fileAppender.setContext(lc); fileAppender.setAppend(false); fileAppender.setEncoder(getOrCreateEncoderByMethod(lc, method)); fileAppender.setFile(logFileByMethod.get(method)); fileAppender.start(); 

Format sendiri untuk setiap metode


Untuk melakukan ini, simpan cache dari objek Encoder dibuat secara otomatis:


  Map<String, String> patternByMethod = new HashMap<>(); //  ;  Encoder getOrCreateEncoderByMethod(LoggerContext lc, String method) { String pattern = patternByMethod.get(method); encoder = new PatternLayoutEncoder(); encoder.setContext(lc); encoder.setPattern(pattern); encoder.start(); return encoder; } 

Setiap metode memiliki tingkat pencatatannya sendiri


MultiAppender menambahkan tanda centang ke kelas MultiAppender : jika tingkat peristiwa MultiAppender ambang yang ditentukan untuk metode ini, hanya kami yang mencatatnya:


  Map<String, Level> levelByMethod = new HashMap<>(); protected void append(ILoggingEvent event) { Level minLevel = levelByMethod.get(methodName); if (event.getLevel().levelInt >= minLevel.levelInt) { appender.doAppend(event); } 

Pada prinsipnya, logika ini bisa dimasukkan ke dalam filter.


Memperluas format dengan variabel Anda


Agar tidak memagari taman Anda, tetapi untuk mengambil keuntungan dari infrastruktur logback yang telah terbukti, Anda perlu menentukan kelas konverter Anda sendiri, pastikan untuk sepenuhnya publik sehingga dapat dipakai dari luar. Jika Anda membutuhkan MDC , ambil dari acara tersebut. %custom Handler variabel %custom dimulai di sini:


  public class CustomConverter extends ClassicConverter { public String convert(ILoggingEvent event) { // mdc = event.getMDCPropertyMap(); return "variable-expanded"; } } 

Selama proses konfigurasi umum, daftarkan handler:


  void configurePatterns(LoggerContext lc) { Map<String, String> rules = lc.getObject(CoreConstants.PATTERN_RULE_REGISTRY); if (rules == null) { rules = new HashMap<String, String>(); lc.putObject(CoreConstants.PATTERN_RULE_REGISTRY, rules); } rules.put("custom", CustomConverter.class.getName()); } 

Dan kita akan gunakan sebagai pembuat enkode, misalnya, PatternLayoutEncoder , yang akan mengambil semuanya. Dalam kasus ini, %custom variabel %custom akan diperluas ke string "variable-expanded" .


Konfigurasikan pembaruan dengan cepat


Ada peluang seperti itu di luar kotak: itu sudah cukup untuk memanggil fungsi konfigurator lagi, tidak lupa untuk melakukan LoggerContext::reset() dan menghapus cache yang terakumulasi.


Multithreading


Jika metode yang dikonfigurasikan oleh kami menghidupkan kembali utas, maka, tentu saja, aturan pencatatan yang ditentukan tidak akan berlaku bagi mereka - utas lokal tidak akan muncul sendiri di utas baru. Jadi, jika Anda ingin menerapkan pengaturan metode ke aliran baru, Anda harus menyalin MDC sana:


  Map<String, String> mdcOrig = MDC.getCopyOfContextMap(); ExecutorService es = Executors.newFixedThreadPool(1); es.submit(() -> threadWorker(mdcOrig)); void threadWorker(Map<String, String> parentMdc) { MDC.setContextMap(parentMdc); log.error("expected to appear in method2*.log"); } 

Seluruh contoh


https://github.com/zencd/logback-setup


Sastra


Manual Logback Resmi

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


All Articles