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