Skip to content

Commit 23799f2

Browse files
authored
Implement SDK metrics for logs (#7931)
1 parent 5809d2c commit 23799f2

File tree

21 files changed

+841
-69
lines changed

21 files changed

+841
-69
lines changed
Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,16 @@
11
Comparing source compatibility of opentelemetry-sdk-logs-1.58.0-SNAPSHOT.jar against opentelemetry-sdk-logs-1.57.0.jar
2-
No changes.
2+
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.logs.export.BatchLogRecordProcessorBuilder (not serializable)
3+
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
4+
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.logs.export.BatchLogRecordProcessorBuilder setInternalTelemetryVersion(io.opentelemetry.sdk.common.InternalTelemetryVersion)
5+
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.logs.export.BatchLogRecordProcessorBuilder setMeterProvider(java.util.function.Supplier<io.opentelemetry.api.metrics.MeterProvider>)
6+
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor (not serializable)
7+
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
8+
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessorBuilder builder(io.opentelemetry.sdk.logs.export.LogRecordExporter)
9+
+++ NEW CLASS: PUBLIC(+) FINAL(+) io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessorBuilder (not serializable)
10+
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a.
11+
+++ NEW SUPERCLASS: java.lang.Object
12+
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor build()
13+
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessorBuilder setMeterProvider(java.util.function.Supplier<io.opentelemetry.api.metrics.MeterProvider>)
14+
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder (not serializable)
15+
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
16+
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder setMeterProvider(java.util.function.Supplier<io.opentelemetry.api.metrics.MeterProvider>)

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ static void configureLoggerProvider(
4444
List<Closeable> closeables) {
4545

4646
loggerProviderBuilder.setLogLimits(() -> configureLogLimits(config));
47+
loggerProviderBuilder.setMeterProvider(() -> meterProvider);
4748

4849
Map<String, LogRecordExporter> exportersByName =
4950
configureLogRecordExporters(config, spiHelper, logRecordExporterCustomizer, closeables);
@@ -71,7 +72,10 @@ static List<LogRecordProcessor> configureLogRecordProcessors(
7172
for (String simpleProcessorExporterName : simpleProcessorExporterNames) {
7273
LogRecordExporter exporter = exportersByNameCopy.remove(simpleProcessorExporterName);
7374
if (exporter != null) {
74-
LogRecordProcessor logRecordProcessor = SimpleLogRecordProcessor.create(exporter);
75+
LogRecordProcessor logRecordProcessor =
76+
SimpleLogRecordProcessor.builder(exporter)
77+
.setMeterProvider(() -> meterProvider)
78+
.build();
7579
closeables.add(logRecordProcessor);
7680
logRecordProcessors.add(logRecordProcessor);
7781
}

sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/LogRecordProcessorFactory.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,11 @@ public LogRecordProcessor create(
6868
simpleModel.getExporter(), "simple log record processor exporter");
6969
LogRecordExporter logRecordExporter =
7070
LogRecordExporterFactory.getInstance().create(exporterModel, context);
71-
return context.addCloseable(SimpleLogRecordProcessor.create(logRecordExporter));
71+
MeterProvider meterProvider = context.getMeterProvider();
72+
return context.addCloseable(
73+
SimpleLogRecordProcessor.builder(logRecordExporter)
74+
.setMeterProvider(() -> meterProvider)
75+
.build());
7276
}
7377

7478
Map.Entry<String, Object> keyValue =

sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/OpenTelemetryConfigurationFactoryTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -325,17 +325,17 @@ void create_Configured() throws NoSuchFieldException, IllegalAccessException {
325325
// test that the meter provider is wired through to the tracer and logger providers
326326
Field field = SdkMeterProvider.class.getDeclaredField("sharedState");
327327
field.setAccessible(true);
328-
Object sharedState = field.get(sdk.getSdkMeterProvider());
328+
329+
// Lazily initialized
329330
assertThat(sdk)
330331
.extracting("loggerProvider")
331332
.extracting("delegate")
332333
.extracting("sharedState")
333334
.extracting("logRecordProcessor")
334335
.extracting("worker")
335-
.extracting("processedLogsCounter")
336-
.extracting("sdkMeter")
337-
.extracting("meterProviderSharedState")
338-
.isEqualTo(sharedState);
336+
.extracting("logProcessorInstrumentation")
337+
.extracting("processedLogs")
338+
.isNull();
339339

340340
// Lazily initialized
341341
assertThat(sdk)

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,22 @@ final class LoggerSharedState {
2323
private final LogRecordProcessor logRecordProcessor;
2424
private final Clock clock;
2525
private final ExceptionAttributeResolver exceptionAttributeResolver;
26+
private final SdkLoggerInstrumentation loggerInstrumentation;
2627
@Nullable private volatile CompletableResultCode shutdownResult = null;
2728

2829
LoggerSharedState(
2930
Resource resource,
3031
Supplier<LogLimits> logLimitsSupplier,
3132
LogRecordProcessor logRecordProcessor,
3233
Clock clock,
33-
ExceptionAttributeResolver exceptionAttributeResolver) {
34+
ExceptionAttributeResolver exceptionAttributeResolver,
35+
SdkLoggerInstrumentation loggerInstrumentation) {
3436
this.resource = resource;
3537
this.logLimitsSupplier = logLimitsSupplier;
3638
this.logRecordProcessor = logRecordProcessor;
3739
this.clock = clock;
3840
this.exceptionAttributeResolver = exceptionAttributeResolver;
41+
this.loggerInstrumentation = loggerInstrumentation;
3942
}
4043

4144
Resource getResource() {
@@ -58,6 +61,10 @@ ExceptionAttributeResolver getExceptionAttributeResolver() {
5861
return exceptionAttributeResolver;
5962
}
6063

64+
SdkLoggerInstrumentation getLoggerInstrumentation() {
65+
return loggerInstrumentation;
66+
}
67+
6168
boolean hasBeenShutdown() {
6269
return shutdownResult != null;
6370
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ public void emit() {
132132
this.observedTimestampEpochNanos == 0
133133
? this.loggerSharedState.getClock().now()
134134
: this.observedTimestampEpochNanos;
135+
136+
loggerSharedState.getLoggerInstrumentation().emitLog();
135137
loggerSharedState
136138
.getLogRecordProcessor()
137139
.onEmit(context, createLogRecord(context, observedTimestampEpochNanos));
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.sdk.logs;
7+
8+
import io.opentelemetry.api.metrics.LongCounter;
9+
import io.opentelemetry.api.metrics.Meter;
10+
import io.opentelemetry.api.metrics.MeterProvider;
11+
import java.util.function.Supplier;
12+
import javax.annotation.Nullable;
13+
14+
/**
15+
* SDK metrics exported for emitted logs as defined in the <a
16+
* href="https://opentelemetry.io/docs/specs/semconv/otel/sdk-metrics/#log-metrics">semantic
17+
* conventions</a>.
18+
*/
19+
final class SdkLoggerInstrumentation {
20+
private final Object lock = new Object();
21+
22+
private final Supplier<MeterProvider> meterProvider;
23+
24+
@Nullable private Meter meter;
25+
@Nullable private volatile LongCounter createdLogs;
26+
27+
SdkLoggerInstrumentation(Supplier<MeterProvider> meterProvider) {
28+
this.meterProvider = meterProvider;
29+
}
30+
31+
void emitLog() {
32+
createdLogs().add(1);
33+
}
34+
35+
private LongCounter createdLogs() {
36+
LongCounter createdLogs = this.createdLogs;
37+
if (createdLogs == null) {
38+
synchronized (lock) {
39+
createdLogs = this.createdLogs;
40+
if (createdLogs == null) {
41+
createdLogs =
42+
meter()
43+
.counterBuilder("otel.sdk.log.created")
44+
.setUnit("{log_record}")
45+
.setDescription("The number of logs submitted to enabled SDK Loggers.")
46+
.build();
47+
this.createdLogs = createdLogs;
48+
}
49+
}
50+
}
51+
return createdLogs;
52+
}
53+
54+
private Meter meter() {
55+
if (meter == null) {
56+
// Safe to call from multiple threads.
57+
meter = meterProvider.get().get("io.opentelemetry.sdk.logs");
58+
}
59+
return meter;
60+
}
61+
}

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import io.opentelemetry.api.logs.Logger;
1010
import io.opentelemetry.api.logs.LoggerBuilder;
1111
import io.opentelemetry.api.logs.LoggerProvider;
12+
import io.opentelemetry.api.metrics.MeterProvider;
1213
import io.opentelemetry.sdk.common.Clock;
1314
import io.opentelemetry.sdk.common.CompletableResultCode;
1415
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
@@ -58,11 +59,17 @@ public static SdkLoggerProviderBuilder builder() {
5859
List<LogRecordProcessor> processors,
5960
Clock clock,
6061
ScopeConfigurator<LoggerConfig> loggerConfigurator,
61-
ExceptionAttributeResolver exceptionAttributeResolver) {
62+
ExceptionAttributeResolver exceptionAttributeResolver,
63+
Supplier<MeterProvider> meterProvider) {
6264
LogRecordProcessor logRecordProcessor = LogRecordProcessor.composite(processors);
6365
this.sharedState =
6466
new LoggerSharedState(
65-
resource, logLimitsSupplier, logRecordProcessor, clock, exceptionAttributeResolver);
67+
resource,
68+
logLimitsSupplier,
69+
logRecordProcessor,
70+
clock,
71+
exceptionAttributeResolver,
72+
new SdkLoggerInstrumentation(meterProvider));
6673
this.loggerComponentRegistry =
6774
new ComponentRegistry<>(
6875
instrumentationScopeInfo ->

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import io.opentelemetry.api.logs.LogRecordBuilder;
1111
import io.opentelemetry.api.logs.Logger;
12+
import io.opentelemetry.api.metrics.MeterProvider;
1213
import io.opentelemetry.context.Context;
1314
import io.opentelemetry.sdk.common.Clock;
1415
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
@@ -40,6 +41,7 @@ public final class SdkLoggerProviderBuilder {
4041
LoggerConfig.configuratorBuilder();
4142
private ExceptionAttributeResolver exceptionAttributeResolver =
4243
ExceptionAttributeResolver.getDefault();
44+
private Supplier<MeterProvider> meterProvider = MeterProvider::noop;
4345

4446
SdkLoggerProviderBuilder() {}
4547

@@ -186,6 +188,17 @@ SdkLoggerProviderBuilder setExceptionAttributeResolver(
186188
return this;
187189
}
188190

191+
/**
192+
* Sets the {@link MeterProvider} to use to generate <a
193+
* href="https://opentelemetry.io/docs/specs/semconv/otel/sdk-metrics/#span-metrics">SDK Span
194+
* Metrics</a>.
195+
*/
196+
public SdkLoggerProviderBuilder setMeterProvider(Supplier<MeterProvider> meterProvider) {
197+
requireNonNull(meterProvider, "meterProvider");
198+
this.meterProvider = meterProvider;
199+
return this;
200+
}
201+
189202
/**
190203
* Create a {@link SdkLoggerProvider} instance.
191204
*
@@ -198,6 +211,7 @@ public SdkLoggerProvider build() {
198211
logRecordProcessors,
199212
clock,
200213
loggerConfiguratorBuilder.build(),
201-
exceptionAttributeResolver);
214+
exceptionAttributeResolver,
215+
meterProvider);
202216
}
203217
}

0 commit comments

Comments
 (0)