Skip to content

Commit c471c04

Browse files
committed
Add minimum severity and trace-based logger configuration
1 parent 39e60f8 commit c471c04

File tree

8 files changed

+363
-11
lines changed

8 files changed

+363
-11
lines changed

sdk/logs/src/main/java/io/opentelemetry/sdk/logs/ExtendedSdkLogRecordBuilder.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ final class ExtendedSdkLogRecordBuilder extends SdkLogRecordBuilder
2525
@Nullable private ExtendedAttributesMap extendedAttributes;
2626

2727
ExtendedSdkLogRecordBuilder(
28-
LoggerSharedState loggerSharedState, InstrumentationScopeInfo instrumentationScopeInfo) {
29-
super(loggerSharedState, instrumentationScopeInfo);
28+
LoggerSharedState loggerSharedState,
29+
InstrumentationScopeInfo instrumentationScopeInfo,
30+
SdkLogger logger) {
31+
super(loggerSharedState, instrumentationScopeInfo, logger);
3032
}
3133

3234
@Override
@@ -132,7 +134,12 @@ public void emit() {
132134
if (loggerSharedState.hasBeenShutdown()) {
133135
return;
134136
}
137+
135138
Context context = this.context == null ? Context.current() : this.context;
139+
if (!logger.isEnabled(severity, context)) {
140+
return;
141+
}
142+
136143
long observedTimestampEpochNanos =
137144
this.observedTimestampEpochNanos == 0
138145
? this.loggerSharedState.getClock().now()

sdk/logs/src/main/java/io/opentelemetry/sdk/logs/IncubatingUtil.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ static SdkLogger createExtendedLogger(
2525
}
2626

2727
static SdkLogRecordBuilder createExtendedLogRecordBuilder(
28-
LoggerSharedState loggerSharedState, InstrumentationScopeInfo instrumentationScopeInfo) {
29-
return new ExtendedSdkLogRecordBuilder(loggerSharedState, instrumentationScopeInfo);
28+
LoggerSharedState loggerSharedState,
29+
InstrumentationScopeInfo instrumentationScopeInfo,
30+
SdkLogger logger) {
31+
return new ExtendedSdkLogRecordBuilder(loggerSharedState, instrumentationScopeInfo, logger);
3032
}
3133
}

sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilder.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class SdkLogRecordBuilder implements LogRecordBuilder {
2222

2323
protected final LoggerSharedState loggerSharedState;
2424
protected final LogLimits logLimits;
25+
protected final SdkLogger logger;
2526

2627
protected final InstrumentationScopeInfo instrumentationScopeInfo;
2728
protected long timestampEpochNanos;
@@ -34,10 +35,13 @@ class SdkLogRecordBuilder implements LogRecordBuilder {
3435
@Nullable private AttributesMap attributes;
3536

3637
SdkLogRecordBuilder(
37-
LoggerSharedState loggerSharedState, InstrumentationScopeInfo instrumentationScopeInfo) {
38+
LoggerSharedState loggerSharedState,
39+
InstrumentationScopeInfo instrumentationScopeInfo,
40+
SdkLogger logger) {
3841
this.loggerSharedState = loggerSharedState;
3942
this.logLimits = loggerSharedState.getLogLimits();
4043
this.instrumentationScopeInfo = instrumentationScopeInfo;
44+
this.logger = logger;
4145
}
4246

4347
@Override
@@ -121,6 +125,9 @@ public void emit() {
121125
return;
122126
}
123127
Context context = this.context == null ? Context.current() : this.context;
128+
if (!logger.isEnabled(severity, context)) {
129+
return;
130+
}
124131
long observedTimestampEpochNanos =
125132
this.observedTimestampEpochNanos == 0
126133
? this.loggerSharedState.getClock().now()

sdk/logs/src/main/java/io/opentelemetry/sdk/logs/SdkLogger.java

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import io.opentelemetry.api.logs.Logger;
1010
import io.opentelemetry.api.logs.LoggerProvider;
1111
import io.opentelemetry.api.logs.Severity;
12+
import io.opentelemetry.api.trace.Span;
13+
import io.opentelemetry.api.trace.SpanContext;
1214
import io.opentelemetry.context.Context;
1315
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
1416
import io.opentelemetry.sdk.logs.internal.LoggerConfig;
@@ -34,6 +36,8 @@ class SdkLogger implements Logger {
3436
private final InstrumentationScopeInfo instrumentationScopeInfo;
3537

3638
protected volatile boolean loggerEnabled;
39+
protected volatile int minimumSeverity;
40+
protected volatile boolean traceBased;
3741

3842
SdkLogger(
3943
LoggerSharedState loggerSharedState,
@@ -42,6 +46,8 @@ class SdkLogger implements Logger {
4246
this.loggerSharedState = loggerSharedState;
4347
this.instrumentationScopeInfo = instrumentationScopeInfo;
4448
this.loggerEnabled = loggerConfig.isEnabled();
49+
this.minimumSeverity = loggerConfig.getMinimumSeverity();
50+
this.traceBased = loggerConfig.isTraceBased();
4551
}
4652

4753
static SdkLogger create(
@@ -58,8 +64,8 @@ public LogRecordBuilder logRecordBuilder() {
5864
if (loggerEnabled) {
5965
return INCUBATOR_AVAILABLE
6066
? IncubatingUtil.createExtendedLogRecordBuilder(
61-
loggerSharedState, instrumentationScopeInfo)
62-
: new SdkLogRecordBuilder(loggerSharedState, instrumentationScopeInfo);
67+
loggerSharedState, instrumentationScopeInfo, this)
68+
: new SdkLogRecordBuilder(loggerSharedState, instrumentationScopeInfo, this);
6369
}
6470
return NOOP_LOGGER.logRecordBuilder();
6571
}
@@ -71,10 +77,28 @@ InstrumentationScopeInfo getInstrumentationScopeInfo() {
7177

7278
// Visible for testing
7379
public boolean isEnabled(Severity severity, Context context) {
74-
return loggerEnabled;
80+
if (!loggerEnabled) {
81+
return false;
82+
}
83+
84+
if (severity != Severity.UNDEFINED_SEVERITY_NUMBER
85+
&& severity.getSeverityNumber() < minimumSeverity) {
86+
return false;
87+
}
88+
89+
if (traceBased) {
90+
SpanContext spanContext = Span.fromContext(context).getSpanContext();
91+
if (spanContext.isValid() && !spanContext.getTraceFlags().isSampled()) {
92+
return false;
93+
}
94+
}
95+
96+
return true;
7597
}
7698

7799
void updateLoggerConfig(LoggerConfig loggerConfig) {
78100
loggerEnabled = loggerConfig.isEnabled();
101+
minimumSeverity = loggerConfig.getMinimumSeverity();
102+
traceBased = loggerConfig.isTraceBased();
79103
}
80104
}

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

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@
3030
public abstract class LoggerConfig {
3131

3232
private static final LoggerConfig DEFAULT_CONFIG =
33-
new AutoValue_LoggerConfig(/* enabled= */ true);
33+
new AutoValue_LoggerConfig(
34+
/* enabled= */ true, /* minimumSeverity= */ 0, /* traceBased= */ false);
3435
private static final LoggerConfig DISABLED_CONFIG =
35-
new AutoValue_LoggerConfig(/* enabled= */ false);
36+
new AutoValue_LoggerConfig(
37+
/* enabled= */ false, /* minimumSeverity= */ 0, /* traceBased= */ false);
3638

3739
/** Returns a disabled {@link LoggerConfig}. */
3840
public static LoggerConfig disabled() {
@@ -44,6 +46,11 @@ public static LoggerConfig enabled() {
4446
return DEFAULT_CONFIG;
4547
}
4648

49+
/** Returns a new {@link Builder} for creating a {@link LoggerConfig}. */
50+
public static Builder builder() {
51+
return new Builder();
52+
}
53+
4754
/**
4855
* Returns the default {@link LoggerConfig}, which is used when no configurator is set or when the
4956
* logger configurator returns {@code null} for a {@link InstrumentationScopeInfo}.
@@ -62,6 +69,85 @@ public static ScopeConfiguratorBuilder<LoggerConfig> configuratorBuilder() {
6269

6370
LoggerConfig() {}
6471

72+
/**
73+
* Builder for {@link LoggerConfig}.
74+
*
75+
* <p>This class is internal and experimental. Its APIs are unstable and can change at any time.
76+
* Its APIs (or a version of them) may be promoted to the public stable API in the future, but no
77+
* guarantees are made.
78+
*/
79+
public static final class Builder {
80+
private boolean enabled = true;
81+
private int minimumSeverity = 0;
82+
private boolean traceBased = false;
83+
84+
private Builder() {}
85+
86+
/**
87+
* Sets whether the logger is enabled.
88+
*
89+
* @param enabled whether the logger is enabled
90+
* @return this builder
91+
*/
92+
public Builder setEnabled(boolean enabled) {
93+
this.enabled = enabled;
94+
return this;
95+
}
96+
97+
/**
98+
* Sets the minimum severity level for log records to be processed.
99+
*
100+
* <p>Log records with a severity number less than this value will be dropped. Log records
101+
* without a specified severity are not affected by this setting.
102+
*
103+
* @param minimumSeverity minimum severity level for log records to be processed
104+
* @return this builder
105+
*/
106+
public Builder setMinimumSeverity(int minimumSeverity) {
107+
this.minimumSeverity = minimumSeverity;
108+
return this;
109+
}
110+
111+
/**
112+
* Sets whether to only process log records from traces when the trace is sampled.
113+
*
114+
* <p>When enabled, log records from unsampled traces will be dropped. Log records that are not
115+
* associated with a trace context are unaffected.
116+
*
117+
* @param traceBased whether to only process log records from traces when the trace is sampled
118+
* @return this builder
119+
*/
120+
public Builder setTraceBased(boolean traceBased) {
121+
this.traceBased = traceBased;
122+
return this;
123+
}
124+
125+
/** Builds and returns a {@link LoggerConfig}. */
126+
public LoggerConfig build() {
127+
return new AutoValue_LoggerConfig(enabled, minimumSeverity, traceBased);
128+
}
129+
}
130+
65131
/** Returns {@code true} if this logger is enabled. Defaults to {@code true}. */
66132
public abstract boolean isEnabled();
133+
134+
/**
135+
* Returns the minimum severity level for log records to be processed.
136+
*
137+
* <p>Log records with a severity number less than this value will be dropped. Log records without
138+
* a specified severity are not affected by this setting.
139+
*
140+
* <p>Defaults to {@code 0}.
141+
*/
142+
public abstract int getMinimumSeverity();
143+
144+
/**
145+
* Returns {@code true} if this logger should only process log records from traces when the trace is sampled.
146+
*
147+
* <p>When enabled, log records from unsampled traces will be dropped. Log records that are not
148+
* associated with a trace context are unaffected.
149+
*
150+
* <p>Defaults to {@code false}.
151+
*/
152+
public abstract boolean isTraceBased();
67153
}

sdk/logs/src/test/java/io/opentelemetry/sdk/logs/SdkLogRecordBuilderTest.java

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import io.opentelemetry.context.Context;
2525
import io.opentelemetry.sdk.common.Clock;
2626
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
27+
import io.opentelemetry.sdk.logs.internal.LoggerConfig;
2728
import io.opentelemetry.sdk.resources.Resource;
2829
import java.time.Instant;
2930
import java.util.concurrent.TimeUnit;
@@ -57,7 +58,8 @@ void setup() {
5758
when(loggerSharedState.getResource()).thenReturn(RESOURCE);
5859
when(loggerSharedState.getClock()).thenReturn(clock);
5960

60-
builder = new SdkLogRecordBuilder(loggerSharedState, SCOPE_INFO);
61+
SdkLogger logger = new SdkLogger(loggerSharedState, SCOPE_INFO, LoggerConfig.enabled());
62+
builder = new SdkLogRecordBuilder(loggerSharedState, SCOPE_INFO, logger);
6163
}
6264

6365
@Test
@@ -121,6 +123,78 @@ void emit_NoFields() {
121123
.hasSeverity(Severity.UNDEFINED_SEVERITY_NUMBER);
122124
}
123125

126+
@Test
127+
void emit_WithMinimumSeverityConfiguration() {
128+
LoggerConfig config =
129+
LoggerConfig.builder().setMinimumSeverity(Severity.INFO.getSeverityNumber()).build();
130+
SdkLogger logger = new SdkLogger(loggerSharedState, SCOPE_INFO, config);
131+
builder = new SdkLogRecordBuilder(loggerSharedState, SCOPE_INFO, logger);
132+
133+
builder.setBody("too-low").setSeverity(Severity.DEBUG).emit();
134+
assertThat(emittedLog.get()).isNull();
135+
136+
builder.setBody("allowed").setSeverity(Severity.INFO).emit();
137+
assertThat(emittedLog.get().toLogRecordData()).hasBody("allowed");
138+
}
139+
140+
@Test
141+
void emit_DropsUnsampledTraceWhenTraceBased() {
142+
LoggerConfig config = LoggerConfig.builder().setTraceBased(true).build();
143+
SdkLogger logger = new SdkLogger(loggerSharedState, SCOPE_INFO, config);
144+
builder = new SdkLogRecordBuilder(loggerSharedState, SCOPE_INFO, logger);
145+
146+
SpanContext unsampledSpanContext =
147+
SpanContext.create(
148+
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
149+
"bbbbbbbbbbbbbbbb",
150+
TraceFlags.getDefault(),
151+
TraceState.getDefault());
152+
builder
153+
.setBody("unsampled")
154+
.setContext(Span.wrap(unsampledSpanContext).storeInContext(Context.root()))
155+
.emit();
156+
assertThat(emittedLog.get()).isNull();
157+
158+
SpanContext sampledSpanContext =
159+
SpanContext.create(
160+
"cccccccccccccccccccccccccccccccc",
161+
"dddddddddddddddd",
162+
TraceFlags.getSampled(),
163+
TraceState.getDefault());
164+
builder
165+
.setBody("sampled")
166+
.setContext(Span.wrap(sampledSpanContext).storeInContext(Context.root()))
167+
.emit();
168+
assertThat(emittedLog.get().toLogRecordData())
169+
.hasSpanContext(sampledSpanContext)
170+
.hasBody("sampled");
171+
}
172+
173+
@Test
174+
void emit_AllowsUndefinedSeverityWithMinimumSeverity() {
175+
LoggerConfig config =
176+
LoggerConfig.builder().setMinimumSeverity(Severity.WARN.getSeverityNumber()).build();
177+
SdkLogger logger = new SdkLogger(loggerSharedState, SCOPE_INFO, config);
178+
builder = new SdkLogRecordBuilder(loggerSharedState, SCOPE_INFO, logger);
179+
180+
// Undefined severity should bypass the minimum severity filter
181+
builder.setBody("undefined-severity").setSeverity(Severity.UNDEFINED_SEVERITY_NUMBER).emit();
182+
assertThat(emittedLog.get().toLogRecordData())
183+
.hasBody("undefined-severity")
184+
.hasSeverity(Severity.UNDEFINED_SEVERITY_NUMBER);
185+
}
186+
187+
@Test
188+
void emit_AllowsNoTraceContextWithTraceBased() {
189+
LoggerConfig config = LoggerConfig.builder().setTraceBased(true).build();
190+
SdkLogger logger = new SdkLogger(loggerSharedState, SCOPE_INFO, config);
191+
builder = new SdkLogRecordBuilder(loggerSharedState, SCOPE_INFO, logger);
192+
193+
// No trace context should bypass the trace-based filter
194+
builder.setBody("no-trace-context").emit();
195+
assertThat(emittedLog.get().toLogRecordData()).hasBody("no-trace-context");
196+
}
197+
124198
@Test
125199
void testConvenienceAttributeMethods() {
126200
builder

0 commit comments

Comments
 (0)