diff --git a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/logs/OtlpStdoutLogRecordExporterBuilder.java b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/logs/OtlpStdoutLogRecordExporterBuilder.java index 76e6adb20ad..eab4c747e16 100644 --- a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/logs/OtlpStdoutLogRecordExporterBuilder.java +++ b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/logs/OtlpStdoutLogRecordExporterBuilder.java @@ -14,6 +14,7 @@ import io.opentelemetry.sdk.common.export.MemoryMode; import java.io.OutputStream; import java.util.logging.Logger; +import javax.annotation.Nullable; /** * Builder for {@link OtlpJsonLoggingLogRecordExporter}. @@ -26,13 +27,14 @@ public final class OtlpStdoutLogRecordExporterBuilder { private static final String TYPE = "log records"; private final Logger logger; - private JsonWriter jsonWriter; + @Nullable private OutputStream outputStream; + @Nullable private Logger outputLogger; private boolean wrapperJsonObject = true; private MemoryMode memoryMode = MemoryMode.IMMUTABLE_DATA; + private boolean prettyPrint; public OtlpStdoutLogRecordExporterBuilder(Logger logger) { this.logger = logger; - this.jsonWriter = new LoggerJsonWriter(logger, TYPE); } /** @@ -57,6 +59,12 @@ public OtlpStdoutLogRecordExporterBuilder setMemoryMode(MemoryMode memoryMode) { return this; } + /** Sets the exporter to use pretty-printed JSON output. */ + public OtlpStdoutLogRecordExporterBuilder setPrettyPrint(boolean prettyPrint) { + this.prettyPrint = prettyPrint; + return this; + } + /** * Sets the exporter to use the specified output stream. * @@ -67,14 +75,16 @@ public OtlpStdoutLogRecordExporterBuilder setMemoryMode(MemoryMode memoryMode) { */ public OtlpStdoutLogRecordExporterBuilder setOutput(OutputStream outputStream) { requireNonNull(outputStream, "outputStream"); - this.jsonWriter = new StreamJsonWriter(outputStream, TYPE); + this.outputStream = outputStream; + this.outputLogger = null; return this; } /** Sets the exporter to use the specified logger. */ public OtlpStdoutLogRecordExporterBuilder setOutput(Logger logger) { requireNonNull(logger, "logger"); - this.jsonWriter = new LoggerJsonWriter(logger, TYPE); + this.outputLogger = logger; + this.outputStream = null; return this; } @@ -88,6 +98,13 @@ public OtlpStdoutLogRecordExporter build() { throw new IllegalArgumentException( "Reusable data mode is not supported without wrapperJsonObject"); } + JsonWriter jsonWriter; + if (outputStream != null) { + jsonWriter = new StreamJsonWriter(outputStream, TYPE, prettyPrint); + } else { + Logger writerLogger = outputLogger != null ? outputLogger : this.logger; + jsonWriter = new LoggerJsonWriter(writerLogger, TYPE, prettyPrint); + } return new OtlpStdoutLogRecordExporter(logger, jsonWriter, wrapperJsonObject, memoryMode); } } diff --git a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/logs/OtlpStdoutLogRecordExporterComponentProvider.java b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/logs/OtlpStdoutLogRecordExporterComponentProvider.java index 3feebf33811..ed914b0af6f 100644 --- a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/logs/OtlpStdoutLogRecordExporterComponentProvider.java +++ b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/logs/OtlpStdoutLogRecordExporterComponentProvider.java @@ -32,6 +32,10 @@ public String getName() { public LogRecordExporter create(DeclarativeConfigProperties config) { OtlpStdoutLogRecordExporterBuilder builder = OtlpStdoutLogRecordExporter.builder(); IncubatingExporterBuilderUtil.configureExporterMemoryMode(config, builder::setMemoryMode); + Boolean prettyPrint = config.getBoolean("pretty_print"); + if (prettyPrint != null) { + builder.setPrettyPrint(prettyPrint); + } return builder.build(); } } diff --git a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/metrics/OtlpStdoutMetricExporterBuilder.java b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/metrics/OtlpStdoutMetricExporterBuilder.java index 945ffd778bd..963616a9c1f 100644 --- a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/metrics/OtlpStdoutMetricExporterBuilder.java +++ b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/metrics/OtlpStdoutMetricExporterBuilder.java @@ -18,6 +18,7 @@ import io.opentelemetry.sdk.metrics.export.MetricExporter; import java.io.OutputStream; import java.util.logging.Logger; +import javax.annotation.Nullable; /** * Builder for {@link OtlpJsonLoggingMetricExporter}. @@ -39,13 +40,14 @@ public final class OtlpStdoutMetricExporterBuilder { DefaultAggregationSelector.getDefault(); private final Logger logger; - private JsonWriter jsonWriter; + @Nullable private OutputStream outputStream; + @Nullable private Logger outputLogger; private boolean wrapperJsonObject = true; private MemoryMode memoryMode = MemoryMode.IMMUTABLE_DATA; + private boolean prettyPrint; public OtlpStdoutMetricExporterBuilder(Logger logger) { this.logger = logger; - this.jsonWriter = new LoggerJsonWriter(logger, TYPE); } /** @@ -70,6 +72,12 @@ public OtlpStdoutMetricExporterBuilder setMemoryMode(MemoryMode memoryMode) { return this; } + /** Sets the exporter to use pretty-printed JSON output. */ + public OtlpStdoutMetricExporterBuilder setPrettyPrint(boolean prettyPrint) { + this.prettyPrint = prettyPrint; + return this; + } + /** * Sets the exporter to use the specified output stream. * @@ -80,14 +88,16 @@ public OtlpStdoutMetricExporterBuilder setMemoryMode(MemoryMode memoryMode) { */ public OtlpStdoutMetricExporterBuilder setOutput(OutputStream outputStream) { requireNonNull(outputStream, "outputStream"); - this.jsonWriter = new StreamJsonWriter(outputStream, TYPE); + this.outputStream = outputStream; + this.outputLogger = null; return this; } /** Sets the exporter to use the specified logger. */ public OtlpStdoutMetricExporterBuilder setOutput(Logger logger) { requireNonNull(logger, "logger"); - this.jsonWriter = new LoggerJsonWriter(logger, TYPE); + this.outputLogger = logger; + this.outputStream = null; return this; } @@ -131,6 +141,13 @@ public OtlpStdoutMetricExporter build() { throw new IllegalArgumentException( "Reusable data mode is not supported without wrapperJsonObject"); } + JsonWriter jsonWriter; + if (outputStream != null) { + jsonWriter = new StreamJsonWriter(outputStream, TYPE, prettyPrint); + } else { + Logger writerLogger = outputLogger != null ? outputLogger : this.logger; + jsonWriter = new LoggerJsonWriter(writerLogger, TYPE, prettyPrint); + } return new OtlpStdoutMetricExporter( logger, jsonWriter, diff --git a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/metrics/OtlpStdoutMetricExporterComponentProvider.java b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/metrics/OtlpStdoutMetricExporterComponentProvider.java index bf8d8a73be9..1479b37373b 100644 --- a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/metrics/OtlpStdoutMetricExporterComponentProvider.java +++ b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/metrics/OtlpStdoutMetricExporterComponentProvider.java @@ -36,6 +36,10 @@ public MetricExporter create(DeclarativeConfigProperties config) { config, builder::setAggregationTemporalitySelector); IncubatingExporterBuilderUtil.configureOtlpHistogramDefaultAggregation( config, builder::setDefaultAggregationSelector); + Boolean prettyPrint = config.getBoolean("pretty_print"); + if (prettyPrint != null) { + builder.setPrettyPrint(prettyPrint); + } return builder.build(); } } diff --git a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/traces/OtlpStdoutSpanExporterBuilder.java b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/traces/OtlpStdoutSpanExporterBuilder.java index 341f63c6e49..e3717375700 100644 --- a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/traces/OtlpStdoutSpanExporterBuilder.java +++ b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/traces/OtlpStdoutSpanExporterBuilder.java @@ -14,6 +14,7 @@ import io.opentelemetry.sdk.common.export.MemoryMode; import java.io.OutputStream; import java.util.logging.Logger; +import javax.annotation.Nullable; /** * Builder for {@link OtlpJsonLoggingSpanExporter}. @@ -26,13 +27,14 @@ public final class OtlpStdoutSpanExporterBuilder { private static final String TYPE = "spans"; private final Logger logger; - private JsonWriter jsonWriter; + @Nullable private OutputStream outputStream; + @Nullable private Logger outputLogger; private boolean wrapperJsonObject = true; private MemoryMode memoryMode = MemoryMode.IMMUTABLE_DATA; + private boolean prettyPrint; public OtlpStdoutSpanExporterBuilder(Logger logger) { this.logger = logger; - this.jsonWriter = new LoggerJsonWriter(logger, TYPE); } /** @@ -57,6 +59,12 @@ public OtlpStdoutSpanExporterBuilder setMemoryMode(MemoryMode memoryMode) { return this; } + /** Sets the exporter to use pretty-printed JSON output. */ + public OtlpStdoutSpanExporterBuilder setPrettyPrint(boolean prettyPrint) { + this.prettyPrint = prettyPrint; + return this; + } + /** * Sets the exporter to use the specified output stream. * @@ -67,14 +75,16 @@ public OtlpStdoutSpanExporterBuilder setMemoryMode(MemoryMode memoryMode) { */ public OtlpStdoutSpanExporterBuilder setOutput(OutputStream outputStream) { requireNonNull(outputStream, "outputStream"); - this.jsonWriter = new StreamJsonWriter(outputStream, TYPE); + this.outputStream = outputStream; + this.outputLogger = null; return this; } /** Sets the exporter to use the specified logger. */ public OtlpStdoutSpanExporterBuilder setOutput(Logger logger) { requireNonNull(logger, "logger"); - this.jsonWriter = new LoggerJsonWriter(logger, TYPE); + this.outputLogger = logger; + this.outputStream = null; return this; } @@ -88,6 +98,13 @@ public OtlpStdoutSpanExporter build() { throw new IllegalArgumentException( "Reusable data mode is not supported without wrapperJsonObject"); } + JsonWriter jsonWriter; + if (outputStream != null) { + jsonWriter = new StreamJsonWriter(outputStream, TYPE, prettyPrint); + } else { + Logger writerLogger = outputLogger != null ? outputLogger : this.logger; + jsonWriter = new LoggerJsonWriter(writerLogger, TYPE, prettyPrint); + } return new OtlpStdoutSpanExporter(logger, jsonWriter, wrapperJsonObject, memoryMode); } } diff --git a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/traces/OtlpStdoutSpanExporterComponentProvider.java b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/traces/OtlpStdoutSpanExporterComponentProvider.java index 969b804f863..500a5c338df 100644 --- a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/traces/OtlpStdoutSpanExporterComponentProvider.java +++ b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/traces/OtlpStdoutSpanExporterComponentProvider.java @@ -32,6 +32,10 @@ public String getName() { public SpanExporter create(DeclarativeConfigProperties config) { OtlpStdoutSpanExporterBuilder builder = OtlpStdoutSpanExporter.builder(); IncubatingExporterBuilderUtil.configureExporterMemoryMode(config, builder::setMemoryMode); + Boolean prettyPrint = config.getBoolean("pretty_print"); + if (prettyPrint != null) { + builder.setPrettyPrint(prettyPrint); + } return builder.build(); } } diff --git a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/writer/JsonUtil.java b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/writer/JsonUtil.java index 0b74ed8a478..82aa8e37f7a 100644 --- a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/writer/JsonUtil.java +++ b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/writer/JsonUtil.java @@ -9,6 +9,7 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.io.SegmentedStringWriter; import java.io.IOException; +import java.io.OutputStream; /** * This class is internal and is hence not for public use. Its APIs are unstable and can change at @@ -26,5 +27,13 @@ public static JsonGenerator create(SegmentedStringWriter stringWriter) { } } + public static JsonGenerator create(OutputStream os) { + try { + return JSON_FACTORY.createGenerator(os); + } catch (IOException e) { + throw new IllegalStateException("Unable to create in-memory JsonGenerator, can't happen.", e); + } + } + private JsonUtil() {} } diff --git a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/writer/LoggerJsonWriter.java b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/writer/LoggerJsonWriter.java index a95c5e0d2c1..2c87cf1530c 100644 --- a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/writer/LoggerJsonWriter.java +++ b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/writer/LoggerJsonWriter.java @@ -23,16 +23,21 @@ public class LoggerJsonWriter implements JsonWriter { private final Logger logger; private final String type; + private final boolean prettyPrint; - public LoggerJsonWriter(Logger logger, String type) { + public LoggerJsonWriter(Logger logger, String type, boolean prettyPrint) { this.logger = logger; this.type = type; + this.prettyPrint = prettyPrint; } @Override public CompletableResultCode write(Marshaler exportRequest) { SegmentedStringWriter sw = new SegmentedStringWriter(JSON_FACTORY._getBufferRecycler()); try (JsonGenerator gen = JsonUtil.create(sw)) { + if (prettyPrint) { + gen.useDefaultPrettyPrinter(); + } exportRequest.writeJsonToGenerator(gen); } catch (IOException e) { logger.log(Level.WARNING, "Unable to write OTLP JSON " + type, e); diff --git a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/writer/StreamJsonWriter.java b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/writer/StreamJsonWriter.java index 6c45ee0d696..9b8da91ce3b 100644 --- a/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/writer/StreamJsonWriter.java +++ b/exporters/logging-otlp/src/main/java/io/opentelemetry/exporter/logging/otlp/internal/writer/StreamJsonWriter.java @@ -5,7 +5,6 @@ package io.opentelemetry.exporter.logging.otlp.internal.writer; -import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.sdk.common.CompletableResultCode; @@ -21,27 +20,28 @@ */ public class StreamJsonWriter implements JsonWriter { - public static final JsonFactory JSON_FACTORY = new JsonFactory(); - private static final Logger internalLogger = Logger.getLogger(StreamJsonWriter.class.getName()); private final ThrottlingLogger logger = new ThrottlingLogger(internalLogger); private final String type; private final OutputStream outputStream; + private final boolean prettyPrint; - public StreamJsonWriter(OutputStream originalStream, String type) { + public StreamJsonWriter(OutputStream originalStream, String type, boolean prettyPrint) { this.outputStream = originalStream; this.type = type; + this.prettyPrint = prettyPrint; } @Override public CompletableResultCode write(Marshaler exportRequest) { - try { - exportRequest.writeJsonWithNewline( - JSON_FACTORY - .createGenerator(outputStream) - .disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET)); + try (JsonGenerator gen = JsonUtil.create(outputStream)) { + gen.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); + if (prettyPrint) { + gen.useDefaultPrettyPrinter(); + } + exportRequest.writeJsonWithNewline(gen); return CompletableResultCode.ofSuccess(); } catch (IOException e) { logger.log(Level.WARNING, "Unable to write OTLP JSON " + type, e); diff --git a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/AbstractOtlpStdoutExporterTest.java b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/AbstractOtlpStdoutExporterTest.java index 87b2a14c1aa..57d6bd333d4 100644 --- a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/AbstractOtlpStdoutExporterTest.java +++ b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/AbstractOtlpStdoutExporterTest.java @@ -79,7 +79,10 @@ public AbstractOtlpStdoutExporterTest( } protected abstract T createExporter( - @Nullable OutputStream outputStream, MemoryMode memoryMode, boolean wrapperJsonObject); + @Nullable OutputStream outputStream, + MemoryMode memoryMode, + boolean wrapperJsonObject, + boolean prettyPrint); protected abstract T createDefaultExporter(); @@ -137,11 +140,17 @@ public static class TestCase { private final MemoryMode memoryMode; private final boolean wrapperJsonObject; private final OutputType outputType; + private final boolean prettyPrint; - public TestCase(OutputType outputType, MemoryMode memoryMode, boolean wrapperJsonObject) { + public TestCase( + OutputType outputType, + MemoryMode memoryMode, + boolean wrapperJsonObject, + boolean prettyPrint) { this.outputType = outputType; this.memoryMode = memoryMode; this.wrapperJsonObject = wrapperJsonObject; + this.prettyPrint = prettyPrint; } public OutputType getOutputType() { @@ -155,6 +164,10 @@ public boolean isWrapperJsonObject() { public MemoryMode getMemoryMode() { return memoryMode; } + + public boolean isPrettyPrint() { + return prettyPrint; + } } static Stream exportTestCases() { @@ -186,20 +199,42 @@ static Stream exportTestCases() { MemoryMode.REUSABLE_DATA, /* wrapperJsonObject= */ false), testCase(OutputType.LOGGER, MemoryMode.REUSABLE_DATA, /* wrapperJsonObject= */ true), - testCase(OutputType.LOGGER, MemoryMode.REUSABLE_DATA, /* wrapperJsonObject= */ false)) + testCase(OutputType.LOGGER, MemoryMode.REUSABLE_DATA, /* wrapperJsonObject= */ false), + testCase( + OutputType.SYSTEM_OUT, + MemoryMode.IMMUTABLE_DATA, + /* wrapperJsonObject= */ false, + /* prettyPrint= */ true), + testCase( + OutputType.FILE, + MemoryMode.IMMUTABLE_DATA, + /* wrapperJsonObject= */ false, + /* prettyPrint= */ true), + testCase( + OutputType.LOGGER, + MemoryMode.IMMUTABLE_DATA, + /* wrapperJsonObject= */ false, + /* prettyPrint= */ true)) .stream(); } private static Arguments testCase( OutputType type, MemoryMode memoryMode, boolean wrapperJsonObject) { + return testCase(type, memoryMode, wrapperJsonObject, /* prettyPrint= */ false); + } + + private static Arguments testCase( + OutputType type, MemoryMode memoryMode, boolean wrapperJsonObject, boolean prettyPrint) { return Arguments.of( "output=" + type + ", wrapperJsonObject=" + wrapperJsonObject + ", memoryMode=" - + memoryMode, - new TestCase(type, memoryMode, wrapperJsonObject)); + + memoryMode + + ", prettyPrint=" + + prettyPrint, + new TestCase(type, memoryMode, wrapperJsonObject, prettyPrint)); } @SuppressWarnings("SystemOut") @@ -229,7 +264,11 @@ void exportWithProgrammaticConfig(String name, TestCase testCase) throws Excepti Supplier exporter = () -> - createExporter(outputStream, testCase.getMemoryMode(), testCase.isWrapperJsonObject()); + createExporter( + outputStream, + testCase.getMemoryMode(), + testCase.isWrapperJsonObject(), + testCase.isPrettyPrint()); if (testCase.getMemoryMode() == MemoryMode.REUSABLE_DATA && !testCase.isWrapperJsonObject()) { assertThatExceptionOfType(IllegalArgumentException.class) @@ -244,12 +283,15 @@ void exportWithProgrammaticConfig(String name, TestCase testCase) throws Excepti String expectedJson = testDataExporter.getExpectedJson(testCase.isWrapperJsonObject()); JSONAssert.assertEquals("Got \n" + output, expectedJson, output, false); - if (testCase.isWrapperJsonObject()) { + if (testCase.isPrettyPrint()) { + assertThat(output).contains("\n "); + } else if (testCase.isWrapperJsonObject()) { assertThat(output).doesNotContain("\n"); } - if (file == null) { + if (file == null && !testCase.isPrettyPrint()) { // no need to test again for file - and it's not working with files + // skip double output for pretty print since multi-line output can't be split by \n assertDoubleOutput(exporter, expectedJson, outputStream); } } @@ -325,6 +367,13 @@ void componentProviderConfig() { assertThat(exporterFromComponentProvider(properties)) .extracting("memoryMode") .isEqualTo(MemoryMode.REUSABLE_DATA); + + DeclarativeConfigProperties prettyPrintProperties = spy(DeclarativeConfigProperties.empty()); + when(prettyPrintProperties.getBoolean("pretty_print")).thenReturn(true); + assertThat(exporterFromComponentProvider(prettyPrintProperties)) + .extracting("jsonWriter") + .extracting("prettyPrint") + .isEqualTo(true); } @SuppressWarnings("unchecked") diff --git a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutLogRecordExporterTest.java b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutLogRecordExporterTest.java index c19fba0fe3e..2bec37a1de6 100644 --- a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutLogRecordExporterTest.java +++ b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutLogRecordExporterTest.java @@ -33,11 +33,15 @@ protected OtlpStdoutLogRecordExporter createDefaultExporter() { @Override protected OtlpStdoutLogRecordExporter createExporter( - @Nullable OutputStream outputStream, MemoryMode memoryMode, boolean wrapperJsonObject) { + @Nullable OutputStream outputStream, + MemoryMode memoryMode, + boolean wrapperJsonObject, + boolean prettyPrint) { OtlpStdoutLogRecordExporterBuilder builder = OtlpStdoutLogRecordExporter.builder() .setMemoryMode(memoryMode) - .setWrapperJsonObject(wrapperJsonObject); + .setWrapperJsonObject(wrapperJsonObject) + .setPrettyPrint(prettyPrint); if (outputStream != null) { builder.setOutput(outputStream); } else { diff --git a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutMetricExporterTest.java b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutMetricExporterTest.java index 28b0b2e3587..e3399d131df 100644 --- a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutMetricExporterTest.java +++ b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutMetricExporterTest.java @@ -47,11 +47,15 @@ protected OtlpStdoutMetricExporter createDefaultExporter() { @Override protected OtlpStdoutMetricExporter createExporter( - @Nullable OutputStream outputStream, MemoryMode memoryMode, boolean wrapperJsonObject) { + @Nullable OutputStream outputStream, + MemoryMode memoryMode, + boolean wrapperJsonObject, + boolean prettyPrint) { OtlpStdoutMetricExporterBuilder builder = OtlpStdoutMetricExporter.builder() .setMemoryMode(memoryMode) - .setWrapperJsonObject(wrapperJsonObject); + .setWrapperJsonObject(wrapperJsonObject) + .setPrettyPrint(prettyPrint); if (outputStream != null) { builder.setOutput(outputStream); } else { diff --git a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutSpanExporterTest.java b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutSpanExporterTest.java index 01d3a96ccd2..e5049a96add 100644 --- a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutSpanExporterTest.java +++ b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/OtlpStdoutSpanExporterTest.java @@ -32,11 +32,15 @@ protected OtlpStdoutSpanExporter createDefaultExporter() { @Override protected OtlpStdoutSpanExporter createExporter( - @Nullable OutputStream outputStream, MemoryMode memoryMode, boolean wrapperJsonObject) { + @Nullable OutputStream outputStream, + MemoryMode memoryMode, + boolean wrapperJsonObject, + boolean prettyPrint) { OtlpStdoutSpanExporterBuilder builder = OtlpStdoutSpanExporter.builder() .setMemoryMode(memoryMode) - .setWrapperJsonObject(wrapperJsonObject); + .setWrapperJsonObject(wrapperJsonObject) + .setPrettyPrint(prettyPrint); if (outputStream != null) { builder.setOutput(outputStream); } else { diff --git a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/internal/writer/LoggerJsonWriterTest.java b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/internal/writer/LoggerJsonWriterTest.java index 680c7cc444d..2c1b2c2d29c 100644 --- a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/internal/writer/LoggerJsonWriterTest.java +++ b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/internal/writer/LoggerJsonWriterTest.java @@ -7,13 +7,16 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import com.fasterxml.jackson.core.JsonGenerator; import io.github.netmikey.logunit.api.LogCapturer; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.internal.testing.slf4j.SuppressLogger; import java.io.IOException; +import java.util.logging.Level; import java.util.logging.Logger; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -25,9 +28,46 @@ class LoggerJsonWriterTest { @RegisterExtension static final LogCapturer logs = LogCapturer.create().captureForType(LoggerJsonWriter.class); + @Test + void write_prettyPrintEnabled() throws IOException { + Logger logger = mock(Logger.class); + LoggerJsonWriter writer = new LoggerJsonWriter(logger, "type", true); + + writer.write(simpleObjectMarshaler()); + + verify(logger) + .log(Level.INFO, "{\n \"key\" : \"value\"\n}".replaceAll("\n", System.lineSeparator())); + } + + @Test + void write_prettyPrintDisabled() throws IOException { + Logger logger = mock(Logger.class); + LoggerJsonWriter writer = new LoggerJsonWriter(logger, "type", false); + + writer.write(simpleObjectMarshaler()); + + verify(logger).log(Level.INFO, "{\"key\":\"value\"}"); + } + + private static Marshaler simpleObjectMarshaler() throws IOException { + Marshaler marshaler = mock(Marshaler.class); + doAnswer( + invocation -> { + JsonGenerator gen = invocation.getArgument(0); + gen.writeStartObject(); + gen.writeFieldName("key"); + gen.writeString("value"); + gen.writeEndObject(); + return null; + }) + .when(marshaler) + .writeJsonToGenerator(any(JsonGenerator.class)); + return marshaler; + } + @Test void testToString() { - LoggerJsonWriter writer = new LoggerJsonWriter(null, "type"); + LoggerJsonWriter writer = new LoggerJsonWriter(null, "type", false); assertThat(writer.toString()).isEqualTo("LoggerJsonWriter"); } @@ -40,7 +80,7 @@ void error() throws IOException { Logger logger = Logger.getLogger(LoggerJsonWriter.class.getName()); - LoggerJsonWriter writer = new LoggerJsonWriter(logger, "type"); + LoggerJsonWriter writer = new LoggerJsonWriter(logger, "type", false); writer.write(marshaler); logs.assertContains("Unable to write OTLP JSON type"); diff --git a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/internal/writer/StreamJsonWriterTest.java b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/internal/writer/StreamJsonWriterTest.java index 048fcab1c17..accecc9c753 100644 --- a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/internal/writer/StreamJsonWriterTest.java +++ b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/internal/writer/StreamJsonWriterTest.java @@ -7,15 +7,18 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import com.fasterxml.jackson.core.JsonGenerator; import io.github.netmikey.logunit.api.LogCapturer; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.internal.testing.slf4j.SuppressLogger; +import java.io.ByteArrayOutputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import org.junit.jupiter.api.Test; @@ -32,16 +35,56 @@ class StreamJsonWriterTest { @TempDir Path tempDir; + @Test + void write_prettyPrintEnabled() throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + StreamJsonWriter writer = new StreamJsonWriter(baos, "type", true); + + writer.write(simpleObjectMarshaler()); + + assertThat(new String(baos.toByteArray(), StandardCharsets.UTF_8).replace("\r\n", "\n")) + .isEqualTo("{\n \"key\" : \"value\"\n}\n"); + } + + @Test + void write_prettyPrintDisabled() throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + StreamJsonWriter writer = new StreamJsonWriter(baos, "type", false); + + writer.write(simpleObjectMarshaler()); + + assertThat(new String(baos.toByteArray(), StandardCharsets.UTF_8)) + .isEqualTo("{\"key\":\"value\"}\n"); + } + + private static Marshaler simpleObjectMarshaler() throws IOException { + Marshaler marshaler = mock(Marshaler.class); + doAnswer( + invocation -> { + JsonGenerator gen = invocation.getArgument(0); + gen.writeStartObject(); + gen.writeFieldName("key"); + gen.writeString("value"); + gen.writeEndObject(); + gen.writeRaw("\n"); + return null; + }) + .when(marshaler) + .writeJsonWithNewline(any(JsonGenerator.class)); + return marshaler; + } + @Test @SuppressWarnings("SystemOut") void testToString() throws IOException { assertThat( - new StreamJsonWriter(Files.newOutputStream(tempDir.resolve("foo")), "type").toString()) + new StreamJsonWriter(Files.newOutputStream(tempDir.resolve("foo")), "type", false) + .toString()) .startsWith("StreamJsonWriter{outputStream=") .contains("Channel"); - assertThat(new StreamJsonWriter(System.out, "type").toString()) + assertThat(new StreamJsonWriter(System.out, "type", false).toString()) .isEqualTo("StreamJsonWriter{outputStream=stdout}"); - assertThat(new StreamJsonWriter(System.err, "type").toString()) + assertThat(new StreamJsonWriter(System.err, "type", false).toString()) .isEqualTo("StreamJsonWriter{outputStream=stderr}"); } @@ -52,7 +95,7 @@ void errorWriting() throws IOException { .when(marshaler) .writeJsonWithNewline(any(JsonGenerator.class)); - StreamJsonWriter writer = new StreamJsonWriter(System.out, "type"); + StreamJsonWriter writer = new StreamJsonWriter(System.out, "type", false); writer.write(marshaler); logs.assertContains("Unable to write OTLP JSON type"); @@ -68,7 +111,7 @@ public void flush() throws IOException { } }; - StreamJsonWriter writer = new StreamJsonWriter(outputStream, "type"); + StreamJsonWriter writer = new StreamJsonWriter(outputStream, "type", false); writer.flush(); logs.assertContains("Failed to flush items");