diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedDefaultLogger.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedDefaultLogger.java index 3e8dce08e74..378af1c6b00 100644 --- a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedDefaultLogger.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedDefaultLogger.java @@ -17,7 +17,8 @@ class ExtendedDefaultLogger implements ExtendedLogger { private static final Logger INSTANCE = new ExtendedDefaultLogger(); - private static final LogRecordBuilder NOOP_LOG_RECORD_BUILDER = new NoopLogRecordBuilder(); + private static final ExtendedLogRecordBuilder NOOP_LOG_RECORD_BUILDER = + new NoopExtendedLogRecordBuilder(); private ExtendedDefaultLogger() {} @@ -26,13 +27,18 @@ static Logger getNoop() { } @Override - public LogRecordBuilder logRecordBuilder() { + public ExtendedLogRecordBuilder logRecordBuilder() { return NOOP_LOG_RECORD_BUILDER; } - private static final class NoopLogRecordBuilder implements ExtendedLogRecordBuilder { + private static final class NoopExtendedLogRecordBuilder implements ExtendedLogRecordBuilder { - private NoopLogRecordBuilder() {} + private NoopExtendedLogRecordBuilder() {} + + @Override + public ExtendedLogRecordBuilder setEventName(String eventName) { + return this; + } @Override public LogRecordBuilder setTimestamp(long timestamp, TimeUnit unit) { diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedLogRecordBuilder.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedLogRecordBuilder.java index 4e8af8eee70..4a18baa07ed 100644 --- a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedLogRecordBuilder.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedLogRecordBuilder.java @@ -10,6 +10,13 @@ /** Extended {@link LogRecordBuilder} with experimental APIs. */ public interface ExtendedLogRecordBuilder extends LogRecordBuilder { - // Nothing at the moment, but experimental methods may be added in the future. + // keep this class even if it is empty, since experimental methods may be added in the future. + /** + * Sets the event name, which identifies the class / type of the Event. + * + *

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); } diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedLogger.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedLogger.java index db5e2a3b029..de159406c0e 100644 --- a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedLogger.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/ExtendedLogger.java @@ -20,4 +20,7 @@ public interface ExtendedLogger extends Logger { default boolean isEnabled() { return true; } + + @Override + ExtendedLogRecordBuilder logRecordBuilder(); } diff --git a/api/testing-internal/src/main/java/io/opentelemetry/api/testing/internal/AbstractDefaultLoggerTest.java b/api/testing-internal/src/main/java/io/opentelemetry/api/testing/internal/AbstractDefaultLoggerTest.java index 82d9f0b1f67..4b38e6fab19 100644 --- a/api/testing-internal/src/main/java/io/opentelemetry/api/testing/internal/AbstractDefaultLoggerTest.java +++ b/api/testing-internal/src/main/java/io/opentelemetry/api/testing/internal/AbstractDefaultLoggerTest.java @@ -51,6 +51,8 @@ void buildAndEmit() { () -> getLogger() .logRecordBuilder() + // TODO (trask) once event name stabilizes + // .setEventName("event name") .setTimestamp(100, TimeUnit.SECONDS) .setTimestamp(Instant.now()) .setObservedTimestamp(100, TimeUnit.SECONDS) diff --git a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/TestDataExporter.java b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/TestDataExporter.java index 54032fca3e0..63040a9beb1 100644 --- a/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/TestDataExporter.java +++ b/exporters/logging-otlp/src/test/java/io/opentelemetry/exporter/logging/otlp/TestDataExporter.java @@ -28,6 +28,7 @@ import io.opentelemetry.sdk.metrics.internal.data.ImmutableSumData; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.testing.logs.TestLogRecordData; +import io.opentelemetry.sdk.testing.logs.internal.TestExtendedLogRecordData; import io.opentelemetry.sdk.testing.trace.TestSpanData; import io.opentelemetry.sdk.trace.data.EventData; import io.opentelemetry.sdk.trace.data.SpanData; @@ -47,13 +48,14 @@ abstract class TestDataExporter { Resource.create(Attributes.builder().put("key", "value").build()); private static final LogRecordData LOG1 = - TestLogRecordData.builder() + TestExtendedLogRecordData.builder() .setResource(RESOURCE) .setInstrumentationScopeInfo( InstrumentationScopeInfo.builder("instrumentation") .setVersion("1") .setAttributes(Attributes.builder().put("key", "value").build()) .build()) + .setEventName("event name") .setBody("body1") .setSeverity(Severity.INFO) .setSeverityText("INFO") diff --git a/exporters/logging-otlp/src/test/resources/expected-logs-wrapper.json b/exporters/logging-otlp/src/test/resources/expected-logs-wrapper.json index 887fdb13d4b..c298d824182 100644 --- a/exporters/logging-otlp/src/test/resources/expected-logs-wrapper.json +++ b/exporters/logging-otlp/src/test/resources/expected-logs-wrapper.json @@ -27,6 +27,7 @@ }, "logRecords": [ { + "eventName": "event name", "timeUnixNano": "100", "observedTimeUnixNano": "200", "severityNumber": 9, diff --git a/exporters/logging-otlp/src/test/resources/expected-logs.json b/exporters/logging-otlp/src/test/resources/expected-logs.json index fe84d67c4ef..6eae7f28ad3 100644 --- a/exporters/logging-otlp/src/test/resources/expected-logs.json +++ b/exporters/logging-otlp/src/test/resources/expected-logs.json @@ -25,6 +25,7 @@ }, "logRecords": [ { + "eventName": "event name", "timeUnixNano": "100", "observedTimeUnixNano": "200", "severityNumber": 9, diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogMarshaler.java index 530e38dbe05..0ad265e9655 100644 --- a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogMarshaler.java +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogMarshaler.java @@ -19,12 +19,14 @@ 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; 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; @@ -36,6 +38,7 @@ final class LogMarshaler extends MarshalerWithSize { private final TraceFlags traceFlags; @Nullable private final String traceId; @Nullable private final String spanId; + private final byte[] eventName; static LogMarshaler create(LogRecordData logRecordData) { KeyValueMarshaler[] attributeMarshalers = @@ -57,7 +60,10 @@ static LogMarshaler create(LogRecordData logRecordData) { logRecordData.getTotalAttributeCount() - logRecordData.getAttributes().size(), spanContext.getTraceFlags(), spanContext.getTraceId().equals(INVALID_TRACE_ID) ? null : spanContext.getTraceId(), - spanContext.getSpanId().equals(INVALID_SPAN_ID) ? null : spanContext.getSpanId()); + spanContext.getSpanId().equals(INVALID_SPAN_ID) ? null : spanContext.getSpanId(), + logRecordData instanceof ExtendedLogRecordData + ? MarshalerUtil.toBytes(((ExtendedLogRecordData) logRecordData).getEventName()) + : EMPTY_BYTES); } private LogMarshaler( @@ -70,7 +76,8 @@ private LogMarshaler( int droppedAttributesCount, TraceFlags traceFlags, @Nullable String traceId, - @Nullable String spanId) { + @Nullable String spanId, + byte[] eventName) { super( calculateSize( timeUnixNano, @@ -82,7 +89,8 @@ private LogMarshaler( droppedAttributesCount, traceFlags, traceId, - spanId)); + spanId, + eventName)); this.timeUnixNano = timeUnixNano; this.observedTimeUnixNano = observedTimeUnixNano; this.traceId = traceId; @@ -93,6 +101,7 @@ private LogMarshaler( this.anyValueMarshaler = anyValueMarshaler; this.attributeMarshalers = attributeMarshalers; this.droppedAttributesCount = droppedAttributesCount; + this.eventName = eventName; } @Override @@ -115,6 +124,8 @@ protected void writeTo(Serializer output) throws IOException { output.serializeByteAsFixed32(LogRecord.FLAGS, traceFlags.asByte()); output.serializeTraceId(LogRecord.TRACE_ID, traceId); output.serializeSpanId(LogRecord.SPAN_ID, spanId); + + output.serializeString(LogRecord.EVENT_NAME, eventName); } private static int calculateSize( @@ -127,7 +138,8 @@ private static int calculateSize( int droppedAttributesCount, TraceFlags traceFlags, @Nullable String traceId, - @Nullable String spanId) { + @Nullable String spanId, + byte[] eventName) { int size = 0; size += MarshalerUtil.sizeFixed64(LogRecord.TIME_UNIX_NANO, timeUnixNano); @@ -147,6 +159,8 @@ private static int calculateSize( size += MarshalerUtil.sizeByteAsFixed32(LogRecord.FLAGS, traceFlags.asByte()); size += MarshalerUtil.sizeTraceId(LogRecord.TRACE_ID, traceId); size += MarshalerUtil.sizeSpanId(LogRecord.SPAN_ID, spanId); + + size += MarshalerUtil.sizeBytes(LogRecord.EVENT_NAME, eventName); return size; } diff --git a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogStatelessMarshaler.java b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogStatelessMarshaler.java index 1f16ebbe41c..1477af88ce6 100644 --- a/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogStatelessMarshaler.java +++ b/exporters/otlp/common/src/main/java/io/opentelemetry/exporter/internal/otlp/logs/LogStatelessMarshaler.java @@ -19,6 +19,7 @@ import io.opentelemetry.exporter.internal.otlp.AttributeKeyValueStatelessMarshaler; 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}. */ @@ -55,6 +56,10 @@ public void writeTo(Serializer output, LogRecordData log, MarshalerContext conte 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); + } } @Override @@ -93,6 +98,12 @@ public int getBinarySerializedSize(LogRecordData log, MarshalerContext context) size += MarshalerUtil.sizeSpanId(LogRecord.SPAN_ID, spanContext.getSpanId()); } + if (log instanceof ExtendedLogRecordData) { + size += + StatelessMarshalerUtil.sizeStringWithContext( + LogRecord.EVENT_NAME, ((ExtendedLogRecordData) log).getEventName(), context); + } + return size; } } diff --git a/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LogsRequestMarshalerTest.java b/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LogsRequestMarshalerTest.java index 9c01ddc7aed..0e54a10f0e2 100644 --- a/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LogsRequestMarshalerTest.java +++ b/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LogsRequestMarshalerTest.java @@ -33,7 +33,7 @@ import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.logs.data.LogRecordData; import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.sdk.testing.logs.TestLogRecordData; +import io.opentelemetry.sdk.testing.logs.internal.TestExtendedLogRecordData; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UncheckedIOException; @@ -52,6 +52,7 @@ class LogsRequestMarshalerTest { private static final String TRACE_ID = TraceId.fromBytes(TRACE_ID_BYTES); private static final byte[] SPAN_ID_BYTES = new byte[] {0, 0, 0, 0, 4, 3, 2, 1}; private static final String SPAN_ID = SpanId.fromBytes(SPAN_ID_BYTES); + private static final String EVENT_NAME = "hello"; private static final String BODY = "Hello world from this log..."; @Test @@ -59,7 +60,7 @@ void toProtoResourceLogs() { ResourceLogsMarshaler[] resourceLogsMarshalers = ResourceLogsMarshaler.create( Collections.singleton( - TestLogRecordData.builder() + TestExtendedLogRecordData.builder() .setResource( Resource.builder().put("one", 1).setSchemaUrl("http://url").build()) .setInstrumentationScopeInfo( @@ -68,6 +69,7 @@ void toProtoResourceLogs() { .setSchemaUrl("http://url") .setAttributes(Attributes.builder().put("key", "value").build()) .build()) + .setEventName(EVENT_NAME) .setBody(BODY) .setSeverity(Severity.INFO) .setSeverityText("INFO") @@ -108,11 +110,12 @@ void toProtoLogRecord(MarshalerSource marshalerSource) { parse( LogRecord.getDefaultInstance(), marshalerSource.create( - TestLogRecordData.builder() + TestExtendedLogRecordData.builder() .setResource( Resource.create(Attributes.builder().put("testKey", "testValue").build())) .setInstrumentationScopeInfo( InstrumentationScopeInfo.builder("instrumentation").setVersion("1").build()) + .setEventName(EVENT_NAME) .setBody(BODY) .setSeverity(Severity.INFO) .setSeverityText("INFO") @@ -128,6 +131,7 @@ void toProtoLogRecord(MarshalerSource marshalerSource) { assertThat(logRecord.getTraceId().toByteArray()).isEqualTo(TRACE_ID_BYTES); assertThat(logRecord.getSpanId().toByteArray()).isEqualTo(SPAN_ID_BYTES); assertThat(logRecord.getSeverityText()).isEqualTo("INFO"); + assertThat(logRecord.getEventName()).isEqualTo(EVENT_NAME); assertThat(logRecord.getBody()).isEqualTo(AnyValue.newBuilder().setStringValue(BODY).build()); assertThat(logRecord.getAttributesList()) .containsExactly( @@ -147,7 +151,7 @@ void toProtoLogRecord_MinimalFields(MarshalerSource marshalerSource) { parse( LogRecord.getDefaultInstance(), marshalerSource.create( - TestLogRecordData.builder() + TestExtendedLogRecordData.builder() .setResource( Resource.create(Attributes.builder().put("testKey", "testValue").build())) .setInstrumentationScopeInfo( diff --git a/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LowAllocationLogRequestMarshalerTest.java b/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LowAllocationLogRequestMarshalerTest.java index 4890e02dd66..2d8d61e207d 100644 --- a/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LowAllocationLogRequestMarshalerTest.java +++ b/exporters/otlp/common/src/test/java/io/opentelemetry/exporter/internal/otlp/logs/LowAllocationLogRequestMarshalerTest.java @@ -16,7 +16,7 @@ import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.logs.data.LogRecordData; import io.opentelemetry.sdk.resources.Resource; -import io.opentelemetry.sdk.testing.logs.TestLogRecordData; +import io.opentelemetry.sdk.testing.logs.internal.TestExtendedLogRecordData; import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -39,6 +39,7 @@ class LowAllocationLogRequestMarshalerTest { AttributeKey.doubleArrayKey("key_double_array"); private static final AttributeKey> KEY_BOOLEAN_ARRAY = AttributeKey.booleanArrayKey("key_boolean_array"); + private static final String EVENT_NAME = "hello"; private static final String BODY = "Hello world from this log..."; private static final Resource RESOURCE = @@ -72,9 +73,10 @@ private static List createLogRecordDataList() { } private static LogRecordData createLogRecordData() { - return TestLogRecordData.builder() + return TestExtendedLogRecordData.builder() .setResource(RESOURCE) .setInstrumentationScopeInfo(INSTRUMENTATION_SCOPE_INFO) + .setEventName(EVENT_NAME) .setBody(BODY) .setSeverity(Severity.INFO) .setSeverityText("INFO") diff --git a/integration-tests/otlp/src/main/java/io/opentelemetry/integrationtest/OtlpExporterIntegrationTest.java b/integration-tests/otlp/src/main/java/io/opentelemetry/integrationtest/OtlpExporterIntegrationTest.java index 9a0ffec76c6..578bb57eca5 100644 --- a/integration-tests/otlp/src/main/java/io/opentelemetry/integrationtest/OtlpExporterIntegrationTest.java +++ b/integration-tests/otlp/src/main/java/io/opentelemetry/integrationtest/OtlpExporterIntegrationTest.java @@ -648,8 +648,9 @@ private static void testLogRecordExporter(LogRecordExporter logRecordExporter) { TraceFlags.getDefault(), TraceState.getDefault()); - try (Scope unused = Span.wrap(spanContext).makeCurrent()) { + try (Scope ignored = Span.wrap(spanContext).makeCurrent()) { ((ExtendedLogRecordBuilder) logger.logRecordBuilder()) + .setEventName("event name") .setBody( of( KeyValue.of("str_key", of("value")), @@ -699,6 +700,7 @@ private static void testLogRecordExporter(LogRecordExporter logRecordExporter) { // LogRecord via Logger.logRecordBuilder()...emit() io.opentelemetry.proto.logs.v1.LogRecord protoLog1 = ilLogs.getLogRecords(0); + assertThat(protoLog1.getEventName()).isEqualTo("event name"); assertThat(protoLog1.getBody()) .isEqualTo( AnyValue.newBuilder() diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogRecordBuilder.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogRecordBuilder.java index ba1d0e6cfd2..9f874d1f4b3 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogRecordBuilder.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogRecordBuilder.java @@ -23,6 +23,12 @@ final class ExtendedSdkLogRecordBuilder extends SdkLogRecordBuilder super(loggerSharedState, instrumentationScopeInfo); } + @Override + public ExtendedSdkLogRecordBuilder setEventName(String eventName) { + super.setEventName(eventName); + return this; + } + @Override public ExtendedSdkLogRecordBuilder setTimestamp(long timestamp, TimeUnit unit) { super.setTimestamp(timestamp, unit); diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogger.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogger.java index 67813bb933d..e3ab57a9711 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogger.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogger.java @@ -5,6 +5,7 @@ package io.opentelemetry.sdk.logs; +import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.api.incubator.logs.ExtendedLogger; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.logs.internal.LoggerConfig; @@ -26,4 +27,9 @@ final class ExtendedSdkLogger extends SdkLogger implements ExtendedLogger { public boolean isEnabled() { return loggerEnabled; } + + @Override + public ExtendedLogRecordBuilder logRecordBuilder() { + return (ExtendedLogRecordBuilder) super.logRecordBuilder(); + } } diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ReadWriteLogRecord.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ReadWriteLogRecord.java index 03639e31e9f..8b69483d2a8 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ReadWriteLogRecord.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ReadWriteLogRecord.java @@ -52,6 +52,8 @@ default ReadWriteLogRecord setAllAttributes(Attributes attributes) { /** Return an immutable {@link LogRecordData} instance representing this log record. */ LogRecordData toLogRecordData(); + // TODO (trask) once event name stabilizes, add getEventName() + /** * Returns the value of a given attribute if it exists. This is the equivalent of calling {@code * getAttributes().get(key)}. diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java index ce43bc8d2c6..da7e1f45e7e 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java @@ -24,6 +24,7 @@ class SdkLogRecordBuilder implements LogRecordBuilder { private final LogLimits logLimits; private final InstrumentationScopeInfo instrumentationScopeInfo; + @Nullable private String eventName; private long timestampEpochNanos; private long observedTimestampEpochNanos; @Nullable private Context context; @@ -39,6 +40,12 @@ class SdkLogRecordBuilder implements LogRecordBuilder { this.instrumentationScopeInfo = instrumentationScopeInfo; } + // accessible via ExtendedSdkLogRecordBuilder + SdkLogRecordBuilder setEventName(String eventName) { + this.eventName = eventName; + return this; + } + @Override public SdkLogRecordBuilder setTimestamp(long timestamp, TimeUnit unit) { this.timestampEpochNanos = unit.toNanos(timestamp); @@ -126,6 +133,7 @@ public void emit() { loggerSharedState.getLogLimits(), loggerSharedState.getResource(), instrumentationScopeInfo, + eventName, timestampEpochNanos, observedTimestampEpochNanos, Span.fromContext(context).getSpanContext(), diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordData.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordData.java index 1927a0ec572..eb8e966a7fd 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordData.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordData.java @@ -11,7 +11,7 @@ import io.opentelemetry.api.logs.Severity; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; -import io.opentelemetry.sdk.logs.data.LogRecordData; +import io.opentelemetry.sdk.logs.data.internal.ExtendedLogRecordData; import io.opentelemetry.sdk.resources.Resource; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -19,13 +19,14 @@ @AutoValue @AutoValue.CopyAnnotations @Immutable -abstract class SdkLogRecordData implements LogRecordData { +abstract class SdkLogRecordData implements ExtendedLogRecordData { SdkLogRecordData() {} static SdkLogRecordData create( Resource resource, InstrumentationScopeInfo instrumentationScopeInfo, + @Nullable String eventName, long epochNanos, long observedEpochNanos, SpanContext spanContext, @@ -44,13 +45,18 @@ static SdkLogRecordData create( 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() { diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkReadWriteLogRecord.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkReadWriteLogRecord.java index e6c68ce6ec4..ad6197d8cf3 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkReadWriteLogRecord.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkReadWriteLogRecord.java @@ -24,6 +24,7 @@ class SdkReadWriteLogRecord implements ReadWriteLogRecord { private final LogLimits logLimits; private final Resource resource; private final InstrumentationScopeInfo instrumentationScopeInfo; + @Nullable private final String eventName; private final long timestampEpochNanos; private final long observedTimestampEpochNanos; private final SpanContext spanContext; @@ -40,6 +41,7 @@ private SdkReadWriteLogRecord( LogLimits logLimits, Resource resource, InstrumentationScopeInfo instrumentationScopeInfo, + @Nullable String eventName, long timestampEpochNanos, long observedTimestampEpochNanos, SpanContext spanContext, @@ -50,6 +52,7 @@ private SdkReadWriteLogRecord( this.logLimits = logLimits; this.resource = resource; this.instrumentationScopeInfo = instrumentationScopeInfo; + this.eventName = eventName; this.timestampEpochNanos = timestampEpochNanos; this.observedTimestampEpochNanos = observedTimestampEpochNanos; this.spanContext = spanContext; @@ -64,6 +67,7 @@ static SdkReadWriteLogRecord create( LogLimits logLimits, Resource resource, InstrumentationScopeInfo instrumentationScopeInfo, + @Nullable String eventName, long timestampEpochNanos, long observedTimestampEpochNanos, SpanContext spanContext, @@ -75,6 +79,7 @@ static SdkReadWriteLogRecord create( logLimits, resource, instrumentationScopeInfo, + eventName, timestampEpochNanos, observedTimestampEpochNanos, spanContext, @@ -115,6 +120,7 @@ public LogRecordData toLogRecordData() { return SdkLogRecordData.create( resource, instrumentationScopeInfo, + eventName, timestampEpochNanos, observedTimestampEpochNanos, spanContext, diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/data/internal/ExtendedLogRecordData.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/data/internal/ExtendedLogRecordData.java new file mode 100644 index 00000000000..61e48eb7821 --- /dev/null +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/data/internal/ExtendedLogRecordData.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.logs.data.internal; + +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 + * APIs (or a version of them) may be promoted to the public stable API in the future, but no + * guarantees are made. + */ +public interface ExtendedLogRecordData extends LogRecordData { + + @Nullable + String getEventName(); +} diff --git a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/ReadWriteLogRecordTest.java b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/ReadWriteLogRecordTest.java index 7a444817d40..95215ee57db 100644 --- a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/ReadWriteLogRecordTest.java +++ b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/ReadWriteLogRecordTest.java @@ -62,6 +62,7 @@ SdkReadWriteLogRecord buildLogRecord() { limits, resource, scope, + "event name", 0L, 0L, spanContext, diff --git a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilderTest.java b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilderTest.java index 3c5743e673d..0be11e67b31 100644 --- a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilderTest.java +++ b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilderTest.java @@ -60,6 +60,7 @@ void emit_AllFields() { Instant timestamp = Instant.now(); Instant observedTimestamp = Instant.now().plusNanos(100); + String eventName = "event name"; String bodyStr = "body"; String sevText = "sevText"; Severity severity = Severity.DEBUG3; @@ -70,6 +71,7 @@ void emit_AllFields() { TraceFlags.getSampled(), TraceState.getDefault()); + builder.setEventName(eventName); builder.setBody(bodyStr); builder.setTimestamp(123, TimeUnit.SECONDS); builder.setTimestamp(timestamp); @@ -85,6 +87,8 @@ void emit_AllFields() { assertThat(emittedLog.get().toLogRecordData()) .hasResource(RESOURCE) .hasInstrumentationScope(SCOPE_INFO) + // TODO (trask) once event name stabilizes + // .hasEventName(eventName) .hasBody(bodyStr) .hasTimestamp(TimeUnit.SECONDS.toNanos(timestamp.getEpochSecond()) + timestamp.getNano()) .hasObservedTimestamp( diff --git a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLoggerProviderTest.java b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLoggerProviderTest.java index 05da79ee3eb..63063731ac4 100644 --- a/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLoggerProviderTest.java +++ b/sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLoggerProviderTest.java @@ -246,6 +246,8 @@ void loggerBuilder_WithLogRecordProcessor() { sdkLoggerProvider .get("test") .logRecordBuilder() + // TODO (trask) once event name stabilizes + // .setEventName("event name") .setTimestamp(100, TimeUnit.NANOSECONDS) .setContext(Span.wrap(spanContext).storeInContext(Context.root())) .setSeverity(Severity.DEBUG) @@ -258,6 +260,8 @@ void loggerBuilder_WithLogRecordProcessor() { assertThat(logRecordData.get()) .hasResource(resource) .hasInstrumentationScope(InstrumentationScopeInfo.create("test")) + // TODO (trask) once event name stabilizes + // .hasEventName("event name") .hasTimestamp(100) .hasSpanContext(spanContext) .hasSeverity(Severity.DEBUG) diff --git a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/LogRecordDataAssert.java b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/LogRecordDataAssert.java index e2256dcbbef..323e5b7813d 100644 --- a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/LogRecordDataAssert.java +++ b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/LogRecordDataAssert.java @@ -84,6 +84,8 @@ public LogRecordDataAssert hasInstrumentationScope( return this; } + // TODO (trask) once event name stabilizes, add hasEventName(String) + /** Asserts the log has the given epoch {@code timestamp}. */ public LogRecordDataAssert hasTimestamp(long timestampEpochNanos) { isNotNull(); diff --git a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/logs/internal/TestExtendedLogRecordData.java b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/logs/internal/TestExtendedLogRecordData.java new file mode 100644 index 00000000000..750344bf748 --- /dev/null +++ b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/logs/internal/TestExtendedLogRecordData.java @@ -0,0 +1,191 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.testing.logs.internal; + +import com.google.auto.value.AutoValue; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.Value; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.logs.data.LogRecordData; +import io.opentelemetry.sdk.logs.data.internal.ExtendedLogRecordData; +import io.opentelemetry.sdk.resources.Resource; +import java.time.Instant; +import java.util.concurrent.TimeUnit; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * Immutable representation of {@link LogRecordData}. + * + *

This class is internal and experimental. Its APIs are unstable and can change at any time. Its + * APIs (or a version of them) may be promoted to the public stable API in the future, but no + * guarantees are made. + * + * @since 1.27.0 + */ +// TODO (trask) delete this class once event name stabilizes +@Immutable +@AutoValue +@AutoValue.CopyAnnotations +// Carry suppression for Body to AutoValue implementation via @AutoValue.CopyAnnotations +@SuppressWarnings("deprecation") +public abstract class TestExtendedLogRecordData implements ExtendedLogRecordData { + + /** Creates a new Builder for creating an {@link LogRecordData} instance. */ + public static Builder builder() { + return new AutoValue_TestExtendedLogRecordData.Builder() + .setResource(Resource.empty()) + .setInstrumentationScopeInfo(InstrumentationScopeInfo.empty()) + .setTimestamp(0, TimeUnit.NANOSECONDS) + .setObservedTimestamp(0, TimeUnit.NANOSECONDS) + .setSpanContext(SpanContext.getInvalid()) + .setSeverity(Severity.UNDEFINED_SEVERITY_NUMBER) + .setAttributes(Attributes.empty()) + .setTotalAttributeCount(0); + } + + @Deprecated + public io.opentelemetry.sdk.logs.data.Body getBody() { + Value valueBody = getBodyValue(); + return valueBody == null + ? io.opentelemetry.sdk.logs.data.Body.empty() + : io.opentelemetry.sdk.logs.data.Body.string(valueBody.asString()); + } + + /** + * {@inheritDoc} + * + * @since 1.42.0 + */ + @Override + @Nullable + public abstract Value getBodyValue(); + + TestExtendedLogRecordData() {} + + /** + * A {@code Builder} class for {@link TestExtendedLogRecordData}. + * + *

This class is internal and experimental. Its APIs are unstable and can change at any time. + * Its APIs (or a version of them) may be promoted to the public stable API in the future, but no + * guarantees are made. + */ + @AutoValue.Builder + public abstract static class Builder { + + abstract TestExtendedLogRecordData autoBuild(); + + /** Create a new {@link LogRecordData} instance from the data in this. */ + public TestExtendedLogRecordData build() { + return autoBuild(); + } + + /** Set the {@link Resource}. */ + public abstract Builder setResource(Resource resource); + + /** Sets the {@link InstrumentationScopeInfo}. */ + public abstract Builder setInstrumentationScopeInfo( + InstrumentationScopeInfo instrumentationScopeInfo); + + public abstract Builder setEventName(String eventName); + + /** + * Set the epoch {@code timestamp}, using the instant. + * + *

The {@code timestamp} is the time at which the log record occurred. + */ + public Builder setTimestamp(Instant instant) { + return setTimestampEpochNanos( + TimeUnit.SECONDS.toNanos(instant.getEpochSecond()) + instant.getNano()); + } + + /** + * Set the epoch {@code timestamp}, using the timestamp and unit. + * + *

The {@code timestamp} is the time at which the log record occurred. + */ + public Builder setTimestamp(long timestamp, TimeUnit unit) { + return setTimestampEpochNanos(unit.toNanos(timestamp)); + } + + /** + * Set the epoch {@code timestamp}. + * + *

The {@code timestamp} is the time at which the log record occurred. + */ + abstract Builder setTimestampEpochNanos(long epochNanos); + + /** + * Set the {@code observedTimestamp}, using the instant. + * + *

The {@code observedTimestamp} is the time at which the log record was observed. + */ + public Builder setObservedTimestamp(Instant instant) { + return setObservedTimestampEpochNanos( + TimeUnit.SECONDS.toNanos(instant.getEpochSecond()) + instant.getNano()); + } + + /** + * Set the epoch {@code observedTimestamp}, using the timestamp and unit. + * + *

The {@code observedTimestamp} is the time at which the log record was observed. + */ + public Builder setObservedTimestamp(long timestamp, TimeUnit unit) { + return setObservedTimestampEpochNanos(unit.toNanos(timestamp)); + } + + /** + * Set the epoch {@code observedTimestamp}. + * + *

The {@code observedTimestamp} is the time at which the log record was observed. + */ + abstract Builder setObservedTimestampEpochNanos(long epochNanos); + + /** Set the span context. */ + public abstract Builder setSpanContext(SpanContext spanContext); + + /** Set the severity. */ + public abstract Builder setSeverity(Severity severity); + + /** Set the severity text. */ + public abstract Builder setSeverityText(String severityText); + + /** Set the body string. */ + public Builder setBody(String body) { + return setBodyValue(Value.of(body)); + } + + /** + * Set the body. + * + * @deprecated Use {@link #setBodyValue(Value)}. + */ + @Deprecated + public Builder setBody(io.opentelemetry.sdk.logs.data.Body body) { + if (body.getType() == io.opentelemetry.sdk.logs.data.Body.Type.STRING) { + setBodyValue(Value.of(body.asString())); + } else if (body.getType() == io.opentelemetry.sdk.logs.data.Body.Type.EMPTY) { + setBodyValue(null); + } + return this; + } + + /** + * Set the body. + * + * @since 1.42.0 + */ + public abstract Builder setBodyValue(@Nullable Value body); + + /** Set the attributes. */ + public abstract Builder setAttributes(Attributes attributes); + + /** Set the total attribute count. */ + public abstract Builder setTotalAttributeCount(int totalAttributeCount); + } +} diff --git a/sdk/testing/src/test/java/io/opentelemetry/sdk/testing/assertj/LogAssertionsTest.java b/sdk/testing/src/test/java/io/opentelemetry/sdk/testing/assertj/LogAssertionsTest.java index 849ba25b90f..e30210eb9d6 100644 --- a/sdk/testing/src/test/java/io/opentelemetry/sdk/testing/assertj/LogAssertionsTest.java +++ b/sdk/testing/src/test/java/io/opentelemetry/sdk/testing/assertj/LogAssertionsTest.java @@ -34,7 +34,7 @@ import io.opentelemetry.sdk.logs.internal.SdkEventLoggerProvider; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter; -import io.opentelemetry.sdk.testing.logs.TestLogRecordData; +import io.opentelemetry.sdk.testing.logs.internal.TestExtendedLogRecordData; import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; @@ -62,9 +62,10 @@ public class LogAssertionsTest { .build(); private static final LogRecordData LOG_DATA = - TestLogRecordData.builder() + TestExtendedLogRecordData.builder() .setResource(RESOURCE) .setInstrumentationScopeInfo(INSTRUMENTATION_SCOPE_INFO) + .setEventName("event name") .setTimestamp(100, TimeUnit.NANOSECONDS) .setObservedTimestamp(200, TimeUnit.NANOSECONDS) .setSpanContext( @@ -112,6 +113,8 @@ void passing() { 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") .hasTimestamp(100) .hasObservedTimestamp(200) .hasSpanContext(