implementação logback de link distribuído logs do sistema de rastreamento

A necessidade de log de controle de ligação

I Radisson Jeter que log consulta seja em um ambiente de teste ou um ambiente de produção, é frequentemente utilizado como um desenvolvedor para ir, coisas para ver. A empresa apareça BUG, então como localizar rapidamente o que estamos à procura de registros e outros registos relacionados? Relacionados com os testes e pessoal de desenvolvimento de posicionamento de eficiência, que é muitas vezes baseada em palavras-chave de seu próprio log de impressão para consulta de log, mas na capacidade concorrente, ou processo de negócio longos registros não levar a uma cena coerente, muitas vezes você não pode passar uma vez, dois consulta de log registra para ver toda a linha de negócios. E mesmo assim apenas por ver log em tempo real para reproduzir, a fim de ver o problema. No entanto, em muitos casos, o ambiente de produção que lhe dará a chance de reproduzir, enquanto micro-serviços vigorosamente agora distribuídos, uma vez invocado outros serviços fora do BUG não vai re-verificar os registros para encontrar palavras-chave?
A questão é, por que tantas pessoas não se preocupam em otimizar? Nós obter o registo de impressão Jingdong como compras, como, todos os logs se apossar do número de ordem para este pedido são colocados no número de ordem, o plano sempre que os registros de resposta são colocados em número de ordem, ou lançar uma exceção, desde que o número de ordem sobre o retorno log tape para a frente. Em seguida, procure o log verificar diretamente o número de ordem não pode ele fazer? Testadores segurando uma ID de registro para o desenvolvedor que também precisa de reproduzir? 10 vezes mais eficiente lá? Com o programa perfeito Vamos trabalhar duro!

novo Filtro

O primeiro passo, claro, é para interceptar todos os logs de solicitações HTTP para cada solicitação são gerados além de uma identificação única.

dependência maven introduzido

<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-api</artifactId>
	<version>1.7.13</version>
	<scope>provided</scope>
</dependency>
<dependency>
	<groupId>ch.qos.logback</groupId>
	<artifactId>logback-classic</artifactId>
	<scope>provided</scope>
	<version>1.1.7</version>
</dependency>

New MdcFilter Filtro herdou MDCInsertingServletFilter

Logback vem pedido ch.qos.logback.classic.helpers.MDCInsertingServletFilter HTTP pode ser hostname, pedido URI, as informações do usuário-agente carregado MDC

package com.test.trace.web.filter;

import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.MDC;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.test.trace.support.AbstractMyThreadContext;
import com.test.trace.support.AbstractUUIDShort;
import ch.qos.logback.classic.helpers.MDCInsertingServletFilter;

public class MdcFilter extends MDCInsertingServletFilter {
    private static final String HEARDER_FOR_TRACE_ID = "X-TRACE-ID";
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        if (!(request instanceof HttpServletRequest)) {
            return;
        }
        /**如前端传递了唯一标识ID 拿出来直接用 根据业务这段也可以删除 然后去掉if判断*/
        String traceId =
                ((HttpServletRequest) request).getHeader(HEARDER_FOR_TRACE_ID);
        if (StringUtils.isEmpty(traceId)) {
            traceId = AbstractUUIDShort.generate();
        }
        AbstractMyThreadContext.setTraceId(traceId);
        MDC.put(AbstractMyThreadContext.MDC_TRACE_ID,traceId);
        try {
            //从新调动父类的doFilter方法
            super.doFilter(request, response, chain);
        } finally {
            //资源回收
            MDC.remove(AbstractMyThreadContext.MDC_TRACE_ID);
            AbstractMyThreadContext.removeTraceId();
        }
    }
}
package com.test.trace.web.filter;

import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@ConditionalOnClass(name = "org.springframework.web.servlet.DispatcherServlet")
public class MdcFilterAutoConfigure {

    @Bean
    @ConditionalOnMissingBean(MdcFilter.class)
    public MdcFilter mdcFilter() {
        return new MdcFilter();
    }
}

Primavera anotação pequena sala de aula começou amigos

  • classe @Configuration é representado classe Primavera organizado
  • @ConditionalOnClass determinar se existe uma classe no caminho da classe está presente em conjunto com dois org.springframework.web.servlet.DispatcherServlet quando carregado no caminho de classe MdcFilterAutoConfigure
  • @Bean gerar um feijão, e, em seguida, para a gestão de contentores da mola. Use @Configuration e combinação @Bean. @Configuration entendida como uma mola quando o xml dentro <beans> tag, @ Feijão entendida como uma mola quando o interior xml <bean> tag
  • Retorna verdadeiro se @ConditionalOnMissingBean Bena especificado não existe, isto é, se usado em conjunto embarcação @Bean MdcFilter ausente geramos uma mola para MdcFilter

