Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Semantic sugar Event API #4961

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,12 @@ dependency as follows, replacing `{{artifact-id}}` with the value from the "Arti

### API Extensions

| Component | Description | Artifact ID | Version |
|---------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------|-------------------------------------------------------------|
| [Kotlin Extension](./extensions/kotlin) | Context extension for coroutines | `opentelemetry-extension-kotlin` | <!--VERSION_STABLE-->1.20.1<!--/VERSION_STABLE--> |
| [Trace Propagators Extension](./extensions/trace-propagators) | Trace propagators, including B3, Jaeger, OT Trace | `opentelemetry-extension-trace-propagators` | <!--VERSION_STABLE-->1.20.1<!--/VERSION_STABLE--> |
| [Incubator Extension](./extensions/incubator) | API incubator, including pass through propagator, and extended tracer | `opentelemetry-extension-incubator` | <!--VERSION_UNSTABLE-->1.20.1-alpha<!--/VERSION_UNSTABLE--> |
| Component | Description | Artifact ID | Version |
|---------------------------------------------------------------|-----------------------------------------------------------------------|---------------------------------------------|-------------------------------------------------------------|
| [Event API Extension](./extensions/event-api) | Event API extension for Logs | `opentelemetry-extension-event-api` | TODO: add after publication |
| [Kotlin Extension](./extensions/kotlin) | Context extension for coroutines | `opentelemetry-extension-kotlin` | <!--VERSION_STABLE-->1.20.1<!--/VERSION_STABLE--> |
| [Trace Propagators Extension](./extensions/trace-propagators) | Trace propagators, including B3, Jaeger, OT Trace | `opentelemetry-extension-trace-propagators` | <!--VERSION_STABLE-->1.20.1<!--/VERSION_STABLE--> |
| [Incubator Extension](./extensions/incubator) | API incubator, including pass through propagator, and extended tracer | `opentelemetry-extension-incubator` | <!--VERSION_UNSTABLE-->1.20.1-alpha<!--/VERSION_UNSTABLE--> |

### SDK

Expand Down
1 change: 1 addition & 0 deletions api/logs/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ otelJava.moduleName.set("io.opentelemetry.api.logs")

