From 0cfe7e6f1a68b65edcfb1a2dfa1f8d08c25b55de Mon Sep 17 00:00:00 2001 From: Trask Stalnaker Date: Wed, 30 Jul 2025 16:58:06 -0700 Subject: [PATCH 1/4] Dependency submission --- .github/workflows/dependency-submission.yml | 33 +++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/dependency-submission.yml diff --git a/.github/workflows/dependency-submission.yml b/.github/workflows/dependency-submission.yml new file mode 100644 index 00000000000..975b487b39c --- /dev/null +++ b/.github/workflows/dependency-submission.yml @@ -0,0 +1,33 @@ +# This workflow submits dependency information to GitHub's dependency graph +# for analysis by security features like Dependabot, security advisories, and supply chain reports. +# It runs on the default branch to ensure accurate dependency information is submitted. +# +# Source: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/configuring-automatic-dependency-submission-for-your-repository + +name: Dependency submission + +on: + push: + branches: + - main + +permissions: + contents: read + +jobs: + dependency-submission: + permissions: + contents: write + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Set up JDK + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + with: + distribution: temurin + java-version: 17 + + - name: Generate and submit dependency graph + uses: gradle/actions/dependency-submission@ac638b010cf58a27ee6c972d7336334ccaf61c96 # v4.4.1 From 1ab3533a69f2e3af6ae2ae387be1eadaf1c44742 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 Aug 2025 19:01:31 +0000 Subject: [PATCH 2/4] Initial plan From 46213dcdbded77230e624802322ed8aad0392005 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 Aug 2025 19:31:32 +0000 Subject: [PATCH 3/4] Implement minimum_severity and trace_based logger configuration parameters Co-authored-by: trask <218610+trask@users.noreply.github.com> --- .../sdk/logs/ExtendedSdkLogRecordBuilder.java | 23 +++ .../sdk/logs/IncubatingUtil.java | 5 +- .../sdk/logs/SdkLogRecordBuilder.java | 27 +++- .../io/opentelemetry/sdk/logs/SdkLogger.java | 31 +++- .../sdk/logs/internal/LoggerConfig.java | 45 +++++- .../sdk/logs/LoggerConfigTest.java | 132 ++++++++++++++++++ 6 files changed, 255 insertions(+), 8 deletions(-) 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 41596afa3da..353d2d2cb61 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 @@ -24,6 +24,12 @@ final class ExtendedSdkLogRecordBuilder extends SdkLogRecordBuilder @Nullable private ExtendedAttributesMap extendedAttributes; + ExtendedSdkLogRecordBuilder( + LoggerSharedState loggerSharedState, InstrumentationScopeInfo instrumentationScopeInfo, @Nullable SdkLogger logger) { + super(loggerSharedState, instrumentationScopeInfo, logger); + } + + // Backward compatible constructor ExtendedSdkLogRecordBuilder( LoggerSharedState loggerSharedState, InstrumentationScopeInfo instrumentationScopeInfo) { super(loggerSharedState, instrumentationScopeInfo); @@ -133,6 +139,23 @@ public void emit() { return; } Context context = this.context == null ? Context.current() : this.context; + + // Apply filtering rules if logger is available + if (logger != null) { + // 1. Check minimum severity level + if (severity != Severity.UNDEFINED_SEVERITY_NUMBER && severity.getSeverityNumber() < logger.minimumSeverity) { + return; + } + + // 2. Check trace-based filtering + if (logger.traceBased) { + Span span = Span.fromContext(context); + if (span.getSpanContext().isValid() && !span.getSpanContext().getTraceFlags().isSampled()) { + return; + } + } + } + long observedTimestampEpochNanos = this.observedTimestampEpochNanos == 0 ? this.loggerSharedState.getClock().now() diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/IncubatingUtil.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/IncubatingUtil.java index fd7582644be..27f92460257 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/IncubatingUtil.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/IncubatingUtil.java @@ -7,6 +7,7 @@ import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.logs.internal.LoggerConfig; +import javax.annotation.Nullable; /** * Utilities for interacting with {@code io.opentelemetry:opentelemetry-api-incubator}, which is not @@ -25,7 +26,7 @@ static SdkLogger createExtendedLogger( } static SdkLogRecordBuilder createExtendedLogRecordBuilder( - LoggerSharedState loggerSharedState, InstrumentationScopeInfo instrumentationScopeInfo) { - return new ExtendedSdkLogRecordBuilder(loggerSharedState, instrumentationScopeInfo); + LoggerSharedState loggerSharedState, InstrumentationScopeInfo instrumentationScopeInfo, @Nullable SdkLogger logger) { + return new ExtendedSdkLogRecordBuilder(loggerSharedState, instrumentationScopeInfo, logger); } } 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 1fa78926396..2ffeaa05518 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 @@ -22,6 +22,7 @@ class SdkLogRecordBuilder implements LogRecordBuilder { protected final LoggerSharedState loggerSharedState; protected final LogLimits logLimits; + @Nullable protected final SdkLogger logger; protected final InstrumentationScopeInfo instrumentationScopeInfo; protected long timestampEpochNanos; @@ -34,10 +35,17 @@ class SdkLogRecordBuilder implements LogRecordBuilder { @Nullable private AttributesMap attributes; SdkLogRecordBuilder( - LoggerSharedState loggerSharedState, InstrumentationScopeInfo instrumentationScopeInfo) { + LoggerSharedState loggerSharedState, InstrumentationScopeInfo instrumentationScopeInfo, @Nullable SdkLogger logger) { this.loggerSharedState = loggerSharedState; this.logLimits = loggerSharedState.getLogLimits(); this.instrumentationScopeInfo = instrumentationScopeInfo; + this.logger = logger; + } + + // Backward compatible constructor for existing usages + SdkLogRecordBuilder( + LoggerSharedState loggerSharedState, InstrumentationScopeInfo instrumentationScopeInfo) { + this(loggerSharedState, instrumentationScopeInfo, null); } @Override @@ -121,6 +129,23 @@ public void emit() { return; } Context context = this.context == null ? Context.current() : this.context; + + // Apply filtering rules if logger is available + if (logger != null) { + // 1. Check minimum severity level + if (severity != Severity.UNDEFINED_SEVERITY_NUMBER && severity.getSeverityNumber() < logger.minimumSeverity) { + return; + } + + // 2. Check trace-based filtering + if (logger.traceBased) { + Span span = Span.fromContext(context); + if (span.getSpanContext().isValid() && !span.getSpanContext().getTraceFlags().isSampled()) { + return; + } + } + } + long observedTimestampEpochNanos = this.observedTimestampEpochNanos == 0 ? this.loggerSharedState.getClock().now() 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 7539a5c3b17..dda48c5382c 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 @@ -9,6 +9,8 @@ import io.opentelemetry.api.logs.Logger; import io.opentelemetry.api.logs.LoggerProvider; import io.opentelemetry.api.logs.Severity; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.logs.internal.LoggerConfig; @@ -36,6 +38,8 @@ class SdkLogger implements Logger { // deliberately not volatile because of performance concerns // - which means its eventually consistent protected boolean loggerEnabled; + protected int minimumSeverity; + protected boolean traceBased; SdkLogger( LoggerSharedState loggerSharedState, @@ -44,6 +48,8 @@ class SdkLogger implements Logger { this.loggerSharedState = loggerSharedState; this.instrumentationScopeInfo = instrumentationScopeInfo; this.loggerEnabled = loggerConfig.isEnabled(); + this.minimumSeverity = loggerConfig.getMinimumSeverity(); + this.traceBased = loggerConfig.isTraceBased(); } static SdkLogger create( @@ -60,8 +66,8 @@ public LogRecordBuilder logRecordBuilder() { if (loggerEnabled) { return INCUBATOR_AVAILABLE ? IncubatingUtil.createExtendedLogRecordBuilder( - loggerSharedState, instrumentationScopeInfo) - : new SdkLogRecordBuilder(loggerSharedState, instrumentationScopeInfo); + loggerSharedState, instrumentationScopeInfo, this) + : new SdkLogRecordBuilder(loggerSharedState, instrumentationScopeInfo, this); } return NOOP_LOGGER.logRecordBuilder(); } @@ -73,10 +79,29 @@ InstrumentationScopeInfo getInstrumentationScopeInfo() { // Visible for testing public boolean isEnabled(Severity severity, Context context) { - return loggerEnabled; + if (!loggerEnabled) { + return false; + } + + // Check minimum severity level + if (severity != Severity.UNDEFINED_SEVERITY_NUMBER && severity.getSeverityNumber() < minimumSeverity) { + return false; + } + + // Check trace-based filtering + if (traceBased) { + SpanContext spanContext = Span.fromContext(context).getSpanContext(); + if (spanContext.isValid() && !spanContext.getTraceFlags().isSampled()) { + return false; + } + } + + return true; } void updateLoggerConfig(LoggerConfig loggerConfig) { loggerEnabled = loggerConfig.isEnabled(); + minimumSeverity = loggerConfig.getMinimumSeverity(); + traceBased = loggerConfig.isTraceBased(); } } diff --git a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/LoggerConfig.java b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/LoggerConfig.java index 00ffdc86b41..0d0a2078c2c 100644 --- a/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/LoggerConfig.java +++ b/sdk/logs/src/main/java/io/opentelemetry/sdk/logs/internal/LoggerConfig.java @@ -30,9 +30,9 @@ public abstract class LoggerConfig { private static final LoggerConfig DEFAULT_CONFIG = - new AutoValue_LoggerConfig(/* enabled= */ true); + new AutoValue_LoggerConfig(/* enabled= */ true, /* minimumSeverity= */ 0, /* traceBased= */ false); private static final LoggerConfig DISABLED_CONFIG = - new AutoValue_LoggerConfig(/* enabled= */ false); + new AutoValue_LoggerConfig(/* enabled= */ false, /* minimumSeverity= */ 0, /* traceBased= */ false); /** Returns a disabled {@link LoggerConfig}. */ public static LoggerConfig disabled() { @@ -44,6 +44,33 @@ public static LoggerConfig enabled() { return DEFAULT_CONFIG; } + /** + * Returns a {@link LoggerConfig} with the specified parameters. + * + * @param enabled whether the logger is enabled + * @param minimumSeverity minimum severity level for log records to be processed (default 0) + * @param traceBased whether to only process log records from sampled traces (default false) + */ + public static LoggerConfig create(boolean enabled, int minimumSeverity, boolean traceBased) { + return new AutoValue_LoggerConfig(enabled, minimumSeverity, traceBased); + } + + /** + * Returns a {@link LoggerConfig} with the specified minimum severity level. + * + * @param minimumSeverity minimum severity level for log records to be processed + */ + public static LoggerConfig withMinimumSeverity(int minimumSeverity) { + return create(/* enabled= */ true, minimumSeverity, /* traceBased= */ false); + } + + /** + * Returns a {@link LoggerConfig} with trace-based filtering enabled. + */ + public static LoggerConfig withTraceBased() { + return create(/* enabled= */ true, /* minimumSeverity= */ 0, /* traceBased= */ true); + } + /** * Returns the default {@link LoggerConfig}, which is used when no configurator is set or when the * logger configurator returns {@code null} for a {@link InstrumentationScopeInfo}. @@ -64,4 +91,18 @@ public static ScopeConfiguratorBuilder configuratorBuilder() { /** Returns {@code true} if this logger is enabled. Defaults to {@code true}. */ public abstract boolean isEnabled(); + + /** + * Returns the minimum severity level for log records to be processed. + * Log records with severity below this level are dropped. + * Defaults to {@code 0}. + */ + public abstract int getMinimumSeverity(); + + /** + * Returns {@code true} if this logger should only process log records from sampled traces. + * When {@code true}, log records not associated with sampled traces are dropped. + * Defaults to {@code false}. + */ + public abstract boolean isTraceBased(); } diff --git a/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/LoggerConfigTest.java b/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/LoggerConfigTest.java index 699f9be4c4d..cc4e615106c 100644 --- a/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/LoggerConfigTest.java +++ b/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/LoggerConfigTest.java @@ -193,4 +193,136 @@ void setScopeConfigurator() { .satisfiesExactly( log -> assertThat(log).hasBody("logA"), log -> assertThat(log).hasBody("logC")); } + + @Test + void minimumSeverityFiltering() { + InMemoryLogRecordExporter exporter = InMemoryLogRecordExporter.create(); + SdkLoggerProvider loggerProvider = + SdkLoggerProvider.builder() + // Set minimum severity to WARN for loggerA + .addLoggerConfiguratorCondition(nameEquals("loggerA"), LoggerConfig.withMinimumSeverity(Severity.WARN.getSeverityNumber())) + .addLogRecordProcessor(SimpleLogRecordProcessor.create(exporter)) + .build(); + + Logger loggerA = loggerProvider.get("loggerA"); + Logger loggerB = loggerProvider.get("loggerB"); // Uses default config (no filtering) + + // Emit logs with different severity levels + loggerA.logRecordBuilder().setSeverity(Severity.DEBUG).setBody("debug").emit(); // Should be dropped + loggerA.logRecordBuilder().setSeverity(Severity.INFO).setBody("info").emit(); // Should be dropped + loggerA.logRecordBuilder().setSeverity(Severity.WARN).setBody("warn").emit(); // Should pass + loggerA.logRecordBuilder().setSeverity(Severity.ERROR).setBody("error").emit(); // Should pass + loggerA.logRecordBuilder().setBody("unspecified").emit(); // Should pass (unspecified severity) + + // LoggerB should emit all logs (no filtering) + loggerB.logRecordBuilder().setSeverity(Severity.DEBUG).setBody("debug-b").emit(); + loggerB.logRecordBuilder().setSeverity(Severity.INFO).setBody("info-b").emit(); + + // Only logs with severity >= WARN from loggerA, plus all from loggerB + assertThat(exporter.getFinishedLogRecordItems()) + .satisfiesExactlyInAnyOrder( + log -> assertThat(log).hasBody("warn"), + log -> assertThat(log).hasBody("error"), + log -> assertThat(log).hasBody("unspecified"), + log -> assertThat(log).hasBody("debug-b"), + log -> assertThat(log).hasBody("info-b")); + } + + @Test + void traceBasedFiltering() { + InMemoryLogRecordExporter exporter = InMemoryLogRecordExporter.create(); + SdkLoggerProvider loggerProvider = + SdkLoggerProvider.builder() + // Enable trace-based filtering for loggerA + .addLoggerConfiguratorCondition(nameEquals("loggerA"), LoggerConfig.withTraceBased()) + .addLogRecordProcessor(SimpleLogRecordProcessor.create(exporter)) + .build(); + + Logger loggerA = loggerProvider.get("loggerA"); + Logger loggerB = loggerProvider.get("loggerB"); // Uses default config (no filtering) + + // Test with no active span - should pass through + loggerA.logRecordBuilder().setBody("no-span").emit(); + loggerB.logRecordBuilder().setBody("no-span-b").emit(); + + // Test with sampled trace - should pass through + // Note: creating a proper sampled span context requires tracer setup + loggerA.logRecordBuilder().setBody("sampled").emit(); + loggerB.logRecordBuilder().setBody("sampled-b").emit(); + + // For this basic test, we expect logs without valid span context to pass through + assertThat(exporter.getFinishedLogRecordItems()) + .satisfiesExactlyInAnyOrder( + log -> assertThat(log).hasBody("no-span"), + log -> assertThat(log).hasBody("no-span-b"), + log -> assertThat(log).hasBody("sampled"), + log -> assertThat(log).hasBody("sampled-b")); + } + + @Test + void combinedFiltering() { + InMemoryLogRecordExporter exporter = InMemoryLogRecordExporter.create(); + SdkLoggerProvider loggerProvider = + SdkLoggerProvider.builder() + // Combine minimum severity and trace-based filtering + .addLoggerConfiguratorCondition(nameEquals("loggerA"), + LoggerConfig.create(/* enabled= */ true, Severity.WARN.getSeverityNumber(), /* traceBased= */ true)) + .addLogRecordProcessor(SimpleLogRecordProcessor.create(exporter)) + .build(); + + Logger loggerA = loggerProvider.get("loggerA"); + + // Test with various combinations + loggerA.logRecordBuilder().setSeverity(Severity.DEBUG).setBody("debug-filtered").emit(); // Dropped by severity + loggerA.logRecordBuilder().setSeverity(Severity.WARN).setBody("warn-passed").emit(); // Should pass (no active span) + loggerA.logRecordBuilder().setBody("unspecified-passed").emit(); // Should pass (unspecified severity) + + assertThat(exporter.getFinishedLogRecordItems()) + .satisfiesExactlyInAnyOrder( + log -> assertThat(log).hasBody("warn-passed"), + log -> assertThat(log).hasBody("unspecified-passed")); + } + + @Test + void loggerConfigDefaults() { + LoggerConfig defaultConfig = LoggerConfig.defaultConfig(); + assertThat(defaultConfig.isEnabled()).isTrue(); + assertThat(defaultConfig.getMinimumSeverity()).isEqualTo(0); + assertThat(defaultConfig.isTraceBased()).isFalse(); + + LoggerConfig disabledConfig = LoggerConfig.disabled(); + assertThat(disabledConfig.isEnabled()).isFalse(); + assertThat(disabledConfig.getMinimumSeverity()).isEqualTo(0); + assertThat(disabledConfig.isTraceBased()).isFalse(); + + LoggerConfig customConfig = LoggerConfig.create(/* enabled= */ true, /* minimumSeverity= */ 100, /* traceBased= */ true); + assertThat(customConfig.isEnabled()).isTrue(); + assertThat(customConfig.getMinimumSeverity()).isEqualTo(100); + assertThat(customConfig.isTraceBased()).isTrue(); + } + + @Test + void simpleDebugTest() { + InMemoryLogRecordExporter exporter = InMemoryLogRecordExporter.create(); + SdkLoggerProvider loggerProvider = + SdkLoggerProvider.builder() + .addLoggerConfiguratorCondition(nameEquals("testLogger"), + LoggerConfig.withMinimumSeverity(Severity.WARN.getSeverityNumber())) + .addLogRecordProcessor(SimpleLogRecordProcessor.create(exporter)) + .build(); + + Logger logger = loggerProvider.get("testLogger"); + + // Emit one DEBUG log that should be filtered + logger.logRecordBuilder().setSeverity(Severity.DEBUG).setBody("should-be-filtered").emit(); + + // Emit one WARN log that should pass + logger.logRecordBuilder().setSeverity(Severity.WARN).setBody("should-pass").emit(); + + // Should only see the WARN log + List logs = exporter.getFinishedLogRecordItems(); + + assertThat(logs).hasSize(1); + assertThat(logs.get(0).getBodyValue().asString()).isEqualTo("should-pass"); + } } From e1cfc45b3995b90d155dfb42bb2a4ce5e1b39f10 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 Aug 2025 19:37:10 +0000 Subject: [PATCH 4/4] Add comprehensive tests for minimum_severity and trace_based logger configuration Co-authored-by: trask <218610+trask@users.noreply.github.com> --- .../sdk/logs/LoggerConfigTest.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/LoggerConfigTest.java b/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/LoggerConfigTest.java index cc4e615106c..96ccd940cd3 100644 --- a/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/LoggerConfigTest.java +++ b/sdk/logs/src/testIncubating/java/io/opentelemetry/sdk/logs/LoggerConfigTest.java @@ -325,4 +325,40 @@ void simpleDebugTest() { assertThat(logs).hasSize(1); assertThat(logs.get(0).getBodyValue().asString()).isEqualTo("should-pass"); } + + @Test + void isEnabledConsidersMinimumSeverity() { + InMemoryLogRecordExporter exporter = InMemoryLogRecordExporter.create(); + SdkLoggerProvider loggerProvider = + SdkLoggerProvider.builder() + .addLoggerConfiguratorCondition(nameEquals("testLogger"), + LoggerConfig.withMinimumSeverity(Severity.WARN.getSeverityNumber())) + .addLogRecordProcessor(SimpleLogRecordProcessor.create(exporter)) + .build(); + + ExtendedSdkLogger logger = (ExtendedSdkLogger) loggerProvider.get("testLogger"); + + // Test isEnabled method with different severity levels + assertThat(logger.isEnabled(Severity.DEBUG, Context.current())).isFalse(); + assertThat(logger.isEnabled(Severity.INFO, Context.current())).isFalse(); + assertThat(logger.isEnabled(Severity.WARN, Context.current())).isTrue(); + assertThat(logger.isEnabled(Severity.ERROR, Context.current())).isTrue(); + assertThat(logger.isEnabled(Severity.UNDEFINED_SEVERITY_NUMBER, Context.current())).isTrue(); // unspecified should pass + } + + @Test + void isEnabledConsidersTraceBased() { + InMemoryLogRecordExporter exporter = InMemoryLogRecordExporter.create(); + SdkLoggerProvider loggerProvider = + SdkLoggerProvider.builder() + .addLoggerConfiguratorCondition(nameEquals("testLogger"), + LoggerConfig.withTraceBased()) + .addLogRecordProcessor(SimpleLogRecordProcessor.create(exporter)) + .build(); + + ExtendedSdkLogger logger = (ExtendedSdkLogger) loggerProvider.get("testLogger"); + + // Test isEnabled method with no active span (should return true - no valid span context) + assertThat(logger.isEnabled(Severity.INFO, Context.current())).isTrue(); + } }