Stabilize log record event name (#7277)

This commit is contained in:
jack-berg 2025-04-17 12:30:43 -05:00 committed by GitHub
parent 65472798f6
commit 9e1e9c6e9f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 133 additions and 57 deletions

View File

@ -198,6 +198,16 @@ public interface LogRecordBuilder {
return setAttribute(key, (long) value);
}
/**
* Sets the event name, which identifies the class / type of the Event.
*
* <p>This name should uniquely identify the event structure (both attributes and body). A log
* record with a non-empty event name is an Event.
*/
default LogRecordBuilder setEventName(String eventName) {
return this;
}
/** Emit the log record. */
void emit();
}

View File

@ -61,6 +61,15 @@ public interface ExtendedLogRecordBuilder extends LogRecordBuilder {
return this;
}
/**
* Sets the event name, which identifies the class / type of the Event.
*
* <p>This name should uniquely identify the event structure (both attributes and body). A log
* record with a non-empty event name is an Event.
*/
@Override
ExtendedLogRecordBuilder setEventName(String eventName);
/**
* {@inheritDoc}
*
@ -124,14 +133,6 @@ public interface ExtendedLogRecordBuilder extends LogRecordBuilder {
*/
<T> ExtendedLogRecordBuilder setAttribute(ExtendedAttributeKey<T> key, T value);
/**
* Sets the event name, which identifies the class / type of the Event.
*
* <p>This name should uniquely identify the event structure (both attributes and body). A log
* record with a non-empty event name is an Event.
*/
ExtendedLogRecordBuilder setEventName(String eventName);
/** Set standard {@code exception.*} attributes based on the {@code throwable}. */
ExtendedLogRecordBuilder setException(Throwable throwable);
}

View File