ferramentas de geração de ligação ID Entrar

package com.test.trace.support;

import java.util.Base64;
import java.util.UUID;

public abstract class AbstractUUIDShort {

    /**生成唯一ID*/
    public static String generate() {
        UUID uuid = UUID.randomUUID();
        return compressedUUID(uuid);
    }

    protected static String compressedUUID(UUID uuid) {
        byte[] byUuid = new byte[16];
        long least = uuid.getLeastSignificantBits();
        long most = uuid.getMostSignificantBits();
        long2bytes(most, byUuid, 0);
        long2bytes(least, byUuid, 8);
        return Base64.getEncoder().encodeToString(byUuid);
    }

    protected static void long2bytes(long value, byte[] bytes, int offset) {
        for (int i = 7; i > -1; i--) {
            bytes[offset++] = (byte) ((value >> 8 * i) & 0xFF);
        }
    }
}

Log ligação ID é armazenado, e ferramentas de destruição

Aplicado principalmente para a interface quando o dubbo chamada fora traceid e feitos para garantir a rosca criança pode obter traceid, na verdade, não envolve uma chamada de interface entre sistemas desta classe não está mais no necessário.

package com.test.trace.support;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;

public abstract class AbstractMyThreadContext {

    private static final Logger log = LoggerFactory.getLogger(AbstractMyThreadContext.class);
    /**
     * logback模板对应的变量
     */
    public final static String MDC_TRACE_ID = "traceId";

    public static final String TRACE_ID = "com.test.trace.MyThreadContext_TRACE_ID_KEY";
    //确保子线程能够继承父线程的数据
    private static final ThreadLocal<Map<Object, Object>> RESOURCES =
            new InheritableThreadLocalMap<Map<Object, Object>>();


    protected AbstractMyThreadContext() {}


    public static Map<Object, Object> getResources() {
        return RESOURCES != null ? new HashMap<Object, Object>(RESOURCES.get()) : null;
    }

    public static void setResources(Map<Object, Object> newResources) {
        if (newResources == null || newResources.isEmpty()) {
            return;
        }
        RESOURCES.get().clear();
        RESOURCES.get().putAll(newResources);
    }


    private static Object getValue(Object key) {
        return RESOURCES.get().get(key);
    }


    public static Object get(Object key) {
        if (log.isTraceEnabled()) {
            log.trace("get() - in thread [{}]",Thread.currentThread().getName());
        }

        Object value = getValue(key);
        if ((value != null) && log.isTraceEnabled()) {
            log.trace("Retrieved value of type [{}]  for key [{}] bound to thread [{}]",
                    value.getClass().getName(), key, Thread.currentThread().getName());
        }
        return value;
    }

    public static void put(Object key, Object value) {
        if (key == null) {
            throw new IllegalArgumentException("key cannot be null");
        }

        if (value == null) {
            remove(key);
            return;
        }

        RESOURCES.get().put(key, value);

        if (log.isTraceEnabled()) {
            log.trace("Bound value of type [{}]  for key [{}]  to thread [{}]",
                    value.getClass().getName(), key, Thread.currentThread().getName());
        }
    }

    public static Object remove(Object key) {
        Object value = RESOURCES.get().remove(key);

        if ((value != null) && log.isTraceEnabled()) {
            log.trace("Retrieved value of type [{}]  for key [{}] from thread [{}]",
                    value.getClass().getName(), key, Thread.currentThread().getName());
        }

        return value;
    }

    public static void remove() {
        RESOURCES.remove();
    }

    //从线程局部变量中获取TraceId
    public static String getTraceId() {
        return (String) get(TRACE_ID);
    }

    //将TraceId摄入线程局部变量中
    public static void setTraceId(String xid) {
        put(TRACE_ID, xid);
    }

    //清除线程局部变量中的TraceId
    public static void removeTraceId() {
        remove(TRACE_ID);
    }

    private static final class InheritableThreadLocalMap<T extends Map<Object, Object>>
            extends InheritableThreadLocal<Map<Object, Object>> {
        @Override
        protected Map<Object, Object> initialValue() {
            return new HashMap<Object, Object>(1 << 4);
        }

        @SuppressWarnings({"unchecked"})
        @Override
        protected Map<Object, Object> childValue(Map<Object, Object> parentValue) {
            if (parentValue != null) {
                return (Map<Object, Object>) ((HashMap<Object, Object>) parentValue).clone();
            } else {
                return null;
            }
        }
    }
}

logback.xml registrar a saída set formato

<Padrão> atributo Formato configurado :( afixado [% X {traceid}] é a identificação de segmento de configuração I)
[-5level%] [% contextName puder]% {D MM-dd-AAAA HH: MM: ss.SSS } [% rosca] [% X {} req.remoteHost] [% X {} req.requestURI] [% X {} traceid]% registador -% msg% n

