From a4287c54a95dedf9368aaf17c3106401cf4b11ad Mon Sep 17 00:00:00 2001 From: Brian Marks Date: Thu, 23 Jul 2026 18:40:11 -0400 Subject: [PATCH 1/2] Add OTLP export flags to tracer startup diagnostic log Report whether the tracer exports each telemetry signal over OTLP in the "DATADOG TRACER CONFIGURATION" startup log via three boolean fields: otlp_traces_export_enabled, otlp_metrics_export_enabled, and otlp_logs_export_enabled. Metrics and logs require both the OTel signal to be enabled and the OTLP exporter to be selected. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../java/datadog/trace/core/StatusLogger.java | 6 ++ .../datadog/trace/core/StatusLoggerTest.java | 61 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 dd-trace-core/src/test/java/datadog/trace/core/StatusLoggerTest.java diff --git a/dd-trace-core/src/main/java/datadog/trace/core/StatusLogger.java b/dd-trace-core/src/main/java/datadog/trace/core/StatusLogger.java index 78b88d786c9..d6509767ae7 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/StatusLogger.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/StatusLogger.java @@ -158,6 +158,12 @@ public void toJson(JsonWriter writer, Config config) throws IOException { writer.value(config.isDataStreamsEnabled()); writer.name("data_streams_transaction_extractors"); writer.value(config.getDataStreamsTransactionExtractors()); + writer.name("otlp_traces_export_enabled"); + writer.value(config.isTraceOtlpExporterEnabled()); + writer.name("otlp_metrics_export_enabled"); + writer.value(config.isMetricsOtelEnabled() && config.isMetricsOtlpExporterEnabled()); + writer.name("otlp_logs_export_enabled"); + writer.value(config.isLogsOtelEnabled() && config.isLogsOtlpExporterEnabled()); writer.name("app_logs_collection_enabled"); writer.value(config.isAppLogsCollectionEnabled()); diff --git a/dd-trace-core/src/test/java/datadog/trace/core/StatusLoggerTest.java b/dd-trace-core/src/test/java/datadog/trace/core/StatusLoggerTest.java new file mode 100644 index 00000000000..75795efb052 --- /dev/null +++ b/dd-trace-core/src/test/java/datadog/trace/core/StatusLoggerTest.java @@ -0,0 +1,61 @@ +package datadog.trace.core; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.squareup.moshi.Moshi; +import datadog.trace.api.Config; +import datadog.trace.api.config.OtlpConfig; +import datadog.trace.test.junit.utils.config.WithConfig; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +@Timeout(value = 10, unit = TimeUnit.SECONDS) +public class StatusLoggerTest extends DDCoreJavaSpecification { + + @Test + void otlpExportDisabledByDefault() throws IOException { + Map startupLog = startupLog(Config.get()); + + assertEquals(false, startupLog.get("otlp_traces_export_enabled")); + assertEquals(false, startupLog.get("otlp_metrics_export_enabled")); + assertEquals(false, startupLog.get("otlp_logs_export_enabled")); + } + + @Test + @WithConfig(key = OtlpConfig.TRACE_OTEL_EXPORTER, value = "otlp") + @WithConfig(key = OtlpConfig.METRICS_OTEL_ENABLED, value = "true") + @WithConfig(key = OtlpConfig.METRICS_OTEL_EXPORTER, value = "otlp") + @WithConfig(key = OtlpConfig.LOGS_OTEL_ENABLED, value = "true") + @WithConfig(key = OtlpConfig.LOGS_OTEL_EXPORTER, value = "otlp") + void otlpExportEnabledWhenConfigured() throws IOException { + Map startupLog = startupLog(Config.get()); + + assertEquals(true, startupLog.get("otlp_traces_export_enabled")); + assertEquals(true, startupLog.get("otlp_metrics_export_enabled")); + assertEquals(true, startupLog.get("otlp_logs_export_enabled")); + } + + @Test + @WithConfig(key = OtlpConfig.TRACE_OTEL_EXPORTER, value = "otlp") + @WithConfig(key = OtlpConfig.METRICS_OTEL_EXPORTER, value = "otlp") + @WithConfig(key = OtlpConfig.LOGS_OTEL_EXPORTER, value = "otlp") + void metricsAndLogsRequireOtelSignalEnabled() throws IOException { + // The OTLP exporter is selected for every signal, but the metrics and logs OTel signals are + // left disabled, so only trace export should be reported as enabled. + Map startupLog = startupLog(Config.get()); + + assertEquals(true, startupLog.get("otlp_traces_export_enabled")); + assertEquals(false, startupLog.get("otlp_metrics_export_enabled")); + assertEquals(false, startupLog.get("otlp_logs_export_enabled")); + } + + @SuppressWarnings("unchecked") + private static Map startupLog(Config config) throws IOException { + String json = + new Moshi.Builder().add(new StatusLogger()).build().adapter(Config.class).toJson(config); + return (Map) new Moshi.Builder().build().adapter(Object.class).fromJson(json); + } +} From 6d1065b2e502a82ac264e22349b3ad946ee7f9c4 Mon Sep 17 00:00:00 2001 From: Brian Marks Date: Fri, 24 Jul 2026 18:39:25 -0400 Subject: [PATCH 2/2] Base OTLP export flags on effective writer and span metrics Report otlp_traces_export_enabled from the resolved writer type (OtlpWriter) rather than the OTLP exporter selection, since an explicit dd.writer.type override wins over dd.trace.otel.exporter=otlp in WriterFactory. Also report otlp_metrics_export_enabled when client-side span metrics are enabled, since those are exported over OTLP via OtlpStatsMetricWriter independently of the OTel metrics signal. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../java/datadog/trace/core/StatusLogger.java | 13 +++++++++-- .../datadog/trace/core/StatusLoggerTest.java | 23 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/dd-trace-core/src/main/java/datadog/trace/core/StatusLogger.java b/dd-trace-core/src/main/java/datadog/trace/core/StatusLogger.java index d6509767ae7..470471bbc32 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/StatusLogger.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/StatusLogger.java @@ -2,6 +2,7 @@ import static datadog.trace.api.Config.isDatadogProfilerEnablementOverridden; import static datadog.trace.api.Config.isDatadogProfilerSafeInCurrentEnvironment; +import static datadog.trace.bootstrap.instrumentation.api.WriterConstants.OTLP_WRITER_TYPE; import static java.util.concurrent.TimeUnit.MILLISECONDS; import com.squareup.moshi.JsonAdapter; @@ -159,9 +160,17 @@ public void toJson(JsonWriter writer, Config config) throws IOException { writer.name("data_streams_transaction_extractors"); writer.value(config.getDataStreamsTransactionExtractors()); writer.name("otlp_traces_export_enabled"); - writer.value(config.isTraceOtlpExporterEnabled()); + // Report the effective trace writer, not just the exporter selection: an explicit + // dd.writer.type override wins over dd.trace.otel.exporter=otlp in WriterFactory, so traces are + // only exported over OTLP when the resolved writer is actually the OtlpWriter. + writer.value(OTLP_WRITER_TYPE.equals(config.getWriterType())); writer.name("otlp_metrics_export_enabled"); - writer.value(config.isMetricsOtelEnabled() && config.isMetricsOtlpExporterEnabled()); + // Client-side trace span-metrics are exported over OTLP whenever span metrics are enabled + // (MetricsAggregatorFactory routes them through OtlpStatsMetricWriter), independently of the + // OTel metrics signal, so OR that path in alongside OTel-API metrics export. + writer.value( + (config.isMetricsOtelEnabled() && config.isMetricsOtlpExporterEnabled()) + || config.isOtelTracesSpanMetricsEnabled()); writer.name("otlp_logs_export_enabled"); writer.value(config.isLogsOtelEnabled() && config.isLogsOtlpExporterEnabled()); diff --git a/dd-trace-core/src/test/java/datadog/trace/core/StatusLoggerTest.java b/dd-trace-core/src/test/java/datadog/trace/core/StatusLoggerTest.java index 75795efb052..8e196fe7695 100644 --- a/dd-trace-core/src/test/java/datadog/trace/core/StatusLoggerTest.java +++ b/dd-trace-core/src/test/java/datadog/trace/core/StatusLoggerTest.java @@ -5,6 +5,7 @@ import com.squareup.moshi.Moshi; import datadog.trace.api.Config; import datadog.trace.api.config.OtlpConfig; +import datadog.trace.api.config.TracerConfig; import datadog.trace.test.junit.utils.config.WithConfig; import java.io.IOException; import java.util.Map; @@ -52,6 +53,28 @@ void metricsAndLogsRequireOtelSignalEnabled() throws IOException { assertEquals(false, startupLog.get("otlp_logs_export_enabled")); } + @Test + @WithConfig(key = OtlpConfig.TRACE_OTEL_EXPORTER, value = "otlp") + @WithConfig(key = TracerConfig.WRITER_TYPE, value = "DDAgentWriter") + void tracesNotExportedWhenWriterTypeOverridesOtlpExporter() throws IOException { + // The OTLP trace exporter is selected, but an explicit dd.writer.type override wins in + // WriterFactory, so the effective writer is the DDAgentWriter and traces are not exported over + // OTLP. + Map startupLog = startupLog(Config.get()); + + assertEquals(false, startupLog.get("otlp_traces_export_enabled")); + } + + @Test + @WithConfig(key = OtlpConfig.OTEL_TRACES_SPAN_METRICS_ENABLED, value = "true") + void metricsExportedWhenSpanMetricsEnabled() throws IOException { + // Span metrics are exported over OTLP via OtlpStatsMetricWriter whenever span metrics are + // enabled, independently of the OTel metrics signal, so metrics export should be reported. + Map startupLog = startupLog(Config.get()); + + assertEquals(true, startupLog.get("otlp_metrics_export_enabled")); + } + @SuppressWarnings("unchecked") private static Map startupLog(Config config) throws IOException { String json =