GuanceCloud / dd-trace-java

Datadog APM client for Java
https://docs.datadoghq.com/tracing/languages/java
Apache License 2.0
9 stars 3 forks source link

log patten #13

Closed songlonqi-java closed 1 year ago

songlonqi-java commented 1 year ago

在日志中增加 dd 或者其他 agent 链路信息

在 agent 加载时候,修改用户的日志输出,增加 “traceid” 信息

可以在每条日志中修改patten 也可以直接修改log 对象的patten。

这只是大纲,做法可以在楼下贴出。

lrwh commented 1 year ago

目前是通过手动修改的方式插入 traceId,这种方式不利于大规模应用部署。

主要是需要对 logback 或者 log4j 配置文件进行动态 pattern 调整,可以利用 java 字节码技术进行调整。

lrwh commented 1 year ago

otel 通过 log-exporter 调整输出的方式来插入 traceId,同时要维护一套日志输出机制,投入及维护成本较高,不推荐采用类似的方式实现。

image

lrwh commented 1 year ago

以logback为基础,通过分析 logback 的执行流程和源码,使用Instrumentation 尝试了四种方案,均无效果。

方式一(logback-core:PatternLayoutBase)

ch.qos.logback.core.pattern.PatternLayoutBase 是一个abstract 类,其中有一个属性为 pattern,通过对 public void setPattern(String pattern)pattern进行动态调整。

package ch.qos.logback.core.pattern;
...
public abstract class PatternLayoutBase<E> extends LayoutBase<E> {
    static final int INTIAL_STRING_BUILDER_SIZE = 256;
    Converter<E> head;
    String pattern;

    public void setPattern(String pattern) {
        this.pattern = pattern;
    }
......

探针实现:

......

@Override
  public String hierarchyMarkerType() {
    return "ch.qos.logback.core.pattern.PatternLayoutBase";
  }
  @Override
  public void adviceTransformations(AdviceTransformation transformation) {
    transformation.applyAdvice(
        isMethod()
            .and(isPublic())
            .and(named("setPattern")),
        PatternLayoutBaseInstrumentation.class.getName() + "$StartAdvice");
  }

  public static class StartAdvice{
    @Advice.OnMethodEnter
    public static void onEnter() {
      System.out.println("do StartAdvice,pattern:");
    }
  }
......

方式二(logback-core:PatternLayoutEncoderBase)

ch.qos.logback.core.pattern.PatternLayoutEncoderBase 也可以初始化pattern的值,主要通过public void setProperty(String name, String value)函数。

PatternLayoutEncoderBase部分源码

package ch.qos.logback.core.pattern;

import ch.qos.logback.core.Layout;
import ch.qos.logback.core.encoder.LayoutWrappingEncoder;

public class PatternLayoutEncoderBase<E> extends LayoutWrappingEncoder<E> {
    String pattern;
    protected boolean outputPatternAsHeader = false;

    public PatternLayoutEncoderBase() {
    }

    public String getPattern() {
        return this.pattern;
    }

    public void setPattern(String pattern) {
        this.pattern = pattern;
    }
    ......

探针实现;

 @Override
  public String hierarchyMarkerType() {
    return "ch.qos.logback.core.pattern.PatternLayoutEncoderBase";
  }

  @Override
  public ElementMatcher<TypeDescription> hierarchyMatcher() {
    return extendsClass(named(hierarchyMarkerType()));
  }

  @Override
  public void adviceTransformations(AdviceTransformation transformation) {
    transformation.applyAdvice(
        isMethod()
            .and(isPublic())
            .and(named("setPattern")),
        PatternLayoutEncoderBaseInstrumentation.class.getName() + "$StartAdvice");
  }

  public static class StartAdvice{
    @Advice.OnMethodEnter
    public static void onEnter() {
      System.out.println("do PatternLayoutEncoderBase,pattern:");
    }
  }

方式三(logback-core:PropertySetter)

PropertySetter(ch.qos.logback.core.joran.util.PropertySetter) 是用来初始化xml 对象属性(值),通过public void setProperty(String name, String value) 来修改对象pattern值。

image

探针实现;

......
@Override
  public String instrumentedType() {
    return "ch.qos.logback.core.joran.util.PropertySetter";
  }

  @Override
  public void adviceTransformations(AdviceTransformation transformation) {
    System.out.println(" PropertySetterInstrumentation  transformation");
    transformation.applyAdvice(
        isMethod()
            .and(isPublic())
            .and(named("setProperty"))
            .and(takesArguments(2)),
        PropertySetterInstrumentation.class.getName() + "$PropertySetterAdvice");
  }

