001package io.prometheus.metrics.exporter.opentelemetry; 002 003import io.opentelemetry.sdk.metrics.export.MetricReader; 004import io.prometheus.metrics.annotations.StableApi; 005import io.prometheus.metrics.config.PrometheusProperties; 006import io.prometheus.metrics.model.registry.PrometheusRegistry; 007import java.util.HashMap; 008import java.util.Map; 009import javax.annotation.Nullable; 010 011public class OpenTelemetryExporter implements AutoCloseable { 012 private final MetricReader reader; 013 014 /** 015 * @deprecated This constructor is not part of the stable API. Use {@link #builder()} or {@link 016 * #builder(PrometheusProperties)} instead. 017 */ 018 @Deprecated 019 public OpenTelemetryExporter(MetricReader reader) { 020 this.reader = reader; 021 } 022 023 @StableApi 024 @Override 025 public void close() { 026 reader.shutdown(); 027 } 028 029 @StableApi 030 public static Builder builder() { 031 return new Builder(PrometheusProperties.get()); 032 } 033 034 @StableApi 035 public static Builder builder(PrometheusProperties config) { 036 return new Builder(config); 037 } 038 039 @StableApi 040 public static class Builder { 041 042 private final PrometheusProperties config; 043 @Nullable private PrometheusRegistry registry = null; 044 @Nullable String protocol; 045 @Nullable String endpoint; 046 final Map<String, String> headers = new HashMap<>(); 047 @Nullable String interval; 048 @Nullable String timeout; 049 @Nullable String serviceName; 050 @Nullable String serviceNamespace; 051 @Nullable String serviceInstanceId; 052 @Nullable String serviceVersion; 053 final Map<String, String> resourceAttributes = new HashMap<>(); 054 @Nullable Boolean preserveNames; 055 056 private Builder(PrometheusProperties config) { 057 this.config = config; 058 } 059 060 public Builder registry(PrometheusRegistry registry) { 061 this.registry = registry; 062 return this; 063 } 064 065 /** 066 * Specifies the OTLP transport protocol to be used when exporting metrics. 067 * 068 * <p>Supported values are {@code "grpc"} and {@code "http/protobuf"}. Default is {@code 069 * "grpc"}. 070 * 071 * <p>See OpenTelemetry's <a 072 * href="https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/#otel_exporter_otlp_protocol">OTEL_EXPORTER_OTLP_PROTOCOL</a>. 073 */ 074 public Builder protocol(String protocol) { 075 if (!protocol.equals("grpc") && !protocol.equals("http/protobuf")) { 076 throw new IllegalArgumentException( 077 protocol + ": Unsupported protocol. Expecting grpc or http/protobuf"); 078 } 079 this.protocol = protocol; 080 return this; 081 } 082 083 /** 084 * The OTLP endpoint to send metric data to. 085 * 086 * <p>The default depends on the protocol: 087 * 088 * <ul> 089 * <li>{@code "grpc"}: {@code "http://localhost:4317"} 090 * <li>{@code "http/protobuf"}: {@code "http://localhost:4318/v1/metrics"} 091 * </ul> 092 * 093 * If the protocol is {@code "http/protobuf"} and the endpoint does not have the {@code 094 * "/v1/metrics"} suffix, the {@code "/v1/metrics"} suffix will automatically be appended. 095 * 096 * <p>See OpenTelemetry's <a 097 * href="https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/#otel_exporter_otlp_metrics_endpoint">OTEL_EXPORTER_OTLP_METRICS_ENDPOINT</a>. 098 */ 099 public Builder endpoint(String endpoint) { 100 this.endpoint = endpoint; 101 return this; 102 } 103 104 /** 105 * Add an HTTP header to be applied to outgoing requests. Call multiple times to add multiple 106 * headers. 107 * 108 * <p>See OpenTelemetry's <a 109 * href="https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/#otel_exporter_otlp_headers">OTEL_EXPORTER_OTLP_HEADERS</a>. 110 */ 111 public Builder header(String name, String value) { 112 this.headers.put(name, value); 113 return this; 114 } 115 116 /** 117 * The interval between the start of two export attempts. Default is 60000. 118 * 119 * <p>Like OpenTelemetry's <a 120 * href="https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md#periodic-metric-reader">OTEL_METRIC_EXPORT_INTERVAL</a>, 121 * but in seconds rather than milliseconds. 122 */ 123 public Builder intervalSeconds(int intervalSeconds) { 124 if (intervalSeconds <= 0) { 125 throw new IllegalStateException(intervalSeconds + ": expecting a push interval > 0s"); 126 } 127 this.interval = intervalSeconds + "s"; 128 return this; 129 } 130 131 /** 132 * The timeout for outgoing requests. Default is 10. 133 * 134 * <p>Like OpenTelemetry's <a 135 * href="https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/#otel_exporter_otlp_metrics_timeout">OTEL_EXPORTER_OTLP_METRICS_TIMEOUT</a>, 136 * but in seconds rather than milliseconds. 137 */ 138 public Builder timeoutSeconds(int timeoutSeconds) { 139 if (timeoutSeconds <= 0) { 140 throw new IllegalStateException(timeoutSeconds + ": expecting a push interval > 0s"); 141 } 142 this.timeout = timeoutSeconds + "s"; 143 return this; 144 } 145 146 /** 147 * The {@code service.name} resource attribute. 148 * 149 * <p>If not explicitly specified, {@code client_java} will try to initialize it with a 150 * reasonable default, like the JAR file name. 151 * 152 * <p>See {@code service.name} in OpenTelemetry's <a 153 * href="https://opentelemetry.io/docs/specs/otel/resource/semantic_conventions/#service">Resource 154 * Semantic Conventions</a>. 155 */ 156 public Builder serviceName(String serviceName) { 157 this.serviceName = serviceName; 158 return this; 159 } 160 161 /** 162 * The {@code service.namespace} resource attribute. 163 * 164 * <p>See {@code service.namespace} in OpenTelemetry's <a 165 * href="https://opentelemetry.io/docs/specs/otel/resource/semantic_conventions/#service-experimental">Resource 166 * Semantic Conventions</a>. 167 */ 168 public Builder serviceNamespace(String serviceNamespace) { 169 this.serviceNamespace = serviceNamespace; 170 return this; 171 } 172 173 /** 174 * The {@code service.instance.id} resource attribute. 175 * 176 * <p>See {@code service.instance.id} in OpenTelemetry's <a 177 * href="https://opentelemetry.io/docs/specs/otel/resource/semantic_conventions/#service-experimental">Resource 178 * Semantic Conventions</a>. 179 */ 180 public Builder serviceInstanceId(String serviceInstanceId) { 181 this.serviceInstanceId = serviceInstanceId; 182 return this; 183 } 184 185 /** 186 * The {@code service.version} resource attribute. 187 * 188 * <p>See {@code service.version} in OpenTelemetry's <a 189 * href="https://opentelemetry.io/docs/specs/otel/resource/semantic_conventions/#service-experimental">Resource 190 * Semantic Conventions</a>. 191 */ 192 public Builder serviceVersion(String serviceVersion) { 193 this.serviceVersion = serviceVersion; 194 return this; 195 } 196 197 /** 198 * Add a resource attribute. Call multiple times to add multiple resource attributes. 199 * 200 * <p>See OpenTelemetry's <a 201 * href="https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/#general-sdk-configuration">OTEL_RESOURCE_ATTRIBUTES</a>. 202 */ 203 public Builder resourceAttribute(String name, String value) { 204 this.resourceAttributes.put(name, value); 205 return this; 206 } 207 208 /** 209 * When {@code true}, metric names are preserved as-is (including suffixes like {@code _total}). 210 * When {@code false} (default), standard OTel name normalization is applied. 211 */ 212 public Builder preserveNames(boolean preserveNames) { 213 this.preserveNames = preserveNames; 214 return this; 215 } 216 217 public OpenTelemetryExporter buildAndStart() { 218 if (registry == null) { 219 registry = PrometheusRegistry.defaultRegistry; 220 } 221 return new OpenTelemetryExporter(OtelAutoConfig.createReader(this, config, registry)); 222 } 223 } 224}