skywalking番外01 - 如何扩展%tid的logback占位符

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
应用实时监控服务-应用监控,每月50GB免费额度
简介: skywalking番外01 - 如何扩展%tid的logback占位符

skywalking番外01 - 如何扩展%tid的logback占位符

配置

<appender name="FILE"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <FileNamePattern>./logs/ruoyi-vue-pro-%d{yyyy-MM-dd_HH}.log</FileNamePattern>
            <MaxHistory>3</MaxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
                <pattern>%d{ISO8601} | %tid | %thread | %-5level | %msg%n</pattern>
            </layout>
        </encoder>
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>10MB</MaxFileSize>
        </triggeringPolicy>
    </appender>

这是skywalking使用%tid做占位符来展示TraceId的方式.那么究竟怎么实现的呢?

Layout

观察配置很容易发现在/appender/encoder/layout中class属性指向的那个类是skywalking提供的工具包中的类.

package org.apache.skywalking.apm.toolkit.log.logback.v1.x;
import ch.qos.logback.classic.PatternLayout;
public class TraceIdPatternLogbackLayout extends PatternLayout {
    public TraceIdPatternLogbackLayout() {
    }
    static {
        defaultConverterMap.put("tid", LogbackPatternConverter.class.getName());
    }
}

这个类很简单,看似什么都没做,就是继承了Logback的PatternLayout类,并在该类的static 变量defaultConverterMap放入了一个key为tid,一个value为Sting类型的className.

Converter

package org.apache.skywalking.apm.toolkit.log.logback.v1.x;
import ch.qos.logback.classic.pattern.ClassicConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
public class LogbackPatternConverter extends ClassicConverter {
    public LogbackPatternConverter() {
    }
    public String convert(ILoggingEvent iLoggingEvent) {
        return "TID: N/A";
    }
}

看起来这个Converter也很简单,就是继承了logback的ClassicConverter,并实现了converter方法,返回一个固定值!根本就不是动态的啊!

所以,运行过工程,且没有打探针包的朋友们会发现就是输出了TID: N/A.而有探针的情况下,会输出动态的TraceId.那么问题肯定在探针中了.

探针增强

skywalking中探针在做增强的时候,在源码中会有被增强类的全类名来匹配,所以找到了这样的代码:

package org.apache.skywalking.apm.toolkit.activation.log.logback.v1.x;
public class LogbackPatternConverterActivation extends ClassInstanceMethodsEnhancePluginDefine {
    public static final String INTERCEPT_CLASS = "org.apache.skywalking.apm.toolkit.activation.log.logback.v1.x.PrintTraceIdInterceptor";
    public static final String ENHANCE_CLASS = "org.apache.skywalking.apm.toolkit.log.logback.v1.x.LogbackPatternConverter";
    public static final String ENHANCE_METHOD = "convert";
    /**
     * @return the target class, which needs active.
     */
    @Override
    protected ClassMatch enhanceClass() {
        return byName(ENHANCE_CLASS);
    }
    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[] {
            new InstanceMethodsInterceptPoint() {
                @Override
                public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return named(ENHANCE_METHOD).and(takesArgumentWithType(0, "ch.qos.logback.classic.spi.ILoggingEvent"));
                }
                @Override
                public String getMethodsInterceptor() {
                    return INTERCEPT_CLASS;
                }
            }
        };
    }
}

删除掉多余的代码,可以看到,通过匹配ENHANCE_CLASS,以及ENHANCE_METHOD,然后返回一个增强过的类INTERCEPT_CLASS,即org.apache.skywalking.apm.toolkit.activation.log.logback.v1.x.PrintTraceIdInterceptor

package org.apache.skywalking.apm.toolkit.activation.log.logback.v1.x;
public class PrintTraceIdInterceptor implements InstanceMethodsAroundInterceptor {
    @Override
    public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
        Object ret) throws Throwable {
        if (!ContextManager.isActive()) {
            if (allArguments[0] instanceof EnhancedInstance) {
                String tid = (String) ((EnhancedInstance) allArguments[0]).getSkyWalkingDynamicField();
                if (tid != null) {
                    return "TID:" + tid;
                }
            }
        }
        return "TID:" + ContextManager.getGlobalTraceId();
    }
}

就是这个类去上下文管理器中去取来了全局流水号.

学以致用

