
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...");
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();
Secara singkat tentang pencatatan:
- Programmer menarik logger ,
- Logger menarik appenders yang ditugaskan kepadanya,
- Appender berpikir dan memanggil pembuat kode,
- Encoder memformat tepat satu baris log,
- Untuk melakukan ini, ia menarik rantai konverter , yang masing-masing mengungkapkan
%
, - 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();
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);
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();
Untuk melakukan ini, simpan cache dari objek Encoder
dibuat secara otomatis:
Map<String, String> patternByMethod = new HashMap<>();
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.
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) {
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