Aqui o link para todo o registro para localizar metade concluída já pode fazer tudo solicitação HTTP deu mais traceid, mas se nós chamamos dubbo interfaces MdcFilter não pode ser interceptado, resultando em logback não encontrar traceid, por isso precisamos juntamente com um com.alibaba.dubbo.rpc.Filter manter a porta para os consumidores e prestadores de serviços Dubbo, os consumidores precisam para dar a sua traceid, prestador de serviços recebe uma solicitação para ingestão traceid.
Ver meu blog anterior: o filtro Filtro de Dubbo (interceptor) usando
preguiçoso eu não vou fazer muito para descrever arrependido.

Quando você concluir esta etapa depois de todo o sistema de ligação log basicamente 90% completa, se você trabalhar duro o suficiente, mas para chegar até EFK eficiência logs de consulta diretamente pedindo a lua

  • Prazo EFK análise

troncos de transmissão em tempo real coletados por log de acesso filebeat nginx ▷ filebeat coletados por Kibana para ElasticSearch conjunto ▷ exibir o log.

No entanto, os alunos serão convidados a pensar, tempo para executar a tarefa de ser dada a velha senhora não é o mesmo dia em que o cão dentro log traceid é ah nulo.
Claramente o método acima é ainda só se um pedido de tempo externa lançou a sua própria tarefa Zezheng isso?
Esse problema geralmente ocorre quando o AOP entrevista à tona.
AOP tem uma necessidade de saber os estudantes podem entrar Portal: função aop fácil de três maneiras

New interceptor

package com.test.trace.interceptor;

import com.test.trace.support.AbstractMyThreadContext;
import com.test.trace.support.AbstractUUIDShort;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

/**
 * @description logback全局日志交易id拦截器
 */
@Order(Ordered.HIGHEST_PRECEDENCE)
public class MdcTraceIdMethodInteceptor implements MethodInterceptor {
    protected final Logger log = LoggerFactory.getLogger(this.getClass());

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        if (MDC.get(AbstractMyThreadContext.MDC_TRACE_ID) != null) {
            return invocation.proceed();
        }
        try {
            String traceId = AbstractUUIDShort.generate();
            AbstractMyThreadContext.setTraceId(traceId);
            MDC.put(AbstractMyThreadContext.MDC_TRACE_ID,traceId);
            return invocation.proceed();
        } catch (Throwable e) {
            log.warn("MdcTraceIdMethodInteceptor error", e.getMessage());
            throw e;
        } finally {
            MDC.remove(AbstractMyThreadContext.MDC_TRACE_ID);
            AbstractMyThreadContext.removeTraceId();
        }
    }
}
package com.test.trace.interceptor;

import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @descriptionlogback全局日志交易id拦截器配置 <br/>
 *  主要针对例如 定时任务 MQ等 非HTTP发起的请求没有traceId 配置需要拦截过滤的地址使用 <br/>
 *  配置demo :<br/>
 *  log.traceId.pointcutExpression=execution(* com.test.service.rabbitmq..*.*(..)) || execution(* com.test.job..*.*(..))
 */
@Configuration
@ConditionalOnProperty(name = "log.traceId.pointcutExpression")
public class MdcTraceIdConfiguration {
    @Value("${log.traceId.pointcutExpression}")
    private String POINTCUT_EXPRESSION;

    @Bean("MdcTraceIdMethodInteceptor")
    public MdcTraceIdMethodInteceptor mdcTraceIdMethodInteceptor() {
        return new MdcTraceIdMethodInteceptor();
    }

    @Bean("MdcTraceIdAdvisor")
    public Advisor MdcTraceIdAdvisor(MdcTraceIdMethodInteceptor mdcTraceIdMethodInteceptor) {
        AspectJExpressionPointcut cut = new AspectJExpressionPointcut();
        cut.setExpression(POINTCUT_EXPRESSION);
        Advisor advisor = new DefaultPointcutAdvisor(cut, mdcTraceIdMethodInteceptor);
        return advisor;
    }
}

Começando pequenas notas em sala de aula e amigos: o log de erro não pode ser encontrado, principalmente preguiçoso, e bater bem

  • Quando @ConditionalOnProperty é verdadeiro quando você pode ler o nome da configuração especificada, se tiver configurado o valor da propriedade, e que seria melhor do que o valor especificado como o valor da configuração é verdade.

Aqui é completamente caminho acabado vai escorregar através do bit configuração das redes, atingiu um pacote jar cada item depende apenas dele. Se você precisar configurar cada log.traceId.pointcutExpression de obter.

Publicado 18 artigos originais · Louvor obteve 45 · vista 110 000 +

Acho que você gosta

Origin blog.csdn.net/zhibo_lv/article/details/105093808
Recomendado
Clasificación