JIT exporter metrics (#4993)

* Build ExporterMetrics instruments just in time

* Exporters use GlobalOpenTelemetry#getMeterProvider() if meter provider is not set

* FullConfigTest reset GlobalOpenTelemetry

* MetricExporters use MeterProvider.noop()
This commit is contained in:
jack-berg 2022-12-17 15:19:00 -06:00 committed by GitHub
parent fdc56b354d
commit 551e764df8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 106 additions and 52 deletions

View File

@ -13,6 +13,8 @@ import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.MeterProvider;
import java.util.function.Supplier;
import javax.annotation.Nullable;
/**
* Helper for recording metrics from exporters.
@ -25,37 +27,69 @@ public class ExporterMetrics {
private static final AttributeKey<String> ATTRIBUTE_KEY_TYPE = stringKey("type");
private static final AttributeKey<Boolean> ATTRIBUTE_KEY_SUCCESS = booleanKey("success");
private final LongCounter seen;
private final LongCounter exported;
private final Supplier<MeterProvider> meterProviderSupplier;
private final String exporterName;
private final String transportName;
private final Attributes seenAttrs;
private final Attributes successAttrs;
private final Attributes failedAttrs;
/** Access via {@link #seen()}. */
@Nullable private volatile LongCounter seen;
/** Access via {@link #exported()} . */
@Nullable private volatile LongCounter exported;
private ExporterMetrics(
MeterProvider meterProvider, String exporterName, String type, String transportName) {
Meter meter =
meterProvider.get("io.opentelemetry.exporters." + exporterName + "-" + transportName);
seenAttrs = Attributes.builder().put(ATTRIBUTE_KEY_TYPE, type).build();
seen = meter.counterBuilder(exporterName + ".exporter.seen").build();
exported = meter.counterBuilder(exporterName + ".exporter.exported").build();
successAttrs = seenAttrs.toBuilder().put(ATTRIBUTE_KEY_SUCCESS, true).build();
failedAttrs = seenAttrs.toBuilder().put(ATTRIBUTE_KEY_SUCCESS, false).build();
Supplier<MeterProvider> meterProviderSupplier,
String exporterName,
String type,
String transportName) {
this.meterProviderSupplier = meterProviderSupplier;
this.exporterName = exporterName;
this.transportName = transportName;
this.seenAttrs = Attributes.builder().put(ATTRIBUTE_KEY_TYPE, type).build();
this.successAttrs = this.seenAttrs.toBuilder().put(ATTRIBUTE_KEY_SUCCESS, true).build();
this.failedAttrs = this.seenAttrs.toBuilder().put(ATTRIBUTE_KEY_SUCCESS, false).build();
}
/** Record number of records seen. */
public void addSeen(long value) {
seen.add(value, seenAttrs);
seen().add(value, seenAttrs);
}
/** Record number of records which successfully exported. */
public void addSuccess(long value) {
exported.add(value, successAttrs);
exported().add(value, successAttrs);
}
/** Record number of records which failed to export. */
public void addFailed(long value) {
exported.add(value, failedAttrs);
exported().add(value, failedAttrs);
}
private LongCounter seen() {
LongCounter seen = this.seen;
if (seen == null) {
seen = meter().counterBuilder(exporterName + ".exporter.seen").build();
this.seen = seen;
}
return seen;
}
private LongCounter exported() {
LongCounter exported = this.exported;
if (exported == null) {
exported = meter().counterBuilder(exporterName + ".exporter.exported").build();
this.exported = exported;
}
return exported;
}
private Meter meter() {
return meterProviderSupplier
.get()
.get("io.opentelemetry.exporters." + exporterName + "-" + transportName);
}
/**
@ -63,7 +97,7 @@ public class ExporterMetrics {
* "io.opentelemetry.exporters." + exporterName + "-grpc}".
*/
public static ExporterMetrics createGrpc(
String exporterName, String type, MeterProvider meterProvider) {
String exporterName, String type, Supplier<MeterProvider> meterProvider) {
return new ExporterMetrics(meterProvider, exporterName, type, "grpc");
}
@ -72,7 +106,7 @@ public class ExporterMetrics {
* "io.opentelemetry.exporters." + exporterName + "-grpc-okhttp}".
*/
public static ExporterMetrics createGrpcOkHttp(
String exporterName, String type, MeterProvider meterProvider) {
String exporterName, String type, Supplier<MeterProvider> meterProvider) {
return new ExporterMetrics(meterProvider, exporterName, type, "grpc-okhttp");
}
@ -81,7 +115,7 @@ public class ExporterMetrics {
* "io.opentelemetry.exporters." + exporterName + "-http}".
*/
public static ExporterMetrics createHttpProtobuf(
String exporterName, String type, MeterProvider meterProvider) {
String exporterName, String type, Supplier<MeterProvider> meterProvider) {
return new ExporterMetrics(meterProvider, exporterName, type, "http");
}
@ -90,7 +124,7 @@ public class ExporterMetrics {
* "io.opentelemetry.exporters." + exporterName + "-http-json}".
*/
public static ExporterMetrics createHttpJson(
String exporterName, String type, MeterProvider meterProvider) {
String exporterName, String type, Supplier<MeterProvider> meterProvider) {
return new ExporterMetrics(meterProvider, exporterName, type, "http-json");
}
}

View File

@ -11,6 +11,7 @@ import io.grpc.Codec;
import io.grpc.ManagedChannel;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.exporter.internal.ExporterBuilderUtil;
import io.opentelemetry.exporter.internal.TlsUtil;
@ -58,7 +59,7 @@ public class GrpcExporterBuilder<T extends Marshaler> {
@Nullable private byte[] privateKeyPem;
@Nullable private byte[] certificatePem;
@Nullable private RetryPolicy retryPolicy;
private MeterProvider meterProvider = MeterProvider.noop();
private Supplier<MeterProvider> meterProviderSupplier = GlobalOpenTelemetry::getMeterProvider;
// Use Object type since gRPC may not be on the classpath.
@Nullable private Object grpcChannel;
@ -124,7 +125,7 @@ public class GrpcExporterBuilder<T extends Marshaler> {
}
public GrpcExporterBuilder<T> setMeterProvider(MeterProvider meterProvider) {
this.meterProvider = meterProvider;
this.meterProviderSupplier = () -> meterProvider;
return this;
}
@ -177,7 +178,7 @@ public class GrpcExporterBuilder<T extends Marshaler> {
exporterName,
type,
clientBuilder.build(),
meterProvider,
meterProviderSupplier,
endpoint,
headers.build(),
compressionEnabled);
@ -209,7 +210,8 @@ public class GrpcExporterBuilder<T extends Marshaler> {
.get()
.apply(channel, authorityOverride)
.withCompression(codec.getMessageEncoding());
return new UpstreamGrpcExporter<>(exporterName, type, stub, meterProvider, timeoutNanos);
return new UpstreamGrpcExporter<>(
exporterName, type, stub, meterProviderSupplier, timeoutNanos);
}
}
}

View File

@ -33,6 +33,7 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
@ -75,12 +76,13 @@ public final class OkHttpGrpcExporter<T extends Marshaler> implements GrpcExport
String exporterName,
String type,
OkHttpClient client,
MeterProvider meterProvider,
Supplier<MeterProvider> meterProviderSupplier,
String endpoint,
Headers headers,
boolean compressionEnabled) {
this.type = type;
this.exporterMetrics = ExporterMetrics.createGrpcOkHttp(exporterName, type, meterProvider);
this.exporterMetrics =
ExporterMetrics.createGrpcOkHttp(exporterName, type, meterProviderSupplier);
this.client = client;
this.url = HttpUrl.get(endpoint);
this.headers = headers;

View File

@ -16,6 +16,7 @@ import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.internal.ThrottlingLogger;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -46,10 +47,10 @@ public final class UpstreamGrpcExporter<T extends Marshaler> implements GrpcExpo
String exporterName,
String type,
MarshalerServiceStub<T, ?, ?> stub,
MeterProvider meterProvider,
Supplier<MeterProvider> meterProviderSupplier,
long timeoutNanos) {
this.type = type;
this.exporterMetrics = ExporterMetrics.createGrpc(exporterName, type, meterProvider);
this.exporterMetrics = ExporterMetrics.createGrpc(exporterName, type, meterProviderSupplier);
this.timeoutNanos = timeoutNanos;
this.stub = stub;
}

View File

@ -14,6 +14,7 @@ import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.internal.ThrottlingLogger;
import java.io.IOException;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
@ -57,7 +58,7 @@ public final class OkHttpExporter<T extends Marshaler> {
String exporterName,
String type,
OkHttpClient client,
MeterProvider meterProvider,
Supplier<MeterProvider> meterProviderSupplier,
String endpoint,
@Nullable Headers headers,
boolean compressionEnabled,
@ -70,8 +71,8 @@ public final class OkHttpExporter<T extends Marshaler> {
this.requestBodyCreator = exportAsJson ? JsonRequestBody::new : ProtoRequestBody::new;
this.exporterMetrics =
exportAsJson
? ExporterMetrics.createHttpJson(exporterName, type, meterProvider)
: ExporterMetrics.createHttpProtobuf(exporterName, type, meterProvider);
? ExporterMetrics.createHttpJson(exporterName, type, meterProviderSupplier)
: ExporterMetrics.createHttpProtobuf(exporterName, type, meterProviderSupplier);
}
public CompletableResultCode export(T exportRequest, int numItems) {

View File

@ -5,6 +5,7 @@
package io.opentelemetry.exporter.internal.okhttp;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.exporter.internal.ExporterBuilderUtil;
import io.opentelemetry.exporter.internal.TlsUtil;
@ -15,6 +16,7 @@ import io.opentelemetry.exporter.internal.retry.RetryPolicy;
import java.net.URI;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import javax.net.ssl.SSLException;
import javax.net.ssl.X509KeyManager;
@ -46,7 +48,7 @@ public final class OkHttpExporterBuilder<T extends Marshaler> {
@Nullable private byte[] privateKeyPem;
@Nullable private byte[] certificatePem;
@Nullable private RetryPolicy retryPolicy;
private MeterProvider meterProvider = MeterProvider.noop();
private Supplier<MeterProvider> meterProviderSupplier = GlobalOpenTelemetry::getMeterProvider;
@Nullable private Authenticator authenticator;
public OkHttpExporterBuilder(String exporterName, String type, String defaultEndpoint) {
@ -101,7 +103,7 @@ public final class OkHttpExporterBuilder<T extends Marshaler> {
}
public OkHttpExporterBuilder<T> setMeterProvider(MeterProvider meterProvider) {
this.meterProvider = meterProvider;
this.meterProviderSupplier = () -> meterProvider;
return this;
}
@ -158,7 +160,7 @@ public final class OkHttpExporterBuilder<T extends Marshaler> {
exporterName,
type,
clientBuilder.build(),
meterProvider,
meterProviderSupplier,
endpoint,
headers,
compressionEnabled,

View File

@ -9,6 +9,7 @@ import static io.opentelemetry.api.internal.Utils.checkArgument;
import static java.util.Objects.requireNonNull;
import io.grpc.ManagedChannel;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.exporter.internal.grpc.GrpcExporter;
import io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder;
@ -119,8 +120,8 @@ public final class JaegerGrpcSpanExporterBuilder {
}
/**
* Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, metrics
* will not be collected.
* Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, uses
* {@link GlobalOpenTelemetry#getMeterProvider()}.
*
* @since 1.15.0
*/

View File

@ -8,6 +8,7 @@ package io.opentelemetry.exporter.otlp.http.metrics;
import static io.opentelemetry.api.internal.Utils.checkArgument;
import static java.util.Objects.requireNonNull;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.exporter.internal.okhttp.OkHttpExporterBuilder;
import io.opentelemetry.exporter.internal.otlp.OtlpUserAgent;
import io.opentelemetry.exporter.internal.otlp.metrics.MetricsRequestMarshaler;
@ -39,6 +40,7 @@ public final class OtlpHttpMetricExporterBuilder {
OtlpHttpMetricExporterBuilder() {
delegate = new OkHttpExporterBuilder<>("otlp", "metric", DEFAULT_ENDPOINT);
delegate.setMeterProvider(MeterProvider.noop());
OtlpUserAgent.addUserAgentHeader(delegate::addHeader);
}

View File

@ -8,6 +8,7 @@ package io.opentelemetry.exporter.otlp.http.trace;
import static io.opentelemetry.api.internal.Utils.checkArgument;
import static java.util.Objects.requireNonNull;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.exporter.internal.okhttp.OkHttpExporterBuilder;
import io.opentelemetry.exporter.internal.otlp.OtlpUserAgent;
@ -100,8 +101,8 @@ public final class OtlpHttpSpanExporterBuilder {
}
/**
* Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, metrics
* will not be collected.
* Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, uses
* {@link GlobalOpenTelemetry#getMeterProvider()}.
*/
public OtlpHttpSpanExporterBuilder setMeterProvider(MeterProvider meterProvider) {
requireNonNull(meterProvider, "meterProvider");

View File

@ -9,6 +9,7 @@ import static io.opentelemetry.api.internal.Utils.checkArgument;
import static java.util.Objects.requireNonNull;
import io.grpc.ManagedChannel;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.exporter.internal.grpc.GrpcExporter;
import io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder;
import io.opentelemetry.exporter.internal.otlp.OtlpUserAgent;
@ -57,6 +58,7 @@ public final class OtlpGrpcMetricExporterBuilder {
DEFAULT_ENDPOINT,
() -> MarshalerMetricsServiceGrpc::newFutureStub,
GRPC_ENDPOINT_PATH);
delegate.setMeterProvider(MeterProvider.noop());
OtlpUserAgent.addUserAgentHeader(delegate::addHeader);
}

View File

@ -9,6 +9,7 @@ import static io.opentelemetry.api.internal.Utils.checkArgument;
import static java.util.Objects.requireNonNull;
import io.grpc.ManagedChannel;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.exporter.internal.grpc.GrpcExporter;
import io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder;
@ -141,8 +142,8 @@ public final class OtlpGrpcSpanExporterBuilder {
}
/**
* Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, metrics
* will not be collected.
* Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, uses
* {@link GlobalOpenTelemetry#getMeterProvider()}.
*/
public OtlpGrpcSpanExporterBuilder setMeterProvider(MeterProvider meterProvider) {
requireNonNull(meterProvider, "meterProvider");

View File

@ -8,6 +8,7 @@ package io.opentelemetry.exporter.otlp.http.logs;
import static io.opentelemetry.api.internal.Utils.checkArgument;
import static java.util.Objects.requireNonNull;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.exporter.internal.okhttp.OkHttpExporterBuilder;
import io.opentelemetry.exporter.internal.otlp.OtlpUserAgent;
@ -97,8 +98,8 @@ public final class OtlpHttpLogRecordExporterBuilder {
}
/**
* Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, metrics
* will not be collected.
* Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, uses
* {@link GlobalOpenTelemetry#getMeterProvider()}.
*/
public OtlpHttpLogRecordExporterBuilder setMeterProvider(MeterProvider meterProvider) {
requireNonNull(meterProvider, "meterProvider");

View File

@ -9,6 +9,7 @@ import static io.opentelemetry.api.internal.Utils.checkArgument;
import static java.util.Objects.requireNonNull;
import io.grpc.ManagedChannel;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.exporter.internal.grpc.GrpcExporter;
import io.opentelemetry.exporter.internal.grpc.GrpcExporterBuilder;
@ -142,8 +143,8 @@ public final class OtlpGrpcLogRecordExporterBuilder {
}
/**
* Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, metrics
* will not be collected.
* Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, uses
* {@link GlobalOpenTelemetry#getMeterProvider()}.
*/
public OtlpGrpcLogRecordExporterBuilder setMeterProvider(MeterProvider meterProvider) {
requireNonNull(meterProvider, "meterProvider");

View File

@ -15,6 +15,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
import zipkin2.Callback;
@ -44,14 +45,14 @@ public final class ZipkinSpanExporter implements SpanExporter {
ZipkinSpanExporter(
BytesEncoder<Span> encoder,
Sender sender,
MeterProvider meterProvider,
Supplier<MeterProvider> meterProviderSupplier,
OtelToZipkinSpanTransformer transformer) {
this.encoder = encoder;
this.sender = sender;
this.exporterMetrics =
sender.encoding() == Encoding.JSON
? ExporterMetrics.createHttpJson("zipkin", "span", meterProvider)
: ExporterMetrics.createHttpProtobuf("zipkin", "span", meterProvider);
? ExporterMetrics.createHttpJson("zipkin", "span", meterProviderSupplier)
: ExporterMetrics.createHttpProtobuf("zipkin", "span", meterProviderSupplier);
this.transformer = transformer;
}

View File

@ -8,6 +8,7 @@ package io.opentelemetry.exporter.zipkin;
import static io.opentelemetry.api.internal.Utils.checkArgument;
import static java.util.Objects.requireNonNull;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import java.net.InetAddress;
import java.time.Duration;
@ -30,7 +31,7 @@ public final class ZipkinSpanExporterBuilder {
// which is created when no custom sender is set (see OkHttpSender.Builder)
private boolean compressionEnabled = true;
private long readTimeoutMillis = TimeUnit.SECONDS.toMillis(10);
private MeterProvider meterProvider = MeterProvider.noop();
private Supplier<MeterProvider> meterProviderSupplier = GlobalOpenTelemetry::getMeterProvider;
/**
* Sets the Zipkin sender. Implements the client side of the span transport. An {@link
@ -138,15 +139,15 @@ public final class ZipkinSpanExporterBuilder {
}
/**
* Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, metrics
* will not be collected.
* Sets the {@link MeterProvider} to use to collect metrics related to export. If not set, uses
* {@link GlobalOpenTelemetry#getMeterProvider()}.
*
* @return this.
* @since 1.17.0
*/
public ZipkinSpanExporterBuilder setMeterProvider(MeterProvider meterProvider) {
requireNonNull(meterProvider, "meterProvider");
this.meterProvider = meterProvider;
this.meterProviderSupplier = () -> meterProvider;
return this;
}
@ -167,6 +168,6 @@ public final class ZipkinSpanExporterBuilder {
}
OtelToZipkinSpanTransformer transformer =
OtelToZipkinSpanTransformer.create(localIpAddressSupplier);
return new ZipkinSpanExporter(encoder, sender, meterProvider, transformer);
return new ZipkinSpanExporter(encoder, sender, meterProviderSupplier, transformer);
}
}

View File

@ -46,7 +46,7 @@ class ZipkinSpanExporterTest {
TestSpanData testSpanData = spanBuilder().build();
ZipkinSpanExporter zipkinSpanExporter =
new ZipkinSpanExporter(mockEncoder, mockSender, MeterProvider.noop(), mockTransformer);
new ZipkinSpanExporter(mockEncoder, mockSender, MeterProvider::noop, mockTransformer);
byte[] someBytes = new byte[0];
Span zipkinSpan =
@ -77,7 +77,7 @@ class ZipkinSpanExporterTest {
TestSpanData testSpanData = spanBuilder().build();
ZipkinSpanExporter zipkinSpanExporter =
new ZipkinSpanExporter(mockEncoder, mockSender, MeterProvider.noop(), mockTransformer);
new ZipkinSpanExporter(mockEncoder, mockSender, MeterProvider::noop, mockTransformer);
byte[] someBytes = new byte[0];
Span zipkinSpan =

View File

@ -155,6 +155,7 @@ class FullConfigTest {
System.setProperty("otel.exporter.otlp.timeout", "10000");
// Initialize here so we can shutdown when done
GlobalOpenTelemetry.resetForTest();
autoConfiguredOpenTelemetrySdk = AutoConfiguredOpenTelemetrySdk.initialize();
}