Às vezes precisamos de toda a traceid em um pedido são os mesmos, a fim de obter uma resolução unificada do arquivo de log. Fácil de solucionar.
Atribuído a cada pedido com um traceid tanto quanto eu sei, existem duas maneiras: MDC e ThreadLocal, implementação interna do MDC também é ThreadLocal, seções a seguir descrevem esses dois métodos.
A, MDC
MDC (mapeada diagnóstico Contextos), que se traduz em: Mapa contexto de diagnóstico. Meios: no ID do pedido de registro (requestId) (mapa), que pode ser posicionado como uma palavra-chave (de diagnóstico) problema (contexto).
Com a ferramenta MDC, enquanto os implantes de colocar ou remover o código ou a secção de interface, quando o problema de posicionamento pode, de acordo com o mapeamento única RequestID rapidamente filtrar todos registrar um pedido particular.
MDC slf4j de seu mecanismo interno com base na implementação ThreadLocal, consulte este blog em base Java ThreadLocal, https://www.cnblogs.com/yangyongjie/p/10574591.html .
1, sem os fios criança implementos código de pedido:
1) pedido Use interceptação Aop
/ ** * pedido para adicionar um número de segmento para cada do HTTP * * @author yangyongjie * @date 2019/09/02 * @desc * / @Aspect @Component pública classe LogAspect { Privada estática final de Cordas STR_THREAD_ID = "O tópico" ; @ pointcut (valor = "@annotation (org.springframework.web.bind.annotation.RequestMapping)" ) privado vazio webPointcut () { // DoNothing } / ** * para todas as solicitações HTTP para adicionar o número de rosca * * @ param Joinpoint *@throws o Throwable * / @Around (valor = "webPointcut ()" ) público vazio ao redor (ProceedingJoinPoint Joinpoint) lança o Throwable { // método anterior realizada mais o número fio MDC.put (STR_THREAD_ID, UUID.randomUUID (). toString () .replaceAll ( "-", "" )); // executar método de intercepção joinPoint.proceed (); // método tiver terminado a execução remover o número do segmento MDC.remove (STR_THREAD_ID); } }
2) registo de configuração log4j
log4j.appender.stdout.layout.ConversionPattern = [% - 5p]% d {aaaa-MM-dd HH: MM: ss.SSS} [% T] % X {} ThreadID [% C:% G] - m% % N
Deve-se notar no log corda vermelha ThreadID necessidade de interceptar e log em MDC colocar a chave é a mesma.
2, quando há um pedido criança threads.
MDC slf4j de seu mecanismo interno com base na implementação ThreadLocal, consulte este blog em base Java ThreadLocal, https: //www.cnblogs.com/yangyongjie/p/10574591.html. Então chamamos MDC.put () passar método
O ID do pedido é válido apenas no segmento atual. Então, MDC principal conjunto de dados segmento, em que não é obtida sub-thread (o pool de threads). Então como é que o segmento principal MDC dados para o sub-thread?
recomendações oficiais
1) antes de o segmento pai Nova rosca do método chamada MDC.getCopyOfContextMap () para passar o conteúdo é retirado MDC sub-segmento
2) o segmento de criança antes de prosseguir para chamar o método MDC.setContextMap () do teor de MDC do segmento pai ao conjunto segmento de criança
implementação de código
1) O uso Aop intercepta o pedido, o mesmo que acima
2) de configuração de log log4j mesmo que acima
3) Rosca decorador decorador, existem duas maneiras:
Método 1: Usando o padrão de decorador, a camada decorativa na interface Runnable, a criação de classes MDCRunnable Runnable ser decorados interfaces de camada.
valor Salvar MDC atual do segmento ao criar classe MDCRunnable, e em seguida, executar o método run ()
Decorator MDCRunnable decoração Runnable:
Import org.slf4j.MDC; Import um java.util.Map; / ** * padrão decorador decorativo Runnable, segmento pai número fio passando * * @author yangyongjie * @date 2020/03/09 * @desc * / público classe MDCRunnable os implementos do Runnable { privada do Runnable Runnable; / ** * MDC salvar o valor atual do thread principal * / privado final do Map <string, string> mainMdcMap; pública MDCRunnable (o Runnable Runnable) { a este .runnable = Runnable; a este .mainMdcMap = MDC. getCopyOfContextMap (); } @ override pública vazio run () { // os valores MDC atribuídos para o segmento segmento pai da criança para (de Map.Entry <String, String> entrada: mainMdcMap.entrySet ()) { MDC.put (entry.getKey (), entry.getValue ()); } // executa decorativo método segmento run Runnable.run (); // execução de remoção de conclusão valores MDC para (de Map.Entry <string, string> entrada: mainMdcMap.entrySet ()) { MDC.put (entry.getKey (), entry.getValue ()); } } }
Use MDCRunnable vez de Runnable:
// log impressão segmento assíncrono, decorado com MDCRunnable o Runnable novo novo a Thread ( new new MDCRunnable ( nova novo o Runnable () { @Override pública vazio run () { logger.debug ( "OUTRAS log in thread" ); } })) Iniciar (). ; // log impressão pool de thread assíncrono, com MDCRunnable decorativo do Runnable executor.execute ( nova nova MDCRunnable ( nova novo o Runnable () { @Override pública vazio run () { logger.debug ( "log segmento diferente na piscina" ); } })) ; EXECUTOR.shutdown ();
A segunda maneira: decorativo pool de threads
/ ** * ThreadPoolExecutor decorativo, o conteúdo do segmento pai passar MDC segmento infantil * @author yangyongjie * @date 2020/03/19 * @desc * / público classe MDCThreadPoolExecutor o estende a ThreadPoolExecutor { Privada estática final Logger Logger LoggerFactory.getLogger = ( . MDCThreadPoolExecutor classe ); pública MDCThreadPoolExecutor ( int corePoolSize, int maximumPoolSize, longo KeepAliveTime, TimeUnit Unit, BlockingQueue <o Runnable> WorkQueue, um ThreadFactory ThreadFactory, RejectedExecutionHandler Handler) { Super(CorePoolSize, maximumPoolSize, KeepAliveTime, Unit, WorkQueue, ThreadFactory, Handler); } @Override pública vazio Executar ( final do Runnable Runnable) { // obter conteúdo em MDC segmento pai, o método deve ser executado antes ou outra thread assíncrono de execução quando o valor do qual é possível MDC foi esvaziado, e desta vez vai retornar nulo final do Map <string, string> context = MDC.getCopyOfContextMap (); Super .Execute ( nova novo o Runnable () { @Override pública vazio run () { // o conteúdo MDC passado para o segmento segmento pai criança MDC.setContextMap (contexto); o try { //Realizando operação assíncrona Runnable.run (); } o finalmente { // conteúdo claro MDC MDC.clear (); } } }); } }
用 MDCThreadPoolExecutor 代替 ThreadPoolExecutor:
Privado estática final MDCThreadPoolExecutor MDCEXECUTORS = new new MDCThreadPoolExecutor (1,10,60, TimeUnit.SECONDS, nova novo um LinkedBlockingQueue <o Runnable> (600), nova novo CustomThreadFactory ( "mdcThreadPoolTest"), nova novo RejectedExecutionHandler () { @Override pública vazio rejectedExecution (R & lt o Runnable, executor do ThreadPoolExecutor) { // imprimir log, e reiniciar um fio de execução da tarefa rejeitado LOGGER.error ( "a tarefa: {}, rejeitou a partir de: {}" , r.toString (), executor.toString ()); // execução direta da tarefa for rejeitado, JVM de outro segmento de execução r.run (); } }); Logger.info ( "log segmento pai" ); MDCEXECUTORS.execute ( nova novo o Runnable () { @Override pública vazio run () { logger.info ( "log criança thread" ); } });
Dois, maneira ThreadLocal
ThreadLocal pode ser utilizado no mesmo segmento, de cross-classe, o método de comunicação de dados através de. Ele pode ser usado para o contexto transparente de transmissão mundial
1, não há nenhum caso de fio de criança
FIM