diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/events/DefaultEventLogger.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/events/DefaultEventLogger.java index 9a9450b009c..8017965bf00 100644 --- a/api/incubator/src/main/java/io/opentelemetry/api/incubator/events/DefaultEventLogger.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/events/DefaultEventLogger.java @@ -6,6 +6,9 @@ package io.opentelemetry.api.incubator.events; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.incubator.logs.AnyValue; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.context.Context; import java.time.Instant; import java.util.concurrent.TimeUnit; @@ -20,10 +23,7 @@ static EventLogger getInstance() { } @Override - public void emit(String eventName, Attributes attributes) {} - - @Override - public EventBuilder builder(String eventName, Attributes attributes) { + public EventBuilder builder(String eventName) { return NoOpEventBuilder.INSTANCE; } @@ -31,6 +31,11 @@ private static class NoOpEventBuilder implements EventBuilder { public static final EventBuilder INSTANCE = new NoOpEventBuilder(); + @Override + public EventBuilder put(String key, AnyValue value) { + return this; + } + @Override public EventBuilder setTimestamp(long timestamp, TimeUnit unit) { return this; @@ -41,6 +46,21 @@ public EventBuilder setTimestamp(Instant instant) { return this; } + @Override + public EventBuilder setContext(Context context) { + return this; + } + + @Override + public EventBuilder setSeverity(Severity severity) { + return this; + } + + @Override + public EventBuilder setAttributes(Attributes attributes) { + return this; + } + @Override public void emit() {} } diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/events/EventBuilder.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/events/EventBuilder.java index d5eb5db85f3..27cd6f8c367 100644 --- a/api/incubator/src/main/java/io/opentelemetry/api/incubator/events/EventBuilder.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/events/EventBuilder.java @@ -5,14 +5,79 @@ package io.opentelemetry.api.incubator.events; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.incubator.logs.AnyValue; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.context.Context; import java.time.Instant; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeUnit; /** The EventBuilder is used to {@link #emit()} events. */ public interface EventBuilder { + /** Put the given {@code key} and {@code value} in the payload. */ + default EventBuilder put(String key, String value) { + return put(key, AnyValue.of(value)); + } + + /** Put the given {@code key} and {@code value} in the payload. */ + default EventBuilder put(String key, long value) { + return put(key, AnyValue.of(value)); + } + + /** Put the given {@code key} and {@code value} in the payload. */ + default EventBuilder put(String key, double value) { + return put(key, AnyValue.of(value)); + } + + /** Put the given {@code key} and {@code value} in the payload. */ + default EventBuilder put(String key, boolean value) { + return put(key, AnyValue.of(value)); + } + + /** Put the given {@code key} and {@code value} in the payload. */ + default EventBuilder put(String key, String... value) { + List> values = new ArrayList<>(value.length); + for (String val : value) { + values.add(AnyValue.of(val)); + } + return put(key, AnyValue.of(values)); + } + + /** Put the given {@code key} and {@code value} in the payload. */ + default EventBuilder put(String key, long... value) { + List> values = new ArrayList<>(value.length); + for (long val : value) { + values.add(AnyValue.of(val)); + } + return put(key, AnyValue.of(values)); + } + + /** Put the given {@code key} and {@code value} in the payload. */ + default EventBuilder put(String key, double... value) { + List> values = new ArrayList<>(value.length); + for (double val : value) { + values.add(AnyValue.of(val)); + } + return put(key, AnyValue.of(values)); + } + + /** Put the given {@code key} and {@code value} in the payload. */ + default EventBuilder put(String key, boolean... value) { + List> values = new ArrayList<>(value.length); + for (boolean val : value) { + values.add(AnyValue.of(val)); + } + return put(key, AnyValue.of(values)); + } + + /** Put the given {@code key} and {@code value} in the payload. */ + EventBuilder put(String key, AnyValue value); + /** - * Set the epoch {@code timestamp} for the event, using the timestamp and unit. + * Set the epoch {@code timestamp}, using the timestamp and unit. * *

The {@code timestamp} is the time at which the event occurred. If unset, it will be set to * the current time when {@link #emit()} is called. @@ -20,13 +85,27 @@ public interface EventBuilder { EventBuilder setTimestamp(long timestamp, TimeUnit unit); /** - * Set the epoch {@code timestamp} for the event, using the instant. + * Set the epoch {@code timestamp}, using the instant. * *

The {@code timestamp} is the time at which the event occurred. If unset, it will be set to * the current time when {@link #emit()} is called. */ EventBuilder setTimestamp(Instant instant); + /** Set the context. */ + EventBuilder setContext(Context context); + + /** Set the severity. */ + EventBuilder setSeverity(Severity severity); + + /** + * Set the attributes. + * + *

Event {@link io.opentelemetry.api.common.Attributes} provide additional details about the + * Event which are not part of the well-defined {@link AnyValue} {@code payload}. + */ + EventBuilder setAttributes(Attributes attributes); + /** Emit an event. */ void emit(); } diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/events/EventLogger.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/events/EventLogger.java index 785d8e000ca..3d273a30c7a 100644 --- a/api/incubator/src/main/java/io/opentelemetry/api/incubator/events/EventLogger.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/events/EventLogger.java @@ -5,7 +5,6 @@ package io.opentelemetry.api.incubator.events; -import io.opentelemetry.api.common.Attributes; import javax.annotation.concurrent.ThreadSafe; /** @@ -20,10 +19,10 @@ * .build(); * * void doWork() { - * eventLogger.emit("my-namespace.my-event", Attributes.builder() + * eventLogger.builder("my-namespace.my-event") * .put("key1", "value1") * .put("key2", "value2") - * .build()) + * .emit(); * // do work * } * } @@ -32,18 +31,6 @@ @ThreadSafe public interface EventLogger { - /** - * Emit an event. - * - * @param eventName the event name, which identifies the class or type of event. Event with the - * same name are structurally similar to one another. Event names are subject to the same - * naming rules as attribute names. Notably, they are namespaced to avoid collisions. See event.name semantic - * conventions for more details. - * @param attributes attributes associated with the event - */ - void emit(String eventName, Attributes attributes); - /** * Return a {@link EventBuilder} to emit an event. * @@ -52,7 +39,6 @@ public interface EventLogger { * naming rules as attribute names. Notably, they are namespaced to avoid collisions. See event.name semantic * conventions for more details. - * @param attributes attributes associated with the event */ - EventBuilder builder(String eventName, Attributes attributes); + EventBuilder builder(String eventName); } diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/AnyValue.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/AnyValue.java index 1a1519a1044..602e9e289b7 100644 --- a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/AnyValue.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/AnyValue.java @@ -62,6 +62,11 @@ static AnyValue>> of(AnyValue... value) { return AnyValueArray.create(value); } + /** Returns an {@link AnyValue} for the list of {@link AnyValue} values. */ + static AnyValue>> of(List> value) { + return AnyValueArray.create(value); + } + /** * Returns an {@link AnyValue} for the array of {@link KeyAnyValue} values. {@link * KeyAnyValue#getKey()} values should not repeat - duplicates may be dropped. diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/AnyValueArray.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/AnyValueArray.java index ae448038b18..2332c253392 100644 --- a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/AnyValueArray.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/AnyValueArray.java @@ -28,6 +28,10 @@ static AnyValue>> create(AnyValue... value) { return new AnyValueArray(Collections.unmodifiableList(list)); } + static AnyValue>> create(List> value) { + return new AnyValueArray(Collections.unmodifiableList(value)); + } + @Override public AnyValueType getType() { return AnyValueType.ARRAY; diff --git a/api/incubator/src/test/java/io/opentelemetry/api/incubator/events/DefaultEventLoggerProviderTest.java b/api/incubator/src/test/java/io/opentelemetry/api/incubator/events/DefaultEventLoggerProviderTest.java index 431130045cc..196c0bc307a 100644 --- a/api/incubator/src/test/java/io/opentelemetry/api/incubator/events/DefaultEventLoggerProviderTest.java +++ b/api/incubator/src/test/java/io/opentelemetry/api/incubator/events/DefaultEventLoggerProviderTest.java @@ -8,7 +8,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; -import io.opentelemetry.api.common.Attributes; import org.junit.jupiter.api.Test; class DefaultEventLoggerProviderTest { @@ -33,7 +32,8 @@ void noopEventLoggerProvider_doesNotThrow() { provider .eventLoggerBuilder("scope-name") .build() - .emit("event-name", Attributes.empty())) + .builder("namespace.event-name") + .emit()) .doesNotThrowAnyException(); } } diff --git a/api/incubator/src/test/java/io/opentelemetry/api/incubator/events/DefaultEventLoggerTest.java b/api/incubator/src/test/java/io/opentelemetry/api/incubator/events/DefaultEventLoggerTest.java index 9d14ef63c48..e1f1f2e7ce7 100644 --- a/api/incubator/src/test/java/io/opentelemetry/api/incubator/events/DefaultEventLoggerTest.java +++ b/api/incubator/src/test/java/io/opentelemetry/api/incubator/events/DefaultEventLoggerTest.java @@ -8,35 +8,27 @@ import static org.assertj.core.api.Assertions.assertThatCode; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.context.Context; import java.time.Instant; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; class DefaultEventLoggerTest { - @Test - void emit() { - assertThatCode(() -> DefaultEventLogger.getInstance().emit("event-name", Attributes.empty())) - .doesNotThrowAnyException(); - assertThatCode( - () -> - DefaultEventLogger.getInstance() - .emit( - "event-domain.event-name", - Attributes.builder().put("key1", "value1").build())) - .doesNotThrowAnyException(); - } - @Test void builder() { - Attributes attributes = Attributes.builder().put("key1", "value1").build(); EventLogger eventLogger = DefaultEventLogger.getInstance(); assertThatCode( () -> eventLogger - .builder("com.example.MyEvent", attributes) + .builder("namespace.myEvent") + .put("key", "value") .setTimestamp(123456L, TimeUnit.NANOSECONDS) .setTimestamp(Instant.now()) + .setContext(Context.current()) + .setSeverity(Severity.DEBUG) + .setAttributes(Attributes.empty()) .emit()) .doesNotThrowAnyException(); } diff --git a/integration-tests/otlp/src/main/java/io/opentelemetry/integrationtest/OtlpExporterIntegrationTest.java b/integration-tests/otlp/src/main/java/io/opentelemetry/integrationtest/OtlpExporterIntegrationTest.java index 810b51f5131..141159c3d8d 100644 --- a/integration-tests/otlp/src/main/java/io/opentelemetry/integrationtest/OtlpExporterIntegrationTest.java +++ b/integration-tests/otlp/src/main/java/io/opentelemetry/integrationtest/OtlpExporterIntegrationTest.java @@ -568,7 +568,7 @@ private static void testLogRecordExporter(LogRecordExporter logRecordExporter) { .setSeverityText("DEBUG") .setContext(Context.current()) .emit(); - eventLogger.emit("event-name", Attributes.builder().put("key", "value").build()); + eventLogger.builder("namespace.event-name").put("key", "value").emit(); } // Closing triggers flush of processor @@ -708,17 +708,18 @@ private static void testLogRecordExporter(LogRecordExporter logRecordExporter) { // LogRecord via EventLogger.emit(String, Attributes) io.opentelemetry.proto.logs.v1.LogRecord protoLog2 = ilLogs.getLogRecords(1); - assertThat(protoLog2.getBody().getStringValue()).isEmpty(); - assertThat(protoLog2.getAttributesList()) + assertThat(protoLog2.getBody().getKvlistValue().getValuesList()) .containsExactlyInAnyOrder( - KeyValue.newBuilder() - .setKey("event.name") - .setValue(AnyValue.newBuilder().setStringValue("event-name").build()) - .build(), KeyValue.newBuilder() .setKey("key") .setValue(AnyValue.newBuilder().setStringValue("value").build()) .build()); + assertThat(protoLog2.getAttributesList()) + .containsExactlyInAnyOrder( + KeyValue.newBuilder() + .setKey("event.name") + .setValue(AnyValue.newBuilder().setStringValue("namespace.event-name").build()) + .build()); assertThat(protoLog2.getSeverityText()).isEmpty(); assertThat(TraceId.fromBytes(protoLog2.getTraceId().toByteArray())) .isEqualTo(spanContext.getTraceId()); diff --git a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/FullConfigTest.java b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/FullConfigTest.java index 2097af7945c..e4ac7963ea8 100644 --- a/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/FullConfigTest.java +++ b/sdk-extensions/autoconfigure/src/testFullConfig/java/io/opentelemetry/sdk/autoconfigure/FullConfigTest.java @@ -38,6 +38,7 @@ import io.opentelemetry.proto.collector.trace.v1.TraceServiceGrpc; import io.opentelemetry.proto.common.v1.AnyValue; import io.opentelemetry.proto.common.v1.KeyValue; +import io.opentelemetry.proto.logs.v1.SeverityNumber; import io.opentelemetry.proto.metrics.v1.Metric; import io.opentelemetry.sdk.OpenTelemetrySdk; import java.util.ArrayList; @@ -207,7 +208,8 @@ void configures() throws Exception { logger.logRecordBuilder().setBody("info log message").setSeverity(Severity.INFO).emit(); EventLogger eventLogger = GlobalEventLoggerProvider.get().eventLoggerBuilder("test").build(); - eventLogger.emit("test-name", Attributes.builder().put("cow", "moo").build()); + eventLogger.builder("namespace.test-name").put("cow", "moo").emit(); + ; openTelemetrySdk.getSdkTracerProvider().forceFlush().join(10, TimeUnit.SECONDS); openTelemetrySdk.getSdkLoggerProvider().forceFlush().join(10, TimeUnit.SECONDS); @@ -329,17 +331,23 @@ void configures() throws Exception { assertThat(logRecord.getSeverityNumberValue()) .isEqualTo(Severity.INFO.getSeverityNumber()); }, - logRecord -> - assertThat(logRecord.getAttributesList()) - .containsExactlyInAnyOrder( - KeyValue.newBuilder() - .setKey("event.name") - .setValue(AnyValue.newBuilder().setStringValue("test-name").build()) - .build(), - KeyValue.newBuilder() - .setKey("cow") - .setValue(AnyValue.newBuilder().setStringValue("moo").build()) - .build())); + logRecord -> { + assertThat(logRecord.getBody().getKvlistValue().getValuesList()) + .containsExactlyInAnyOrder( + KeyValue.newBuilder() + .setKey("cow") + .setValue(AnyValue.newBuilder().setStringValue("moo").build()) + .build()); + assertThat(logRecord.getSeverityNumber()) + .isEqualTo(SeverityNumber.SEVERITY_NUMBER_INFO); + assertThat(logRecord.getAttributesList()) + .containsExactlyInAnyOrder( + KeyValue.newBuilder() + .setKey("event.name") + .setValue( + AnyValue.newBuilder().setStringValue("namespace.test-name").build()) + .build()); + }); } private static List getFirstDataPointLabels(Metric metric) { diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkEventBuilder.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkEventBuilder.java index ce4f5d69623..5daa5ae56f4 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkEventBuilder.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkEventBuilder.java @@ -5,35 +5,83 @@ package io.opentelemetry.sdk.logs.internal; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.incubator.events.EventBuilder; +import io.opentelemetry.api.incubator.logs.AnyValue; +import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.api.logs.LogRecordBuilder; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.common.Clock; import java.time.Instant; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.TimeUnit; class SdkEventBuilder implements EventBuilder { + + private static final AttributeKey EVENT_NAME = AttributeKey.stringKey("event.name"); + + private final Map> payload = new HashMap<>(); + private final Clock clock; private final LogRecordBuilder logRecordBuilder; private final String eventName; + private boolean hasTimestamp = false; - SdkEventBuilder(LogRecordBuilder logRecordBuilder, String eventName) { + SdkEventBuilder(Clock clock, LogRecordBuilder logRecordBuilder, String eventName) { + this.clock = clock; this.logRecordBuilder = logRecordBuilder; this.eventName = eventName; } + @Override + public EventBuilder put(String key, AnyValue value) { + payload.put(key, value); + return this; + } + @Override public EventBuilder setTimestamp(long timestamp, TimeUnit unit) { this.logRecordBuilder.setTimestamp(timestamp, unit); + this.hasTimestamp = true; return this; } @Override public EventBuilder setTimestamp(Instant instant) { this.logRecordBuilder.setTimestamp(instant); + this.hasTimestamp = true; + return this; + } + + @Override + public EventBuilder setContext(Context context) { + logRecordBuilder.setContext(context); + return this; + } + + @Override + public EventBuilder setSeverity(Severity severity) { + logRecordBuilder.setSeverity(severity); + return this; + } + + @Override + public EventBuilder setAttributes(Attributes attributes) { + logRecordBuilder.setAllAttributes(attributes); return this; } @Override public void emit() { - SdkEventLoggerProvider.addEventName(logRecordBuilder, eventName); + if (!payload.isEmpty()) { + ((ExtendedLogRecordBuilder) logRecordBuilder).setBody(AnyValue.of(payload)); + } + if (!hasTimestamp) { + logRecordBuilder.setTimestamp(clock.now(), TimeUnit.NANOSECONDS); + } + logRecordBuilder.setAttribute(EVENT_NAME, eventName); logRecordBuilder.emit(); } } diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkEventLoggerProvider.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkEventLoggerProvider.java index 3eb6bb708ab..391547201df 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkEventLoggerProvider.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkEventLoggerProvider.java @@ -5,18 +5,16 @@ package io.opentelemetry.sdk.logs.internal; -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.incubator.events.EventBuilder; import io.opentelemetry.api.incubator.events.EventLogger; import io.opentelemetry.api.incubator.events.EventLoggerBuilder; import io.opentelemetry.api.incubator.events.EventLoggerProvider; -import io.opentelemetry.api.logs.LogRecordBuilder; import io.opentelemetry.api.logs.Logger; import io.opentelemetry.api.logs.LoggerBuilder; import io.opentelemetry.api.logs.LoggerProvider; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.Clock; -import java.util.concurrent.TimeUnit; /** * SDK implementation for {@link EventLoggerProvider}. @@ -26,7 +24,7 @@ */ public final class SdkEventLoggerProvider implements EventLoggerProvider { - static final AttributeKey EVENT_NAME = AttributeKey.stringKey("event.name"); + private static final Severity DEFAULT_SEVERITY = Severity.INFO; private final LoggerProvider delegateLoggerProvider; private final Clock clock; @@ -100,28 +98,14 @@ private SdkEventLogger(Clock clock, Logger delegateLogger) { } @Override - public EventBuilder builder(String eventName, Attributes attributes) { + public EventBuilder builder(String eventName) { return new SdkEventBuilder( + clock, delegateLogger .logRecordBuilder() - .setTimestamp(clock.now(), TimeUnit.NANOSECONDS) - .setAllAttributes(attributes), + .setSeverity(DEFAULT_SEVERITY) + .setContext(Context.current()), eventName); } - - @Override - public void emit(String eventName, Attributes attributes) { - LogRecordBuilder logRecordBuilder = - delegateLogger - .logRecordBuilder() - .setTimestamp(clock.now(), TimeUnit.NANOSECONDS) - .setAllAttributes(attributes); - addEventName(logRecordBuilder, eventName); - logRecordBuilder.emit(); - } - } - - static void addEventName(LogRecordBuilder logRecordBuilder, String eventName) { - logRecordBuilder.setAttribute(EVENT_NAME, eventName); } } diff --git a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/internal/SdkEventBuilderTest.java b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/internal/SdkEventBuilderTest.java index e53fd6549a9..bf45863d985 100644 --- a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/internal/SdkEventBuilderTest.java +++ b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/internal/SdkEventBuilderTest.java @@ -8,14 +8,15 @@ import static io.opentelemetry.api.common.AttributeKey.stringKey; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.logs.LogRecordBuilder; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.common.Clock; import java.time.Instant; import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.Test; @@ -29,16 +30,26 @@ void emit() { LogRecordBuilder logRecordBuilder = mock(LogRecordBuilder.class); when(logRecordBuilder.setTimestamp(anyLong(), any())).thenReturn(logRecordBuilder); when(logRecordBuilder.setAttribute(any(), any())).thenReturn(logRecordBuilder); + when(logRecordBuilder.setContext(any())).thenReturn(logRecordBuilder); + when(logRecordBuilder.setSeverity(any())).thenReturn(logRecordBuilder); + when(logRecordBuilder.setAllAttributes(any())).thenReturn(logRecordBuilder); Instant instant = Instant.now(); - new SdkEventBuilder(logRecordBuilder, eventName) + Context context = Context.root(); + Attributes attributes = Attributes.builder().put("extra-attribute", "value").build(); + new SdkEventBuilder(Clock.getDefault(), logRecordBuilder, eventName) .setTimestamp(123456L, TimeUnit.NANOSECONDS) .setTimestamp(instant) + .setContext(context) + .setSeverity(Severity.DEBUG) + .setAttributes(attributes) .emit(); - verify(logRecordBuilder, never()).setAttribute(eq(stringKey("event.domain")), anyString()); verify(logRecordBuilder).setAttribute(stringKey("event.name"), eventName); verify(logRecordBuilder).setTimestamp(123456L, TimeUnit.NANOSECONDS); verify(logRecordBuilder).setTimestamp(instant); + verify(logRecordBuilder).setContext(context); + verify(logRecordBuilder).setSeverity(Severity.DEBUG); + verify(logRecordBuilder).setAllAttributes(attributes); verify(logRecordBuilder).emit(); } } diff --git a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/internal/SdkEventLoggerProviderTest.java b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/internal/SdkEventLoggerProviderTest.java index 847d3142e40..898fc1d3f1d 100644 --- a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/internal/SdkEventLoggerProviderTest.java +++ b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/internal/SdkEventLoggerProviderTest.java @@ -5,18 +5,24 @@ package io.opentelemetry.sdk.logs.internal; -import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import com.google.common.collect.ImmutableMap; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.incubator.events.EventLogger; +import io.opentelemetry.api.incubator.logs.AnyValue; +import io.opentelemetry.api.logs.Severity; import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.logs.ReadWriteLogRecord; import io.opentelemetry.sdk.logs.SdkLoggerProvider; import io.opentelemetry.sdk.resources.Resource; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.Test; @@ -28,7 +34,7 @@ class SdkEventLoggerProviderTest { private final Clock clock = mock(Clock.class); private final AtomicReference seenLog = new AtomicReference<>(); - private final SdkEventLoggerProvider eventLoggerProvider = + private final SdkEventLoggerProvider eventEmitterProvider = SdkEventLoggerProvider.create( SdkLoggerProvider.builder() .setResource(RESOURCE) @@ -37,44 +43,81 @@ class SdkEventLoggerProviderTest { clock); @Test - void emit() { + void builder() { when(clock.now()).thenReturn(10L); - eventLoggerProvider - .eventLoggerBuilder("test-scope") - .build() - .emit( - "event-name", - Attributes.builder() - .put("key1", "value1") - // should be overridden by the eventName argument passed to emit - .put("event.name", "foo") - .build()); + long yesterday = System.nanoTime() - TimeUnit.DAYS.toNanos(1); + EventLogger eventLogger = eventEmitterProvider.eventLoggerBuilder("test-scope").build(); + + eventLogger + .builder("namespace.event-name") + .put("key1", "value1") + .setTimestamp(yesterday, TimeUnit.NANOSECONDS) + .setSeverity(Severity.DEBUG) + .setAttributes(Attributes.builder().put("extra-attribute", "value").build()) + .emit(); assertThat(seenLog.get().toLogRecordData()) .hasResource(RESOURCE) .hasInstrumentationScope(InstrumentationScopeInfo.create("test-scope")) - .hasTimestamp(10L) + .hasTimestamp(yesterday) + .hasSeverity(Severity.DEBUG) .hasAttributes( - Attributes.builder().put("key1", "value1").put("event.name", "event-name").build()); + Attributes.builder() + .put("event.name", "namespace.event-name") + .put("extra-attribute", "value") + .build()); + assertThat(seenLog.get().toLogRecordData().getObservedTimestampEpochNanos()).isPositive(); + AnyValue expectedPayload = + AnyValue.of(Collections.singletonMap("key1", AnyValue.of("value1"))); + assertThat(((AnyValueBody) seenLog.get().toLogRecordData().getBody()).asAnyValue()) + .isEqualTo(expectedPayload); } @Test - void builder() { - long yesterday = System.nanoTime() - TimeUnit.DAYS.toNanos(1); - Attributes attributes = Attributes.of(stringKey("foo"), "bar"); + void eventBuilder_FullPayload() { + EventLogger eventLogger = eventEmitterProvider.get("test-scoe"); - EventLogger eventLogger = eventLoggerProvider.eventLoggerBuilder("test-scope").build(); - - eventLogger.builder("testing", attributes).setTimestamp(yesterday, TimeUnit.NANOSECONDS).emit(); - verifySeen(yesterday, attributes); - } + eventLogger + .builder("namespace.my-event-name") + // Helper methods to set primitive types + .put("stringKey", "value") + .put("longKey", 1) + .put("doubleKey", 1.0) + .put("boolKey", true) + // Helper methods to set primitive array types + .put("stringArrKey", "value1", "value2") + .put("longArrKey", 1, 2) + .put("doubleArrKey", 1.0, 2.0) + .put("boolArrKey", true, false) + // Escape hatch to set AnyValue types, which can't be modeled using Attributes + .put( + "anyValueKey", + AnyValue.of( + ImmutableMap.of( + "childKey1", AnyValue.of("value"), + "childKey2", AnyValue.of("value")))) + .emit(); - private void verifySeen(long timestamp, Attributes attributes) { - assertThat(seenLog.get().toLogRecordData()) - .hasResource(RESOURCE) - .hasInstrumentationScope(InstrumentationScopeInfo.create("test-scope")) - .hasTimestamp(timestamp) - .hasAttributes(attributes.toBuilder().put("event.name", "testing").build()); + Map> expectedPayload = new HashMap<>(); + expectedPayload.put("stringKey", AnyValue.of("value")); + expectedPayload.put("longKey", AnyValue.of(1)); + expectedPayload.put("doubleKey", AnyValue.of(1.0)); + expectedPayload.put("boolKey", AnyValue.of(true)); + expectedPayload.put( + "stringArrKey", AnyValue.of(Arrays.asList(AnyValue.of("value1"), AnyValue.of("value2")))); + expectedPayload.put("longArrKey", AnyValue.of(Arrays.asList(AnyValue.of(1), AnyValue.of("2")))); + expectedPayload.put( + "doubleArrKey", AnyValue.of(Arrays.asList(AnyValue.of(1.0), AnyValue.of(2.0)))); + expectedPayload.put( + "boolArrKey", AnyValue.of(Arrays.asList(AnyValue.of(true), AnyValue.of(false)))); + expectedPayload.put( + "anyValueKey", + AnyValue.of( + ImmutableMap.of( + "childKey1", AnyValue.of("value"), + "childKey2", AnyValue.of("value")))); + assertThat(((AnyValueBody) seenLog.get().toLogRecordData().getBody()).asAnyValue().toString()) + .isEqualTo(AnyValue.of(expectedPayload).toString()); } }