Skip to content

Commit

Permalink
add stdout exporter
Browse files Browse the repository at this point in the history
  • Loading branch information
zeitlinger committed Aug 21, 2024
1 parent 4232c08 commit 069ce26
Show file tree
Hide file tree
Showing 13 changed files with 271 additions and 173 deletions.
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
Comparing source compatibility of opentelemetry-exporter-logging-otlp-1.42.0-SNAPSHOT.jar against opentelemetry-exporter-logging-otlp-1.41.0.jar
No changes.
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingLogRecordExporter (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) java.lang.String toString()
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingMetricExporter (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.metrics.Aggregation getDefaultAggregation(io.opentelemetry.sdk.metrics.InstrumentType)
+++ NEW METHOD: PUBLIC(+) java.lang.String toString()
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.logging.otlp.OtlpJsonLoggingSpanExporter (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) java.lang.String toString()
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,12 @@ public String toString() {
joiner.add("memoryMode=" + memoryMode);
joiner.add("wrapperJsonObject=" + wrapperJsonObject);
joiner.add("jsonWriter=" + jsonWriter);
joiner.add(
"aggregationTemporalitySelector="
+ AggregationTemporalitySelector.asString(aggregationTemporalitySelector));
joiner.add(
"defaultAggregationSelector="
+ DefaultAggregationSelector.asString(defaultAggregationSelector));
return joiner.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package io.opentelemetry.exporter.logging.otlp.internal.logs;

import io.opentelemetry.exporter.internal.ExporterBuilderUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogRecordExporterProvider;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
Expand All @@ -18,7 +19,9 @@
public class OtlpStdoutLogRecordExporterProvider implements ConfigurableLogRecordExporterProvider {
@Override
public LogRecordExporter createExporter(ConfigProperties config) {
return OtlpStdoutLogRecordExporter.create();
OtlpJsonLoggingLogRecordExporterBuilder builder = OtlpStdoutLogRecordExporter.builder();
ExporterBuilderUtil.configureExporterMemoryMode(config, builder::setMemoryMode);
return builder.build();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package io.opentelemetry.exporter.logging.otlp.internal.metrics;

import io.opentelemetry.exporter.internal.ExporterBuilderUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.metrics.ConfigurableMetricExporterProvider;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
Expand All @@ -18,7 +19,13 @@
public class OtlpStdoutMetricExporterProvider implements ConfigurableMetricExporterProvider {
@Override
public MetricExporter createExporter(ConfigProperties config) {
return OtlpStdoutMetricExporter.create();
OtlpJsonLoggingMetricExporterBuilder builder = OtlpStdoutMetricExporter.builder();
ExporterBuilderUtil.configureExporterMemoryMode(config, builder::setMemoryMode);
ExporterBuilderUtil.configureOtlpAggregationTemporality(
config, builder::setAggregationTemporalitySelector);
ExporterBuilderUtil.configureOtlpHistogramDefaultAggregation(
config, builder::setDefaultAggregationSelector);
return builder.build();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package io.opentelemetry.exporter.logging.otlp.internal.trace;

import io.opentelemetry.exporter.internal.ExporterBuilderUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider;
import io.opentelemetry.sdk.trace.export.SpanExporter;
Expand All @@ -18,7 +19,9 @@
public class OtlpStdoutSpanExporterProvider implements ConfigurableSpanExporterProvider {
@Override
public SpanExporter createExporter(ConfigProperties config) {
return OtlpStdoutSpanExporter.create();
OtlpJsonLoggingSpanExporterBuilder builder = OtlpStdoutSpanExporter.builder();
ExporterBuilderUtil.configureExporterMemoryMode(config, builder::setMemoryMode);
return builder.build();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@

package io.opentelemetry.exporter.logging.otlp;

import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Streams;
import com.google.common.io.Resources;
import io.github.netmikey.logunit.api.LogCapturer;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.common.export.MemoryMode;
Expand All @@ -22,6 +27,9 @@
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
import javax.annotation.Nullable;
Expand Down Expand Up @@ -49,15 +57,29 @@ abstract class AbstractOtlpJsonLoggingExporterTest<T> {
private final Class<?> exporterClass;

@RegisterExtension LogCapturer logs;
private final String defaultConfigString;
private final Class<?> providerClass;

@SuppressWarnings("unused")
private final Class<?> componentProviderType; // todo remove once used

private final String expectedFileNoWrapper;
private final String expectedFileWrapper;

public AbstractOtlpJsonLoggingExporterTest(
Class<?> exporterClass, String expectedFileNoWrapper, String expectedFileWrapper) {
Class<?> exporterClass,
Class<?> providerClass,
Class<?> componentProviderType,
String expectedFileNoWrapper,
String expectedFileWrapper,
String defaultConfigString) {
this.exporterClass = exporterClass;
logs = LogCapturer.create().captureForType(exporterClass);
this.providerClass = providerClass;
this.componentProviderType = componentProviderType;
this.expectedFileNoWrapper = expectedFileNoWrapper;
this.expectedFileWrapper = expectedFileWrapper;
logs = LogCapturer.create().captureForType(exporterClass);
this.defaultConfigString = defaultConfigString;
}

protected abstract T createExporter(
Expand All @@ -67,19 +89,21 @@ protected abstract T createExporter(

protected abstract T createDefaultStdoutExporter();

protected abstract T createExporterWithProperties(ConfigProperties properties);

protected abstract T createStdoutExporterWithProperties(ConfigProperties properties);

protected abstract T createStdoutExporterWithStructuredProperties(
StructuredConfigProperties properties);

protected abstract T toBuilderAndBack(T exporter);

protected abstract CompletableResultCode export(T exporter);

protected abstract CompletableResultCode shutdown(T exporter);

protected Map<ConfigProperties, Map<String, String>> stdoutConfigPropertiesTestCases() {
return emptyMap();
}

protected Map<StructuredConfigProperties, Map<String, String>>
stdoutStructuredPropertiesTestCases() {
return emptyMap();
}

private String output(@Nullable OutputStream outputStream) {
if (outputStream == null) {
return logs.getEvents().stream()
Expand Down Expand Up @@ -168,7 +192,8 @@ public static Stream<Arguments> exportTestCases() {

@ParameterizedTest(name = "{0}")
@MethodSource("exportTestCases")
void testExport(String name, TestCase testCase) throws JSONException, IOException {
void exportWithProgrammaticConfig(String name, TestCase testCase)
throws JSONException, IOException {
setUp();

if (testCase.getMemoryMode() == MemoryMode.REUSABLE_DATA && !testCase.isWrapperJsonObject()) {
Expand Down Expand Up @@ -203,22 +228,6 @@ void testExport(String name, TestCase testCase) throws JSONException, IOExceptio
}
}

@Test
void config() {
assertToString(
createDefaultExporter(),
"{memoryMode=IMMUTABLE_DATA, wrapperJsonObject=false, jsonWriter=LoggerJsonWriter}");
assertToString(
createDefaultStdoutExporter(),
"{memoryMode=IMMUTABLE_DATA, wrapperJsonObject=true, jsonWriter=StreamJsonWriter{outputStream=stdout}}");
}

private void assertToString(T exporter, String expected) {
assertThat(exporter.toString()).isEqualTo(exporterClass.getSimpleName() + expected);
assertThat(toBuilderAndBack(exporter).toString())
.isEqualTo(exporterClass.getSimpleName() + expected);
}

@Test
void testShutdown() {
T exporter = createDefaultExporter();
Expand All @@ -228,4 +237,128 @@ void testShutdown() {
assertThat(shutdown(exporter).isSuccess()).isTrue();
logs.assertContains("Calling shutdown() multiple times.");
}

@Test
void loggingOtlpProviderConfig() {
assertFullToString(createDefaultExporter(), defaultConfigString);

assertFullToString(
loadExporter(DefaultConfigProperties.createFromMap(emptyMap()), "logging-otlp"),
defaultConfigString);
}

@Test
void stdoutProviderConfig() {
assertToStringProperties(
createDefaultStdoutExporter(),
ImmutableMap.of(
"memoryMode", "IMMUTABLE_DATA",
"wrapperJsonObject", "true",
"jsonWriter", "StreamJsonWriter{outputStream=stdout}"));

assertToStringProperties(
loadExporter(DefaultConfigProperties.createFromMap(emptyMap()), "otlp-stdout"),
ImmutableMap.of(
"memoryMode", "IMMUTABLE_DATA",
"wrapperJsonObject", "true",
"jsonWriter", "StreamJsonWriter{outputStream=stdout}"));

assertToStringProperties(
loadExporter(
DefaultConfigProperties.createFromMap(
singletonMap("otel.java.experimental.exporter.memory_mode", "reusable_data")),
"otlp-stdout"),
ImmutableMap.of("memoryMode", "REUSABLE_DATA"));

stdoutConfigPropertiesTestCases()
.forEach(
(config, expected) -> {
assertToStringProperties(loadExporter(config, "otlp-stdout"), expected);
});
}

@Test
void stdoutComponentProviderConfig() {
// ComponentProvider<?> provider = (ComponentProvider<?>)
// loadSpi(ComponentProvider.class).filter(
// p -> {
// ComponentProvider<?> c = (ComponentProvider<?>) p;
// return "otlp-stdout".equals(c.getName()) && c.getType()
// .equals(componentProviderType);
// }).findFirst().orElseThrow(() -> new IllegalStateException("No provider found"));

// todo: implement DefaultStructuredConfigProperties

// assertToString(
// provider.create(DefaultStructuredConfigProperties.createFromMap(emptyMap())),
// loadExporter(DefaultConfigProperties.createFromMap(emptyMap()), "otlp-stdout"),
// "{memoryMode=IMMUTABLE_DATA, wrapperJsonObject=true,
// jsonWriter=StreamJsonWriter{outputStream=stdout}}");
//
// assertToString(
// provider.create((DefaultStructuredConfigProperties.createFromMap(singletonMap(
// "memory_mode", "reusable_data"
// )), "otlp-stdout"),
// "{memoryMode=REUSABLE_DATA, wrapperJsonObject=true,
// jsonWriter=StreamJsonWriter{outputStream=stdout}}");
//
// stdoutStructuredPropertiesTestCases().forEach(
// (config, expected) -> {
// assertToString(
// (T) provider.create(config),
// expected);
// });
}

@SuppressWarnings("unchecked")
private T loadExporter(ConfigProperties config, String name) {
Object provider = loadProvider(name);

try {
return (T)
provider
.getClass()
.getDeclaredMethod("createExporter", ConfigProperties.class)
.invoke(provider, config);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

private Object loadProvider(String want) {
return loadSpi(providerClass)
.filter(
p -> {
try {
return want.equals(p.getClass().getDeclaredMethod("getName").invoke(p));
} catch (Exception e) {
throw new RuntimeException(e);
}
})
.findFirst()
.orElseThrow(() -> new IllegalStateException("No provider found"));
}

private static Stream<?> loadSpi(Class<?> type) {
return Streams.stream(ServiceLoader.load(type, type.getClassLoader()).iterator());
}

private void assertFullToString(T exporter, String expected) {
assertThat(exporter.toString()).isEqualTo(expected);
assertThat(toBuilderAndBack(exporter).toString()).isEqualTo(expected);
}

private void assertToStringProperties(T exporter, Map<String, String> expected) {
String exporterString = exporter.toString();
Map<String, String> got = new HashMap<>();
for (String entry :
exporterString
.substring(exporterClass.getSimpleName().length() + 1, exporterString.length() - 1)
.split(", ")) {
String[] split = entry.split("=", 2);
got.put(split[0], split[1]);
}
assertThat(got).containsAllEntriesOf(expected);
assertThat(toBuilderAndBack(exporter).toString()).isEqualTo(exporterString);
}
}
Loading

0 comments on commit 069ce26

Please sign in to comment.