Skip to content

Commit abf8f51

Browse files
committed
Add logs sampler
1 parent 9c1b9f6 commit abf8f51

File tree

7 files changed

+128
-2
lines changed

7 files changed

+128
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.sdk.autoconfigure.spi.logs;
7+
8+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
9+
import io.opentelemetry.sdk.logs.samplers.LogSampler;
10+
11+
public interface ConfigurableLogSamplerProvider {
12+
13+
/**
14+
* Returns a {@link LogSampler} that can be registered to OpenTelemetry by providing the property
15+
* value specified by {@link #getName()}.
16+
*/
17+
LogSampler createSampler(ConfigProperties config);
18+
19+
/**
20+
* Returns the name of this sampler, which can be specified with the {@code otel.logs.sampler}
21+
* property to enable it. The name returned should NOT be the same as any other exporter name. If
22+
* the name does conflict with another exporter name, the resulting behavior is undefined and it
23+
* is explicitly unspecified which exporter will actually be used.
24+
*/
25+
String getName();
26+
}

sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/LoggerProviderConfiguration.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
import static io.opentelemetry.sdk.autoconfigure.LogRecordExporterConfiguration.configureLogRecordExporters;
99

1010
import io.opentelemetry.api.metrics.MeterProvider;
11+
import io.opentelemetry.sdk.autoconfigure.internal.NamedSpiManager;
1112
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
1213
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
14+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
15+
import io.opentelemetry.sdk.autoconfigure.spi.logs.ConfigurableLogSamplerProvider;
1316
import io.opentelemetry.sdk.logs.LogLimits;
1417
import io.opentelemetry.sdk.logs.LogLimitsBuilder;
1518
import io.opentelemetry.sdk.logs.LogRecordProcessor;
@@ -18,6 +21,7 @@
1821
import io.opentelemetry.sdk.logs.export.BatchLogRecordProcessorBuilder;
1922
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
2023
import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor;
24+
import io.opentelemetry.sdk.logs.samplers.LogSampler;
2125
import java.io.Closeable;
2226
import java.time.Duration;
2327
import java.util.ArrayList;
@@ -31,6 +35,7 @@ final class LoggerProviderConfiguration {
3135

3236
private static final List<String> simpleProcessorExporterNames =
3337
Arrays.asList("console", "logging");
38+
public static final String ALWAYS_ON = "always_on";
3439

3540
static void configureLoggerProvider(
3641
SdkLoggerProviderBuilder loggerProviderBuilder,
@@ -48,6 +53,9 @@ static void configureLoggerProvider(
4853
Map<String, LogRecordExporter> exportersByName =
4954
configureLogRecordExporters(config, spiHelper, logRecordExporterCustomizer, closeables);
5055

56+
String sampler = config.getString("otel.logs.sampler", ALWAYS_ON);
57+
loggerProviderBuilder.setLogSampler(configSampler(sampler, config, spiHelper));
58+
5159
List<LogRecordProcessor> processors =
5260
configureLogRecordProcessors(config, exportersByName, meterProvider, closeables);
5361
for (LogRecordProcessor processor : processors) {
@@ -59,6 +67,27 @@ static void configureLoggerProvider(
5967
}
6068
}
6169

70+
static LogSampler configSampler(String sampler, ConfigProperties config, SpiHelper spiHelper) {
71+
NamedSpiManager<LogSampler> spiSamplersManager =
72+
spiHelper.loadConfigurable(
73+
ConfigurableLogSamplerProvider.class,
74+
ConfigurableLogSamplerProvider::getName,
75+
ConfigurableLogSamplerProvider::createSampler,
76+
config);
77+
switch (sampler) {
78+
case "always_on":
79+
return LogSampler.alwaysOnSampler();
80+
case "parentbased":
81+
return LogSampler.parentBasedSampler();
82+
default:
83+
LogSampler spiSampler = spiSamplersManager.getByName(sampler);
84+
if (spiSampler == null) {
85+
throw new ConfigurationException("Unrecognized value for otel.logs.sampler: " + sampler);
86+
}
87+
return spiSampler;
88+
}
89+
}
90+
6291
// Visible for testing
6392
static List<LogRecordProcessor> configureLogRecordProcessors(
6493
ConfigProperties config,

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import io.opentelemetry.sdk.common.Clock;
99
import io.opentelemetry.sdk.common.CompletableResultCode;
10+
import io.opentelemetry.sdk.logs.samplers.LogSampler;
1011
import io.opentelemetry.sdk.resources.Resource;
1112
import java.util.function.Supplier;
1213
import javax.annotation.Nullable;
@@ -21,16 +22,19 @@ final class LoggerSharedState {
2122
private final Supplier<LogLimits> logLimitsSupplier;
2223
private final LogRecordProcessor logRecordProcessor;
2324
private final Clock clock;
25+
private final LogSampler sampler;
2426
@Nullable private volatile CompletableResultCode shutdownResult = null;
2527

2628
LoggerSharedState(
2729
Resource resource,
2830
Supplier<LogLimits> logLimitsSupplier,
2931
LogRecordProcessor logRecordProcessor,
32+
LogSampler sampler,
3033
Clock clock) {
3134
this.resource = resource;
3235
this.logLimitsSupplier = logLimitsSupplier;
3336
this.logRecordProcessor = logRecordProcessor;
37+
this.sampler = sampler;
3438
this.clock = clock;
3539
}
3640

@@ -46,6 +50,10 @@ LogRecordProcessor getLogRecordProcessor() {
4650
return logRecordProcessor;
4751
}
4852

53+
LogSampler getSampler() {
54+
return this.sampler;
55+
}
56+
4957
Clock getClock() {
5058
return clock;
5159
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ public void emit() {
125125
this.observedTimestampEpochNanos == 0
126126
? this.loggerSharedState.getClock().now()
127127
: this.observedTimestampEpochNanos;
128+
boolean isShouldSample =
129+
loggerSharedState.getSampler().shouldSample(context, this.severity, body, attributes);
130+
if (!isShouldSample) {
131+
return;
132+
}
128133
loggerSharedState
129134
.getLogRecordProcessor()
130135
.onEmit(

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import io.opentelemetry.sdk.internal.ComponentRegistry;
1616
import io.opentelemetry.sdk.internal.ScopeConfigurator;
1717
import io.opentelemetry.sdk.logs.internal.LoggerConfig;
18+
import io.opentelemetry.sdk.logs.samplers.LogSampler;
1819
import io.opentelemetry.sdk.resources.Resource;
1920
import java.io.Closeable;
2021
import java.util.List;
@@ -52,11 +53,12 @@ public static SdkLoggerProviderBuilder builder() {
5253
Resource resource,
5354
Supplier<LogLimits> logLimitsSupplier,
5455
List<LogRecordProcessor> processors,
56+
LogSampler sampler,
5557
Clock clock,
5658
ScopeConfigurator<LoggerConfig> loggerConfigurator) {
5759
LogRecordProcessor logRecordProcessor = LogRecordProcessor.composite(processors);
5860
this.sharedState =
59-
new LoggerSharedState(resource, logLimitsSupplier, logRecordProcessor, clock);
61+
new LoggerSharedState(resource, logLimitsSupplier, logRecordProcessor, sampler, clock);
6062
this.loggerComponentRegistry =
6163
new ComponentRegistry<>(
6264
instrumentationScopeInfo ->

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import io.opentelemetry.sdk.logs.data.LogRecordData;
1818
import io.opentelemetry.sdk.logs.internal.LoggerConfig;
1919
import io.opentelemetry.sdk.logs.internal.SdkLoggerProviderUtil;
20+
import io.opentelemetry.sdk.logs.samplers.LogSampler;
2021
import io.opentelemetry.sdk.resources.Resource;
2122
import java.util.ArrayList;
2223
import java.util.List;
@@ -38,6 +39,8 @@ public final class SdkLoggerProviderBuilder {
3839
private ScopeConfiguratorBuilder<LoggerConfig> loggerConfiguratorBuilder =
3940
LoggerConfig.configuratorBuilder();
4041

42+
private LogSampler sampler = LogSampler.alwaysOnSampler();
43+
4144
SdkLoggerProviderBuilder() {}
4245

4346
/**
@@ -96,6 +99,18 @@ public SdkLoggerProviderBuilder addLogRecordProcessor(LogRecordProcessor process
9699
return this;
97100
}
98101

102+
/**
103+
* Set log sampler
104+
*
105+
* @param sampler the log sampler
106+
* @return this
107+
*/
108+
public SdkLoggerProviderBuilder setLogSampler(LogSampler sampler) {
109+
requireNonNull(sampler, "sampler");
110+
this.sampler = sampler;
111+
return this;
112+
}
113+
99114
/**
100115
* Assign a {@link Clock}.
101116
*
@@ -156,6 +171,11 @@ SdkLoggerProviderBuilder addLoggerConfiguratorCondition(
156171
*/
157172
public SdkLoggerProvider build() {
158173
return new SdkLoggerProvider(
159-
resource, logLimitsSupplier, logRecordProcessors, clock, loggerConfiguratorBuilder.build());
174+
resource,
175+
logLimitsSupplier,
176+
logRecordProcessors,
177+
sampler,
178+
clock,
179+
loggerConfiguratorBuilder.build());
160180
}
161181
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.sdk.logs.samplers;
7+
8+
import io.opentelemetry.api.common.Attributes;
9+
import io.opentelemetry.api.common.Value;
10+
import io.opentelemetry.api.logs.Severity;
11+
import io.opentelemetry.api.trace.Span;
12+
import io.opentelemetry.api.trace.SpanContext;
13+
import io.opentelemetry.context.Context;
14+
import javax.annotation.Nullable;
15+
import javax.annotation.concurrent.ThreadSafe;
16+
17+
@ThreadSafe
18+
public interface LogSampler {
19+
20+
static LogSampler alwaysOnSampler() {
21+
return (parentContext, severity, value, attributes) -> true;
22+
}
23+
24+
static LogSampler parentBasedSampler() {
25+
return (parentContext, severity, value, attributes) -> {
26+
SpanContext spanContext = Span.fromContext(parentContext).getSpanContext();
27+
return spanContext.isSampled();
28+
};
29+
}
30+
31+
boolean shouldSample(
32+
Context parentContext,
33+
Severity severity,
34+
@Nullable Value<?> value,
35+
@Nullable Attributes attributes);
36+
}

0 commit comments

Comments
 (0)