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

Allow events to be emitted with timestamp #5928

Merged
merged 9 commits into from
Nov 9, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,47 @@ static EventEmitter getInstance() {
return INSTANCE;
}

@Override
public void emit(long epochNanos, String eventName, Attributes attributes) {}

@Override
public void emit(String eventName, Attributes attributes) {}

@Override
public EventBuilder builder() {
jack-berg marked this conversation as resolved.
Show resolved Hide resolved
return NoOpEventBuilder.INSTANCE;
}

@Override
public EventBuilder builder(String eventName) {
return NoOpEventBuilder.INSTANCE;
}

@Override
public EventBuilder builder(String eventName, Attributes attributes) {
return NoOpEventBuilder.INSTANCE;
}

private static class NoOpEventBuilder implements EventBuilder {

public static final EventBuilder INSTANCE = new NoOpEventBuilder();

@Override
public EventBuilder setEventName(String eventName) {
return this;
}

@Override
public EventBuilder setAttributes(Attributes attributes) {
return this;
}

@Override
public EventBuilder setTimestamp(long epochNanos) {
return this;
}

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

package io.opentelemetry.api.events;

import io.opentelemetry.api.common.Attributes;

/** The EventBuilder is used to emit() events. */
public interface EventBuilder {

/** Set the name of the event. */
EventBuilder setEventName(String eventName);

/** Set the attributes to attach to the event. */
EventBuilder setAttributes(Attributes attributes);

/** Sets the timestamp for the event. */
EventBuilder setTimestamp(long epochNanos);

/** Emit an event. */
void emit();
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@
@ThreadSafe
public interface EventEmitter {

/**
* Emit an event.
*
* @param epochNanos The time at which the event happened, in epoch nanoseconds.
* @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
*/
@SuppressWarnings("InconsistentOverloads")
void emit(long epochNanos, String eventName, Attributes attributes);
jack-berg marked this conversation as resolved.
Show resolved Hide resolved

/**
* Emit an event.
*
Expand All @@ -40,4 +51,10 @@ public interface EventEmitter {
* @param attributes attributes associated with the event
*/
void emit(String eventName, Attributes attributes);

EventBuilder builder();

EventBuilder builder(String eventName);

EventBuilder builder(String eventName, Attributes attributes);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,44 @@ void emit() {
.emit("event-name", Attributes.builder().put("key1", "value1").build()))
.doesNotThrowAnyException();
}

@Test
void emitWithTimestamp() {
EventEmitter emitter = DefaultEventEmitter.getInstance();
Attributes attributes = Attributes.builder().put("key1", "value1").build();
assertThatCode(() -> emitter.emit(System.nanoTime(), "event-name", attributes))
.doesNotThrowAnyException();
}

@Test
void builder() {
Attributes attributes = Attributes.builder().put("key1", "value1").build();
EventEmitter emitter = DefaultEventEmitter.getInstance();
assertThatCode(
() ->
emitter
.builder()
.setEventName("myEvent")
.setAttributes(attributes)
.setTimestamp(123456L)
.emit())
.doesNotThrowAnyException();
}

@Test
void builderWithName() {
Attributes attributes = Attributes.builder().put("key1", "value1").build();
EventEmitter emitter = DefaultEventEmitter.getInstance();
assertThatCode(
() -> emitter.builder("myEvent").setAttributes(attributes).setTimestamp(123456L).emit())
.doesNotThrowAnyException();
}

@Test
void builderWithNameAndAttrs() {
Attributes attributes = Attributes.builder().put("key1", "value1").build();
EventEmitter emitter = DefaultEventEmitter.getInstance();
assertThatCode(() -> emitter.builder("myEvent", attributes).setTimestamp(123456L).emit())
.doesNotThrowAnyException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.logs.internal;

import static io.opentelemetry.sdk.logs.internal.SdkEventEmitterProvider.EVENT_DOMAIN;
import static io.opentelemetry.sdk.logs.internal.SdkEventEmitterProvider.EVENT_NAME;

import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.events.EventBuilder;
import io.opentelemetry.api.logs.Logger;
import io.opentelemetry.sdk.common.Clock;
import java.util.concurrent.TimeUnit;

class SdkEventBuilder implements EventBuilder {
private static final String DEFAULT_EVENT_NAME = "unknown-event";
private final Clock clock;
private final Logger delegateLogger;
private final String eventDomain;

private String eventName = DEFAULT_EVENT_NAME;
private Attributes attributes = Attributes.empty();
private long epochNanos = Long.MIN_VALUE;

SdkEventBuilder(Clock clock, Logger delegateLogger, String eventDomain) {
this.clock = clock;
this.delegateLogger = delegateLogger;
this.eventDomain = eventDomain;
}

@Override
public EventBuilder setEventName(String eventName) {
this.eventName = eventName;
return this;
}

@Override
public EventBuilder setAttributes(Attributes attributes) {
this.attributes = attributes;
return this;
}

@Override
public EventBuilder setTimestamp(long epochNanos) {
this.epochNanos = epochNanos;
return this;
}

@Override
public void emit() {
long timestamp = epochNanos == Long.MIN_VALUE ? clock.now() : epochNanos;
delegateLogger
.logRecordBuilder()
.setTimestamp(timestamp, TimeUnit.NANOSECONDS)
.setAllAttributes(attributes)
.setAttribute(EVENT_DOMAIN, eventDomain)
.setAttribute(EVENT_NAME, eventName)
.emit();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.events.EventBuilder;
import io.opentelemetry.api.events.EventEmitter;
import io.opentelemetry.api.events.EventEmitterBuilder;
import io.opentelemetry.api.events.EventEmitterProvider;
import io.opentelemetry.api.logs.Logger;
import io.opentelemetry.api.logs.LoggerBuilder;
import io.opentelemetry.api.logs.LoggerProvider;
import io.opentelemetry.sdk.common.Clock;
import java.util.concurrent.TimeUnit;

/**
* SDK implementation for {@link EventEmitterProvider}.
Expand All @@ -24,7 +24,10 @@
*/
public final class SdkEventEmitterProvider implements EventEmitterProvider {

private static final String DEFAULT_EVENT_DOMAIN = "unknown";
static final AttributeKey<String> EVENT_DOMAIN = AttributeKey.stringKey("event.domain");
static final AttributeKey<String> EVENT_NAME = AttributeKey.stringKey("event.name");

static final String DEFAULT_EVENT_DOMAIN = "unknown";

private final LoggerProvider delegateLoggerProvider;
private final Clock clock;
Expand Down Expand Up @@ -98,9 +101,6 @@ public EventEmitter build() {

private static class SdkEventEmitter implements EventEmitter {

private static final AttributeKey<String> EVENT_DOMAIN = AttributeKey.stringKey("event.domain");
private static final AttributeKey<String> EVENT_NAME = AttributeKey.stringKey("event.name");

private final Clock clock;
private final Logger delegateLogger;
private final String eventDomain;
Expand All @@ -111,14 +111,32 @@ private SdkEventEmitter(Clock clock, Logger delegateLogger, String eventDomain)
this.eventDomain = eventDomain;
}

@Override
public EventBuilder builder() {
return new SdkEventBuilder(clock, delegateLogger, eventDomain);
}

@Override
public EventBuilder builder(String eventName) {
return builder().setEventName(eventName);
}

@Override
public EventBuilder builder(String eventName, Attributes attributes) {
return builder().setEventName(eventName).setAttributes(attributes);
}

@Override
public void emit(String eventName, Attributes attributes) {
delegateLogger
.logRecordBuilder()
.setTimestamp(clock.now(), TimeUnit.NANOSECONDS)
.setAllAttributes(attributes)
.setAttribute(EVENT_DOMAIN, eventDomain)
.setAttribute(EVENT_NAME, eventName)
emit(clock.now(), eventName, attributes);
}

@Override
public void emit(long epochNanos, String eventName, Attributes attributes) {
new SdkEventBuilder(clock, delegateLogger, eventDomain)
.setTimestamp(epochNanos)
.setAttributes(attributes)
.setEventName(eventName)
.emit();
}
}
Expand Down
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.sdk.logs.internal;

import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mock;
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.Logger;
import io.opentelemetry.sdk.common.Clock;
import org.junit.jupiter.api.Test;

class SdkEventBuilderTest {

@Test
void emit() {
String eventDomain = "mydomain";
String eventName = "banana";
Attributes attributes = Attributes.of(stringKey("foo"), "bar");

Logger logger = mock(Logger.class);
LogRecordBuilder logRecordBuilder = mock(LogRecordBuilder.class);
when(logger.logRecordBuilder()).thenReturn(logRecordBuilder);
when(logRecordBuilder.setTimestamp(anyLong(), any())).thenReturn(logRecordBuilder);
when(logRecordBuilder.setAttribute(any(), any())).thenReturn(logRecordBuilder);
when(logRecordBuilder.setAllAttributes(any())).thenReturn(logRecordBuilder);

new SdkEventBuilder(Clock.getDefault(), logger, eventDomain)
.setEventName(eventName)
.setTimestamp(123456L)
.setAttributes(attributes)
.emit();
verify(logRecordBuilder).setAllAttributes(attributes);
verify(logRecordBuilder).setAttribute(stringKey("event.domain"), eventDomain);
verify(logRecordBuilder).setAttribute(stringKey("event.name"), eventName);
verify(logRecordBuilder).emit();
}
}
Loading