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..da324a07def 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.isOtlpTracesExportEnabled()); + writer.name("otlp_metrics_export_enabled"); + writer.value(config.isOtlpMetricsExportEnabled()); + writer.name("otlp_logs_export_enabled"); + writer.value(config.isOtlpLogsExportEnabled()); 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..14d2ff6a421 --- /dev/null +++ b/dd-trace-core/src/test/java/datadog/trace/core/StatusLoggerTest.java @@ -0,0 +1,141 @@ +package datadog.trace.core; + +import static datadog.trace.api.config.OtlpConfig.LOGS_OTEL_ENABLED; +import static datadog.trace.api.config.OtlpConfig.LOGS_OTEL_EXPORTER; +import static datadog.trace.api.config.OtlpConfig.METRICS_OTEL_ENABLED; +import static datadog.trace.api.config.OtlpConfig.METRICS_OTEL_EXPORTER; +import static datadog.trace.api.config.OtlpConfig.OTEL_TRACES_SPAN_METRICS_ENABLED; +import static datadog.trace.api.config.OtlpConfig.TRACE_OTEL_EXPORTER; +import static datadog.trace.api.config.TracerConfig.WRITER_TYPE; +import static datadog.trace.bootstrap.instrumentation.api.WriterConstants.DD_AGENT_WRITER_TYPE; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.squareup.moshi.Moshi; +import datadog.trace.api.Config; +import datadog.trace.test.junit.utils.config.WithConfig; +import datadog.trace.test.util.DDJavaSpecification; +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 DDJavaSpecification { + + @Test + void otlpExportDisabledByDefault() throws IOException { + Map startupLog = startupLog(); + + assertFalse(flag(startupLog, "otlp_traces_export_enabled")); + assertFalse(flag(startupLog, "otlp_metrics_export_enabled")); + assertFalse(flag(startupLog, "otlp_logs_export_enabled")); + } + + @Test + @WithConfig(key = TRACE_OTEL_EXPORTER, value = "otlp") + @WithConfig(key = METRICS_OTEL_ENABLED, value = "true") + @WithConfig(key = METRICS_OTEL_EXPORTER, value = "otlp") + @WithConfig(key = LOGS_OTEL_ENABLED, value = "true") + @WithConfig(key = LOGS_OTEL_EXPORTER, value = "otlp") + void otlpExportEnabledWhenConfigured() throws IOException { + Map startupLog = startupLog(); + + assertTrue(flag(startupLog, "otlp_traces_export_enabled")); + assertTrue(flag(startupLog, "otlp_metrics_export_enabled")); + assertTrue(flag(startupLog, "otlp_logs_export_enabled")); + } + + @Test + @WithConfig(key = TRACE_OTEL_EXPORTER, value = "otlp") + @WithConfig(key = METRICS_OTEL_EXPORTER, value = "otlp") + @WithConfig(key = LOGS_OTEL_EXPORTER, value = "otlp") + void metricsAndLogsRequireOtelSignalEnabled() throws IOException { + Map startupLog = startupLog(); + + assertTrue(flag(startupLog, "otlp_traces_export_enabled")); + assertFalse(flag(startupLog, "otlp_metrics_export_enabled")); + assertFalse(flag(startupLog, "otlp_logs_export_enabled")); + } + + @Test + @WithConfig(key = TRACE_OTEL_EXPORTER, value = "otlp") + @WithConfig(key = WRITER_TYPE, value = DD_AGENT_WRITER_TYPE) + void tracesNotExportedWhenWriterTypeOverridesOtlpExporter() throws IOException { + assertFalse(flag(startupLog(), "otlp_traces_export_enabled")); + } + + @Test + @WithConfig(key = WRITER_TYPE, value = "MultiWriter:OtlpWriter,DDAgentWriter") + void tracesExportedWhenMultiWriterIncludesOtlpWriter() throws IOException { + assertTrue(flag(startupLog(), "otlp_traces_export_enabled")); + } + + @Test + @WithConfig(key = WRITER_TYPE, value = "MultiWriter:DDAgentWriter,MultiWriter:OtlpWriter") + void tracesExportedWhenMultiWriterPrefixRepeats() throws IOException { + assertTrue(flag(startupLog(), "otlp_traces_export_enabled")); + } + + @Test + @WithConfig(key = WRITER_TYPE, value = "MultiWriter:LoggingWriter,DDAgentWriter") + void tracesNotExportedWhenMultiWriterExcludesOtlpWriter() throws IOException { + assertFalse(flag(startupLog(), "otlp_traces_export_enabled")); + } + + @Test + @WithConfig(key = WRITER_TYPE, value = "MultiWriter: OtlpWriter") + void tracesNotExportedWhenMultiWriterSubTypeIsPadded() throws IOException { + assertFalse(flag(startupLog(), "otlp_traces_export_enabled")); + } + + @Test + @WithConfig(key = WRITER_TYPE, value = "MultiWriter:OtlpWriterExtra") + void tracesNotExportedWhenMultiWriterSubTypeOnlyPrefixesOtlpWriter() throws IOException { + assertFalse(flag(startupLog(), "otlp_traces_export_enabled")); + } + + @Test + @WithConfig(key = WRITER_TYPE, value = "DDAgentWriter,OtlpWriter") + void tracesNotExportedWhenCommaSeparatedWithoutMultiWriterPrefix() throws IOException { + assertFalse(flag(startupLog(), "otlp_traces_export_enabled")); + } + + @Test + @WithConfig(key = WRITER_TYPE, value = "TraceStructureWriter:/tmp/out,OtlpWriter") + void tracesNotExportedWhenTraceStructureWriterTakesPrecedence() throws IOException { + assertFalse(flag(startupLog(), "otlp_traces_export_enabled")); + } + + @Test + @WithConfig(key = OTEL_TRACES_SPAN_METRICS_ENABLED, value = "true") + void metricsExportedWhenSpanMetricsEnabled() throws IOException { + assertTrue(flag(startupLog(), "otlp_metrics_export_enabled")); + } + + @Test + @WithConfig(key = METRICS_OTEL_ENABLED, value = "true") + @WithConfig(key = METRICS_OTEL_EXPORTER, value = "otlp") + @WithConfig(key = OTEL_TRACES_SPAN_METRICS_ENABLED, value = "false") + void metricsExportedWhenOtelMetricsSignalEnabledWithoutSpanMetrics() throws IOException { + assertTrue(flag(startupLog(), "otlp_metrics_export_enabled")); + } + + @SuppressWarnings("unchecked") + private static Map startupLog() throws IOException { + String json = + new Moshi.Builder() + .add(new StatusLogger()) + .build() + .adapter(Config.class) + .toJson(Config.get()); + return (Map) new Moshi.Builder().build().adapter(Object.class).fromJson(json); + } + + private static boolean flag(Map startupLog, String name) { + Object value = startupLog.get(name); + assertTrue(value instanceof Boolean, name + " should be a boolean, was " + value); + return (Boolean) value; + } +} diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index 7d6df22df1c..8ee70319631 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -727,6 +727,7 @@ import static datadog.trace.api.config.TracerConfig.WRITER_LINKS_INJECT; import static datadog.trace.api.config.TracerConfig.WRITER_TYPE; import static datadog.trace.api.telemetry.LogCollector.SEND_TELEMETRY; +import static datadog.trace.bootstrap.instrumentation.api.WriterConstants.MULTI_WRITER_TYPE; import static datadog.trace.bootstrap.instrumentation.api.WriterConstants.OTLP_WRITER_TYPE; import static datadog.trace.util.CollectionUtils.tryMakeImmutableList; import static datadog.trace.util.CollectionUtils.tryMakeImmutableSet; @@ -828,6 +829,9 @@ public class Config { private static final int MAX_CODE_COVERAGE_FLAGS = 32; private static final Pattern COLON = Pattern.compile(":"); + private static final Pattern COMMA = Pattern.compile(","); + private static final Pattern MULTI_WRITER_PREFIX = + Pattern.compile(MULTI_WRITER_TYPE + ":", Pattern.LITERAL); // Historical conflating-Batch size; used to translate TRACER_METRICS_MAX_PENDING (configured in // legacy batch units) into the new per-SpanSnapshot inbox capacity. @@ -5577,6 +5581,10 @@ public boolean isLogsOtlpExporterEnabled() { return "otlp".equalsIgnoreCase(logsOtelExporter); } + public boolean isOtlpLogsExportEnabled() { + return isLogsOtelEnabled() && isLogsOtlpExporterEnabled(); + } + public int getLogsOtelInterval() { return logsOtelInterval; } @@ -5621,6 +5629,11 @@ public boolean isMetricsOtlpExporterEnabled() { return "otlp".equalsIgnoreCase(metricsOtelExporter); } + public boolean isOtlpMetricsExportEnabled() { + return (isMetricsOtelEnabled() && isMetricsOtlpExporterEnabled()) + || isOtelTracesSpanMetricsEnabled(); + } + public boolean isMetricsOtelExperimentalEnabled() { return metricsOtelExperimentalEnabled; } @@ -5677,6 +5690,19 @@ public boolean isTraceOtlpExporterEnabled() { return "otlp".equalsIgnoreCase(traceOtelExporter); } + public boolean isOtlpTracesExportEnabled() { + if (!writerType.startsWith(MULTI_WRITER_TYPE)) { + return OTLP_WRITER_TYPE.equals(writerType); + } + String multiWriterConfig = MULTI_WRITER_PREFIX.matcher(writerType).replaceAll(""); + for (String subWriterType : COMMA.split(multiWriterConfig)) { + if (OTLP_WRITER_TYPE.equals(subWriterType)) { + return true; + } + } + return false; + } + public String getOtlpTracesEndpoint() { return otlpTracesEndpoint; }