Skip to content

Commit d8f0544

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

File tree

6 files changed

+208
-13
lines changed

6 files changed

+208
-13
lines changed

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

Lines changed: 11 additions & 3 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,13 @@ public void emit() {
132134
if (loggerSharedState.hasBeenShutdown()) {
133135
return;
134136
}
137+
135138
Context context = this.context == null ? Context.current() : this.context;
139+
Span span = Span.fromContext(context);
140+
if (shouldDropLog(span)) {
141+
return;
142+
}
143+
136144
long observedTimestampEpochNanos =
137145
this.observedTimestampEpochNanos == 0
138146
? this.loggerSharedState.getClock().now()
@@ -148,7 +156,7 @@ public void emit() {
148156
eventName,
149157
timestampEpochNanos,
150158
observedTimestampEpochNanos,
151-
Span.fromContext(context).getSpanContext(),
159+
span.getSpanContext(),
152160
severity,
153161
severityText,
154162
body,

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: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io.opentelemetry.api.logs.LogRecordBuilder;
1111
import io.opentelemetry.api.logs.Severity;
1212
import io.opentelemetry.api.trace.Span;
13+
import io.opentelemetry.api.trace.SpanContext;
1314
import io.opentelemetry.context.Context;
1415
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
1516
import io.opentelemetry.sdk.internal.AttributesMap;
@@ -22,6 +23,7 @@ class SdkLogRecordBuilder implements LogRecordBuilder {
2223

2324
protected final LoggerSharedState loggerSharedState;
2425
protected final LogLimits logLimits;
26+
protected final SdkLogger logger;
2527

2628
protected final InstrumentationScopeInfo instrumentationScopeInfo;
2729
protected long timestampEpochNanos;
@@ -34,10 +36,13 @@ class SdkLogRecordBuilder implements LogRecordBuilder {
3436
@Nullable private AttributesMap attributes;
3537

3638
SdkLogRecordBuilder(
37-
LoggerSharedState loggerSharedState, InstrumentationScopeInfo instrumentationScopeInfo) {
39+
LoggerSharedState loggerSharedState,
40+
InstrumentationScopeInfo instrumentationScopeInfo,
41+
SdkLogger logger) {
3842
this.loggerSharedState = loggerSharedState;
3943
this.logLimits = loggerSharedState.getLogLimits();
4044
this.instrumentationScopeInfo = instrumentationScopeInfo;
45+
this.logger = logger;
4146
}
4247

4348
@Override
@@ -121,6 +126,11 @@ public void emit() {
121126
return;
122127
}
123128
Context context = this.context == null ? Context.current() : this.context;
129+
Span span = Span.fromContext(context);
130+
if (shouldDropLog(span)) {
131+
return;
132+
}
133+
SpanContext spanContext = span.getSpanContext();
124134
long observedTimestampEpochNanos =
125135
this.observedTimestampEpochNanos == 0
126136
? this.loggerSharedState.getClock().now()
@@ -135,11 +145,27 @@ public void emit() {
135145
instrumentationScopeInfo,
136146
timestampEpochNanos,
137147
observedTimestampEpochNanos,
138-
Span.fromContext(context).getSpanContext(),
148+
spanContext,
139149
severity,
140150
severityText,
141151
body,
142152
attributes,
143153
eventName));
144154
}
155+
156+
protected boolean shouldDropLog(Span span) {
157+
if (severity != Severity.UNDEFINED_SEVERITY_NUMBER
158+
&& severity.getSeverityNumber() < logger.minimumSeverity) {
159+
return true;
160+
}
161+
162+
if (logger.traceBased) {
163+
SpanContext spanContext = span.getSpanContext();
164+
if (spanContext.isValid() && !spanContext.getTraceFlags().isSampled()) {
165+
return true;
166+
}
167+
}
168+
169+
return false;
170+
}
145171
}

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: 50 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,53 @@ 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_WithTraceBasedConfiguration() {
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+
124173
@Test
125174
void testConvenienceAttributeMethods() {
126175
builder

0 commit comments

Comments
 (0)