logback aplicación de vínculos distribuidos registros del sistema de seguimiento

La necesidad de registrar el seguimiento de enlace

Me Radisson Jeter quien registro de consultas ya sea en un entorno de prueba o un entorno de producción, se utiliza a menudo como un desarrollador para ir, cosas que ver. Un negocio aparece ERROR, a continuación, busque la rapidez con lo que estamos buscando para los registros y otros registros relacionados? Relacionados con las pruebas y el personal de desarrollo de posicionamiento de la eficiencia, se basa a menudo en las palabras clave de su propio registro de impresión a consulta del registro, sino en la capacidad concurrente, o de procesos de negocio troncos largos no conducen a una escena coherente menudo no se puede pasar una vez, dos consultas de registro consigna para ver toda la línea de negocio. E incluso entonces sólo por ver a tiempo real registro de reproducir, con el fin de ver el problema. Sin embargo, en muchos casos el entorno de producción que le dará la oportunidad de reproducirse, mientras vigorosamente ahora distribuidos micro-servicios, una vez que invocan otros servicios fuera del ERROR no se va a volver a comprobar los registros para encontrar palabras clave?
La pregunta es, ¿por qué tantas personas no se molestan en Optimizar? Obtenemos el registro de impresión Jingdong como ir de compras, como, todos los registros de hacerse con el número de orden para esta solicitud se ponen en el número de pedido, el fondo cada registros de respuesta de tiempo se ponen en número de pedido, o una excepción, siempre y cuando el número de orden en la declaración de registro de cinta al frente. A continuación, busque el registro de comprobar directamente el número de orden no puede hacer? Probadores alberga un ID de registro para el desarrollador que también necesitan para reproducirse? 10 veces más eficiente que hay? Con programa perfecto Vamos a metan de lleno!

nuevo filtro

El primer paso, por supuesto, es interceptar todos los registros de solicitud HTTP para cada solicitud se generan además de una identificación única.

dependencia maven introducido

<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>

Nueva MdcFilter filtro heredado MDCInsertingServletFilter

Logback viene solicitud ch.qos.logback.classic.helpers.MDCInsertingServletFilter HTTP puede ser el nombre de host, petición URI, información de agente de usuario cargado 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();
    }
}

anotación de la primavera aula pequeña comenzó amigos

  • clase @Configuration está representado clase muelle dispuesto
  • @ConditionalOnClass determinar si existe una clase en la ruta de clase está presente junto con dos org.springframework.web.servlet.DispatcherServlet cuando se carga en la ruta de clase MdcFilterAutoConfigure
  • @Bean generar un Bean, y luego a la gestión de contenedores de primavera. El uso y la combinación @Configuration @Bean. @Configuration entenderse como un muelle cuando el xml dentro <beans> tag, @ haba de entenderse como un resorte el XML dentro de la etiqueta <bean> cuando
  • Devuelve VERDADERO si @ConditionalOnMissingBean Bena especificado no existe, es decir, si se utiliza en conjunción recipiente @Bean MdcFilter ausentes generamos un resorte para MdcFilter

Link ID herramientas de generación de registro

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

Acceder acoplamiento de la identificación se almacena, y las herramientas de destrucción

Aplicado principalmente a la interfaz cuando el dubbo llamada a cabo traceid y hecho para asegurar el hilo hijo puede obtener traceid, de hecho, y no represente una llamada interfaz entre sistemas de esta clase ya no está en necesario.

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 set log formato de salida

<Patrón> Formato atributo configurado :( publicado [% X {traceid}] es el ID del hilo de la configuración I)
[-5level%] [% contextName puede]% {D el MM-DD-AAAA HH: mm: SS.sss } [% hilo] [% X {req.remoteHost}] [% X {req.requestURI}] [% X {traceid}]% logger -% msg% n

Aquí el enlace a todo el registro para realizar un seguimiento medio completado ya se puede hacer todo petición HTTP dio más traceid, pero si llamamos dubbo las interfaces, MdcFilter no puede ser interceptada, lo que resulta en logback no encontrar traceid, por lo que necesitamos junto con un com.alibaba.dubbo.rpc.Filter mantener la puerta para los consumidores y los proveedores de servicios DUBBO, los consumidores tienen que dar su traceid, proveedor de servicios recibe una solicitud de admisión traceid.
Ver mi blog anterior: el filtro Filtro de Dubbo (interceptor) usando
I perezoso no será demasiado para describir lo siento.

Cuando haya completado este paso después de todo el sistema de registro de enlace, básicamente, el 90% completa si trabaja lo suficientemente duro, pero al levantarse EFK eficiencia registros de consultas preguntar directamente por la luna

  • análisis EFK Term

registros de transmisión en tiempo real obtenida por filebeat nginx registro de acceso ▷ filebeat recogieron por Kibana a elasticsearch clúster ▷ mostrar el registro.

Sin embargo, se les pedirá a los estudiantes a pensar, tiempo para realizar la tarea de ser dado la anciana no es el mismo día que el perro dentro del registro traceid se ah nula.
Claramente el método anterior está aún lejos de conseguir una solicitud de sincronización externa lanzó su propia tarea Zezheng ella?
Este problema ocurre a menudo cuando el AOP entrevista a la superficie.
AOP tiene una necesidad de saber los estudiantes pueden entrar Portal: función AOP fácil de tres maneras

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

A partir de pequeñas notas de clase y amigos: el registro de errores no se puede encontrar, sobre todo perezoso, y batir bien

  • Cuando @ConditionalOnProperty es cierto cuando se puede leer el nombre de la configuración especificada, si ha configurado el valor de la propiedad, y que sería mejor que el valor especificado como el valor de la configuración es cierto.

Aquí es completamente acabada ruta se deslice a través del bit de configuración de la red, golpeó un paquete frasco de cada elemento sólo depende de él. Si necesita configurar cada log.traceId.pointcutExpression de conseguir.

Publicado 18 artículos originales · ganado elogios 45 · vistas 110 000 +

Supongo que te gusta

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