@ -51,8 +51,7 @@ public abstract class AbstractDefaultLoggerTest {
() ->
getLogger()
.logRecordBuilder()
// TODO (trask) once event name stabilizes
// .setEventName("event name")
.setEventName("event name")
.setTimestamp(100, TimeUnit.SECONDS)
.setTimestamp(Instant.now())
.setObservedTimestamp(100, TimeUnit.SECONDS)

View File

@ -1,2 +1,4 @@
Comparing source compatibility of opentelemetry-api-1.50.0-SNAPSHOT.jar against opentelemetry-api-1.49.0.jar
No changes.
*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.api.logs.LogRecordBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.api.logs.LogRecordBuilder setEventName(java.lang.String)

View File

@ -1,2 +1,9 @@
Comparing source compatibility of opentelemetry-sdk-logs-1.50.0-SNAPSHOT.jar against opentelemetry-sdk-logs-1.49.0.jar
No changes.
*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.logs.data.LogRecordData (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) java.lang.String getEventName()
+++ NEW ANNOTATION: javax.annotation.Nullable
*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.logs.ReadWriteLogRecord (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) java.lang.String getEventName()
+++ NEW ANNOTATION: javax.annotation.Nullable

View File

@ -1,4 +1,14 @@
Comparing source compatibility of opentelemetry-sdk-testing-1.50.0-SNAPSHOT.jar against opentelemetry-sdk-testing-1.49.0.jar
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.testing.assertj.LogRecordDataAssert (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.assertj.LogRecordDataAssert hasEventName(java.lang.String)
**** MODIFIED CLASS: PUBLIC ABSTRACT io.opentelemetry.sdk.testing.logs.TestLogRecordData (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++* NEW METHOD: PUBLIC(+) ABSTRACT(+) java.lang.String getEventName()
+++ NEW ANNOTATION: javax.annotation.Nullable
**** MODIFIED CLASS: PUBLIC ABSTRACT STATIC io.opentelemetry.sdk.testing.logs.TestLogRecordData$Builder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++* NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.testing.logs.TestLogRecordData$Builder setEventName(java.lang.String)
+++ NEW CLASS: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.testing.metrics.TestMetricData (not serializable)
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a.
+++ NEW SUPERCLASS: java.lang.Object

View File

@ -20,7 +20,6 @@ import io.opentelemetry.exporter.internal.otlp.KeyValueMarshaler;
import io.opentelemetry.proto.logs.v1.internal.LogRecord;
import io.opentelemetry.proto.logs.v1.internal.SeverityNumber;
import io.opentelemetry.sdk.logs.data.LogRecordData;
import io.opentelemetry.sdk.logs.data.internal.ExtendedLogRecordData;
import java.io.IOException;
import javax.annotation.Nullable;
@ -40,7 +39,6 @@ final class LogMarshaler extends MarshalerWithSize {
private static final String INVALID_TRACE_ID = TraceId.getInvalid();
private static final String INVALID_SPAN_ID = SpanId.getInvalid();
private static final byte[] EMPTY_BYTES = new byte[0];
private final long timeUnixNano;
private final long observedTimeUnixNano;
@ -82,9 +80,7 @@ final class LogMarshaler extends MarshalerWithSize {
spanContext.getTraceFlags(),
spanContext.getTraceId().equals(INVALID_TRACE_ID) ? null : spanContext.getTraceId(),
spanContext.getSpanId().equals(INVALID_SPAN_ID) ? null : spanContext.getSpanId(),
logRecordData instanceof ExtendedLogRecordData
? MarshalerUtil.toBytes(((ExtendedLogRecordData) logRecordData).getEventName())
: EMPTY_BYTES);
MarshalerUtil.toBytes(logRecordData.getEventName()));
}
private LogMarshaler(

View File

@ -20,7 +20,6 @@ import io.opentelemetry.exporter.internal.otlp.AttributeKeyValueStatelessMarshal
import io.opentelemetry.exporter.internal.otlp.IncubatingUtil;
import io.opentelemetry.proto.logs.v1.internal.LogRecord;
import io.opentelemetry.sdk.logs.data.LogRecordData;
import io.opentelemetry.sdk.logs.data.internal.ExtendedLogRecordData;
import java.io.IOException;
/** See {@link LogMarshaler}. */
@ -79,10 +78,7 @@ final class LogStatelessMarshaler implements StatelessMarshaler<LogRecordData> {
if (!spanContext.getSpanId().equals(INVALID_SPAN_ID)) {
output.serializeSpanId(LogRecord.SPAN_ID, spanContext.getSpanId(), context);
}
if (log instanceof ExtendedLogRecordData) {
output.serializeStringWithContext(
LogRecord.EVENT_NAME, ((ExtendedLogRecordData) log).getEventName(), context);
}
output.serializeStringWithContext(LogRecord.EVENT_NAME, log.getEventName(), context);
}
@Override
@ -130,11 +126,9 @@ final class LogStatelessMarshaler implements StatelessMarshaler<LogRecordData> {
size += MarshalerUtil.sizeSpanId(LogRecord.SPAN_ID, spanContext.getSpanId());
}
if (log instanceof ExtendedLogRecordData) {
size +=
StatelessMarshalerUtil.sizeStringWithContext(
LogRecord.EVENT_NAME, ((ExtendedLogRecordData) log).getEventName(), context);
}
size +=
StatelessMarshalerUtil.sizeStringWithContext(
LogRecord.EVENT_NAME, log.getEventName(), context);
return size;
}

View File

@ -23,7 +23,6 @@ import javax.annotation.Nullable;
final class ExtendedSdkLogRecordBuilder extends SdkLogRecordBuilder
implements ExtendedLogRecordBuilder {
@Nullable private String eventName;
@Nullable private ExtendedAttributesMap extendedAttributes;
ExtendedSdkLogRecordBuilder(
@ -33,7 +32,7 @@ final class ExtendedSdkLogRecordBuilder extends SdkLogRecordBuilder
@Override
public ExtendedSdkLogRecordBuilder setEventName(String eventName) {
this.eventName = eventName;
super.setEventName(eventName);
return this;
}

View File

@ -44,15 +44,19 @@ abstract class ExtendedSdkLogRecordData implements ExtendedLogRecordData {
severity,
severityText,
totalAttributeCount,
eventName,
attributes,
body);
body,
eventName);
}
@Override
@Nullable
public abstract Value<?> getBodyValue();
@Override
@Nullable
public abstract String getEventName();
@Override
@SuppressWarnings("deprecation") // Implementation of deprecated method
public io.opentelemetry.sdk.logs.data.Body getBody() {

View File

@ -25,7 +25,6 @@ import javax.annotation.concurrent.ThreadSafe;
class ExtendedSdkReadWriteLogRecord extends SdkReadWriteLogRecord
implements ExtendedReadWriteLogRecord {
@Nullable private final String eventName;
private final Object lock = new Object();
@GuardedBy("lock")
@ -55,8 +54,8 @@ class ExtendedSdkReadWriteLogRecord extends SdkReadWriteLogRecord
severity,
severityText,
body,
null);
this.eventName = eventName;
null,
eventName);
this.extendedAttributes = extendedAttributes;
}

View File

@ -52,7 +52,11 @@ public interface ReadWriteLogRecord {
/** Return an immutable {@link LogRecordData} instance representing this log record. */
LogRecordData toLogRecordData();
// TODO (trask) once event name stabilizes, add getEventName()
/** Returns the log record event name, or {@code null} if none is set. */
@Nullable
default String getEventName() {
return null;
}
/**
* Returns the value of a given attribute if it exists. This is the equivalent of calling {@code

View File

@ -30,6 +30,7 @@ class SdkLogRecordBuilder implements LogRecordBuilder {
protected Severity severity = Severity.UNDEFINED_SEVERITY_NUMBER;
@Nullable protected String severityText;
@Nullable protected Value<?> body;
@Nullable protected String eventName;
@Nullable private AttributesMap attributes;
SdkLogRecordBuilder(
@ -39,6 +40,12 @@ class SdkLogRecordBuilder implements LogRecordBuilder {
this.instrumentationScopeInfo = instrumentationScopeInfo;
}
@Override
public SdkLogRecordBuilder setEventName(String eventName) {
this.eventName = eventName;
return this;
}
@Override
public SdkLogRecordBuilder setTimestamp(long timestamp, TimeUnit unit) {
this.timestampEpochNanos = unit.toNanos(timestamp);
@ -132,6 +139,7 @@ class SdkLogRecordBuilder implements LogRecordBuilder {
severity,
severityText,
body,
attributes));
attributes,
eventName));
}
}

View File

@ -33,7 +33,8 @@ abstract class SdkLogRecordData implements LogRecordData {
@Nullable String severityText,
@Nullable Value<?> body,
Attributes attributes,
int totalAttributeCount) {
int totalAttributeCount,
@Nullable String eventName) {
return new AutoValue_SdkLogRecordData(
resource,
instrumentationScopeInfo,
@ -44,13 +45,18 @@ abstract class SdkLogRecordData implements LogRecordData {
severityText,
attributes,
totalAttributeCount,
body);
body,
eventName);
}
@Override
@Nullable
public abstract Value<?> getBodyValue();
@Override
@Nullable
public abstract String getEventName();
@Override
@SuppressWarnings("deprecation") // Implementation of deprecated method
public io.opentelemetry.sdk.logs.data.Body getBody() {

View File

@ -30,6 +30,7 @@ class SdkReadWriteLogRecord implements ReadWriteLogRecord {
protected final Severity severity;
@Nullable protected final String severityText;
@Nullable protected final Value<?> body;
@Nullable protected String eventName;
private final Object lock = new Object();
@GuardedBy("lock")
@ -46,7 +47,8 @@ class SdkReadWriteLogRecord implements ReadWriteLogRecord {
Severity severity,
@Nullable String severityText,
@Nullable Value<?> body,
@Nullable AttributesMap attributes) {
@Nullable AttributesMap attributes,
@Nullable String eventName) {
this.logLimits = logLimits;
this.resource = resource;
this.instrumentationScopeInfo = instrumentationScopeInfo;
@ -56,6 +58,7 @@ class SdkReadWriteLogRecord implements ReadWriteLogRecord {
this.severity = severity;
this.severityText = severityText;
this.body = body;
this.eventName = eventName;
this.attributes = attributes;
}
@ -70,7 +73,8 @@ class SdkReadWriteLogRecord implements ReadWriteLogRecord {
Severity severity,
@Nullable String severityText,
@Nullable Value<?> body,
@Nullable AttributesMap attributes) {
@Nullable AttributesMap attributes,
@Nullable String eventName) {
return new SdkReadWriteLogRecord(
logLimits,
resource,
@ -81,7 +85,8 @@ class SdkReadWriteLogRecord implements ReadWriteLogRecord {
severity,
severityText,
body,
attributes);
attributes,
eventName);
}
@Override
@ -122,7 +127,8 @@ class SdkReadWriteLogRecord implements ReadWriteLogRecord {
severityText,
body,
getImmutableAttributes(),
attributes == null ? 0 : attributes.getTotalAddedValues());
attributes == null ? 0 : attributes.getTotalAddedValues(),
eventName);
}
}
@ -168,6 +174,12 @@ class SdkReadWriteLogRecord implements ReadWriteLogRecord {
return getImmutableAttributes();
}
@Override
@Nullable
public String getEventName() {
return eventName;
}
@Nullable
@Override
public <T> T getAttribute(AttributeKey<T> key) {

View File

@ -82,4 +82,10 @@ public interface LogRecordData {
* LogLimits#getMaxNumberOfAttributes()}.
*/
int getTotalAttributeCount();
/** Returns the event name, or {@code null} if none is set. */
@Nullable
default String getEventName() {
return null;
}
}

View File

@ -8,7 +8,6 @@ package io.opentelemetry.sdk.logs.data.internal;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.incubator.common.ExtendedAttributes;
import io.opentelemetry.sdk.logs.data.LogRecordData;
import javax.annotation.Nullable;
/**
* This class is internal and experimental. Its APIs are unstable and can change at any time. Its
@ -17,9 +16,6 @@ import javax.annotation.Nullable;
*/
public interface ExtendedLogRecordData extends LogRecordData {
@Nullable
String getEventName();
/** Returns the attributes for this log, or {@link ExtendedAttributes#empty()} if unset. */
ExtendedAttributes getExtendedAttributes();

View File

@ -68,6 +68,7 @@ class ReadWriteLogRecordTest {
Severity.DEBUG,
"buggin",
body,
initialAttributes);
initialAttributes,
"my.event.name");
}
}