dependencies {
api(project(":api:all"))
implementation(project(":semconv"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,28 @@
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_WITH_DOMAIN = new DefaultLogger(/* hasDomain= */ true);
private static final Logger INSTANCE_NO_DOMAIN = new DefaultLogger(/* hasDomain= */ false);
private static final Logger INSTANCE = new DefaultLogger();

private static final EventBuilder NOOP_LOG_RECORD_BUILDER = new NoopLogRecordBuilder();
private static final LogRecordBuilder NOOP_LOG_RECORD_BUILDER = new NoopLogRecordBuilder();

private final boolean hasDomain;
private DefaultLogger() {}

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;
static Logger getInstance() {
return INSTANCE;
}

@Override
public LogRecordBuilder logRecordBuilder() {
return NOOP_LOG_RECORD_BUILDER;
}

private static final class NoopLogRecordBuilder implements EventBuilder {
private static final class NoopLogRecordBuilder implements LogRecordBuilder {

private NoopLogRecordBuilder() {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@
class DefaultLoggerProvider implements LoggerProvider {

private static final LoggerProvider INSTANCE = new DefaultLoggerProvider();
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 static final LoggerBuilder NOOP_BUILDER = new NoopLoggerBuilder();

private DefaultLoggerProvider() {}

Expand All @@ -21,22 +18,12 @@ static LoggerProvider getInstance() {

@Override
public LoggerBuilder loggerBuilder(String instrumentationScopeName) {
return NOOP_BUILDER_NO_DOMAIN;
return NOOP_BUILDER;
}

private static class NoopLoggerBuilder implements LoggerBuilder {

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;
}
private NoopLoggerBuilder() {}

@Override
public LoggerBuilder setSchemaUrl(String schemaUrl) {
Expand All @@ -50,7 +37,7 @@ public LoggerBuilder setInstrumentationVersion(String instrumentationVersion) {

@Override
public Logger build() {
return DefaultLogger.getInstance(hasDomain);
return DefaultLogger.getInstance();
}
}
}

This file was deleted.

40 changes: 4 additions & 36 deletions api/logs/src/main/java/io/opentelemetry/api/logs/Logger.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,47 +10,15 @@
/**
* A {@link Logger} is the entry point into a log pipeline.
*
* <p>Obtain a {@link EventBuilder} or {@link #logRecordBuilder()}, add properties using the
* setters, and emit it via {@link LogRecordBuilder#emit()}.
* <p>Obtain a {{@link #logRecordBuilder()}, add properties using the setters, and emit it via
* {@link LogRecordBuilder#emit()}.
*
* <p>Example usage emitting events:
*
* <pre>{@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
* }
* }
* }</pre>
* <p>See also {@code io.opentelemetry.api.events.EventLogger} in {@code
* io.opentelemetry:opentelemetry-extension-events}.
*/
@ThreadSafe
public interface Logger {

/**
* Return a {@link EventBuilder} to emit an event.
*
* <p><b>NOTE:</b> this API MUST only be called on {@link Logger}s which have been assigned an
* {@link LoggerBuilder#setEventDomain(String) event domain}.
*
* <p>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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,6 @@
/** Builder class for creating {@link Logger} instances. */
public interface LoggerBuilder {

/**
* Set the event domain of the resulting {@link Logger}.
*
* <p><b>NOTE:</b> Event domain is required to use {@link Logger#eventBuilder(String)}.
*
* <p>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}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
* <ol>
* <li>Enable emitting structured <a
* href="https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/semantic_conventions/events.md">events</a>
* via {@link Logger#eventBuilder(String)}. Requires assigning an {@link
* LoggerBuilder#setEventDomain(String) event domain} to the {@link Logger}.
* via {@code io.opentelemetry.api.events.EventLogger} in {@code
* io.opentelemetry:opentelemetry-extension-events}.
* <li>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 <b>NOT</b> a replacement log framework.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,50 +5,29 @@

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.opentelemetry.internal.testing.slf4j.SuppressLogger;
import org.junit.jupiter.api.Test;

class DefaultLoggerProviderTest {

@Test
@SuppressLogger(loggerName = API_USAGE_LOGGER_NAME)
void noopLoggerProvider_doesNotThrow() {
LoggerProvider provider = LoggerProvider.noop();

assertThat(provider).isSameAs(DefaultLoggerProvider.getInstance());
assertThatCode(() -> provider.get("scope-name")).doesNotThrowAnyException();
assertThatCode(() -> provider.get("scope-name").logRecordBuilder().emit())
.doesNotThrowAnyException();
assertThatCode(
() ->
provider
.loggerBuilder("scope-name")
.setInstrumentationVersion("1.0")
.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"))
.logRecordBuilder()
.emit())
.doesNotThrowAnyException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,22 @@

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 io.opentelemetry.internal.testing.slf4j.SuppressLogger;
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 {

@RegisterExtension
LogCapturer apiUsageLogs = LogCapturer.create().captureForLogger(API_USAGE_LOGGER_NAME);

@Test
@SuppressLogger(loggerName = API_USAGE_LOGGER_NAME)
void buildAndEmit() {
// Logger with no event.domain
assertThatCode(
() ->
DefaultLogger.getInstance(true)
DefaultLogger.getInstance()
.logRecordBuilder()
.setEpoch(100, TimeUnit.SECONDS)
.setEpoch(Instant.now())
Expand All @@ -43,57 +32,5 @@ void buildAndEmit() {
.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."));
}
}
6 changes: 6 additions & 0 deletions extensions/event-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# OpenTelemetry Event API

[![Javadocs][javadoc-image]][javadoc-url]

[javadoc-image]: https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-extension-event-api.svg
[javadoc-url]: https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-extension-event-api
14 changes: 14 additions & 0 deletions extensions/event-api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
plugins {
id("otel.java-conventions")
id("otel.publish-conventions")

id("otel.animalsniffer-conventions")
}

description = "OpenTelemetry Event API"
otelJava.moduleName.set("io.opentelemetry.extension.events")

dependencies {
api(project(":api:logs"))
implementation(project(":semconv"))
}
1 change: 1 addition & 0 deletions extensions/event-api/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
otel.release=alpha
Loading