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..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; @@ -158,6 +159,20 @@ 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"); + // 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"); + // 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()); 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..8e196fe7695 --- /dev/null +++ b/dd-trace-core/src/test/java/datadog/trace/core/StatusLoggerTest.java @@ -0,0 +1,84 @@ +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.api.config.TracerConfig; +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")); + } + + @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 = + new Moshi.Builder().add(new StatusLogger()).build().adapter(Config.class).toJson(config); + return (Map) new Moshi.Builder().build().adapter(Object.class).fromJson(json); + } +}