From 01a07b51a1b0068b3cca29d77767c26bccdd5d7c Mon Sep 17 00:00:00 2001
From: jack-berg <34418638+jack-berg@users.noreply.github.com>
Date: Tue, 27 Sep 2022 17:39:33 -0500
Subject: [PATCH] Add event API (#4781)
* Add event API
* Log when emitting event without domain, add javadoc example to Logger
---
.../opentelemetry/api/logs/DefaultLogger.java | 29 +++++++--
.../api/logs/DefaultLoggerProvider.java | 21 +++++--
.../opentelemetry/api/logs/EventBuilder.java | 16 +++++
.../io/opentelemetry/api/logs/Logger.java | 39 +++++++++++-
.../opentelemetry/api/logs/LoggerBuilder.java | 14 +++++
.../api/logs/LoggerProvider.java | 7 ++-
.../api/logs/DefaultLoggerProviderTest.java | 22 ++++++-
.../api/logs/DefaultLoggerTest.java | 63 ++++++++++++++++++-
.../sdk/logs/SdkLogRecordBuilder.java | 19 +++---
.../io/opentelemetry/sdk/logs/SdkLogger.java | 44 +++++++++++++
.../sdk/logs/SdkLoggerBuilder.java | 11 +++-
.../opentelemetry/sdk/logs/SdkLoggerTest.java | 43 +++++++++++++
12 files changed, 300 insertions(+), 28 deletions(-)
create mode 100644 api/logs/src/main/java/io/opentelemetry/api/logs/EventBuilder.java
diff --git a/api/logs/src/main/java/io/opentelemetry/api/logs/DefaultLogger.java b/api/logs/src/main/java/io/opentelemetry/api/logs/DefaultLogger.java
index 2fbd6b72d7..7f86ff9cd9 100644
--- a/api/logs/src/main/java/io/opentelemetry/api/logs/DefaultLogger.java
+++ b/api/logs/src/main/java/io/opentelemetry/api/logs/DefaultLogger.java
@@ -6,20 +6,37 @@
package io.opentelemetry.api.logs;
import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.internal.ValidationUtil;
import io.opentelemetry.context.Context;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
class DefaultLogger implements Logger {
- private static final Logger INSTANCE = new DefaultLogger();
+ private static final Logger INSTANCE_WITH_DOMAIN = new DefaultLogger(/* hasDomain= */ true);
+ private static final Logger INSTANCE_NO_DOMAIN = new DefaultLogger(/* hasDomain= */ false);
- private static final LogRecordBuilder NOOP_LOG_RECORD_BUILDER = new NoopLogRecordBuilder();
+ private static final EventBuilder NOOP_LOG_RECORD_BUILDER = new NoopLogRecordBuilder();
- private DefaultLogger() {}
+ private final boolean hasDomain;
- static Logger getInstance() {
- return INSTANCE;
+ private DefaultLogger(boolean hasDomain) {
+ this.hasDomain = hasDomain;
+ }
+
+ static Logger getInstance(boolean hasDomain) {
+ return hasDomain ? INSTANCE_WITH_DOMAIN : INSTANCE_NO_DOMAIN;
+ }
+
+ @Override
+ public EventBuilder eventBuilder(String eventName) {
+ if (!hasDomain) {
+ ValidationUtil.log(
+ "Cannot emit event from Logger without event domain. Please use LoggerBuilder#setEventDomain(String) when obtaining Logger.",
+ Level.WARNING);
+ }
+ return NOOP_LOG_RECORD_BUILDER;
}
@Override
@@ -27,7 +44,7 @@ class DefaultLogger implements Logger {
return NOOP_LOG_RECORD_BUILDER;
}
- private static final class NoopLogRecordBuilder implements LogRecordBuilder {
+ private static final class NoopLogRecordBuilder implements EventBuilder {
private NoopLogRecordBuilder() {}
diff --git a/api/logs/src/main/java/io/opentelemetry/api/logs/DefaultLoggerProvider.java b/api/logs/src/main/java/io/opentelemetry/api/logs/DefaultLoggerProvider.java
index 25a18f529b..035fb4bc02 100644
--- a/api/logs/src/main/java/io/opentelemetry/api/logs/DefaultLoggerProvider.java
+++ b/api/logs/src/main/java/io/opentelemetry/api/logs/DefaultLoggerProvider.java
@@ -8,7 +8,10 @@ package io.opentelemetry.api.logs;
class DefaultLoggerProvider implements LoggerProvider {
private static final LoggerProvider INSTANCE = new DefaultLoggerProvider();
- private static final LoggerBuilder NOOP_BUILDER = new NoopLoggerBuilder();
+ private static final LoggerBuilder NOOP_BUILDER_WITH_DOMAIN =
+ new NoopLoggerBuilder(/* hasDomain= */ true);
+ private static final LoggerBuilder NOOP_BUILDER_NO_DOMAIN =
+ new NoopLoggerBuilder(/* hasDomain= */ false);
private DefaultLoggerProvider() {}
@@ -18,12 +21,22 @@ class DefaultLoggerProvider implements LoggerProvider {
@Override
public LoggerBuilder loggerBuilder(String instrumentationScopeName) {
- return NOOP_BUILDER;
+ return NOOP_BUILDER_NO_DOMAIN;
}
private static class NoopLoggerBuilder implements LoggerBuilder {
- private NoopLoggerBuilder() {}
+ private final boolean hasDomain;
+
+ private NoopLoggerBuilder(boolean hasDomain) {
+ this.hasDomain = hasDomain;
+ }
+
+ @Override
+ @SuppressWarnings("BuilderReturnThis")
+ public LoggerBuilder setEventDomain(String eventDomain) {
+ return eventDomain == null ? NOOP_BUILDER_NO_DOMAIN : NOOP_BUILDER_WITH_DOMAIN;
+ }
@Override
public LoggerBuilder setSchemaUrl(String schemaUrl) {
@@ -37,7 +50,7 @@ class DefaultLoggerProvider implements LoggerProvider {
@Override
public Logger build() {
- return DefaultLogger.getInstance();
+ return DefaultLogger.getInstance(hasDomain);
}
}
}
diff --git a/api/logs/src/main/java/io/opentelemetry/api/logs/EventBuilder.java b/api/logs/src/main/java/io/opentelemetry/api/logs/EventBuilder.java
new file mode 100644
index 0000000000..e7dbda842f
--- /dev/null
+++ b/api/logs/src/main/java/io/opentelemetry/api/logs/EventBuilder.java
@@ -0,0 +1,16 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.api.logs;
+
+/**
+ * Used to construct an emit events from a {@link Logger}.
+ *
+ *
An event is a log record with attributes for {@code event.domain} and {@code event.name}.
+ *
+ *
Obtain a {@link Logger#eventBuilder(String)}, add properties using the setters, and emit the
+ * log record by calling {@link #emit()}.
+ */
+public interface EventBuilder extends LogRecordBuilder {}
diff --git a/api/logs/src/main/java/io/opentelemetry/api/logs/Logger.java b/api/logs/src/main/java/io/opentelemetry/api/logs/Logger.java
index 900a2a5172..0ccc8227f9 100644
--- a/api/logs/src/main/java/io/opentelemetry/api/logs/Logger.java
+++ b/api/logs/src/main/java/io/opentelemetry/api/logs/Logger.java
@@ -10,12 +10,47 @@ import javax.annotation.concurrent.ThreadSafe;
/**
* A {@link Logger} is the entry point into a log pipeline.
*
- *
Obtain a {@link #logRecordBuilder()}, add properties using the setters, and emit it via {@link
- * LogRecordBuilder#emit()}.
+ *
Obtain a {@link EventBuilder} or {@link #logRecordBuilder()}, add properties using the
+ * setters, and emit it via {@link LogRecordBuilder#emit()}.
+ *
+ *
Example usage emitting events:
+ *
+ *
{@code
+ * class MyClass {
+ * private final Logger eventLogger = openTelemetryLoggerProvider.loggerBuilder("instrumentation-library-name")
+ * .setInstrumentationVersion("1.0.0")
+ * .setEventDomain("acme.observability")
+ * .build();
+ *
+ * void doWork() {
+ * eventLogger.eventBuilder("my-event")
+ * .setAllAttributes(Attributes.builder()
+ * .put("key1", "value1")
+ * .put("key2", "value2")
+ * .build())
+ * .emit();
+ * // do work
+ * }
+ * }
+ * }
*/
@ThreadSafe
public interface Logger {
+ /**
+ * Return a {@link EventBuilder} to emit an event.
+ *
+ * NOTE: this API MUST only be called on {@link Logger}s which have been assigned an
+ * {@link LoggerBuilder#setEventDomain(String) event domain}.
+ *
+ *
Build the event using the {@link EventBuilder} setters, and emit via {@link
+ * EventBuilder#emit()}.
+ *
+ * @param eventName the event name, which acts as a classifier for events. Within a particular
+ * event domain, event name defines a particular class or type of event.
+ */
+ EventBuilder eventBuilder(String eventName);
+
/**
* Return a {@link LogRecordBuilder} to emit a log record.
*
diff --git a/api/logs/src/main/java/io/opentelemetry/api/logs/LoggerBuilder.java b/api/logs/src/main/java/io/opentelemetry/api/logs/LoggerBuilder.java
index cf3d781823..5a81d52bf4 100644
--- a/api/logs/src/main/java/io/opentelemetry/api/logs/LoggerBuilder.java
+++ b/api/logs/src/main/java/io/opentelemetry/api/logs/LoggerBuilder.java
@@ -8,6 +8,20 @@ package io.opentelemetry.api.logs;
/** Builder class for creating {@link Logger} instances. */
public interface LoggerBuilder {
+ /**
+ * Set the event domain of the resulting {@link Logger}.
+ *
+ *
NOTE: Event domain is required to use {@link Logger#eventBuilder(String)}.
+ *
+ *
The event domain will be included in the {@code event.domain} attribute of every event
+ * produced by the resulting {@link Logger}.
+ *
+ * @param eventDomain The event domain, which acts as a namespace for event names. Within a
+ * particular event domain, event name defines a particular class or type of event.
+ * @return this
+ */
+ LoggerBuilder setEventDomain(String eventDomain);
+
/**
* Assign an OpenTelemetry schema URL to the resulting {@link Logger}.
*
diff --git a/api/logs/src/main/java/io/opentelemetry/api/logs/LoggerProvider.java b/api/logs/src/main/java/io/opentelemetry/api/logs/LoggerProvider.java
index a748a59009..fc55f81052 100644
--- a/api/logs/src/main/java/io/opentelemetry/api/logs/LoggerProvider.java
+++ b/api/logs/src/main/java/io/opentelemetry/api/logs/LoggerProvider.java
@@ -14,12 +14,13 @@ import javax.annotation.concurrent.ThreadSafe;
*
The OpenTelemetry logging API exists to satisfy two use cases:
*
*
+ * - Enable emitting structured events
+ * via {@link Logger#eventBuilder(String)}. Requires assigning an {@link
+ * LoggerBuilder#setEventDomain(String) event domain} to the {@link Logger}.
*
- Enable the creation of log appenders, which bridge logs from other log frameworks (e.g.
* SLF4J, Log4j, JUL, Logback, etc) into OpenTelemetry via {@link Logger#logRecordBuilder()}.
* It is NOT a replacement log framework.
- *
- Enable emitting structured events.
- * TODO: add link when event API is added.
*
*
* @see Logger
diff --git a/api/logs/src/test/java/io/opentelemetry/api/logs/DefaultLoggerProviderTest.java b/api/logs/src/test/java/io/opentelemetry/api/logs/DefaultLoggerProviderTest.java
index c4ca0fa415..3f47843917 100644
--- a/api/logs/src/test/java/io/opentelemetry/api/logs/DefaultLoggerProviderTest.java
+++ b/api/logs/src/test/java/io/opentelemetry/api/logs/DefaultLoggerProviderTest.java
@@ -26,6 +26,26 @@ class DefaultLoggerProviderTest {
.setSchemaUrl("http://schema.com")
.build())
.doesNotThrowAnyException();
- ;
+
+ assertThatCode(() -> provider.loggerBuilder("scope-name").build().logRecordBuilder())
+ .doesNotThrowAnyException();
+ assertThatCode(() -> provider.loggerBuilder("scope-name").build().eventBuilder("event-name"))
+ .doesNotThrowAnyException();
+ assertThatCode(
+ () ->
+ provider
+ .loggerBuilder("scope-name")
+ .setEventDomain("event-domain")
+ .build()
+ .logRecordBuilder())
+ .doesNotThrowAnyException();
+ assertThatCode(
+ () ->
+ provider
+ .loggerBuilder("scope-name")
+ .setEventDomain("event-domain")
+ .build()
+ .eventBuilder("event-name"))
+ .doesNotThrowAnyException();
}
}
diff --git a/api/logs/src/test/java/io/opentelemetry/api/logs/DefaultLoggerTest.java b/api/logs/src/test/java/io/opentelemetry/api/logs/DefaultLoggerTest.java
index bf4522f51e..e27c5304e0 100644
--- a/api/logs/src/test/java/io/opentelemetry/api/logs/DefaultLoggerTest.java
+++ b/api/logs/src/test/java/io/opentelemetry/api/logs/DefaultLoggerTest.java
@@ -5,24 +5,31 @@
package io.opentelemetry.api.logs;
+import static io.opentelemetry.api.internal.ValidationUtil.API_USAGE_LOGGER_NAME;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
+import io.github.netmikey.logunit.api.LogCapturer;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.context.Context;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.slf4j.event.LoggingEvent;
class DefaultLoggerTest {
- private static final Logger logger = DefaultLogger.getInstance();
+ @RegisterExtension
+ LogCapturer apiUsageLogs = LogCapturer.create().captureForLogger(API_USAGE_LOGGER_NAME);
@Test
void buildAndEmit() {
+ // Logger with no event.domain
assertThatCode(
() ->
- logger
+ DefaultLogger.getInstance(true)
.logRecordBuilder()
.setEpoch(100, TimeUnit.SECONDS)
.setEpoch(Instant.now())
@@ -34,5 +41,57 @@ class DefaultLoggerTest {
.setAllAttributes(Attributes.builder().put("key2", "value2").build())
.emit())
.doesNotThrowAnyException();
+ assertThatCode(
+ () ->
+ DefaultLogger.getInstance(true)
+ .eventBuilder("event-name")
+ .setEpoch(100, TimeUnit.SECONDS)
+ .setEpoch(Instant.now())
+ .setContext(Context.root())
+ .setSeverity(Severity.DEBUG)
+ .setSeverityText("debug")
+ .setBody("body")
+ .setAttribute(AttributeKey.stringKey("key1"), "value1")
+ .setAllAttributes(Attributes.builder().put("key2", "value2").build())
+ .emit())
+ .doesNotThrowAnyException();
+ assertThat(apiUsageLogs.getEvents()).isEmpty();
+
+ // Logger with event.domain
+ assertThatCode(
+ () ->
+ DefaultLogger.getInstance(false)
+ .logRecordBuilder()
+ .setEpoch(100, TimeUnit.SECONDS)
+ .setEpoch(Instant.now())
+ .setContext(Context.root())
+ .setSeverity(Severity.DEBUG)
+ .setSeverityText("debug")
+ .setBody("body")
+ .setAttribute(AttributeKey.stringKey("key1"), "value1")
+ .setAllAttributes(Attributes.builder().put("key2", "value2").build())
+ .emit())
+ .doesNotThrowAnyException();
+ assertThatCode(
+ () ->
+ DefaultLogger.getInstance(false)
+ .eventBuilder("event-name")
+ .setEpoch(100, TimeUnit.SECONDS)
+ .setEpoch(Instant.now())
+ .setContext(Context.root())
+ .setSeverity(Severity.DEBUG)
+ .setSeverityText("debug")
+ .setBody("body")
+ .setAttribute(AttributeKey.stringKey("key1"), "value1")
+ .setAllAttributes(Attributes.builder().put("key2", "value2").build())
+ .emit())
+ .doesNotThrowAnyException();
+ assertThat(apiUsageLogs.getEvents())
+ .hasSize(1)
+ .extracting(LoggingEvent::getMessage)
+ .allMatch(
+ log ->
+ log.equals(
+ "Cannot emit event from Logger without event domain. Please use LoggerBuilder#setEventDomain(String) when obtaining Logger."));
}
}
diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java
index 04b829276f..0c333cc6fc 100644
--- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java
+++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java
@@ -6,6 +6,7 @@
package io.opentelemetry.sdk.logs;
import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.logs.EventBuilder;
import io.opentelemetry.api.logs.LogRecordBuilder;
import io.opentelemetry.api.logs.Severity;
import io.opentelemetry.api.trace.Span;
@@ -18,8 +19,8 @@ import java.time.Instant;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
-/** SDK implementation of {@link LogRecordBuilder}. */
-final class SdkLogRecordBuilder implements LogRecordBuilder {
+/** SDK implementation of {@link EventBuilder} and {@link LogRecordBuilder}. */
+final class SdkLogRecordBuilder implements EventBuilder {
private final LoggerSharedState loggerSharedState;
private final LogLimits logLimits;
@@ -40,43 +41,43 @@ final class SdkLogRecordBuilder implements LogRecordBuilder {
}
@Override
- public LogRecordBuilder setEpoch(long timestamp, TimeUnit unit) {
+ public SdkLogRecordBuilder setEpoch(long timestamp, TimeUnit unit) {
this.epochNanos = unit.toNanos(timestamp);
return this;
}
@Override
- public LogRecordBuilder setEpoch(Instant instant) {
+ public SdkLogRecordBuilder setEpoch(Instant instant) {
this.epochNanos = TimeUnit.SECONDS.toNanos(instant.getEpochSecond()) + instant.getNano();
return this;
}
@Override
- public LogRecordBuilder setContext(Context context) {
+ public SdkLogRecordBuilder setContext(Context context) {
this.spanContext = Span.fromContext(context).getSpanContext();
return this;
}
@Override
- public LogRecordBuilder setSeverity(Severity severity) {
+ public SdkLogRecordBuilder setSeverity(Severity severity) {
this.severity = severity;
return this;
}
@Override
- public LogRecordBuilder setSeverityText(String severityText) {
+ public SdkLogRecordBuilder setSeverityText(String severityText) {
this.severityText = severityText;
return this;
}
@Override
- public LogRecordBuilder setBody(String body) {
+ public SdkLogRecordBuilder setBody(String body) {
this.body = Body.string(body);
return this;
}
@Override
- public LogRecordBuilder setAttribute(AttributeKey key, T value) {
+ public SdkLogRecordBuilder setAttribute(AttributeKey key, T value) {
if (key == null || key.getKey().isEmpty() || value == null) {
return this;
}
diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogger.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogger.java
index efcb7882ad..ef30a9b2fd 100644
--- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogger.java
+++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogger.java
@@ -5,20 +5,64 @@
package io.opentelemetry.sdk.logs;
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.internal.ValidationUtil;
+import io.opentelemetry.api.logs.EventBuilder;
import io.opentelemetry.api.logs.LogRecordBuilder;
import io.opentelemetry.api.logs.Logger;
+import io.opentelemetry.api.logs.LoggerProvider;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
+import java.util.logging.Level;
+import javax.annotation.Nullable;
/** SDK implementation of {@link Logger}. */
final class SdkLogger implements Logger {
+ // Obtain a noop logger with the domain set so that we can obtain noop EventBuilder without
+ // generating additional warning logs
+ private static final Logger NOOP_LOGGER_WITH_DOMAIN =
+ LoggerProvider.noop().loggerBuilder("unused").setEventDomain("unused").build();
+
private final LoggerSharedState loggerSharedState;
private final InstrumentationScopeInfo instrumentationScopeInfo;
+ @Nullable private final String eventDomain;
SdkLogger(
LoggerSharedState loggerSharedState, InstrumentationScopeInfo instrumentationScopeInfo) {
+ this(loggerSharedState, instrumentationScopeInfo, null);
+ }
+
+ SdkLogger(
+ LoggerSharedState loggerSharedState,
+ InstrumentationScopeInfo instrumentationScopeInfo,
+ @Nullable String eventDomain) {
this.loggerSharedState = loggerSharedState;
this.instrumentationScopeInfo = instrumentationScopeInfo;
+ this.eventDomain = eventDomain;
+ }
+
+ /**
+ * Return a logger identical to {@code this} ensuring the {@link #eventDomain} is equal to {@code
+ * eventDomain}. If {@link #eventDomain} is not equal, creates a new instance.
+ */
+ SdkLogger withEventDomain(String eventDomain) {
+ if (!eventDomain.equals(this.eventDomain)) {
+ return new SdkLogger(loggerSharedState, instrumentationScopeInfo, eventDomain);
+ }
+ return this;
+ }
+
+ @Override
+ public EventBuilder eventBuilder(String eventName) {
+ if (eventDomain == null) {
+ ValidationUtil.log(
+ "Cannot emit event from Logger without event domain. Please use LoggerBuilder#setEventDomain(String) when obtaining Logger.",
+ Level.WARNING);
+ return NOOP_LOGGER_WITH_DOMAIN.eventBuilder(eventName);
+ }
+ return new SdkLogRecordBuilder(loggerSharedState, instrumentationScopeInfo)
+ .setAttribute(AttributeKey.stringKey("event.domain"), eventDomain)
+ .setAttribute(AttributeKey.stringKey("event.name"), eventName);
}
@Override
diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerBuilder.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerBuilder.java
index 99427a2cc2..8d3eeaeb7c 100644
--- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerBuilder.java
+++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLoggerBuilder.java
@@ -9,17 +9,25 @@ import io.opentelemetry.api.logs.LoggerBuilder;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.common.InstrumentationScopeInfoBuilder;
import io.opentelemetry.sdk.internal.ComponentRegistry;
+import javax.annotation.Nullable;
final class SdkLoggerBuilder implements LoggerBuilder {
private final ComponentRegistry registry;
private final InstrumentationScopeInfoBuilder scopeBuilder;
+ @Nullable private String eventDomain;
SdkLoggerBuilder(ComponentRegistry registry, String instrumentationScopeName) {
this.registry = registry;
this.scopeBuilder = InstrumentationScopeInfo.builder(instrumentationScopeName);
}
+ @Override
+ public LoggerBuilder setEventDomain(String eventDomain) {
+ this.eventDomain = eventDomain;
+ return this;
+ }
+
@Override
public SdkLoggerBuilder setSchemaUrl(String schemaUrl) {
scopeBuilder.setSchemaUrl(schemaUrl);
@@ -34,6 +42,7 @@ final class SdkLoggerBuilder implements LoggerBuilder {
@Override
public SdkLogger build() {
- return registry.get(scopeBuilder.build());
+ SdkLogger logger = registry.get(scopeBuilder.build());
+ return eventDomain == null ? logger : logger.withEventDomain(eventDomain);
}
}
diff --git a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLoggerTest.java b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLoggerTest.java
index e9c781f475..d6af99c66e 100644
--- a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLoggerTest.java
+++ b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLoggerTest.java
@@ -9,6 +9,7 @@ import static io.opentelemetry.api.common.AttributeKey.booleanArrayKey;
import static io.opentelemetry.api.common.AttributeKey.doubleArrayKey;
import static io.opentelemetry.api.common.AttributeKey.longArrayKey;
import static io.opentelemetry.api.common.AttributeKey.stringArrayKey;
+import static io.opentelemetry.api.internal.ValidationUtil.API_USAGE_LOGGER_NAME;
import static io.opentelemetry.sdk.testing.assertj.LogAssertions.assertThat;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -17,6 +18,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import io.github.netmikey.logunit.api.LogCapturer;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
@@ -30,9 +32,14 @@ import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.slf4j.event.LoggingEvent;
class SdkLoggerTest {
+ @RegisterExtension
+ LogCapturer apiUsageLogs = LogCapturer.create().captureForLogger(API_USAGE_LOGGER_NAME);
+
@Test
void logRecordBuilder() {
LoggerSharedState state = mock(LoggerSharedState.class);
@@ -134,4 +141,40 @@ class SdkLoggerTest {
verify(logRecordProcessor, never()).onEmit(any());
}
+
+ @Test
+ void eventBuilder() {
+ AtomicReference seenLog = new AtomicReference<>();
+ SdkLoggerProvider loggerProvider =
+ SdkLoggerProvider.builder().addLogRecordProcessor(seenLog::set).build();
+
+ // Emit event from logger with name and add event domain
+ loggerProvider
+ .loggerBuilder("test")
+ .setEventDomain("event-domain")
+ .build()
+ .eventBuilder("event-name")
+ .emit();
+
+ assertThat(seenLog.get().toLogRecordData())
+ .hasAttributes(
+ Attributes.builder()
+ .put("event.domain", "event-domain")
+ .put("event.name", "event-name")
+ .build());
+
+ assertThat(apiUsageLogs.getEvents()).isEmpty();
+ seenLog.set(null);
+
+ // Emit event from logger with name and no event domain
+ loggerProvider.get("test").eventBuilder("event-name");
+
+ assertThat(apiUsageLogs.getEvents())
+ .hasSize(1)
+ .extracting(LoggingEvent::getMessage)
+ .allMatch(
+ log ->
+ log.equals(
+ "Cannot emit event from Logger without event domain. Please use LoggerBuilder#setEventDomain(String) when obtaining Logger."));
+ }
}