From 918faf713d169314bc98374466429d5b2350bc65 Mon Sep 17 00:00:00 2001 From: Ben Roling Date: Fri, 21 Oct 2022 08:53:05 -0500 Subject: [PATCH 1/5] Add autoconfigure support for logging-otlp --- sdk-extensions/autoconfigure/README.md | 10 ++++++++++ sdk-extensions/autoconfigure/build.gradle.kts | 1 + .../LogRecordExporterConfiguration.java | 7 +++++++ .../autoconfigure/MetricExporterConfiguration.java | 12 ++++++++++++ .../sdk/autoconfigure/SpanExporterConfiguration.java | 7 +++++++ 5 files changed, 37 insertions(+) diff --git a/sdk-extensions/autoconfigure/README.md b/sdk-extensions/autoconfigure/README.md index 49fa7fd350d..e0cd8f079e0 100644 --- a/sdk-extensions/autoconfigure/README.md +++ b/sdk-extensions/autoconfigure/README.md @@ -159,6 +159,16 @@ The logging exporter prints the name of the span along with its attributes to st | otel.metrics.exporter=logging | OTEL_METRICS_EXPORTER=logging | Select the logging exporter for metrics | | otel.logs.exporter=logging | OTEL_LOGS_EXPORTER=logging | Select the logging exporter for logs | +### Logging OTLP JSON exporter + +The logging-otlp exporter writes the telemetry data to the JUL logger in OLTP JSON form. It's a more verbose output mainly used for testing and debugging. + +| System property | Environment variable | Description | +|------------------------------------|------------------------------------|----------------------------------------------------| +| otel.traces.exporter=logging-otlp | OTEL_TRACES_EXPORTER=logging-otlp | Select the logging OTLP JSON exporter for tracing | +| otel.metrics.exporter=logging-otlp | OTEL_METRICS_EXPORTER=logging-otlp | Select the logging OTLP JSON exporter for metrics | +| otel.logs.exporter=logging-otlp | OTEL_LOGS_EXPORTER=logging-otlp | Select the logging OTLP JSON exporter for logs | + ## Propagator The propagators determine which distributed tracing header formats are used, and which baggage propagation header formats are used. diff --git a/sdk-extensions/autoconfigure/build.gradle.kts b/sdk-extensions/autoconfigure/build.gradle.kts index 63c94b23b14..9e47fbcfec1 100644 --- a/sdk-extensions/autoconfigure/build.gradle.kts +++ b/sdk-extensions/autoconfigure/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { compileOnly(project(":exporters:jaeger")) compileOnly(project(":exporters:logging")) + compileOnly(project(":exporters:logging-otlp")) compileOnly(project(":exporters:otlp:all")) compileOnly(project(":exporters:otlp:logs")) compileOnly(project(":exporters:otlp:common")) diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/LogRecordExporterConfiguration.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/LogRecordExporterConfiguration.java index 927b4eceb2f..b14ef1fdea1 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/LogRecordExporterConfiguration.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/LogRecordExporterConfiguration.java @@ -12,6 +12,7 @@ import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.internal.retry.RetryUtil; import io.opentelemetry.exporter.logging.SystemOutLogRecordExporter; +import io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingLogRecordExporter; import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporter; import io.opentelemetry.exporter.otlp.http.logs.OtlpHttpLogRecordExporterBuilder; import io.opentelemetry.exporter.otlp.logs.OtlpGrpcLogRecordExporter; @@ -91,6 +92,12 @@ static LogRecordExporter configureExporter( "Logging Log Exporter", "opentelemetry-exporter-logging"); return SystemOutLogRecordExporter.create(); + case "logging-otlp": + ClasspathUtil.checkClassExists( + "io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingLogRecordExporter", + "OTLP JSON Logging Log Exporter", + "opentelemetry-exporter-logging-otlp"); + return OtlpJsonLoggingLogRecordExporter.create(); default: LogRecordExporter spiExporter = spiExportersManager.getByName(name); if (spiExporter == null) { diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfiguration.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfiguration.java index ac8e6784b3f..8cc2b211b17 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfiguration.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/MetricExporterConfiguration.java @@ -11,6 +11,7 @@ import io.opentelemetry.exporter.internal.retry.RetryUtil; import io.opentelemetry.exporter.logging.LoggingMetricExporter; +import io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingMetricExporter; import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporter; import io.opentelemetry.exporter.otlp.http.metrics.OtlpHttpMetricExporterBuilder; import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter; @@ -49,6 +50,9 @@ static MetricReader configureExporter( case "logging": metricExporter = configureLoggingExporter(); break; + case "logging-otlp": + metricExporter = configureLoggingOtlpExporter(); + break; default: MetricExporter spiExporter = configureSpiExporter(name, config, serviceClassLoader); if (spiExporter == null) { @@ -69,6 +73,14 @@ private static MetricExporter configureLoggingExporter() { return LoggingMetricExporter.create(); } + private static MetricExporter configureLoggingOtlpExporter() { + ClasspathUtil.checkClassExists( + "io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingMetricExporter", + "OTLP JSON Logging Metrics Exporter", + "opentelemetry-exporter-logging-otlp"); + return OtlpJsonLoggingMetricExporter.create(); + } + // Visible for testing. @Nullable static MetricExporter configureSpiExporter( diff --git a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/SpanExporterConfiguration.java b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/SpanExporterConfiguration.java index a3d2c394783..294cc9ed2e2 100644 --- a/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/SpanExporterConfiguration.java +++ b/sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/SpanExporterConfiguration.java @@ -15,6 +15,7 @@ import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter; import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporterBuilder; import io.opentelemetry.exporter.logging.LoggingSpanExporter; +import io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingSpanExporter; import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter; import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporterBuilder; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; @@ -98,6 +99,12 @@ static SpanExporter configureExporter( "Logging Trace Exporter", "opentelemetry-exporter-logging"); return LoggingSpanExporter.create(); + case "logging-otlp": + ClasspathUtil.checkClassExists( + "io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingSpanExporter", + "OTLP JSON Logging Trace Exporter", + "opentelemetry-exporter-logging-otlp"); + return OtlpJsonLoggingSpanExporter.create(); default: SpanExporter spiExporter = spiExportersManager.getByName(name); if (spiExporter == null) { From 601e82a702684d5757bd0870a5da6aae124a3b65 Mon Sep 17 00:00:00 2001 From: Ben Roling Date: Thu, 3 Nov 2022 16:50:18 -0500 Subject: [PATCH 2/5] Add NotOnClasspath tests --- .../sdk/autoconfigure/NotOnClasspathTest.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/NotOnClasspathTest.java b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/NotOnClasspathTest.java index a4a476b2c62..eddf3b226f8 100644 --- a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/NotOnClasspathTest.java +++ b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/NotOnClasspathTest.java @@ -81,6 +81,18 @@ void loggingSpans() { + "classpath"); } + @Test + void loggingSpansOtlp() { + assertThatThrownBy( + () -> + SpanExporterConfiguration.configureExporter( + "logging-otlp", EMPTY, NamedSpiManager.createEmpty(), MeterProvider.noop())) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining( + "OTLP JSON Logging Trace Exporter enabled but opentelemetry-exporter-logging-otlp not found on " + + "classpath"); + } + @Test void loggingMetrics() { assertThatThrownBy( @@ -96,6 +108,21 @@ void loggingMetrics() { + "classpath"); } + @Test + void loggingMetricsOtlp() { + assertThatThrownBy( + () -> + MetricExporterConfiguration.configureExporter( + "logging-otlp", + EMPTY, + MetricExporterConfiguration.class.getClassLoader(), + (a, unused) -> a)) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining( + "OTLP JSON Logging Metrics Exporter enabled but opentelemetry-exporter-logging-otlp not found on " + + "classpath"); + } + @Test void loggingLogs() { assertThatThrownBy( @@ -108,6 +135,18 @@ void loggingLogs() { + "classpath"); } + @Test + void loggingLogsOtlp() { + assertThatThrownBy( + () -> + LogRecordExporterConfiguration.configureExporter( + "logging-otlp", EMPTY, NamedSpiManager.createEmpty(), MeterProvider.noop())) + .isInstanceOf(ConfigurationException.class) + .hasMessageContaining( + "OTLP JSON Logging Log Exporter enabled but opentelemetry-exporter-logging-otlp not found on " + + "classpath"); + } + @Test void otlpGrpcMetrics() { assertThatCode( From 0fa4d9437845535d9a5f2c33f7f3c8ae55aaeda8 Mon Sep 17 00:00:00 2001 From: Ben Roling Date: Fri, 4 Nov 2022 08:11:07 -0500 Subject: [PATCH 3/5] Fix formatting --- .../sdk/autoconfigure/NotOnClasspathTest.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/NotOnClasspathTest.java b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/NotOnClasspathTest.java index eddf3b226f8..c4ad015edd7 100644 --- a/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/NotOnClasspathTest.java +++ b/sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/NotOnClasspathTest.java @@ -84,9 +84,9 @@ void loggingSpans() { @Test void loggingSpansOtlp() { assertThatThrownBy( - () -> - SpanExporterConfiguration.configureExporter( - "logging-otlp", EMPTY, NamedSpiManager.createEmpty(), MeterProvider.noop())) + () -> + SpanExporterConfiguration.configureExporter( + "logging-otlp", EMPTY, NamedSpiManager.createEmpty(), MeterProvider.noop())) .isInstanceOf(ConfigurationException.class) .hasMessageContaining( "OTLP JSON Logging Trace Exporter enabled but opentelemetry-exporter-logging-otlp not found on " @@ -111,12 +111,12 @@ void loggingMetrics() { @Test void loggingMetricsOtlp() { assertThatThrownBy( - () -> - MetricExporterConfiguration.configureExporter( - "logging-otlp", - EMPTY, - MetricExporterConfiguration.class.getClassLoader(), - (a, unused) -> a)) + () -> + MetricExporterConfiguration.configureExporter( + "logging-otlp", + EMPTY, + MetricExporterConfiguration.class.getClassLoader(), + (a, unused) -> a)) .isInstanceOf(ConfigurationException.class) .hasMessageContaining( "OTLP JSON Logging Metrics Exporter enabled but opentelemetry-exporter-logging-otlp not found on " @@ -138,9 +138,9 @@ void loggingLogs() { @Test void loggingLogsOtlp() { assertThatThrownBy( - () -> - LogRecordExporterConfiguration.configureExporter( - "logging-otlp", EMPTY, NamedSpiManager.createEmpty(), MeterProvider.noop())) + () -> + LogRecordExporterConfiguration.configureExporter( + "logging-otlp", EMPTY, NamedSpiManager.createEmpty(), MeterProvider.noop())) .isInstanceOf(ConfigurationException.class) .hasMessageContaining( "OTLP JSON Logging Log Exporter enabled but opentelemetry-exporter-logging-otlp not found on " From 0cd2f4fb856803dc09d5a7fc822ba022a1cfaee0 Mon Sep 17 00:00:00 2001 From: Jack Berg Date: Mon, 21 Nov 2022 16:33:26 -0600 Subject: [PATCH 4/5] Declare logging-otlp as experimental, add unit test --- sdk-extensions/autoconfigure/README.md | 3 + sdk-extensions/autoconfigure/build.gradle.kts | 6 ++ .../sdk/autoconfigure/LoggingOtlpTest.java | 61 +++++++++++++++++++ 3 files changed, 70 insertions(+) create mode 100644 sdk-extensions/autoconfigure/src/testLoggingOtlp/java/io/opentelemetry/sdk/autoconfigure/LoggingOtlpTest.java diff --git a/sdk-extensions/autoconfigure/README.md b/sdk-extensions/autoconfigure/README.md index e0cd8f079e0..da27434a963 100644 --- a/sdk-extensions/autoconfigure/README.md +++ b/sdk-extensions/autoconfigure/README.md @@ -169,6 +169,9 @@ The logging-otlp exporter writes the telemetry data to the JUL logger in OLTP JS | otel.metrics.exporter=logging-otlp | OTEL_METRICS_EXPORTER=logging-otlp | Select the logging OTLP JSON exporter for metrics | | otel.logs.exporter=logging-otlp | OTEL_LOGS_EXPORTER=logging-otlp | Select the logging OTLP JSON exporter for logs | +**NOTE:** While the `OtlpJsonLogging{Signal}Exporters` are stable, specifying their use +via `otel.{signal}.exporter=logging-otlp` is experimental and subject to change or removal. + ## Propagator The propagators determine which distributed tracing header formats are used, and which baggage propagation header formats are used. diff --git a/sdk-extensions/autoconfigure/build.gradle.kts b/sdk-extensions/autoconfigure/build.gradle.kts index 9e47fbcfec1..4f0722f3942 100644 --- a/sdk-extensions/autoconfigure/build.gradle.kts +++ b/sdk-extensions/autoconfigure/build.gradle.kts @@ -145,6 +145,12 @@ testing { } } } + val testLoggingOtlp by registering(JvmTestSuite::class) { + dependencies { + implementation(project(":exporters:logging-otlp")) + implementation("com.google.guava:guava") + } + } val testOtlp by registering(JvmTestSuite::class) { dependencies { implementation(project(":exporters:otlp:all")) diff --git a/sdk-extensions/autoconfigure/src/testLoggingOtlp/java/io/opentelemetry/sdk/autoconfigure/LoggingOtlpTest.java b/sdk-extensions/autoconfigure/src/testLoggingOtlp/java/io/opentelemetry/sdk/autoconfigure/LoggingOtlpTest.java new file mode 100644 index 00000000000..cf0d5e38e70 --- /dev/null +++ b/sdk-extensions/autoconfigure/src/testLoggingOtlp/java/io/opentelemetry/sdk/autoconfigure/LoggingOtlpTest.java @@ -0,0 +1,61 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.autoconfigure; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +import com.google.common.collect.ImmutableMap; +import io.github.netmikey.logunit.api.LogCapturer; +import io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingLogRecordExporter; +import io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingMetricExporter; +import io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingSpanExporter; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class LoggingOtlpTest { + + @RegisterExtension + LogCapturer spansCapturer = + LogCapturer.create().captureForType(OtlpJsonLoggingSpanExporter.class); + + @RegisterExtension + LogCapturer metricsCapturer = + LogCapturer.create().captureForType(OtlpJsonLoggingMetricExporter.class); + + @RegisterExtension + LogCapturer logsCapturer = + LogCapturer.create().captureForType(OtlpJsonLoggingLogRecordExporter.class); + + @Test + void configures() { + OpenTelemetrySdk sdk = + AutoConfiguredOpenTelemetrySdk.builder() + .setConfig( + DefaultConfigProperties.createForTest( + ImmutableMap.of( + "otel.traces.exporter", "logging-otlp", + "otel.metrics.exporter", "logging-otlp", + "otel.logs.exporter", "logging-otlp"))) + .setResultAsGlobal(false) + .build() + .getOpenTelemetrySdk(); + + sdk.getTracerProvider().get("tracer").spanBuilder("test").startSpan().end(); + sdk.getMeterProvider().get("meter").counterBuilder("counter").build().add(10); + sdk.getSdkLoggerProvider().get("logger").logRecordBuilder().setBody("message").emit(); + + sdk.getSdkLoggerProvider().forceFlush().join(10, TimeUnit.SECONDS); + sdk.getSdkMeterProvider().forceFlush().join(10, TimeUnit.SECONDS); + sdk.getSdkLoggerProvider().forceFlush().join(10, TimeUnit.SECONDS); + + await().untilAsserted(() -> assertThat(spansCapturer.getEvents().size()).isGreaterThanOrEqualTo(1)); + await().untilAsserted(() -> assertThat(metricsCapturer.getEvents().size()).isGreaterThanOrEqualTo(1)); + await().untilAsserted(() -> assertThat(logsCapturer.getEvents().size()).isGreaterThanOrEqualTo(1)); + } +} From 318f2d62a172ae6d7ab45dbffc94b68887cf58c7 Mon Sep 17 00:00:00 2001 From: Jack Berg Date: Sat, 26 Nov 2022 08:44:27 -0600 Subject: [PATCH 5/5] Spotless --- .../sdk/autoconfigure/LoggingOtlpTest.java | 11 ++++++++--- .../internal/aggregator/HistogramValueGenerator.java | 2 -- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/sdk-extensions/autoconfigure/src/testLoggingOtlp/java/io/opentelemetry/sdk/autoconfigure/LoggingOtlpTest.java b/sdk-extensions/autoconfigure/src/testLoggingOtlp/java/io/opentelemetry/sdk/autoconfigure/LoggingOtlpTest.java index cf0d5e38e70..aba93ae800e 100644 --- a/sdk-extensions/autoconfigure/src/testLoggingOtlp/java/io/opentelemetry/sdk/autoconfigure/LoggingOtlpTest.java +++ b/sdk-extensions/autoconfigure/src/testLoggingOtlp/java/io/opentelemetry/sdk/autoconfigure/LoggingOtlpTest.java @@ -54,8 +54,13 @@ void configures() { sdk.getSdkMeterProvider().forceFlush().join(10, TimeUnit.SECONDS); sdk.getSdkLoggerProvider().forceFlush().join(10, TimeUnit.SECONDS); - await().untilAsserted(() -> assertThat(spansCapturer.getEvents().size()).isGreaterThanOrEqualTo(1)); - await().untilAsserted(() -> assertThat(metricsCapturer.getEvents().size()).isGreaterThanOrEqualTo(1)); - await().untilAsserted(() -> assertThat(logsCapturer.getEvents().size()).isGreaterThanOrEqualTo(1)); + await() + .untilAsserted( + () -> assertThat(spansCapturer.getEvents().size()).isGreaterThanOrEqualTo(1)); + await() + .untilAsserted( + () -> assertThat(metricsCapturer.getEvents().size()).isGreaterThanOrEqualTo(1)); + await() + .untilAsserted(() -> assertThat(logsCapturer.getEvents().size()).isGreaterThanOrEqualTo(1)); } } diff --git a/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramValueGenerator.java b/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramValueGenerator.java index cbe48550dc6..aa603878640 100644 --- a/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramValueGenerator.java +++ b/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramValueGenerator.java @@ -61,8 +61,6 @@ public double getAsDouble() { /** Constructs a pool using explicit bucket histogram boundaries. */ private static double[] explicitDefaultBucketPool() { List fixedBoundaries = new ArrayList(); - // Add minimal recording value. - fixedBoundaries.add(0.0); // Add the bucket LE bucket boundaries (starts at 5). fixedBoundaries.addAll(ExplicitBucketHistogramUtils.DEFAULT_HISTOGRAM_BUCKET_BOUNDARIES); // Add Double max value as our other extreme.