Skip to content

Commit

Permalink
Add auto-configure support for logging-otlp (#4879)
Browse files Browse the repository at this point in the history
* Add autoconfigure support for logging-otlp

* Add NotOnClasspath tests

* Fix formatting

* Declare logging-otlp as experimental, add unit test

* Spotless

Co-authored-by: Jack Berg <jberg@newrelic.com>
  • Loading branch information
ben-roling and jack-berg committed Nov 28, 2022
1 parent cbd629c commit 7dd5c1c
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 2 deletions.
13 changes: 13 additions & 0 deletions sdk-extensions/autoconfigure/README.md
Expand Up @@ -159,6 +159,19 @@ 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 |

**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.
Expand Down
7 changes: 7 additions & 0 deletions sdk-extensions/autoconfigure/build.gradle.kts
Expand Up @@ -16,6 +16,7 @@ dependencies {
implementation(project(":exporters:common"))

compileOnly(project(":exporters:jaeger"))
compileOnly(project(":exporters:logging-otlp"))
compileOnly(project(":exporters:otlp:all"))
compileOnly(project(":exporters:otlp:logs"))
compileOnly(project(":exporters:otlp:common"))
Expand Down Expand Up @@ -141,6 +142,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"))
Expand Down
Expand Up @@ -11,6 +11,7 @@

import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.exporter.internal.retry.RetryUtil;
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;
Expand Down Expand Up @@ -90,6 +91,12 @@ static LogRecordExporter configureExporter(
switch (name) {
case "otlp":
return configureOtlpLogs(config, meterProvider);
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) {
Expand Down
Expand Up @@ -10,6 +10,7 @@
import static io.opentelemetry.sdk.autoconfigure.OtlpConfigUtil.PROTOCOL_HTTP_PROTOBUF;

import io.opentelemetry.exporter.internal.retry.RetryUtil;
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;
Expand Down Expand Up @@ -53,6 +54,9 @@ static MetricReader configureExporter(
case "otlp":
metricExporter = configureOtlpMetrics(config);
break;
case "logging-otlp":
metricExporter = configureLoggingOtlpExporter();
break;
default:
MetricExporter spiExporter = configureSpiExporter(name, config, serviceClassLoader);
if (spiExporter == null) {
Expand All @@ -74,6 +78,14 @@ static MetricReader configureExporter(
return configurePeriodicMetricReader(config, metricExporter);
}

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(
Expand Down
Expand Up @@ -14,6 +14,7 @@
import io.opentelemetry.exporter.internal.retry.RetryUtil;
import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter;
import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporterBuilder;
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;
Expand Down Expand Up @@ -98,6 +99,12 @@ static SpanExporter configureExporter(
return configureJaeger(config, meterProvider);
case "zipkin":
return configureZipkin(config);
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) {
Expand Down
Expand Up @@ -81,6 +81,18 @@ void loggingSpans() {
+ " Make sure to add it as a dependency.");
}

@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(
Expand All @@ -96,6 +108,21 @@ void loggingMetrics() {
+ " Make sure to add it as a dependency.");
}

@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(
Expand All @@ -108,6 +135,18 @@ void loggingLogs() {
+ " Make sure to add it as a dependency.");
}

@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(
Expand Down
@@ -0,0 +1,66 @@
/*
* 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));
}
}
Expand Up @@ -61,8 +61,6 @@ public double getAsDouble() {
/** Constructs a pool using explicit bucket histogram boundaries. */
private static double[] explicitDefaultBucketPool() {
List<Double> fixedBoundaries = new ArrayList<Double>();
// 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.
Expand Down

0 comments on commit 7dd5c1c

Please sign in to comment.