TLOG日志框架springboot项目集成与扩展

目录

前言

一、TLOG开源组件集成

1.maven引入

2. 日志文件logback-spring.xml修改

二、TLOG扩展开发自己的starter

1.项目层级

2.扩展点:1.打印请求url 2.打印响应日志

a.打印请求日志中的url实现RequestBodyAdvice 

b.打印响应日志实现ResponseBodyAdvice

 c.打印内容

三、多线程与kafka消息队列,方法中traceId的传递

1.线程池

2.kafka等消息队列



前言

随着微服务盛行,很多公司都把系统按照业务边界拆成了很多微服务,在排错查日志的时候。因为业务链路贯穿着很多微服务节点,导致定位某个请求的日志以及上下游业务的日志会变得有些困难,那么今天所介绍的这款开源工具一定是一个不错的选择


一、TLOG开源组件集成

  1.maven引入

      <dependency>
            <groupId>com.yomahub</groupId>
            <artifactId>tlog-all-spring-boot-starter</artifactId>
            <version>${tlog.version}</version>
        </dependency>

2. 日志文件logback-spring.xml修改

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!--设置系统日志目录-->
    <springProperty scope="context" name="moduleName" source="spring.application.name" defaultValue="tclAppLog"/>

    <property name="LOG_PATH" value="./logs" />
    <property name="LOG_NAME" value="${moduleName}" />

    <!-- 日志记录器,日期滚动记录 -->
    <appender name="FILEERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${LOG_PATH}/${LOG_NAME}/log_error.log</file>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 归档的日志文件的路径,例如今天是2013-12-21日志,当前写的日志文件路径为file节点指定,可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。
            而2013-12-21的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
            <fileNamePattern>${LOG_PATH}/${LOG_NAME}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 除按日志记录之外,还配置了日志文件不能超过2M,若超过2M,日志文件会以索引0开始,
            命名日志文件,例如log-error-2013-12-21.0.log -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 追加方式记录日志 -->
        <append>true</append>
        <!--这里替换成AspectLogbackEncoder-->
        <encoder class="com.tcl.log.core.AspectLogbackEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %X{tl} [%thread] %-5level %logger{50} - %msg[%A][%R]%n</pattern>
        </encoder>
        <!-- 此日志文件只记录info级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>error</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>


    <!-- 日志记录器,日期滚动记录 -->
    <appender name="FILEINFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 正在记录的日志文件的路径及文件名 -->
        <file>${LOG_PATH}/${LOG_NAME}/log_info.log</file>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 归档的日志文件的路径,例如今天是2013-12-21日志,当前写的日志文件路径为file节点指定,可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。
            而2013-12-21的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
            <fileNamePattern>${LOG_PATH}/${LOG_NAME}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 除按日志记录之外,还配置了日志文件不能超过2M,若超过2M,日志文件会以索引0开始,
            命名日志文件,例如log-error-2013-12-21.0.log -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 追加方式记录日志 -->
        <append>true</append>
        <!--这里替换成AspectLogbackEncoder-->
        <encoder class="com.tcl.log.core.AspectLogbackEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %X{tl} [%thread] %-5level %logger{50} - %msg[%A][%R]%n</pattern>
        </encoder>
        <!-- 此日志文件只记录info级别的 -->
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>info</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!--设置一个向上传递的appender,所有级别的日志都会输出-->
    <appender name="FILEALL" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_PATH}/${LOG_NAME}/log_all.log</file>
        <!-- 日志记录器的滚动策略,按日期,按大小记录 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 归档的日志文件的路径,例如今天是2013-12-21日志,当前写的日志文件路径为file节点指定,可以将此文件与file指定文件路径设置为不同路径,从而将当前日志文件或归档日志文件置不同的目录。
            而2013-12-21的日志文件在由fileNamePattern指定。%d{yyyy-MM-dd}指定日期格式,%i指定索引 -->
            <fileNamePattern>${LOG_PATH}/${LOG_NAME}/all/log-all-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!-- 除按日志记录之外,还配置了日志文件不能超过2M,若超过2M,日志文件会以索引0开始,
            命名日志文件,例如log-error-2013-12-21.0.log -->
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>10MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <!-- 追加方式记录日志 -->
        <append>true</append>
        <!--这里替换成AspectLogbackEncoder-->
        <encoder class="com.tcl.log.core.AspectLogbackEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %X{tl} [%thread] %-5level %logger{50} - %msg[%A][%R]%n</pattern>
        </encoder>
    </appender>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!--这里替换成AspectLogbackEncoder-->
        <encoder class="com.tcl.log.core.AspectLogbackEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %X{tl} [%thread] %-5level %logger{50} - %msg[%A][%R]%n</pattern>
        </encoder>
        <!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>debug</level>
        </filter>
    </appender>

    <logger name="org.springframework" level="${LOG_LEVEL:-info}" />
    <logger name="org.hibernate" level="${LOG_LEVEL:-info}" />
    <logger name="org.mybatis" level="${LOG_LEVEL:-info}"></logger>
    <logger name="org.apache" level="${LOG_LEVEL:-info}"></logger>

    <!-- 生产环境下,将此级别配置为适合的级别,以免日志文件太多或影响程序性能 -->
    <root level="${LOG_LEVEL:-info}">
        <appender-ref ref="FILEERROR" />
        <appender-ref ref="FILEINFO" />
        <appender-ref ref="FILEALL" />

        <!-- 生产环境将请stdout,testfile去掉 -->
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

