Skip to content

Commit 83993e0

Browse files
Allow events to be emitted with timestamp (#5928)
Co-authored-by: Jack Berg <[email protected]>
1 parent b03ec3a commit 83993e0

File tree

8 files changed

+220
-11
lines changed

8 files changed

+220
-11
lines changed

api/events/src/main/java/io/opentelemetry/api/events/DefaultEventEmitter.java

+25
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
package io.opentelemetry.api.events;
77

88
import io.opentelemetry.api.common.Attributes;
9+
import java.time.Instant;
10+
import java.util.concurrent.TimeUnit;
911

1012
class DefaultEventEmitter implements EventEmitter {
1113

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

2022
@Override
2123
public void emit(String eventName, Attributes attributes) {}
24+
25+
@Override
26+
public EventBuilder builder(String eventName, Attributes attributes) {
27+
return NoOpEventBuilder.INSTANCE;
28+
}
29+
30+
private static class NoOpEventBuilder implements EventBuilder {
31+
32+
public static final EventBuilder INSTANCE = new NoOpEventBuilder();
33+
34+
@Override
35+
public EventBuilder setTimestamp(long timestamp, TimeUnit unit) {
36+
return this;
37+
}
38+
39+
@Override
40+
public EventBuilder setTimestamp(Instant instant) {
41+
return this;
42+
}
43+
44+
@Override
45+
public void emit() {}
46+
}
2247
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.api.events;
7+
8+
import java.time.Instant;
9+
import java.util.concurrent.TimeUnit;
10+
11+
/** The EventBuilder is used to {@link #emit()} events. */
12+
public interface EventBuilder {
13+
14+
/**
15+
* Set the epoch {@code timestamp} for the event, using the timestamp and unit.
16+
*
17+
* <p>The {@code timestamp} is the time at which the event occurred. If unset, it will be set to
18+
* the current time when {@link #emit()} is called.
19+
*/
20+
EventBuilder setTimestamp(long timestamp, TimeUnit unit);
21+
22+
/**
23+
* Set the epoch {@code timestamp} for the event, using the instant.
24+
*
25+
* <p>The {@code timestamp} is the time at which the event occurred. If unset, it will be set to
26+
* the current time when {@link #emit()} is called.
27+
*/
28+
EventBuilder setTimestamp(Instant instant);
29+
30+
/** Emit an event. */
31+
void emit();
32+
}

api/events/src/main/java/io/opentelemetry/api/events/EventEmitter.java

+9
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,13 @@ public interface EventEmitter {
4040
* @param attributes attributes associated with the event
4141
*/
4242
void emit(String eventName, Attributes attributes);
43+
44+
/**
45+
* Return a {@link EventBuilder} to emit an event.
46+
*
47+
* @param eventName the event name, which acts as a classifier for events. Within a particular
48+
* event domain, event name defines a particular class or type of event.
49+
* @param attributes attributes associated with the event
50+
*/
51+
EventBuilder builder(String eventName, Attributes attributes);
4352
}

api/events/src/test/java/io/opentelemetry/api/events/DefaultEventEmitterTest.java

+16
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import static org.assertj.core.api.Assertions.assertThatCode;
99

1010
import io.opentelemetry.api.common.Attributes;
11+
import java.time.Instant;
12+
import java.util.concurrent.TimeUnit;
1113
import org.junit.jupiter.api.Test;
1214

1315
class DefaultEventEmitterTest {
@@ -22,4 +24,18 @@ void emit() {
2224
.emit("event-name", Attributes.builder().put("key1", "value1").build()))
2325
.doesNotThrowAnyException();
2426
}
27+
28+
@Test
29+
void builder() {
30+
Attributes attributes = Attributes.builder().put("key1", "value1").build();
31+
EventEmitter emitter = DefaultEventEmitter.getInstance();
32+
assertThatCode(
33+
() ->
34+
emitter
35+
.builder("myEvent", attributes)
36+
.setTimestamp(123456L, TimeUnit.NANOSECONDS)
37+
.setTimestamp(Instant.now())
38+
.emit())
39+
.doesNotThrowAnyException();
40+
}
2541
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.sdk.logs.internal;
7+
8+
import io.opentelemetry.api.events.EventBuilder;
9+
import io.opentelemetry.api.logs.LogRecordBuilder;
10+
import java.time.Instant;
11+
import java.util.concurrent.TimeUnit;
12+
13+
class SdkEventBuilder implements EventBuilder {
14+
private final LogRecordBuilder logRecordBuilder;
15+
private final String eventDomain;
16+
private final String eventName;
17+
18+
SdkEventBuilder(LogRecordBuilder logRecordBuilder, String eventDomain, String eventName) {
19+
this.logRecordBuilder = logRecordBuilder;
20+
this.eventDomain = eventDomain;
21+
this.eventName = eventName;
22+
}
23+
24+
@Override
25+
public EventBuilder setTimestamp(long timestamp, TimeUnit unit) {
26+
this.logRecordBuilder.setTimestamp(timestamp, unit);
27+
return this;
28+
}
29+
30+
@Override
31+
public EventBuilder setTimestamp(Instant instant) {
32+
this.logRecordBuilder.setTimestamp(instant);
33+
return this;
34+
}
35+
36+
@Override
37+
public void emit() {
38+
SdkEventEmitterProvider.addEventNameAndDomain(logRecordBuilder, eventDomain, eventName);
39+
logRecordBuilder.emit();
40+
}
41+
}

sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/SdkEventEmitterProvider.java

+29-11
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77

88
import io.opentelemetry.api.common.AttributeKey;
99
import io.opentelemetry.api.common.Attributes;
10+
import io.opentelemetry.api.events.EventBuilder;
1011
import io.opentelemetry.api.events.EventEmitter;
1112
import io.opentelemetry.api.events.EventEmitterBuilder;
1213
import io.opentelemetry.api.events.EventEmitterProvider;
14+
import io.opentelemetry.api.logs.LogRecordBuilder;
1315
import io.opentelemetry.api.logs.Logger;
1416
import io.opentelemetry.api.logs.LoggerBuilder;
1517
import io.opentelemetry.api.logs.LoggerProvider;
@@ -24,7 +26,10 @@
2426
*/
2527
public final class SdkEventEmitterProvider implements EventEmitterProvider {
2628

27-
private static final String DEFAULT_EVENT_DOMAIN = "unknown";
29+
static final AttributeKey<String> EVENT_DOMAIN = AttributeKey.stringKey("event.domain");
30+
static final AttributeKey<String> EVENT_NAME = AttributeKey.stringKey("event.name");
31+
32+
static final String DEFAULT_EVENT_DOMAIN = "unknown";
2833

2934
private final LoggerProvider delegateLoggerProvider;
3035
private final Clock clock;
@@ -98,9 +103,6 @@ public EventEmitter build() {
98103

99104
private static class SdkEventEmitter implements EventEmitter {
100105

101-
private static final AttributeKey<String> EVENT_DOMAIN = AttributeKey.stringKey("event.domain");
102-
private static final AttributeKey<String> EVENT_NAME = AttributeKey.stringKey("event.name");
103-
104106
private final Clock clock;
105107
private final Logger delegateLogger;
106108
private final String eventDomain;
@@ -111,15 +113,31 @@ private SdkEventEmitter(Clock clock, Logger delegateLogger, String eventDomain)
111113
this.eventDomain = eventDomain;
112114
}
113115

116+
@Override
117+
public EventBuilder builder(String eventName, Attributes attributes) {
118+
return new SdkEventBuilder(
119+
delegateLogger
120+
.logRecordBuilder()
121+
.setTimestamp(clock.now(), TimeUnit.NANOSECONDS)
122+
.setAllAttributes(attributes),
123+
eventDomain,
124+
eventName);
125+
}
126+
114127
@Override
115128
public void emit(String eventName, Attributes attributes) {
116-
delegateLogger
117-
.logRecordBuilder()
118-
.setTimestamp(clock.now(), TimeUnit.NANOSECONDS)
119-
.setAllAttributes(attributes)
120-
.setAttribute(EVENT_DOMAIN, eventDomain)
121-
.setAttribute(EVENT_NAME, eventName)
122-
.emit();
129+
LogRecordBuilder logRecordBuilder =
130+
delegateLogger
131+
.logRecordBuilder()
132+
.setTimestamp(clock.now(), TimeUnit.NANOSECONDS)
133+
.setAllAttributes(attributes);
134+
addEventNameAndDomain(logRecordBuilder, eventDomain, eventName);
135+
logRecordBuilder.emit();
123136
}
124137
}
138+
139+
static void addEventNameAndDomain(
140+
LogRecordBuilder logRecordBuilder, String eventDomain, String eventName) {
141+
logRecordBuilder.setAttribute(EVENT_DOMAIN, eventDomain).setAttribute(EVENT_NAME, eventName);
142+
}
125143
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.sdk.logs.internal;
7+
8+
import static io.opentelemetry.api.common.AttributeKey.stringKey;
9+
import static org.mockito.ArgumentMatchers.any;
10+
import static org.mockito.ArgumentMatchers.anyLong;
11+
import static org.mockito.Mockito.mock;
12+
import static org.mockito.Mockito.verify;
13+
import static org.mockito.Mockito.when;
14+
15+
import io.opentelemetry.api.logs.LogRecordBuilder;
16+
import java.time.Instant;
17+
import java.util.concurrent.TimeUnit;
18+
import org.junit.jupiter.api.Test;
19+
20+
class SdkEventBuilderTest {
21+
22+
@Test
23+
void emit() {
24+
String eventDomain = "mydomain";
25+
String eventName = "banana";
26+
27+
LogRecordBuilder logRecordBuilder = mock(LogRecordBuilder.class);
28+
when(logRecordBuilder.setTimestamp(anyLong(), any())).thenReturn(logRecordBuilder);
29+
when(logRecordBuilder.setAttribute(any(), any())).thenReturn(logRecordBuilder);
30+
31+
Instant instant = Instant.now();
32+
new SdkEventBuilder(logRecordBuilder, eventDomain, eventName)
33+
.setTimestamp(123456L, TimeUnit.NANOSECONDS)
34+
.setTimestamp(instant)
35+
.emit();
36+
verify(logRecordBuilder).setAttribute(stringKey("event.domain"), eventDomain);
37+
verify(logRecordBuilder).setAttribute(stringKey("event.name"), eventName);
38+
verify(logRecordBuilder).setTimestamp(123456L, TimeUnit.NANOSECONDS);
39+
verify(logRecordBuilder).setTimestamp(instant);
40+
verify(logRecordBuilder).emit();
41+
}
42+
}

sdk/logs/src/test/java/io/opentelemetry/sdk/logs/internal/SdkEventEmitterProviderTest.java

+26
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@
55

66
package io.opentelemetry.sdk.logs.internal;
77

8+
import static io.opentelemetry.api.common.AttributeKey.stringKey;
89
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
910
import static org.mockito.Mockito.mock;
1011
import static org.mockito.Mockito.when;
1112

1213
import io.opentelemetry.api.common.Attributes;
14+
import io.opentelemetry.api.events.EventEmitter;
1315
import io.opentelemetry.sdk.common.Clock;
1416
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
1517
import io.opentelemetry.sdk.logs.ReadWriteLogRecord;
1618
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
1719
import io.opentelemetry.sdk.resources.Resource;
20+
import java.util.concurrent.TimeUnit;
1821
import java.util.concurrent.atomic.AtomicReference;
1922
import org.junit.jupiter.api.Test;
2023

@@ -91,4 +94,27 @@ void emit_NoDomain() {
9194
.put("event.name", "event-name")
9295
.build());
9396
}
97+
98+
@Test
99+
void builder() {
100+
long yesterday = System.nanoTime() - TimeUnit.DAYS.toNanos(1);
101+
Attributes attributes = Attributes.of(stringKey("foo"), "bar");
102+
103+
EventEmitter emitter = eventEmitterProvider.eventEmitterBuilder("test-scope").build();
104+
105+
emitter.builder("testing", attributes).setTimestamp(yesterday, TimeUnit.NANOSECONDS).emit();
106+
verifySeen(yesterday, attributes);
107+
}
108+
109+
private void verifySeen(long timestamp, Attributes attributes) {
110+
assertThat(seenLog.get().toLogRecordData())
111+
.hasResource(RESOURCE)
112+
.hasInstrumentationScope(InstrumentationScopeInfo.create("test-scope"))
113+
.hasTimestamp(timestamp)
114+
.hasAttributes(
115+
attributes.toBuilder()
116+
.put("event.domain", "unknown")
117+
.put("event.name", "testing")
118+
.build());
119+
}
94120
}

0 commit comments

Comments
 (0)