1818 */
1919package co .elastic .otel .logging ;
2020
21+ import static java .util .Collections .emptyList ;
22+
23+ import io .opentelemetry .exporter .logging .LoggingSpanExporter ;
24+ import io .opentelemetry .sdk .autoconfigure .spi .ConfigProperties ;
25+ import io .opentelemetry .sdk .common .CompletableResultCode ;
26+ import io .opentelemetry .sdk .trace .SdkTracerProviderBuilder ;
27+ import io .opentelemetry .sdk .trace .data .SpanData ;
28+ import io .opentelemetry .sdk .trace .export .SimpleSpanProcessor ;
29+ import io .opentelemetry .sdk .trace .export .SpanExporter ;
30+ import java .util .Collection ;
31+ import java .util .concurrent .atomic .AtomicBoolean ;
2132import org .apache .logging .log4j .Level ;
2233import org .apache .logging .log4j .core .config .Configurator ;
2334import org .apache .logging .log4j .core .config .builder .api .ConfigurationBuilder ;
2637
2738public class AgentLog {
2839
40+ /** Upstream instrumentation debug boolean option */
41+ public static final String OTEL_JAVAAGENT_DEBUG = "otel.javaagent.debug" ;
42+
2943 private static final String PATTERN = "%d{DEFAULT} [%t] %-5level %logger{36} - %msg{nolookups}%n" ;
3044
31- // logger is an empty string
45+ /** root logger is an empty string */
3246 private static final String ROOT_LOGGER_NAME = "" ;
3347
48+ /**
49+ * debug span logging exporter that can be controlled at runtime, only used when logging span
50+ * exporter has not been explicitly configured.
51+ */
52+ private static final DebugLogSpanExporter debugLogSpanExporter =
53+ new DebugLogSpanExporter (LoggingSpanExporter .create ());
54+
3455 private AgentLog () {}
3556
3657 public static void init () {
@@ -47,6 +68,22 @@ public static void init() {
4768 Configurator .initialize (conf .build (false ));
4869 }
4970
71+ public static void addSpanLoggingIfRequired (
72+ SdkTracerProviderBuilder providerBuilder , ConfigProperties config ) {
73+
74+ boolean otelDebug = config .getBoolean (OTEL_JAVAAGENT_DEBUG , false );
75+
76+ // Replicate behavior of upstream agent: span logging exporter is automatically added
77+ // when not already present when debugging.
78+ // When logging exporter has been explicitly configured, spans logging will be done by the
79+ // explicitly configured logging exporter instance
80+ boolean loggingExporterNotAlreadyConfigured =
81+ !config .getList ("otel.traces.exporter" , emptyList ()).contains ("logging" );
82+ if (otelDebug && loggingExporterNotAlreadyConfigured ) {
83+ providerBuilder .addSpanProcessor (SimpleSpanProcessor .create (debugLogSpanExporter ));
84+ }
85+ }
86+
5087 public static void setLevel (String level ) {
5188 switch (level ) {
5289 case "trace" :
@@ -87,11 +124,46 @@ public static void setLevel(Level level) {
87124
88125 Configurator .setAllLevels (ROOT_LOGGER_NAME , level );
89126
90- // when debugging we should avoid very chatty http client debug messages
127+ boolean isDebug = level .intLevel () >= Level .DEBUG .intLevel ();
128+
129+ // When debugging, we should avoid very chatty http client debug messages
91130 // this behavior is replicated from the upstream distribution.
92- if (level . intLevel () >= Level . DEBUG . intLevel () ) {
131+ if (isDebug ) {
93132 Configurator .setLevel ("okhttp3.internal.http2" , Level .INFO );
94133 Configurator .setLevel ("okhttp3.internal.concurrent.TaskRunner" , Level .INFO );
95134 }
135+
136+ // when debugging the upstream otel agent configures an extra debug exporter
137+ debugLogSpanExporter .setEnabled (isDebug );
138+ }
139+
140+ private static class DebugLogSpanExporter implements SpanExporter {
141+
142+ private final SpanExporter delegate ;
143+ private final AtomicBoolean enabled ;
144+
145+ DebugLogSpanExporter (SpanExporter delegate ) {
146+ this .delegate = delegate ;
147+ this .enabled = new AtomicBoolean (false );
148+ }
149+
150+ void setEnabled (boolean value ) {
151+ enabled .set (value );
152+ }
153+
154+ @ Override
155+ public CompletableResultCode export (Collection <SpanData > spans ) {
156+ return enabled .get () ? delegate .export (spans ) : CompletableResultCode .ofSuccess ();
157+ }
158+
159+ @ Override
160+ public CompletableResultCode flush () {
161+ return enabled .get () ? delegate .flush () : CompletableResultCode .ofSuccess ();
162+ }
163+
164+ @ Override
165+ public CompletableResultCode shutdown () {
166+ return enabled .get () ? delegate .shutdown () : CompletableResultCode .ofSuccess ();
167+ }
96168 }
97169}
0 commit comments