  public static class PropertySetterAdvice{
    @Advice.OnMethodEnter
    public static void onEnter() {
      System.out.println("do PropertySetterAdvice,pattern:");
    }
  }
......

方式四(logback-classic:PatternLayoutEncoder)

ch.qos.logback.classic.encoder.PatternLayoutEncoder 为logback 默认的编码器,通过start函数可以对PatternLayoutEncoder 进行对象初始化,初始化时,同时赋值 pattern

package ch.qos.logback.classic.encoder;

import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.pattern.PatternLayoutEncoderBase;

public class PatternLayoutEncoder extends PatternLayoutEncoderBase<ILoggingEvent> {
    public PatternLayoutEncoder() {
    }

    public void start() {
        PatternLayout patternLayout = new PatternLayout();
        patternLayout.setContext(this.context);
        patternLayout.setPattern(this.getPattern());
        patternLayout.setOutputPatternAsHeader(this.outputPatternAsHeader);
        patternLayout.start();
        this.layout = patternLayout;
        super.start();
    }
}

探针实现


...

@Override
  public String hierarchyMarkerType() {
    return "ch.qos.logback.classic.encoder.PatternLayoutEncoder";
  }

  @Override
  public ElementMatcher<TypeDescription> hierarchyMatcher() {
    return named(hierarchyMarkerType());
  }

  @Override
  public void adviceTransformations(AdviceTransformation transformation) {
    transformation.applyAdvice(isConstructor(), getClass().getName() + "$PatternLayoutEncoderConstructor");

    transformation.applyAdvice(
        isMethod()
            .and(isPublic())
            .and(named("start"))
            .and(takesArguments(0)),
        LogbackPatternLayoutEncoderInstrumentation.class.getName() + "$StartAdvice");
  }

  public static class PatternLayoutEncoderConstructor{
    @Advice.OnMethodExit
    public static void exit() {
      System.out.println("do $PatternLayoutEncoderConstructor,OnMethodExit:");
    }
  }
  public static class StartAdvice{
    @Advice.OnMethodEnter
    public static void onEnter() {
      System.out.println("do StartAdvice,pattern:");
    }
  }
...

以上四种探针测试无效(没有进入对应的探针方法)。

仍需要对logback进行探究,以找到可以对 pattern 进行调整的最佳方式。

lrwh commented 1 year ago

log4j2 已实现 pattern 调整 https://github.com/GuanceCloud/dd-trace-java/commit/89adbac547e745fa7bce5ef12c5dfce3333ea38c

默认 pattern = (%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger - [%method,%line] %X{dd.service} %X{dd.trace_id} %X{dd.span_id} - %msg%n)

songlonqi-java commented 1 year ago

尝试几种方式 没有进入探针位置

  1. ch.qos.logback.classic.LoggerContext

@AutoService(Instrumenter.class) public final class LoggerContextInstrumentation extends Instrumenter.Tracing implements Instrumenter.ForSingleType{

public LoggerContextInstrumentation() { super("logback-1"); }

@Override public String instrumentedType() { return "ch.qos.logback.classic.LoggerContext"; }

@Override public void adviceTransformations(AdviceTransformation transformation) { transformation.applyAdvice( isMethod() .and(named("getLogger")) .and(takesArguments(1)), getClass().getName()+"$EncodeAdvice1" ); }

public static final class EncodeAdvice1 { @Advice.OnMethodExit(onThrowable = Throwable.class,suppress = Throwable.class) public static void getLogger( @Advice.Return final Logger logger ){ System.out.println("------------------into -------------- EncodeAdvice"); logger.info("------------------"); System.out.println("----------------out ------------------------"); } } }


2.  ch.qos.logback.classic.encoder.PatternLayoutEncoder
```java
@AutoService(Instrumenter.class)
public class EncodeInstrumentation extends Instrumenter.Tracing
    implements Instrumenter.ForTypeHierarchy{

  public EncodeInstrumentation() {super("logback");}

  @Override
  public String hierarchyMarkerType() {
    return "ch.qos.logback.classic.encoder.PatternLayoutEncoder";
  }

  @Override
  public ElementMatcher<TypeDescription> hierarchyMatcher() {
    return named(hierarchyMarkerType());
  }

  @Override
  public void adviceTransformations(AdviceTransformation transformation) {
    transformation.applyAdvice(
        isMethod().
            and(named("start")).and(takesArguments(0)),
        EncodeInstrumentation.class.getName()+"$EncodeAdvice"
    );
  }

  public static class EncodeAdvice {
    @Advice.OnMethodExit(suppress = Throwable.class)
    public static void onExit(
        @Advice.This PatternLayoutEncoder encoder
    ){
      System.out.println("------------------into -------------- EncodeAdvice");
      System.out.println(encoder.getPattern());
      System.out.println(encoder.getLayout().getClass().getName());
      System.out.println("----------------out ------------------------");
    }
  }
}
lrwh commented 1 year ago

logback 框架采用多种方式暂时无法提供支持,后续持续跟进,先关掉issue