package org.stagemonitor.tracing.mdc;

import org.slf4j.MDC;
import org.stagemonitor.core.CorePlugin;
import org.stagemonitor.core.MeasurementSession;
import org.stagemonitor.core.Stagemonitor;
import org.stagemonitor.tracing.B3HeaderFormat;
import org.stagemonitor.tracing.TracingPlugin;
import org.stagemonitor.tracing.wrapper.SpanWrapper;
import org.stagemonitor.tracing.wrapper.StatelessSpanEventListener;

/**
 * This class adds the {@link MDC} properties requestId, application, host and instance.
 * <p>
 * If you are using logback or log4j, you can append this to your pattern to append the properties to each log entry:
 * <code>trace:[%X{traceId}] span:[%X{spanId}] A:[%X{application}] H:[%X{host}] I:[%X{instance}]</code>
 */
public class MDCSpanEventListener extends StatelessSpanEventListener {

	private final CorePlugin corePlugin;
	private final TracingPlugin tracingPlugin;

	public MDCSpanEventListener(CorePlugin corePlugin, TracingPlugin tracingPlugin) {
		this.corePlugin = corePlugin;
		this.tracingPlugin = tracingPlugin;
	}

	@Override
	public void onStart(SpanWrapper spanWrapper) {
		if (corePlugin.isStagemonitorActive()) {
			final MeasurementSession measurementSession = corePlugin.getMeasurementSession();
			if (measurementSession != null) {
				addToMdcIfNotNull("application", measurementSession.getApplicationName());
				addToMdcIfNotNull("host", measurementSession.getHostName());
				addToMdcIfNotNull("instance", measurementSession.getInstanceName());
			}

			// don't store the context in MDC if stagemonitor is not active
			// so that thread pools that get created on startup don't inherit the ids
			if (Stagemonitor.isStarted()) {
				tracingPlugin.getTracer().inject(spanWrapper.context(), B3HeaderFormat.INSTANCE, new B3HeaderFormat.B3InjectAdapter() {
					@Override
					public void setParentId(String value) {
						addToMdcIfNotNull("parentId", value);
					}

					@Override
					public void setSpanId(String value) {
						addToMdcIfNotNull("spanId", value);
					}

					@Override
					public void setTraceId(String value) {
						addToMdcIfNotNull("traceId", value);
					}
				});
			}
		}
	}

	@Override
	public void onFinish(SpanWrapper spanWrapper, String operationName, long durationNanos) {
		// the other keys are not span-scoped hence not removed here
		MDC.remove("traceId");
		MDC.remove("spanId");
		MDC.remove("parentId");
	}

	private void addToMdcIfNotNull(String key, String value) {
		if (value != null) {
			MDC.put(key, value);
		}
	}
}

package org.stagemonitor.tracing.mdc;

import org.slf4j.MDC;
import org.stagemonitor.core.CorePlugin;
import org.stagemonitor.core.MeasurementSession;
import org.stagemonitor.core.Stagemonitor;
import org.stagemonitor.tracing.B3HeaderFormat;
import org.stagemonitor.tracing.TracingPlugin;
import org.stagemonitor.tracing.wrapper.SpanWrapper;
import org.stagemonitor.tracing.wrapper.StatelessSpanEventListener;

/**
 * This class adds the {@link MDC} properties requestId, application, host and instance.
 * <p>
 * If you are using logback or log4j, you can append this to your pattern to append the properties to each log entry:
 * <code>trace:[%X{traceId}] span:[%X{spanId}] A:[%X{application}] H:[%X{host}] I:[%X{instance}]</code>
 */
public class MDCSpanEventListener extends StatelessSpanEventListener {

	private static final String TRACE_ID = "traceId";
	private static final String SPAN_ID = "spanId";
	private static final String APPLICATION = "application";
	private static final String HOST = "host";
	private static final String INSTANCE = "instance";

	private final CorePlugin corePlugin;
	private final TracingPlugin tracingPlugin;

	public MDCSpanEventListener(CorePlugin corePlugin, TracingPlugin tracingPlugin) {
		this.corePlugin = corePlugin;
		this.tracingPlugin = tracingPlugin;
	}

	@Override
	public void onStart(SpanWrapper spanWrapper) {
		if (corePlugin.isStagemonitorActive()) {
			final MeasurementSession measurementSession = corePlugin.getMeasurementSession();
			if (measurementSession != null) {
				addToMdcIfNotNull("application", measurementSession.getApplicationName());
				addToMdcIfNotNull("host", measurementSession.getHostName());
				addToMdcIfNotNull("instance", measurementSession.getInstanceName());
			}

			// don't store the context in MDC if stagemonitor is not active
			// so that thread pools that get created on startup don't inherit the ids
			if (Stagemonitor.isStarted()) {
				tracingPlugin.getTracer().inject(spanWrapper.context(), B3HeaderFormat.INSTANCE, new B3HeaderFormat.B3InjectAdapter() {
					@Override
					public void setParentId(String value) {
					}

					@Override
					public void setSpanId(String value) {
						addToMdcIfNotNull("spanId", value);
					}

					@Override
					public void setTraceId(String value) {
						addToMdcIfNotNull("traceId", value);
					}
				});
			}
		}
	}

	@Override
	public void onFinish(SpanWrapper spanWrapper, String operationName, long durationNanos) {
		MDC.remove(TRACE_ID);
		MDC.remove(SPAN_ID);
		MDC.remove(APPLICATION);
		MDC.remove(HOST);
		MDC.remove(INSTANCE);
	}

	private void addToMdcIfNotNull(String key, String value) {
		if (value != null) {
			MDC.put(key, value);
		}
	}
}

Remove all MDC entries onFinish