W naszym oprogramowaniu szeroko używamy MDC do śledzenia takich rzeczy, jak identyfikatory sesji i nazwy użytkowników dla żądań internetowych. Działa to dobrze podczas uruchamiania w oryginalnym wątku. Jest jednak wiele rzeczy, które muszą zostać przetworzone w tle. W tym celu używamy klas java.concurrent.ThreadPoolExecutor
i java.util.Timer
wraz z niektórymi usługami wykonywania asynchronicznego samoczynnego przejścia . Wszystkie te usługi zarządzają własną pulą wątków.
Oto, co podręcznik Logback ma do powiedzenia na temat używania MDC w takim środowisku:
Kopia odwzorowanego kontekstu diagnostycznego nie zawsze może być dziedziczona przez wątki robocze z wątku inicjującego. Dzieje się tak, gdy do zarządzania wątkami używany jest java.util.concurrent.Executors. Na przykład metoda newCachedThreadPool tworzy ThreadPoolExecutor i podobnie jak inny kod puli wątków ma skomplikowaną logikę tworzenia wątków.
W takich przypadkach zaleca się wywołanie MDC.getCopyOfContextMap () w oryginalnym (głównym) wątku przed przesłaniem zadania do modułu wykonawczego. Po uruchomieniu zadania, jako pierwsze działanie, powinno wywołać MDC.setContextMapValues () w celu skojarzenia przechowywanej kopii oryginalnych wartości MDC z nowym wątkiem zarządzanym przez moduł Executor.
Byłoby dobrze, ale bardzo łatwo jest zapomnieć o dodaniu tych połączeń i nie ma łatwego sposobu na rozpoznanie problemu, dopóki nie będzie za późno. Jedynym znakiem w Log4j jest to, że w dziennikach brakuje informacji MDC, a dzięki Logback otrzymujesz nieaktualne informacje MDC (ponieważ wątek w puli bieżnika dziedziczy swój MDC z pierwszego zadania, które zostało na nim uruchomione). Oba są poważnymi problemami w systemie produkcyjnym.
Nie uważam naszej sytuacji za wyjątkową, ale nie mogłem znaleźć wiele informacji na temat tego problemu w sieci. Najwyraźniej nie jest to coś, z czym wielu ludzi się boryka, więc musi istnieć sposób, aby tego uniknąć. Co tu robimy źle?