All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.opentelemetry.instrumentation.logback.mdc.v1_0.OpenTelemetryAppender Maven / Gradle / Ivy

There is a newer version: 2.8.0-alpha
Show newest version
/*
 * Copyright The OpenTelemetry Authors
 * SPDX-License-Identifier: Apache-2.0
 */

package io.opentelemetry.instrumentation.logback.mdc.v1_0;

import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.SPAN_ID;
import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.TRACE_FLAGS;
import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.TRACE_ID;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.LoggerContextVO;
import ch.qos.logback.classic.spi.LoggingEventVO;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import ch.qos.logback.core.spi.AppenderAttachable;
import ch.qos.logback.core.spi.AppenderAttachableImpl;
import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.logback.mdc.v1_0.internal.UnionMap;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class OpenTelemetryAppender extends UnsynchronizedAppenderBase
    implements AppenderAttachable {
  private boolean addBaggage;

  private final AppenderAttachableImpl aai = new AppenderAttachableImpl<>();

  /**
   * When set to true this will enable addition of all baggage entries to MDC. This can be done by
   * adding the following to the logback.xml config for this appender. {@code
   * true}
   *
   * @param addBaggage True if baggage should be added to MDC
   */
  public void setAddBaggage(boolean addBaggage) {
    this.addBaggage = addBaggage;
  }

  public ILoggingEvent wrapEvent(ILoggingEvent event) {
    Map eventContext = event.getMDCPropertyMap();
    if (eventContext != null && eventContext.containsKey(TRACE_ID)) {
      // Assume already instrumented event if traceId is present.
      return event;
    }

    Map contextData = new HashMap<>();
    Context context = Context.current();
    Span currentSpan = Span.fromContext(context);

    if (currentSpan.getSpanContext().isValid()) {
      SpanContext spanContext = currentSpan.getSpanContext();
      contextData.put(TRACE_ID, spanContext.getTraceId());
      contextData.put(SPAN_ID, spanContext.getSpanId());
      contextData.put(TRACE_FLAGS, spanContext.getTraceFlags().asHex());
    }

    if (addBaggage) {
      Baggage baggage = Baggage.fromContext(context);
      baggage.forEach(
          (key, value) ->
              contextData.put(
                  // prefix all baggage values to avoid clashes with existing context
                  "baggage." + key, value.getValue()));
    }

    if (eventContext == null) {
      eventContext = contextData;
    } else {
      eventContext = new UnionMap<>(eventContext, contextData);
    }
    Map eventContextMap = eventContext;
    LoggerContextVO oldVo = event.getLoggerContextVO();
    LoggerContextVO vo =
        oldVo != null
            ? new LoggerContextVO(oldVo.getName(), eventContextMap, oldVo.getBirthTime())
            : null;

    ILoggingEvent wrappedEvent =
        (ILoggingEvent)
            Proxy.newProxyInstance(
                ILoggingEvent.class.getClassLoader(),
                new Class[] {ILoggingEvent.class},
                (proxy, method, args) -> {
                  if ("getMDCPropertyMap".equals(method.getName())) {
                    return eventContextMap;
                  } else if ("getLoggerContextVO".equals(method.getName())) {
                    return vo;
                  }
                  try {
                    return method.invoke(event, args);
                  } catch (InvocationTargetException exception) {
                    throw exception.getCause();
                  }
                });
    // https://github.com/qos-ch/logback/blob/9e833ec858953a2296afdc3292f8542fc08f2a45/logback-classic/src/main/java/ch/qos/logback/classic/net/LoggingEventPreSerializationTransformer.java#L29
    // LoggingEventPreSerializationTransformer accepts only subclasses of LoggingEvent and
    // LoggingEventVO, here we transform our wrapped event into a LoggingEventVO
    return LoggingEventVO.build(wrappedEvent);
  }

  @Override
  protected void append(ILoggingEvent event) {
    aai.appendLoopOnAppenders(wrapEvent(event));
  }

  @Override
  public void addAppender(Appender appender) {
    aai.addAppender(appender);
  }

  @Override
  public Iterator> iteratorForAppenders() {
    return aai.iteratorForAppenders();
  }

  @Override
  public Appender getAppender(String name) {
    return aai.getAppender(name);
  }

  @Override
  public boolean isAttached(Appender appender) {
    return aai.isAttached(appender);
  }

  @Override
  public void detachAndStopAllAppenders() {
    aai.detachAndStopAllAppenders();
  }

  @Override
  public boolean detachAppender(Appender appender) {
    return aai.detachAppender(appender);
  }

  @Override
  public boolean detachAppender(String name) {
    return aai.detachAppender(name);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy