diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-logging-otlp.txt b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-logging-otlp.txt index 0af40c60640..ef5d8d65d1e 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-exporter-logging-otlp.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-exporter-logging-otlp.txt @@ -1,2 +1,10 @@ -Comparing source compatibility of opentelemetry-exporter-logging-otlp-1.53.0-SNAPSHOT.jar against opentelemetry-exporter-logging-otlp-1.52.0.jar -No changes. \ No newline at end of file +Comparing source compatibility of opentelemetry-exporter-logging-otlp-1.52.0-SNAPSHOT.jar against opentelemetry-exporter-logging-otlp-1.51.0.jar +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingLogRecordExporter (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.logs.export.LogRecordExporter create(boolean) +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingMetricExporter (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.export.MetricExporter create(io.opentelemetry.sdk.metrics.data.AggregationTemporality, boolean) +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingSpanExporter (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.trace.export.SpanExporter create(boolean) diff --git a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingLogRecordExporter.java b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingLogRecordExporter.java index 0c00cca908e..6a5e740a8c8 100644 --- a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingLogRecordExporter.java +++ b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingLogRecordExporter.java @@ -8,6 +8,7 @@ import io.opentelemetry.exporter.logging.otlp.internal.logs.OtlpStdoutLogRecordExporter; import io.opentelemetry.exporter.logging.otlp.internal.logs.OtlpStdoutLogRecordExporterBuilder; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.logs.data.LogRecordData; import io.opentelemetry.sdk.logs.export.LogRecordExporter; import java.util.Collection; @@ -33,6 +34,24 @@ public static LogRecordExporter create() { return new OtlpJsonLoggingLogRecordExporter(delegate); } + /** + * Returns a new {@link OtlpJsonLoggingLogRecordExporter}. + * + * @param wrapperJsonObject whether to wrap the JSON object in an outer JSON "resourceLogs" + * object. When {@code true}, uses low allocation OTLP marshalers with {@link + * MemoryMode#REUSABLE_DATA}. When {@code false}, uses {@link MemoryMode#IMMUTABLE_DATA}. + */ + public static LogRecordExporter create(boolean wrapperJsonObject) { + MemoryMode memoryMode = + wrapperJsonObject ? MemoryMode.REUSABLE_DATA : MemoryMode.IMMUTABLE_DATA; + OtlpStdoutLogRecordExporter delegate = + new OtlpStdoutLogRecordExporterBuilder(logger) + .setWrapperJsonObject(wrapperJsonObject) + .setMemoryMode(memoryMode) + .build(); + return new OtlpJsonLoggingLogRecordExporter(delegate); + } + OtlpJsonLoggingLogRecordExporter(OtlpStdoutLogRecordExporter delegate) { this.delegate = delegate; } diff --git a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingMetricExporter.java b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingMetricExporter.java index b42ef4acab7..a9319def90b 100644 --- a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingMetricExporter.java +++ b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingMetricExporter.java @@ -8,6 +8,7 @@ import io.opentelemetry.exporter.logging.otlp.internal.metrics.OtlpStdoutMetricExporter; import io.opentelemetry.exporter.logging.otlp.internal.metrics.OtlpStdoutMetricExporterBuilder; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.metrics.InstrumentType; import io.opentelemetry.sdk.metrics.data.AggregationTemporality; import io.opentelemetry.sdk.metrics.data.MetricData; @@ -46,6 +47,27 @@ public static MetricExporter create(AggregationTemporality aggregationTemporalit return new OtlpJsonLoggingMetricExporter(delegate, aggregationTemporality); } + /** + * Returns a new {@link OtlpJsonLoggingMetricExporter} with the given {@code + * aggregationTemporality}. + * + * @param aggregationTemporality the aggregation temporality to use + * @param wrapperJsonObject whether to wrap the JSON object in an outer JSON "resourceMetrics" + * object. When {@code true}, uses low allocation OTLP marshalers with {@link + * MemoryMode#REUSABLE_DATA}. When {@code false}, uses {@link MemoryMode#IMMUTABLE_DATA}. + */ + public static MetricExporter create( + AggregationTemporality aggregationTemporality, boolean wrapperJsonObject) { + MemoryMode memoryMode = + wrapperJsonObject ? MemoryMode.REUSABLE_DATA : MemoryMode.IMMUTABLE_DATA; + OtlpStdoutMetricExporter delegate = + new OtlpStdoutMetricExporterBuilder(logger) + .setWrapperJsonObject(wrapperJsonObject) + .setMemoryMode(memoryMode) + .build(); + return new OtlpJsonLoggingMetricExporter(delegate, aggregationTemporality); + } + OtlpJsonLoggingMetricExporter( OtlpStdoutMetricExporter delegate, AggregationTemporality aggregationTemporality) { this.delegate = delegate; diff --git a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingSpanExporter.java b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingSpanExporter.java index 63901351326..f74da49c62e 100644 --- a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingSpanExporter.java +++ b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingSpanExporter.java @@ -8,6 +8,7 @@ import io.opentelemetry.exporter.logging.otlp.internal.traces.OtlpStdoutSpanExporter; import io.opentelemetry.exporter.logging.otlp.internal.traces.OtlpStdoutSpanExporterBuilder; import io.opentelemetry.sdk.common.CompletableResultCode; +import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.trace.data.SpanData; import io.opentelemetry.sdk.trace.export.SpanExporter; import java.util.Collection; @@ -31,6 +32,24 @@ public static SpanExporter create() { return new OtlpJsonLoggingSpanExporter(delegate); } + /** + * Returns a new {@link OtlpJsonLoggingSpanExporter}. + * + * @param wrapperJsonObject whether to wrap the JSON object in an outer JSON "resourceSpans" + * object. When {@code true}, uses low allocation OTLP marshalers with {@link + * MemoryMode#REUSABLE_DATA}. When {@code false}, uses {@link MemoryMode#IMMUTABLE_DATA}. + */ + public static SpanExporter create(boolean wrapperJsonObject) { + MemoryMode memoryMode = + wrapperJsonObject ? MemoryMode.REUSABLE_DATA : MemoryMode.IMMUTABLE_DATA; + OtlpStdoutSpanExporter delegate = + new OtlpStdoutSpanExporterBuilder(logger) + .setWrapperJsonObject(wrapperJsonObject) + .setMemoryMode(memoryMode) + .build(); + return new OtlpJsonLoggingSpanExporter(delegate); + } + OtlpJsonLoggingSpanExporter(OtlpStdoutSpanExporter delegate) { this.delegate = delegate; } diff --git a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingLogRecordExporterTest.java b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingLogRecordExporterTest.java index 75677620dbb..7395e38430d 100644 --- a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingLogRecordExporterTest.java +++ b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingLogRecordExporterTest.java @@ -45,6 +45,36 @@ void log() throws Exception { assertThat(message).doesNotContain("\n"); } + @Test + void logWithWrapperJsonObjectFalse() throws Exception { + // Test that wrapperJsonObject=false produces the same output as the default create() + LogRecordExporter exporterWithoutWrapper = OtlpJsonLoggingLogRecordExporter.create(false); + testDataExporter.export(exporterWithoutWrapper); + + assertThat(logs.getEvents()) + .hasSize(1) + .allSatisfy(log -> assertThat(log.getLevel()).isEqualTo(Level.INFO)); + String message = logs.getEvents().get(0).getMessage(); + String expectedJson = testDataExporter.getExpectedJson(false); + JSONAssert.assertEquals("Got \n" + message, expectedJson, message, /* strict= */ false); + assertThat(message).doesNotContain("\n"); + } + + @Test + void logWithWrapperJsonObjectTrue() throws Exception { + // Test that wrapperJsonObject=true produces wrapper format (enables low allocation) + LogRecordExporter exporterWithWrapper = OtlpJsonLoggingLogRecordExporter.create(true); + testDataExporter.export(exporterWithWrapper); + + assertThat(logs.getEvents()) + .hasSize(1) + .allSatisfy(log -> assertThat(log.getLevel()).isEqualTo(Level.INFO)); + String message = logs.getEvents().get(0).getMessage(); + String expectedJson = testDataExporter.getExpectedJson(true); + JSONAssert.assertEquals("Got \n" + message, expectedJson, message, /* strict= */ false); + assertThat(message).doesNotContain("\n"); + } + @Test void shutdown() { assertThat(exporter.shutdown().isSuccess()).isTrue(); diff --git a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingMetricExporterTest.java b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingMetricExporterTest.java index ccbfdb26dce..69b11aa0b60 100644 --- a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingMetricExporterTest.java +++ b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingMetricExporterTest.java @@ -47,6 +47,28 @@ void getAggregationTemporality() { .isEqualTo(AggregationTemporality.DELTA); } + @Test + void getAggregationTemporalityWithWrapperJsonObject() { + // Test that the new create method with wrapperJsonObject parameter maintains correct + // aggregation temporality + assertThat( + OtlpJsonLoggingMetricExporter.create(AggregationTemporality.CUMULATIVE, false) + .getAggregationTemporality(InstrumentType.COUNTER)) + .isEqualTo(AggregationTemporality.CUMULATIVE); + assertThat( + OtlpJsonLoggingMetricExporter.create(AggregationTemporality.DELTA, false) + .getAggregationTemporality(InstrumentType.COUNTER)) + .isEqualTo(AggregationTemporality.DELTA); + assertThat( + OtlpJsonLoggingMetricExporter.create(AggregationTemporality.CUMULATIVE, true) + .getAggregationTemporality(InstrumentType.COUNTER)) + .isEqualTo(AggregationTemporality.CUMULATIVE); + assertThat( + OtlpJsonLoggingMetricExporter.create(AggregationTemporality.DELTA, true) + .getAggregationTemporality(InstrumentType.COUNTER)) + .isEqualTo(AggregationTemporality.DELTA); + } + @Test void log() throws Exception { testDataExporter.export(exporter); @@ -60,6 +82,54 @@ void log() throws Exception { assertThat(message).doesNotContain("\n"); } + @Test + void logWithWrapperJsonObjectFalse() throws Exception { + // Test that wrapperJsonObject=false produces the same output as the default create() + MetricExporter exporterWithoutWrapper = + OtlpJsonLoggingMetricExporter.create(AggregationTemporality.CUMULATIVE, false); + testDataExporter.export(exporterWithoutWrapper); + + assertThat(logs.getEvents()) + .hasSize(1) + .allSatisfy(log -> assertThat(log.getLevel()).isEqualTo(Level.INFO)); + String message = logs.getEvents().get(0).getMessage(); + String expectedJson = testDataExporter.getExpectedJson(false); + JSONAssert.assertEquals("Got \n" + message, expectedJson, message, /* strict= */ false); + assertThat(message).doesNotContain("\n"); + } + + @Test + void logWithWrapperJsonObjectTrue() throws Exception { + // Test that wrapperJsonObject=true produces wrapper format (enables low allocation) + MetricExporter exporterWithWrapper = + OtlpJsonLoggingMetricExporter.create(AggregationTemporality.CUMULATIVE, true); + testDataExporter.export(exporterWithWrapper); + + assertThat(logs.getEvents()) + .hasSize(1) + .allSatisfy(log -> assertThat(log.getLevel()).isEqualTo(Level.INFO)); + String message = logs.getEvents().get(0).getMessage(); + String expectedJson = testDataExporter.getExpectedJson(true); + JSONAssert.assertEquals("Got \n" + message, expectedJson, message, /* strict= */ false); + assertThat(message).doesNotContain("\n"); + } + + @Test + void logWithWrapperJsonObjectTrueAndDeltaTemporality() throws Exception { + // Test that wrapperJsonObject=true works with DELTA temporality too + MetricExporter exporterWithWrapper = + OtlpJsonLoggingMetricExporter.create(AggregationTemporality.DELTA, true); + testDataExporter.export(exporterWithWrapper); + + assertThat(logs.getEvents()) + .hasSize(1) + .allSatisfy(log -> assertThat(log.getLevel()).isEqualTo(Level.INFO)); + String message = logs.getEvents().get(0).getMessage(); + String expectedJson = testDataExporter.getExpectedJson(true); + JSONAssert.assertEquals("Got \n" + message, expectedJson, message, /* strict= */ false); + assertThat(message).doesNotContain("\n"); + } + @Test void flush() { assertThat(exporter.flush().isSuccess()).isTrue(); diff --git a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingSpanExporterTest.java b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingSpanExporterTest.java index ac3129f4a1c..66ed9e3b188 100644 --- a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingSpanExporterTest.java +++ b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpJsonLoggingSpanExporterTest.java @@ -46,6 +46,36 @@ void log() throws Exception { assertThat(message).doesNotContain("\n"); } + @Test + void logWithWrapperJsonObjectFalse() throws Exception { + // Test that wrapperJsonObject=false produces the same output as the default create() + SpanExporter exporterWithoutWrapper = OtlpJsonLoggingSpanExporter.create(false); + testDataExporter.export(exporterWithoutWrapper); + + assertThat(logs.getEvents()) + .hasSize(1) + .allSatisfy(log -> assertThat(log.getLevel()).isEqualTo(Level.INFO)); + String message = logs.getEvents().get(0).getMessage(); + String expectedJson = testDataExporter.getExpectedJson(false); + JSONAssert.assertEquals("Got \n" + message, expectedJson, message, /* strict= */ false); + assertThat(message).doesNotContain("\n"); + } + + @Test + void logWithWrapperJsonObjectTrue() throws Exception { + // Test that wrapperJsonObject=true produces wrapper format (enables low allocation) + SpanExporter exporterWithWrapper = OtlpJsonLoggingSpanExporter.create(true); + testDataExporter.export(exporterWithWrapper); + + assertThat(logs.getEvents()) + .hasSize(1) + .allSatisfy(log -> assertThat(log.getLevel()).isEqualTo(Level.INFO)); + String message = logs.getEvents().get(0).getMessage(); + String expectedJson = testDataExporter.getExpectedJson(true); + JSONAssert.assertEquals("Got \n" + message, expectedJson, message, /* strict= */ false); + assertThat(message).doesNotContain("\n"); + } + @Test void flush() { assertThat(exporter.flush().isSuccess()).isTrue();