View File

@ -65,6 +65,7 @@ class SdkLogRecordBuilderTest {
Instant timestamp = Instant.now();
Instant observedTimestamp = Instant.now().plusNanos(100);
String eventName = "my.event.name";
String bodyStr = "body";
String sevText = "sevText";
Severity severity = Severity.DEBUG3;
@ -75,6 +76,7 @@ class SdkLogRecordBuilderTest {
TraceFlags.getSampled(),
TraceState.getDefault());
builder.setEventName(eventName);
builder.setBody(bodyStr);
builder.setTimestamp(123, TimeUnit.SECONDS);
builder.setTimestamp(timestamp);
@ -90,8 +92,7 @@ class SdkLogRecordBuilderTest {
assertThat(emittedLog.get().toLogRecordData())
.hasResource(RESOURCE)
.hasInstrumentationScope(SCOPE_INFO)
// TODO (trask) once event name stabilizes
// .hasEventName(eventName)
.hasEventName(eventName)
.hasBody(bodyStr)
.hasTimestamp(TimeUnit.SECONDS.toNanos(timestamp.getEpochSecond()) + timestamp.getNano())
.hasObservedTimestamp(

View File

@ -246,8 +246,7 @@ class SdkLoggerProviderTest {
sdkLoggerProvider
.get("test")
.logRecordBuilder()
// TODO (trask) once event name stabilizes
// .setEventName("event name")
.setEventName("my.event.name")
.setTimestamp(100, TimeUnit.NANOSECONDS)
.setContext(Span.wrap(spanContext).storeInContext(Context.root()))
.setSeverity(Severity.DEBUG)
@ -260,8 +259,7 @@ class SdkLoggerProviderTest {
assertThat(logRecordData.get())
.hasResource(resource)
.hasInstrumentationScope(InstrumentationScopeInfo.create("test"))
// TODO (trask) once event name stabilizes
// .hasEventName("event name")
.hasEventName("my.event.name")
.hasTimestamp(100)
.hasSpanContext(spanContext)
.hasSeverity(Severity.DEBUG)

View File

@ -84,7 +84,19 @@ public final class LogRecordDataAssert extends AbstractAssert<LogRecordDataAsser
return this;
}
// TODO (trask) once event name stabilizes, add hasEventName(String)
/** Asserts the log has the given epoch {@code eventName}. */
public LogRecordDataAssert hasEventName(String eventName) {
isNotNull();
if (!eventName.equals(actual.getEventName())) {
failWithActualExpectedAndMessage(
actual.getEventName(),
eventName,
"Expected log to have eventName <%s> but was <%s>",
eventName,
actual.getEventName());
}
return this;
}
/** Asserts the log has the given epoch {@code timestamp}. */
public LogRecordDataAssert hasTimestamp(long timestampEpochNanos) {

View File

@ -60,6 +60,10 @@ public abstract class TestLogRecordData implements LogRecordData {
@Nullable
public abstract Value<?> getBodyValue();
@Override
@Nullable
public abstract String getEventName();
TestLogRecordData() {}
/** A {@code Builder} class for {@link TestLogRecordData}. */
@ -173,5 +177,8 @@ public abstract class TestLogRecordData implements LogRecordData {
/** Set the total attribute count. */
public abstract Builder setTotalAttributeCount(int totalAttributeCount);
/** Set the event name. */
public abstract Builder setEventName(String eventName);
}
}

View File

@ -29,7 +29,6 @@ import javax.annotation.concurrent.Immutable;
*
* @since 1.27.0
*/
// TODO (trask) delete this class once event name stabilizes
@Immutable
@AutoValue
@AutoValue.CopyAnnotations
@ -67,6 +66,10 @@ public abstract class TestExtendedLogRecordData implements ExtendedLogRecordData
@Nullable
public abstract Value<?> getBodyValue();
@Override
@Nullable
public abstract String getEventName();
TestExtendedLogRecordData() {}
/**
@ -93,6 +96,7 @@ public abstract class TestExtendedLogRecordData implements ExtendedLogRecordData
public abstract Builder setInstrumentationScopeInfo(
InstrumentationScopeInfo instrumentationScopeInfo);
/** Sets the event name. */
public abstract Builder setEventName(String eventName);
/**

View File

@ -107,8 +107,7 @@ public class LogAssertionsTest {
satisfies(DOG, val -> val.startsWith("bar")),
satisfies(AttributeKey.booleanKey("dog is cute"), val -> val.isTrue())))
.hasInstrumentationScope(INSTRUMENTATION_SCOPE_INFO)
// TODO (trask) once event name stabilizes
// .hasEventName("event name")
.hasEventName("event name")
.hasTimestamp(100)
.hasObservedTimestamp(200)
.hasSpanContext(
@ -207,6 +206,7 @@ public class LogAssertionsTest {
.isInstanceOf(AssertionError.class);
assertThatThrownBy(
() -> assertThat(LOG_DATA).hasInstrumentationScope(InstrumentationScopeInfo.empty()));
assertThatThrownBy(() -> assertThat(LOG_DATA).hasEventName("foo"));
assertThatThrownBy(() -> assertThat(LOG_DATA).hasTimestamp(200));
assertThatThrownBy(() -> assertThat(LOG_DATA).hasObservedTimestamp(100));
assertThatThrownBy(