Регистрация в Java с вложен диагностичен контекст (NDC)

1. Общ преглед

Вложен диагностичен контекст (NDC) е механизъм, който помага да се разграничат преплетени дневници от различни източници. NDC прави това, като предоставя възможност за добавяне на отличителна контекстна информация към всеки запис в дневника.

В тази статия ще изследваме използването на NDC и неговото използване / поддръжка в различни рамки за регистриране на Java.

2. Диагностични контексти

В типично многонишково приложение като уеб приложение или REST API, всяка клиентска заявка се обслужва от различна нишка. Дневниците, генерирани от такова приложение, ще бъдат комбинация от всички клиентски заявки и източници. Това затруднява какъвто и да е бизнес смисъл на регистрационните файлове или отстраняване на грешки.

Вложен диагностичен контекст (NDC) управлява стек от контекстна информация на база на нишка. Данните в NDC са достъпни за всяка регистрационна заявка в кода и могат да бъдат конфигурирани да се регистрират с всяко дневник съобщение - дори на места, където данните не са в обхвата. Тази контекстна информация във всяко съобщение в дневника помага да се разграничат дневниците по източника и контекста.

Mapped Diagnostic Context (MDC) също управлява информация на основата на нишка, но като карта.

3. NDC стека в примерно приложение

За да демонстрираме използването на NDC стек, нека вземем пример за REST API, който изпраща пари към инвестиционна сметка.

Информацията, която се изисква като вход, е представена в инвестиционен клас:

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 }

Прехвърлянето към инвестиционната сметка се извършва с помощта на InvestmentService . Пълният изходен код за тези класове може да бъде намерен в този github проект.

В примерното приложение, dataId и собственикът на данни се поставят в NDC стека, в нишката, която обработва дадена заявка. Тези данни са налични във всяко съобщение в дневника в тази нишка. По този начин може да се проследи всяка уникална транзакция и да се идентифицира съответният контекст на всяко съобщение в дневника.

4. NDC в Log4j

Log4j предоставя клас, наречен NDC, който предоставя статични методи за управление на данни в NDC стека. Основна употреба:

  • Когато въвеждате контекст, използвайте NDC.push (), за да добавите данни за контекста в текущата нишка
  • Когато напускате контекста, използвайте NDC.pop (), за да извадите данните за контекста
  • Когато излизате от нишката, извикайте NDC.remove (), за да премахнете диагностичния контекст за нишката и да се уверите, че паметта е освободена (от Log4j 1.3, вече не е необходимо)

В примерното приложение нека използваме NDC за добавяне / премахване на контекстуални данни на съответните места в кода:

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); } }

Съдържанието на NDC може да се показва в регистрационни съобщения, като се използва опцията % x в ConversionPattern, използвана от appender в log4j.properties :

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

Нека разгърнем REST API в tomcat. Примерна заявка:

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

Можем да видим информацията за контекста на диагностиката в изхода на регистрационния файл:

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 в Log4j 2

NDC в Log4j 2 се нарича контекстен стек на нишки:

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); } }

Точно както при Log4j, нека използваме опцията % x в конфигурационния файл Log4j 2 log4j2.xml :

Изход на регистрационния файл:

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 в регистрационни фасади (JBoss Logging)

Фасадите за регистриране като SLF4J осигуряват интеграция с различни рамки за регистриране. NDC не се поддържа в SLF4J (но е включен в модула slf4j-ext). JBoss Logging е мост за регистриране, точно като SLF4J. NDC се поддържа в JBoss Logging.

По подразбиране JBoss Logging ще търси ClassLoader за наличието на back- end / доставчици в следния ред на предимство: JBoss LogManager, Log4j 2, Log4j, SLF4J и JDK Logging.

JBoss LogManager като доставчик на регистрация обикновено се използва в сървъра за приложения на WildFly. В нашия случай мостът за регистриране JBoss ще избере следващия по приоритет (който е Log4j 2) като доставчик на регистрация.

Нека започнем с добавяне на необходимата зависимост в pom.xml :

 org.jboss.logging jboss-logging 3.3.0.Final 

Най-новата версия на зависимостта може да бъде проверена тук.

Нека добавим контекстна информация към 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); } }

Изход на регистрационния файл:

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. Заключение

Видяхме как диагностичният контекст помага за корелиране на регистрационните файлове по смислен начин - от бизнес гледна точка, както и за целите на отстраняване на грешки. Това е безценна техника за обогатяване на регистрацията, особено при многонишкови приложения.

Примерът, използван в тази статия, може да бъде намерен в проекта Github.