Skip to content

Commit

Permalink
Allow events to be emitted with timestamp (#5928)
Browse files Browse the repository at this point in the history
Co-authored-by: Jack Berg <jberg@newrelic.com>
  • Loading branch information
breedx-splk and jack-berg committed Nov 9, 2023
1 parent b03ec3a commit 83993e0
Show file tree
Hide file tree
Showing 8 changed files with 220 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -19,4 +21,27 @@ static EventEmitter getInstance() {

@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() {}
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
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 {
Expand All @@ -22,4 +24,18 @@ void emit() {
.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();
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@

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;
Expand All @@ -24,7 +26,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 +103,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,15 +113,31 @@ private SdkEventEmitter(Clock clock, Logger delegateLogger, String eventDomain)
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);
}
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -91,4 +94,27 @@ void emit_NoDomain() {
.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());
}
}

0 comments on commit 83993e0

Please sign in to comment.