二、TLOG扩展开发自己的starter

1.项目层级

2.扩展点:1.打印请求url 2.打印响应日志

a.打印请求日志中的url实现RequestBodyAdvice 


/**
 * 日志请求拦截
 * @author liangxi.zeng
 */
@ControllerAdvice
@Component
public class LogRequestBodyAdvice implements RequestBodyAdvice {

    private static final Logger log = LoggerFactory.getLogger(LogRequestBodyAdvice.class);

    @Autowired
    private LogProperties logProperties;

    @Autowired
    private LogContext logContext;

    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return logProperties.getResponse().isEnabled() && !StringUtils.isEmpty(TLogContext.getTraceId());
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        return httpInputMessage;
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        try {
            if(logProperties.getRequest().isEnabled()) {
                String url = logContext.getUrlTl().get();
                if (StringUtils.isEmpty(url)) {
                    url = LogUtil.getUrl(methodParameter);
                }
                if (!LogUtil.isIgnore(logProperties.getRequest().getIgnoreList(), url)) {
                    HttpHeaders headers = httpInputMessage.getHeaders();
                    if (headers != null) {
                        List<String> contentTypeList = headers.get(LogUtil.CONTENT_TYPE);
                        if (!CollectionUtils.isEmpty(contentTypeList) &&
                                !LogUtil.isUpload(contentTypeList.get(0))) {
                            log.info("requestBody参数:{}", JSON.toJSONString(body));
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("日志请求打印异常,不影响业务,吞并异常",e);
        }
        return body;
    }

    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return body;
    }
}

b.打印响应日志实现ResponseBodyAdvice

/**
 * 脱敏日志响应拦截
 * @author liangxi.zeng
 */
@ControllerAdvice
@Component
public class LogResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    private static final Logger log = LoggerFactory.getLogger(LogResponseBodyAdvice.class);

   @Autowired
   private LogProperties logProperties;

    @Autowired
    private LogContext logContext;

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return logProperties.getResponse().isEnabled() && !StringUtils.isEmpty(TLogContext.getTraceId());
    }

    /**
     * @param body
     * @param methodParameter
     * @param selectedContentType
     * @param selectedConverterType
     * @param request
     * @param response
     * @return
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {
        try {
            if(logProperties.getResponse().isEnabled()) {
                String url = logContext.getUrlTl().get();
                if (StringUtils.isEmpty(url)) {
                    url = LogUtil.getUrl(methodParameter);
                }
                if (!LogUtil.isIgnore(logProperties.getResponse().getIgnoreList(), url)) {
                    StringBuffer logBuffer = new StringBuffer("结束请求URL[{}]的调用,");
                    StopWatch stopWatch = logContext.getInvokeTimeTL().get();
                    if (Objects.nonNull(stopWatch)) {
                        stopWatch.stop();
                        logBuffer.append("耗时为:").append(stopWatch.getTime()).append("毫秒;");
                    }
                    if (LogUtil.isJson(selectedContentType.toString())) {
                        logBuffer.append("响应内容为:").append(JSON.toJSONString(body));
                    } else if (LogUtil.isUpload(selectedContentType.toString())) {
                        logBuffer.append("文件上传,无需打印上传");
                    } else {
                        logBuffer.append("响应内容为:").append(body);
                    }
                    log.info(logBuffer.toString(), url);
                }
            }
        } catch (Exception e) {
            log.error("日志响应打印异常,不影响业务,吞并异常",e);
        }
        return body;
    }


}

 c.打印内容

三、多线程与kafka消息队列,方法中traceId的传递

1.线程池

ExecutorService pool = Executors.newFixedThreadPool(5);
pool.submit(new TLogInheritableTask() {
    @Override
    public void runTask() {
      log.info("我是异步线程日志");
    }
});

2.kafka等消息队列

TLogMqWrapBean<BizBean> tLogMqWrap = new TLogMqWrapBean(bizBean);
mqClient.send(tLogMqWrap);

对于消费者端,你需要这么做:

//从mq里接受到tLogMqWrapBean
TLogMqConsumerProcessor.process(tLogMqWrapBean, new TLogMqRunner<BizBean>() {
    @Override
    public void mqConsume(BizBean o) {
	    //业务操作
    }
});

总结

TLog是一个轻量级的分布式日志标记追踪神器,10分钟即可接入,自动对日志打标签完成微服务的链路追踪。支持log4j,log4j2,logback三大日志框架,在我使用的时候,有些不满足业务需要,所以我自己在读了源码后进行了个性化定制,现在已经在项目中使用

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值