Pembalakan Java dengan Konteks Diagnostik Bersarang (NDC)

1. Gambaran keseluruhan

Konteks Diagnostik Bersarang (NDC) adalah mekanisme untuk membantu membezakan mesej log interleaved dari sumber yang berbeza. NDC melakukan ini dengan memberikan kemampuan untuk menambahkan maklumat kontekstual khas untuk setiap entri log.

Dalam artikel ini, kita akan meneroka penggunaan NDC dan penggunaan / sokongannya dalam berbagai kerangka kerja pembalakan Java.

2. Konteks Diagnostik

Dalam aplikasi berbilang utas khas seperti aplikasi web atau REST API, setiap permintaan pelanggan dilayan oleh utas yang berbeza. Log yang dihasilkan dari aplikasi tersebut akan menjadi gabungan semua permintaan dan sumber pelanggan. Ini menjadikannya sukar untuk membuat log masuk perniagaan atau membuat debug.

Konteks Diagnostik Bersarang (NDC) menguruskan timbunan maklumat kontekstual, berdasarkan setiap urutan. Data di NDC tersedia untuk setiap permintaan log dalam kod dan dapat dikonfigurasi untuk log dengan setiap pesan log - bahkan di tempat-tempat di mana data tidak berada dalam ruang lingkup. Maklumat kontekstual ini dalam setiap mesej log membantu membezakan log mengikut sumber dan konteksnya.

Konteks Diagnostik yang Dipetakan (MDC) juga menguruskan maklumat berdasarkan per-utas, tetapi sebagai peta.

3. Tumpukan NDC dalam Contoh Aplikasi

Untuk menunjukkan penggunaan timbunan NDC, mari kita ambil contoh REST API yang menghantar wang ke akaun pelaburan.

Maklumat yang diperlukan sebagai input ditunjukkan dalam kelas Pelaburan :

public class Investment { private String transactionId; private String owner; private Long amount; public Investment (String transactionId, String owner, Long amount) { this.transactionId = transactionId; this.owner = owner; this.amount = amount; } // standard getters and setters }

Pemindahan ke akaun pelaburan dilakukan menggunakan InvestmentService . Kod sumber penuh untuk kelas ini boleh didapati dalam projek github ini.

Dalam aplikasi sampel, transaksi dataId dan pemilik ditempatkan di tumpukan NDC, di utas yang memproses permintaan yang diberikan. Data ini tersedia dalam setiap mesej log dalam utas itu. Dengan cara ini, setiap transaksi unik dapat dikesan, dan konteks yang relevan dari setiap mesej log dapat dikenal pasti.

4. NDC di Log4j

Log4j menyediakan kelas yang dipanggil NDC yang menyediakan kaedah statik untuk menguruskan data dalam timbunan NDC. Penggunaan asas:

