diff --git a/api/incubator/build.gradle.kts b/api/incubator/build.gradle.kts index 6bd0669222d..a677541764d 100644 --- a/api/incubator/build.gradle.kts +++ b/api/incubator/build.gradle.kts @@ -25,6 +25,9 @@ dependencies { testImplementation("io.opentelemetry.semconv:opentelemetry-semconv-incubating") + testImplementation("org.slf4j:slf4j-api") + testImplementation("org.apache.logging.log4j:log4j-api:2.25.2") + testImplementation("com.google.guava:guava") } 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 14731495606..6ead4b1a1c5 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 @@ -6,6 +6,7 @@ package io.opentelemetry.api.incubator.logs; import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.Value; import io.opentelemetry.api.incubator.common.ExtendedAttributeKey; import io.opentelemetry.api.logs.Logger; @@ -20,6 +21,7 @@ class ExtendedDefaultLogger implements ExtendedLogger { private static final Logger INSTANCE = new ExtendedDefaultLogger(); private static final ExtendedLogRecordBuilder NOOP_LOG_RECORD_BUILDER = new NoopExtendedLogRecordBuilder(); + private static final LogEventBuilder NOOP_LOG_EVENT_BUILDER = new NoopLogEventBuilder(); private ExtendedDefaultLogger() {} @@ -32,6 +34,20 @@ public boolean isEnabled(Severity severity, Context context) { return false; } + @Override + public LogEventBuilder logBuilder(Severity severity, String eventName) { + return NOOP_LOG_EVENT_BUILDER; + } + + @Override + public void log( + Severity severity, + String eventName, + Attributes attributes, + @Nullable Value body, + @Nullable Throwable exception, + Context context) {} + @Override public ExtendedLogRecordBuilder logRecordBuilder() { return NOOP_LOG_RECORD_BUILDER; @@ -109,4 +125,35 @@ public ExtendedLogRecordBuilder setBody(Value body) { @Override public void emit() {} } + + private static final class NoopLogEventBuilder implements LogEventBuilder { + + @Override + public LogEventBuilder setContext(Context context) { + return this; + } + + @Override + public LogEventBuilder setBody(Value body) { + return this; + } + + @Override + public LogEventBuilder setBody(String body) { + return this; + } + + @Override + public LogEventBuilder setAttribute(AttributeKey key, @Nullable T value) { + return this; + } + + @Override + public LogEventBuilder setException(Throwable throwable) { + return this; + } + + @Override + public void emit() {} + } } 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 60f36685900..4336f954cf4 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 @@ -5,9 +5,12 @@ package io.opentelemetry.api.incubator.logs; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.Value; import io.opentelemetry.api.logs.Logger; import io.opentelemetry.api.logs.Severity; import io.opentelemetry.context.Context; +import javax.annotation.Nullable; /** Extended {@link Logger} with experimental APIs. */ public interface ExtendedLogger extends Logger { @@ -41,6 +44,130 @@ default boolean isEnabled() { return isEnabled(Severity.UNDEFINED_SEVERITY_NUMBER); } + // Fluent log API + + /** + * A fluent log event API, with convenience methods for incrementally adding fields and + * attributes. + * + *

Callers must call {@link LogEventBuilder#emit()}. + */ + LogEventBuilder logBuilder(Severity severity, String eventName); + + // Low allocation log APIs + + /** + * A low-allocation alternative to {@link #logBuilder(Severity, String)} which prevents the need + * for allocating a builder instance. + * + * @param severity the log severity number + * @param eventName the name that identifies the class / type of event which uniquely identifies + * the event structure (attributes and body) + * @param attributes the log attributes, or {@link Attributes#empty()} + * @param body the log body, or {@code null} + * @param exception the exception, or {@code null} + * @param context the context, or {@link Context#current()} + */ + void log( + Severity severity, + String eventName, + Attributes attributes, + @Nullable Value body, + @Nullable Throwable exception, + Context context); + + default void log( + Severity severity, + String eventName, + Attributes attributes, + Value body, + Throwable exception) { + log(severity, eventName, attributes, body, exception, Context.current()); + } + + default void log(Severity severity, String eventName, Attributes attributes, Value body) { + log(severity, eventName, attributes, body, null, Context.current()); + } + + default void log( + Severity severity, + String eventName, + Attributes attributes, + String body, + Throwable exception) { + log(severity, eventName, attributes, Value.of(body), exception, Context.current()); + } + + default void log(Severity severity, String eventName, Attributes attributes, String body) { + log(severity, eventName, attributes, Value.of(body)); + } + + default void log( + Severity severity, String eventName, Attributes attributes, Throwable exception) { + log(severity, eventName, attributes, null, exception, Context.current()); + } + + default void log(Severity severity, String eventName, Attributes attributes) { + log(severity, eventName, attributes, null, null, Context.current()); + } + + default void log(Severity severity, String eventName, Throwable exception) { + log(severity, eventName, Attributes.empty(), null, exception, Context.current()); + } + + default void log(Severity severity, String eventName) { + log(severity, eventName, Attributes.empty(), null, null, Context.current()); + } + + // info overloads of log(..) + + /** + * Overload of {@link #log(Severity, String, Attributes, Value, Throwable, Context)} assuming + * {@link Severity#INFO}. + */ + default void info( + String eventName, + Attributes attributes, + @Nullable Value body, + @Nullable Throwable exception, + Context context) { + log(Severity.INFO, eventName, attributes, body, exception, context); + } + + default void info(String eventName, Attributes attributes, Value body, Throwable exception) { + info(eventName, attributes, body, exception, Context.current()); + } + + default void info(String eventName, Attributes attributes, Value body) { + info(eventName, attributes, body, null, Context.current()); + } + + default void info(String eventName, Attributes attributes, String body, Throwable exception) { + info(eventName, attributes, Value.of(body), exception, Context.current()); + } + + default void info(String eventName, Attributes attributes, String body) { + info(eventName, attributes, Value.of(body)); + } + + default void info(String eventName, Attributes attributes, Throwable exception) { + info(eventName, attributes, null, exception, Context.current()); + } + + default void info(String eventName, Attributes attributes) { + info(eventName, attributes, null, null, Context.current()); + } + + default void info(String eventName, Throwable exception) { + info(eventName, Attributes.empty(), null, exception, Context.current()); + } + + default void info(String eventName) { + info(eventName, Attributes.empty(), null, null, Context.current()); + } + + // TODO: add severity overloads for trace, debug, warn, error, fatal + @Override ExtendedLogRecordBuilder logRecordBuilder(); } diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/LogEventBuilder.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/LogEventBuilder.java new file mode 100644 index 00000000000..d3b88326535 --- /dev/null +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/logs/LogEventBuilder.java @@ -0,0 +1,142 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.api.incubator.logs; + +import static io.opentelemetry.api.common.AttributeKey.booleanKey; +import static io.opentelemetry.api.common.AttributeKey.doubleKey; +import static io.opentelemetry.api.common.AttributeKey.longKey; +import static io.opentelemetry.api.common.AttributeKey.stringKey; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.Value; +import io.opentelemetry.api.logs.LogRecordBuilder; +import io.opentelemetry.context.Context; +import javax.annotation.Nullable; + +/** + * Experimental fluent builder for log events. + */ +public interface LogEventBuilder { + + /** Set the context. */ + LogEventBuilder setContext(Context context); + + /** Set the body {@link Value}. */ + LogEventBuilder setBody(Value body); + + /** + * Set the body string. + * + *

Shorthand for calling {@link #setBody(Value)} with {@link Value#of(String)}. + */ + LogEventBuilder setBody(String body); + + /** + * Sets attributes. If the {@link LogRecordBuilder} previously contained a mapping for any of the + * keys, the old values are replaced by the specified values. + */ + @SuppressWarnings("unchecked") + default LogEventBuilder setAllAttributes(Attributes attributes) { + if (attributes == null || attributes.isEmpty()) { + return this; + } + attributes.forEach( + (attributeKey, value) -> setAttribute((AttributeKey) attributeKey, value)); + return this; + } + + /** + * Sets an attribute on the {@code LogRecord}. If the {@code LogRecord} previously contained a + * mapping for the key, the old value is replaced by the specified value. + * + *

Note: Providing a null value is a no-op and will not remove previously set values. + * + * @param key the key for this attribute. + * @param value the value for this attribute. + * @return this. + */ + LogEventBuilder setAttribute(AttributeKey key, @Nullable T value); + + /** + * Sets a String attribute on the {@code LogRecord}. If the {@code LogRecord} previously contained + * a mapping for the key, the old value is replaced by the specified value. + * + *

Note: Providing a null value is a no-op and will not remove previously set values. + * + *

Note: It is strongly recommended to use {@link #setAttribute(AttributeKey, Object)}, and + * pre-allocate your keys, if possible. + * + * @param key the key for this attribute. + * @param value the value for this attribute. + */ + default LogEventBuilder setAttribute(String key, @Nullable String value) { + return setAttribute(stringKey(key), value); + } + + /** + * Sets a Long attribute on the {@code LogRecord}. If the {@code LogRecord} previously contained a + * mapping for the key, the old value is replaced by the specified value. + * + *

Note: It is strongly recommended to use {@link #setAttribute(AttributeKey, Object)}, and + * pre-allocate your keys, if possible. + * + * @param key the key for this attribute. + * @param value the value for this attribute. + */ + default LogEventBuilder setAttribute(String key, long value) { + return setAttribute(longKey(key), value); + } + + /** + * Sets a Double attribute on the {@code LogRecord}. If the {@code LogRecord} previously contained + * a mapping for the key, the old value is replaced by the specified value. + * + *

Note: It is strongly recommended to use {@link #setAttribute(AttributeKey, Object)}, and + * pre-allocate your keys, if possible. + * + * @param key the key for this attribute. + * @param value the value for this attribute. + */ + default LogEventBuilder setAttribute(String key, double value) { + return setAttribute(doubleKey(key), value); + } + + /** + * Sets a Boolean attribute on the {@code LogRecord}. If the {@code LogRecord} previously + * contained a mapping for the key, the old value is replaced by the specified value. + * + *

Note: It is strongly recommended to use {@link #setAttribute(AttributeKey, Object)}, and + * pre-allocate your keys, if possible. + * + * @param key the key for this attribute. + * @param value the value for this attribute. + */ + default LogEventBuilder setAttribute(String key, boolean value) { + return setAttribute(booleanKey(key), value); + } + + /** + * Sets an Integer attribute on the {@code LogRecord}. If the {@code LogRecord} previously + * contained a mapping for the key, the old value is replaced by the specified value. + * + *

Note: It is strongly recommended to use {@link #setAttribute(AttributeKey, Object)}, and + * pre-allocate your keys, if possible. + * + * @param key the key for this attribute. + * @param value the value for this attribute. + */ + default LogEventBuilder setAttribute(String key, int value) { + return setAttribute(key, (long) value); + } + + /** Set standard {@code exception.*} attributes based on the {@code throwable}. */ + LogEventBuilder setException(Throwable throwable); + + /** Emit the log event. */ + void emit(); +} diff --git a/api/incubator/src/test/java/io/opentelemetry/api/incubator/logs/ErgonomicLogApiUsageTest.java b/api/incubator/src/test/java/io/opentelemetry/api/incubator/logs/ErgonomicLogApiUsageTest.java new file mode 100644 index 00000000000..268781c5964 --- /dev/null +++ b/api/incubator/src/test/java/io/opentelemetry/api/incubator/logs/ErgonomicLogApiUsageTest.java @@ -0,0 +1,119 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.api.incubator.logs; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.logs.Severity; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Stream; +import org.apache.logging.log4j.LogManager; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.slf4j.LoggerFactory; + +public class ErgonomicLogApiUsageTest { + + private static final AttributeKey PREVIOUS_TEMP = + AttributeKey.doubleKey("com.acme.previous_temp"); + private static final AttributeKey NEW_TEMP = AttributeKey.doubleKey("com.acme.new_temp"); + + @ParameterizedTest + @MethodSource("loggers") + void simple( + org.apache.logging.log4j.Logger log4j2Logger, + org.slf4j.Logger slf4jLogger, + java.util.logging.Logger julLogger, + io.opentelemetry.api.incubator.logs.ExtendedLogger otelLogger) { + log4j2Logger.info("Hello world"); + log4j2Logger.log(org.apache.logging.log4j.Level.INFO, "Hello world"); + + slf4jLogger.info("Hello world"); + slf4jLogger.atLevel(org.slf4j.event.Level.INFO).log("Hello world"); + + julLogger.info("Hello world"); + julLogger.log(Level.INFO, "Hello world"); + + // In opentelemetry, all log events have an event name. The notion of emitting a simple "Hello + // world" string is a bit of a conceptual mismatch. + // Instead, we emit an event with an event_name set to a value that would allow us to identify + // hello world events + otelLogger.info("com.acme.hello_world"); + otelLogger.log(Severity.INFO, "com.acem.hello_world"); + otelLogger.logBuilder(Severity.INFO, "com.acme.hello_world").emit(); + } + + @ParameterizedTest + @MethodSource("loggers") + void simpleWithException( + org.apache.logging.log4j.Logger log4j2Logger, + org.slf4j.Logger slf4jLogger, + java.util.logging.Logger julLogger, + io.opentelemetry.api.incubator.logs.ExtendedLogger otelLogger) { + log4j2Logger.info("Hello world", new Exception("error!")); + log4j2Logger.log(org.apache.logging.log4j.Level.INFO, "Hello world", new Exception("error!")); + + slf4jLogger.info("Hello world", new Exception("error!")); + slf4jLogger + .atLevel(org.slf4j.event.Level.INFO) + .setCause(new Exception("error!")) + .log("Hello world"); + + // jul doesn't have severity overloads to record exception + julLogger.log(Level.INFO, "Hello world", new Exception("error!")); + + otelLogger.info("com.acme.hello_world", new Exception("error!")); + otelLogger.log(Severity.INFO, "com.acem.hello_world", new Exception("error!")); + otelLogger + .logBuilder(Severity.INFO, "com.acme.hello_world") + .setException(new Exception("error!")) + .emit(); + } + + @ParameterizedTest + @MethodSource("loggers") + void template( + org.apache.logging.log4j.Logger log4j2Logger, + org.slf4j.Logger slf4jLogger, + java.util.logging.Logger julLogger, + io.opentelemetry.api.incubator.logs.ExtendedLogger otelLogger) { + log4j2Logger.info("Temperature changed from {} to {}", 72, 70); + log4j2Logger.log( + org.apache.logging.log4j.Level.INFO, "Temperature changed from {} to {}", 72, 70); + + slf4jLogger.info("Temperature changed from {} to {}", 72, 70); + slf4jLogger + .atLevel(org.slf4j.event.Level.INFO) + .log("Temperature changed from {} to {}", 72, 70); + + // jul doesn't have severity overloads to record params + julLogger.log(Level.INFO, "Temperature changed from {0} to {1}", new Object[] {72, 70}); + + otelLogger.info( + "com.acme.temperature_change", Attributes.of(PREVIOUS_TEMP, 72d, NEW_TEMP, 70d)); + otelLogger.log( + Severity.INFO, + "com.acme.temperature_change", + Attributes.of(PREVIOUS_TEMP, 72d, NEW_TEMP, 70d)); + otelLogger + .logBuilder(Severity.INFO, "com.acme.temperature_change") + .setAttribute(PREVIOUS_TEMP, 72d) + .setAttribute(NEW_TEMP, 70d) + .emit(); + } + + private static Stream loggers() { + return Stream.of( + Arguments.of( + LogManager.getLogger("log4j2-logger"), + LoggerFactory.getLogger("slf4j-logger"), + Logger.getLogger("jul-logger"), + OpenTelemetry.noop().getLogsBridge().get("otel-logger"))); + } +} 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 07444ba738a..feeb6990167 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 @@ -19,8 +19,7 @@ import javax.annotation.Nullable; /** SDK implementation of {@link ExtendedLogRecordBuilder}. */ -final class ExtendedSdkLogRecordBuilder extends SdkLogRecordBuilder - implements ExtendedLogRecordBuilder { +class ExtendedSdkLogRecordBuilder extends SdkLogRecordBuilder implements ExtendedLogRecordBuilder { @Nullable private ExtendedAttributesMap extendedAttributes; 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 5ceb6414f41..a5670e60eff 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,12 +5,18 @@ package io.opentelemetry.sdk.logs; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.Value; import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; import io.opentelemetry.api.incubator.logs.ExtendedLogger; +import io.opentelemetry.api.incubator.logs.LogEventBuilder; import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.internal.AttributesMap; import io.opentelemetry.sdk.logs.internal.LoggerConfig; +import javax.annotation.Nullable; /** SDK implementation of {@link ExtendedLogger}. */ final class ExtendedSdkLogger extends SdkLogger implements ExtendedLogger { @@ -28,6 +34,56 @@ public boolean isEnabled(Severity severity, Context context) { return super.isEnabled(severity, context); } + @Override + public LogEventBuilder logBuilder(Severity severity, String eventName) { + return new SdkLogEventBuilder(loggerSharedState, instrumentationScopeInfo, this); + } + + @Override + public void log( + Severity severity, + String eventName, + Attributes attributes, + @Nullable Value body, + @Nullable Throwable exception, + Context context) { + if (!isEnabled(severity, context)) { + return; + } + // TODO: find way to reuse logic from SdkLogRecordBuilder#emit + if (loggerSharedState.hasBeenShutdown()) { + return; + } + + LogLimits logLimits = loggerSharedState.getLogLimits(); + + long now = loggerSharedState.getClock().now(); + AttributesMap attributesMap = null; + if (!attributes.isEmpty()) { + attributesMap = + AttributesMap.create( + logLimits.getMaxNumberOfAttributes(), logLimits.getMaxAttributeValueLength()); + attributes.forEach(attributesMap::put); + } + + loggerSharedState + .getLogRecordProcessor() + .onEmit( + context, + SdkReadWriteLogRecord.create( + logLimits, + loggerSharedState.getResource(), + instrumentationScopeInfo, + now, + now, + Span.fromContext(context).getSpanContext(), + severity, + null, + body, + attributesMap, + eventName)); + } + @Override public ExtendedLogRecordBuilder logRecordBuilder() { return (ExtendedLogRecordBuilder) super.logRecordBuilder(); diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogEventBuilder.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogEventBuilder.java new file mode 100644 index 00000000000..cd04e550fd0 --- /dev/null +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogEventBuilder.java @@ -0,0 +1,99 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.logs; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.common.Value; +import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder; +import io.opentelemetry.api.incubator.logs.LogEventBuilder; +import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.context.Context; +import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import javax.annotation.Nullable; + +/** SDK implementation of {@link ExtendedLogRecordBuilder}. */ +final class SdkLogEventBuilder extends ExtendedSdkLogRecordBuilder implements LogEventBuilder { + + SdkLogEventBuilder( + LoggerSharedState loggerSharedState, + InstrumentationScopeInfo instrumentationScopeInfo, + SdkLogger logger) { + super(loggerSharedState, instrumentationScopeInfo, logger); + } + + @Override + public SdkLogEventBuilder setException(Throwable throwable) { + super.setException(throwable); + return this; + } + + @Override + public SdkLogEventBuilder setContext(Context context) { + super.setContext(context); + return this; + } + + @Override + public SdkLogEventBuilder setSeverity(Severity severity) { + super.setSeverity(severity); + return this; + } + + @Override + public SdkLogEventBuilder setBody(String body) { + super.setBody(body); + return this; + } + + @Override + public SdkLogEventBuilder setBody(Value value) { + super.setBody(value); + return this; + } + + @Override + public SdkLogEventBuilder setAttribute(AttributeKey key, @Nullable T value) { + super.setAttribute(key, value); + return this; + } + + @Override + public SdkLogEventBuilder setAttribute(String key, @Nullable String value) { + super.setAttribute(key, value); + return this; + } + + @Override + public SdkLogEventBuilder setAttribute(String key, long value) { + super.setAttribute(key, value); + return this; + } + + @Override + public SdkLogEventBuilder setAttribute(String key, double value) { + super.setAttribute(key, value); + return this; + } + + @Override + public SdkLogEventBuilder setAttribute(String key, boolean value) { + super.setAttribute(key, value); + return this; + } + + @Override + public SdkLogEventBuilder setAttribute(String key, int value) { + super.setAttribute(key, value); + return this; + } + + @Override + public SdkLogEventBuilder setAllAttributes(Attributes attributes) { + super.setAllAttributes(attributes); + return this; + } +} diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogger.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogger.java index 0fa97771d1f..a29000f8a4a 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogger.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogger.java @@ -32,8 +32,8 @@ class SdkLogger implements Logger { INCUBATOR_AVAILABLE = incubatorAvailable; } - private final LoggerSharedState loggerSharedState; - private final InstrumentationScopeInfo instrumentationScopeInfo; + protected final LoggerSharedState loggerSharedState; + protected final InstrumentationScopeInfo instrumentationScopeInfo; protected volatile boolean loggerEnabled; protected volatile Severity minimumSeverity;