Allow events to be emitted with timestamp (#5928)

Co-authored-by: Jack Berg <jberg@newrelic.com>
This commit is contained in:
jason plumb 2023-11-09 13:58:58 -08:00 committed by GitHub
parent b03ec3aa62
commit 83993e03d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 220 additions and 11 deletions

View File

@ -6,6 +6,8 @@
package io.opentelemetry.api.events;
import io.opentelemetry.api.common.Attributes;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
class DefaultEventEmitter implements EventEmitter {
@ -19,4 +21,27 @@ class DefaultEventEmitter implements EventEmitter {
@Override
public void emit(String eventName, Attributes attributes) {}
@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 setTimestamp(long timestamp, TimeUnit unit) {
return this;
}
@Override
public EventBuilder setTimestamp(Instant instant) {
return this;
}
@Override
public void emit() {}
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.api.events;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
/** The EventBuilder is used to {@link #emit()} events. */
public interface EventBuilder {
/**
* Set the epoch {@code timestamp} for the event, using the timestamp and unit.
*
* <p>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(long timestamp, TimeUnit unit);
/**
* Set the epoch {@code timestamp} for the event, using the instant.
*
* <p>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);
/** Emit an event. */
void emit();
}

View File

@ -40,4 +40,13 @@ public interface EventEmitter {
* @param attributes attributes associated with the event
*/
void emit(String eventName, Attributes attributes);
/**
* Return a {@link EventBuilder} to 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
*/
EventBuilder builder(String eventName, Attributes attributes);
}

View File

@ -8,6 +8,8 @@ package io.opentelemetry.api.events;
import static org.assertj.core.api.Assertions.assertThatCode;
import io.opentelemetry.api.common.Attributes;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
class DefaultEventEmitterTest {
@ -22,4 +24,18 @@ class DefaultEventEmitterTest {
.emit("event-name", Attributes.builder().put("key1", "value1").build()))
.doesNotThrowAnyException();
}
@Test
void builder() {
Attributes attributes = Attributes.builder().put("key1", "value1").build();
EventEmitter emitter = DefaultEventEmitter.getInstance();
assertThatCode(
() ->
emitter
.builder("myEvent", attributes)
.setTimestamp(123456L, TimeUnit.NANOSECONDS)
.setTimestamp(Instant.now())
.emit())
.doesNotThrowAnyException();
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.logs.internal;
import io.opentelemetry.api.events.EventBuilder;
import io.opentelemetry.api.logs.LogRecordBuilder;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
class SdkEventBuilder implements EventBuilder {
private final LogRecordBuilder logRecordBuilder;
private final String eventDomain;
private final String eventName;
SdkEventBuilder(LogRecordBuilder logRecordBuilder, String eventDomain, String eventName) {
this.logRecordBuilder = logRecordBuilder;
this.eventDomain = eventDomain;
this.eventName = eventName;
}
@Override
public EventBuilder setTimestamp(long timestamp, TimeUnit unit) {
this.logRecordBuilder.setTimestamp(timestamp, unit);
return this;
}
@Override
public EventBuilder setTimestamp(Instant instant) {
this.logRecordBuilder.setTimestamp(instant);
return this;
}
@Override
public void emit() {
SdkEventEmitterProvider.addEventNameAndDomain(logRecordBuilder, eventDomain, eventName);
logRecordBuilder.emit();
}
}

View File

@ -7,9 +7,11 @@ package io.opentelemetry.sdk.logs.internal;
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.LogRecordBuilder;
import io.opentelemetry.api.logs.Logger;
import io.opentelemetry.api.logs.LoggerBuilder;
import io.opentelemetry.api.logs.LoggerProvider;
@ -24,7 +26,10 @@ import java.util.concurrent.TimeUnit;
*/
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;
@ -98,9 +103,6 @@ public final class SdkEventEmitterProvider implements EventEmitterProvider {
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;
@ -111,15 +113,31 @@ public final class SdkEventEmitterProvider implements EventEmitterProvider {
this.eventDomain = eventDomain;
}
@Override
public EventBuilder builder(String eventName, Attributes attributes) {
return new SdkEventBuilder(
delegateLogger
.logRecordBuilder()
.setTimestamp(clock.now(), TimeUnit.NANOSECONDS)
.setAllAttributes(attributes),
eventDomain,
eventName);
}
@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();
LogRecordBuilder logRecordBuilder =
delegateLogger
.logRecordBuilder()
.setTimestamp(clock.now(), TimeUnit.NANOSECONDS)
.setAllAttributes(attributes);
addEventNameAndDomain(logRecordBuilder, eventDomain, eventName);
logRecordBuilder.emit();
}
}
static void addEventNameAndDomain(
LogRecordBuilder logRecordBuilder, String eventDomain, String eventName) {
logRecordBuilder.setAttribute(EVENT_DOMAIN, eventDomain).setAttribute(EVENT_NAME, eventName);
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.logs.LogRecordBuilder;
import java.time.Instant;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
class SdkEventBuilderTest {
@Test
void emit() {
String eventDomain = "mydomain";
String eventName = "banana";
LogRecordBuilder logRecordBuilder = mock(LogRecordBuilder.class);
when(logRecordBuilder.setTimestamp(anyLong(), any())).thenReturn(logRecordBuilder);
when(logRecordBuilder.setAttribute(any(), any())).thenReturn(logRecordBuilder);
Instant instant = Instant.now();
new SdkEventBuilder(logRecordBuilder, eventDomain, eventName)
.setTimestamp(123456L, TimeUnit.NANOSECONDS)
.setTimestamp(instant)
.emit();
verify(logRecordBuilder).setAttribute(stringKey("event.domain"), eventDomain);
verify(logRecordBuilder).setAttribute(stringKey("event.name"), eventName);
verify(logRecordBuilder).setTimestamp(123456L, TimeUnit.NANOSECONDS);
verify(logRecordBuilder).setTimestamp(instant);
verify(logRecordBuilder).emit();
}
}

View File

@ -5,16 +5,19 @@
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 io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.events.EventEmitter;
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.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.Test;
@ -91,4 +94,27 @@ class SdkEventEmitterProviderTest {
.put("event.name", "event-name")
.build());
}
@Test
void builder() {
long yesterday = System.nanoTime() - TimeUnit.DAYS.toNanos(1);
Attributes attributes = Attributes.of(stringKey("foo"), "bar");
EventEmitter emitter = eventEmitterProvider.eventEmitterBuilder("test-scope").build();
emitter.builder("testing", attributes).setTimestamp(yesterday, TimeUnit.NANOSECONDS).emit();
verifySeen(yesterday, attributes);
}
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.domain", "unknown")
.put("event.name", "testing")
.build());
}
}