  • Semasa memasukkan konteks, gunakan NDC.push () untuk menambahkan data konteks dalam utas semasa
  • Semasa meninggalkan konteks, gunakan NDC.pop () untuk mengeluarkan data konteks
  • Semasa keluar dari utas , panggil NDC.hapus () untuk membuang konteks diagnostik untuk utas dan pastikan memori dibebaskan (pada Log4j 1.3, tidak perlu lagi)

Dalam aplikasi sampel, mari kita gunakan NDC untuk menambah / membuang data kontekstual di tempat yang relevan dalam kod:

import org.apache.log4j.NDC; @RestController public class Log4JController { @Autowired @Qualifier("Log4JInvestmentService") private InvestmentService log4jBusinessService; @RequestMapping( value = "/ndc/log4j", method = RequestMethod.POST) public ResponseEntity postPayment( @RequestBody Investment investment) { NDC.push("tx.id=" + investment.getTransactionId()); NDC.push("tx.owner=" + investment.getOwner()); log4jBusinessService.transfer(investment.getAmount()); NDC.pop(); NDC.pop(); NDC.remove(); return new ResponseEntity(investment, HttpStatus.OK); } }

Kandungan NDC dapat ditampilkan dalam mesej log dengan menggunakan pilihan % x di ConversionPattern yang digunakan oleh pelamar di log4j.properties :

log4j.appender.consoleAppender.layout.ConversionPattern = %-4r [%t] %5p %c{1} - %m - [%x]%n

Mari gunakan API REST ke tomcat. Permintaan sampel:

POST /logging-service/ndc/log4j { "transactionId": "4", "owner": "Marc", "amount": 2000 }

Kita dapat melihat maklumat konteks diagnostik dalam output log:

48569 [http-nio-8080-exec-3] INFO Log4JInvestmentService - Preparing to transfer 2000$. - [tx.id=4 tx.owner=Marc] 49231 [http-nio-8080-exec-4] INFO Log4JInvestmentService - Preparing to transfer 1500$. - [tx.id=6 tx.owner=Samantha] 49334 [http-nio-8080-exec-3] INFO Log4JInvestmentService - Has transfer of 2000$ completed successfully ? true. - [tx.id=4 tx.owner=Marc] 50023 [http-nio-8080-exec-4] INFO Log4JInvestmentService - Has transfer of 1500$ completed successfully ? true. - [tx.id=6 tx.owner=Samantha] ...

5. NDC dalam Log4j 2

NDC dalam Log4j 2 dipanggil sebagai Thread Context Stack:

import org.apache.logging.log4j.ThreadContext; @RestController public class Log4J2Controller { @Autowired @Qualifier("Log4J2InvestmentService") private InvestmentService log4j2BusinessService; @RequestMapping( value = "/ndc/log4j2", method = RequestMethod.POST) public ResponseEntity postPayment( @RequestBody Investment investment) { ThreadContext.push("tx.id=" + investment.getTransactionId()); ThreadContext.push("tx.owner=" + investment.getOwner()); log4j2BusinessService.transfer(investment.getAmount()); ThreadContext.pop(); ThreadContext.pop(); ThreadContext.clearAll(); return new ResponseEntity(investment, HttpStatus.OK); } }

Sama seperti Log4j, mari kita gunakan pilihan % x dalam fail konfigurasi Log4j2 log4j2.xml :

Keluaran log:

204724 [http-nio-8080-exec-1] INFO Log4J2InvestmentService - Preparing to transfer 1500$. - [tx.id=6, tx.owner=Samantha] 205455 [http-nio-8080-exec-2] INFO Log4J2InvestmentService - Preparing to transfer 2000$. - [tx.id=4, tx.owner=Marc] 205525 [http-nio-8080-exec-1] INFO Log4J2InvestmentService - Has transfer of 1500$ completed successfully ? false. - [tx.id=6, tx.owner=Samantha] 206064 [http-nio-8080-exec-2] INFO Log4J2InvestmentService - Has transfer of 2000$ completed successfully ? true. - [tx.id=4, tx.owner=Marc] ...

6. NDC di Fasad Pembalakan (JBoss Logging)

Fasad pembalakan seperti SLF4J memberikan integrasi dengan pelbagai rangka kerja pembalakan. NDC tidak disokong dalam SLF4J (tetapi termasuk dalam modul slf4j-ext). JBoss Logging adalah jambatan pembalakan, seperti SLF4J. NDC disokong dalam JBoss Logging.

Secara lalai, JBoss Logging akan mencari ClassLoader untuk ketersediaan back- end / penyedia dalam urutan keutamaan berikut: JBoss LogManager, Log4j 2, Log4j, SLF4J dan JDK Logging.

JBoss LogManager sebagai penyedia pembalakan biasanya digunakan di dalam pelayan aplikasi WildFly. Dalam kes kami, jambatan pembalakan JBoss akan memilih yang berikutnya mengikut urutan keutamaan (iaitu Log4j 2) sebagai penyedia pembalakan.

Mari kita mulakan dengan menambahkan kebergantungan yang diperlukan dalam pom.xml :

 org.jboss.logging jboss-logging 3.3.0.Final 

Versi ketergantungan terkini boleh diperiksa di sini.

Mari tambahkan maklumat kontekstual ke timbunan NDC:

import org.jboss.logging.NDC; @RestController public class JBossLoggingController { @Autowired @Qualifier("JBossLoggingInvestmentService") private InvestmentService jbossLoggingBusinessService; @RequestMapping( value = "/ndc/jboss-logging", method = RequestMethod.POST) public ResponseEntity postPayment( @RequestBody Investment investment) { NDC.push("tx.id=" + investment.getTransactionId()); NDC.push("tx.owner=" + investment.getOwner()); jbossLoggingBusinessService.transfer(investment.getAmount()); NDC.pop(); NDC.pop(); NDC.clear(); return new ResponseEntity(investment, HttpStatus.OK); } }

Keluaran log:

17045 [http-nio-8080-exec-1] INFO JBossLoggingInvestmentService - Preparing to transfer 1,500$. - [tx.id=6, tx.owner=Samantha] 17725 [http-nio-8080-exec-1] INFO JBossLoggingInvestmentService - Has transfer of 1,500$ completed successfully ? true. - [tx.id=6, tx.owner=Samantha] 18257 [http-nio-8080-exec-2] INFO JBossLoggingInvestmentService - Preparing to transfer 2,000$. - [tx.id=4, tx.owner=Marc] 18904 [http-nio-8080-exec-2] INFO JBossLoggingInvestmentService - Has transfer of 2,000$ completed successfully ? true. - [tx.id=4, tx.owner=Marc] ...

7. Kesimpulannya

Kami telah melihat bagaimana konteks diagnostik membantu menghubungkan log dengan cara yang bermakna - dari sudut perniagaan dan juga untuk tujuan penyahpepijatan. Ini adalah teknik yang tidak ternilai untuk memperkayakan pembalakan, terutama dalam aplikasi multi-utas.

Contoh yang digunakan dalam artikel ini boleh didapati dalam projek Github.