那么对于Logback可能陌生的同学,看完这篇应该就大致知道怎么扩展一个logback的占位符了吧.

  1. 继承ClassicConverter, 在convert方法中写入实际需要给占位符替换的内容.
  2. 继承PatternLayout,在static的map中放入(占位符,转换类类名)映射对.
  3. 配置文件中的layout指定class为该Layout的全类名.另外,Encoder节点的class也要使用ch.qos.logback.core.encoder.LayoutWrappingEncoder.

再说一种其他的方式, 如果用过spring cloud sleuth做全链路流水号的朋友会发现他们使用的占位符是这样的:%X{X-B3-TraceId:-},%X{X-B3-SpanId:-}.这是一种%X{key}的形式,使用了logback的MDC进行的扩展.可以参考这篇文章:<logback扩展字段>.

举例

那么究竟实际上有什么使用场景呢,举个例子: 我们不知道这个API是哪个用户登陆后进行的操作.

第一种解决方案: 拦截servlet的filter,从session中获取用户ID,打一条日志. 实际查询时根据全链路流水号来查询用户号.或者拦截spring mvc也是同理,不过是filter级别更高.

第二种解决方案:拦截servlet后,将其用户ID放入ThreadLocal中,利用本篇学到的扩展logback占位符的方式,输出到每一行日志中.

  • 两种方式都行,各取所需.

其他日志框架的扩展

日志框架有很多,需要去官方看文档学习. 而我发现skywalking中基本有了我们常用的日志框架的扩展占位符的方式,是一个很好的例子.

skywalking/apm-snifer/apm-tookit-activation包下涵盖了对log4j-1.x/ log4j-2.x/ logback-1.x/ logging-common的扩展.(github墙高筑,这里的链接改成gitee中的链接了).

相关实践学习
通过轻量消息队列(原MNS)主题HTTP订阅+ARMS实现自定义数据多渠道告警
本场景将自定义告警信息同时分发至多个通知渠道的需求,例如短信、电子邮件及钉钉群组等。通过采用轻量消息队列(原 MNS)的主题模型的HTTP订阅方式,并结合应用实时监控服务提供的自定义集成能力,使得您能够以简便的配置方式实现上述多渠道同步通知的功能。
目录
相关文章
|
监控 Java Docker
从零开始,用Docker-compose打造SkyWalking、Elasticsearch和Spring Cloud的完美融合
从零开始,用Docker-compose打造SkyWalking、Elasticsearch和Spring Cloud的完美融合
2290 0
|
消息中间件 数据采集 JSON
iLogtail社区版使用入门 - iLogtail日志处理实战
本文将会详细介绍使用iLogtail社区版进行日志采集时,对于常见日志格式的处理实战。为了便于调测,以下样例默认将采集到的日志,经过处理后,输出到标准输出,开发者可以根据实际需求进行适当的调整。
3425 0
|
SQL druid Java
springboot +logback+阿里数据源(druid)打印sql日志以及简化日志输出方式
springboot +logback+阿里数据源(druid)打印sql日志以及简化日志输出方式
2274 0
|
Kubernetes Java Docker
Java程序在K8S容器部署CPU和Memory资源限制相关设置
背景 在k8s docker环境中执行Java程序,因为我们设置了cpu,memory的limit,所以Java程序执行时JVM的参数没有跟我们设置的参数关联,导致JVM感知到的cpu和memory是我们k8s的work node上的cpu和memory大小。
8891 0
|
消息中间件 Java Kafka
skywalking日志收集
skywalking日志收集
skywalking日志收集
|
11月前
|
安全 Java 测试技术
详解Spring Profiles:在Spring Boot中实现环境配置管理
详解Spring Profiles:在Spring Boot中实现环境配置管理
1023 10
SkyWalking 8.4.0版本中traceId的生成规则
SkyWalking 8.4.0版本中traceId的生成规则
2857 1
|
消息中间件 Java 关系型数据库
【二十】springboot整合ElasticSearch实战(万字篇)
【二十】springboot整合ElasticSearch实战(万字篇)
3052 47
|
运维 监控 前端开发
[SpringAop + Logback +MDC] 现网必备全链路日志追踪
[SpringAop + Logback +MDC] 现网必备全链路日志追踪
1725 1
|
Java API Maven
解决SLF4J和logback报错SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder“.
解决SLF4J和logback报错SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder“.