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

Event emitter provider #5049

Merged
merged 10 commits into from
Feb 3, 2023
13 changes: 13 additions & 0 deletions api/events/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
plugins {
id("otel.java-conventions")
id("otel.publish-conventions")

id("otel.animalsniffer-conventions")
}

description = "OpenTelemetry Events API"
otelJava.moduleName.set("io.opentelemetry.api.events")

dependencies {
api(project(":api:all"))
}
1 change: 1 addition & 0 deletions api/events/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
otel.release=alpha
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.events;

import io.opentelemetry.api.common.Attributes;

class DefaultEventEmitter implements EventEmitter {

private static final EventEmitter INSTANCE = new DefaultEventEmitter();

private DefaultEventEmitter() {}

static EventEmitter getInstance() {
return INSTANCE;
}

@Override
public void emit(String eventName, Attributes attributes) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.events;

class DefaultEventEmitterProvider implements EventEmitterProvider {

private static final EventEmitterProvider INSTANCE = new DefaultEventEmitterProvider();
private static final EventEmitterBuilder NOOP_EVENT_EMITTER_BUILDER =
new NoopEventEmitterBuilder();

private DefaultEventEmitterProvider() {}

static EventEmitterProvider getInstance() {
return INSTANCE;
}

@Override
public EventEmitterBuilder eventEmitterBuilder(String instrumentationScopeName) {
return NOOP_EVENT_EMITTER_BUILDER;
}

private static class NoopEventEmitterBuilder implements EventEmitterBuilder {

@Override
public EventEmitterBuilder setSchemaUrl(String schemaUrl) {
return this;
}

@Override
public EventEmitterBuilder setInstrumentationVersion(String instrumentationVersion) {
return this;
}

@Override
public EventEmitterBuilder setEventDomain(String eventDomain) {
return this;
}

@Override
public EventEmitter build() {
return DefaultEventEmitter.getInstance();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.events;

import io.opentelemetry.api.common.Attributes;
import javax.annotation.concurrent.ThreadSafe;

/**
* A {@link EventEmitter} is the entry point into an event pipeline.
*
* <p>Example usage emitting events:
*
* <pre>{@code
* class MyClass {
* private final EventEmitter eventEmitter = openTelemetryEventEmitterProvider.eventEmitterBuilder("scope-name")
* .setEventDomain("acme.observability")
* .build();
*
* void doWork() {
* eventEmitter.emit("my-event", Attributes.builder()
* .put("key1", "value1")
* .put("key2", "value2")
* .build())
* // do work
* }
* }
* }</pre>
*/
@ThreadSafe
public interface EventEmitter {

/**
* Emit an event.
*
* @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.
* @param attributes attributes associated with the event
*/
void emit(String eventName, Attributes attributes);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.events;

/**
* Builder class for creating {@link EventEmitter} instances.
*
* <p>{@link EventEmitter}s are identified by their scope name, version, and schema URL. These
* identifying fields, along with attributes, combine to form the instrumentation scope, which is
* attached to all events produced by the {@link EventEmitter}.
*/
public interface EventEmitterBuilder {

/**
* Sets the event domain. Event domain is not part of {@link EventEmitter} identity.
*
* @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
*/
EventEmitterBuilder setEventDomain(String eventDomain);

/**
* Set the scope schema URL of the resulting {@link EventEmitter}. Schema URL is part of {@link
* EventEmitter} identity.
*
* @param schemaUrl The schema URL.
* @return this
*/
EventEmitterBuilder setSchemaUrl(String schemaUrl);

/**
* Sets the instrumentation scope version of the resulting {@link EventEmitter}. Version is part
* of {@link EventEmitter} identity.
*
* @param instrumentationScopeVersion The instrumentation scope version.
* @return this
*/
EventEmitterBuilder setInstrumentationVersion(String instrumentationScopeVersion);

/**
* Gets or creates a {@link EventEmitter} instance.
*
* @return a {@link EventEmitter} instance configured with the provided options.
*/
EventEmitter build();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.events;

import javax.annotation.concurrent.ThreadSafe;

/**
* A registry for creating scoped {@link EventEmitter}s. The name <i>Provider</i> is for consistency
* with other languages and it is <b>NOT</b> loaded using reflection.
*
* @see EventEmitter
*/
@ThreadSafe
public interface EventEmitterProvider {

/**
* Gets or creates a named EventEmitter instance which emits events to the {@code eventDomain}.
*
* @param instrumentationScopeName A name uniquely identifying the instrumentation scope, such as
* the instrumentation library, package, or fully qualified class name. Must not be null.
* @return a Logger instance.
*/
default EventEmitter get(String instrumentationScopeName) {
return eventEmitterBuilder(instrumentationScopeName).build();
}

/**
* Creates a LoggerBuilder for a named EventEmitter instance.
*
* @param instrumentationScopeName A name uniquely identifying the instrumentation scope, such as
* the instrumentation library, package, or fully qualified class name. Must not be null.
* @return a LoggerBuilder instance.
*/
EventEmitterBuilder eventEmitterBuilder(String instrumentationScopeName);

/**
* Returns a no-op {@link EventEmitterProvider} which provides Loggers which do not record or
* emit.
*/
static EventEmitterProvider noop() {
return DefaultEventEmitterProvider.getInstance();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.events;

import io.opentelemetry.api.GlobalOpenTelemetry;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Nullable;

/**
* This class provides a temporary global accessor for {@link EventEmitterProvider} until the event
* API is marked stable. It will eventually be merged into {@link GlobalOpenTelemetry}.
*/
// We intentionally assign to be used for error reporting.
@SuppressWarnings("StaticAssignmentOfThrowable")
public final class GlobalEventEmitterProvider {

private static final AtomicReference<EventEmitterProvider> instance =
new AtomicReference<>(EventEmitterProvider.noop());

@Nullable private static volatile Throwable setInstanceCaller;

private GlobalEventEmitterProvider() {}

/** Returns the globally registered {@link EventEmitterProvider}. */
// instance cannot be set to null
@SuppressWarnings("NullAway")
public static EventEmitterProvider get() {
return instance.get();
}

/**
* Sets the global {@link EventEmitterProvider}. Future calls to {@link #get()} will return the
* provided {@link EventEmitterProvider} instance. This should be called once as early as possible
* in your application initialization logic.
*/
public static void set(EventEmitterProvider eventEmitterProvider) {
boolean changed = instance.compareAndSet(EventEmitterProvider.noop(), eventEmitterProvider);
if (!changed && (eventEmitterProvider != EventEmitterProvider.noop())) {
throw new IllegalStateException(
"GlobalEventEmitterProvider.set has already been called. GlobalEventEmitterProvider.set "
+ "must be called only once before any calls to GlobalEventEmitterProvider.get. "
+ "Previous invocation set to cause of this exception.",
setInstanceCaller);
}
setInstanceCaller = new Throwable();
}

/**
* Unsets the global {@link EventEmitterProvider}. This is only meant to be used from tests which
* need to reconfigure {@link EventEmitterProvider}.
*/
public static void resetForTest() {
instance.set(EventEmitterProvider.noop());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.events;

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 DefaultEventEmitterProviderTest {

@Test
void noopEventEmitterProvider_doesNotThrow() {
EventEmitterProvider provider = EventEmitterProvider.noop();

assertThat(provider).isSameAs(DefaultEventEmitterProvider.getInstance());
assertThatCode(() -> provider.get("scope-name")).doesNotThrowAnyException();
assertThatCode(
() ->
provider
.eventEmitterBuilder("scope-name")
.setEventDomain("event-domain")
.setInstrumentationVersion("1.0")
.setSchemaUrl("http://schema.com")
.build())
.doesNotThrowAnyException();

assertThatCode(
() ->
provider
.eventEmitterBuilder("scope-name")
.build()
.emit("event-name", Attributes.empty()))
.doesNotThrowAnyException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.events;

import static org.assertj.core.api.Assertions.assertThatCode;

import io.opentelemetry.api.common.Attributes;
import org.junit.jupiter.api.Test;

class DefaultEventEmitterTest {

@Test
void emit() {
assertThatCode(() -> DefaultEventEmitter.getInstance().emit("event-name", Attributes.empty()))
.doesNotThrowAnyException();
assertThatCode(
() ->
DefaultEventEmitter.getInstance()
.emit("event-name", Attributes.builder().put("key1", "value1").build()))
.doesNotThrowAnyException();
}
}
Loading