001package io.prometheus.metrics.config; 002 003import io.prometheus.metrics.annotations.StableApi; 004import java.util.HashMap; 005import java.util.Map; 006import javax.annotation.Nullable; 007 008/** 009 * Properties for configuring the OpenTelemetry exporter. 010 * 011 * <p>These properties can be configured via {@code prometheus.properties}, system properties, or 012 * programmatically. 013 * 014 * <p>All properties are prefixed with {@code io.prometheus.exporter.opentelemetry}. 015 * 016 * <p>Available properties: 017 * 018 * <ul> 019 * <li>{@code protocol} - OTLP protocol: {@code "grpc"} or {@code "http/protobuf"} 020 * <li>{@code endpoint} - OTLP endpoint URL 021 * <li>{@code headers} - HTTP headers for outgoing requests 022 * <li>{@code intervalSeconds} - Export interval in seconds 023 * <li>{@code timeoutSeconds} - Request timeout in seconds 024 * <li>{@code serviceName} - Service name resource attribute 025 * <li>{@code serviceNamespace} - Service namespace resource attribute 026 * <li>{@code serviceInstanceId} - Service instance ID resource attribute 027 * <li>{@code serviceVersion} - Service version resource attribute 028 * <li>{@code resourceAttributes} - Additional resource attributes 029 * </ul> 030 * 031 * @see <a 032 * href="https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/">OpenTelemetry 033 * SDK Environment Variables</a> 034 */ 035@StableApi 036public class ExporterOpenTelemetryProperties { 037 038 // See 039 // https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md 040 private static final String PROTOCOL = "protocol"; // otel.exporter.otlp.protocol 041 private static final String ENDPOINT = "endpoint"; // otel.exporter.otlp.endpoint 042 private static final String HEADERS = "headers"; // otel.exporter.otlp.headers 043 private static final String INTERVAL_SECONDS = "interval_seconds"; // otel.metric.export.interval 044 private static final String TIMEOUT_SECONDS = "timeout_seconds"; // otel.exporter.otlp.timeout 045 private static final String SERVICE_NAME = "service_name"; // otel.service.name 046 private static final String SERVICE_NAMESPACE = "service_namespace"; 047 private static final String SERVICE_INSTANCE_ID = "service_instance_id"; 048 private static final String SERVICE_VERSION = "service_version"; 049 private static final String RESOURCE_ATTRIBUTES = 050 "resource_attributes"; // otel.resource.attributes 051 private static final String PRESERVE_NAMES = "preserve_names"; 052 private static final String PREFIX = "io.prometheus.exporter.opentelemetry"; 053 054 @Nullable private final String endpoint; 055 @Nullable private final String protocol; 056 private final Map<String, String> headers; 057 @Nullable private final String interval; 058 @Nullable private final String timeout; 059 @Nullable private final String serviceName; 060 @Nullable private final String serviceNamespace; 061 @Nullable private final String serviceInstanceId; 062 @Nullable private final String serviceVersion; 063 private final Map<String, String> resourceAttributes; 064 @Nullable private final Boolean preserveNames; 065 066 private ExporterOpenTelemetryProperties( 067 @Nullable String protocol, 068 @Nullable String endpoint, 069 Map<String, String> headers, 070 @Nullable String interval, 071 @Nullable String timeout, 072 @Nullable String serviceName, 073 @Nullable String serviceNamespace, 074 @Nullable String serviceInstanceId, 075 @Nullable String serviceVersion, 076 Map<String, String> resourceAttributes, 077 @Nullable Boolean preserveNames) { 078 this.protocol = protocol; 079 this.endpoint = endpoint; 080 this.headers = headers; 081 this.interval = interval; 082 this.timeout = timeout; 083 this.serviceName = serviceName; 084 this.serviceNamespace = serviceNamespace; 085 this.serviceInstanceId = serviceInstanceId; 086 this.serviceVersion = serviceVersion; 087 this.resourceAttributes = resourceAttributes; 088 this.preserveNames = preserveNames; 089 } 090 091 @Nullable 092 public String getProtocol() { 093 return protocol; 094 } 095 096 @Nullable 097 public String getEndpoint() { 098 return endpoint; 099 } 100 101 public Map<String, String> getHeaders() { 102 return headers; 103 } 104 105 @Nullable 106 public String getInterval() { 107 return interval; 108 } 109 110 @Nullable 111 public String getTimeout() { 112 return timeout; 113 } 114 115 @Nullable 116 public String getServiceName() { 117 return serviceName; 118 } 119 120 @Nullable 121 public String getServiceNamespace() { 122 return serviceNamespace; 123 } 124 125 @Nullable 126 public String getServiceInstanceId() { 127 return serviceInstanceId; 128 } 129 130 @Nullable 131 public String getServiceVersion() { 132 return serviceVersion; 133 } 134 135 public Map<String, String> getResourceAttributes() { 136 return resourceAttributes; 137 } 138 139 /** 140 * When {@code true}, metric names are preserved as-is (including suffixes like {@code _total}). 141 * When {@code false} (default), standard OTel name normalization is applied (stripping unit 142 * suffix). 143 */ 144 @Nullable 145 public Boolean getPreserveNames() { 146 return preserveNames; 147 } 148 149 /** 150 * Note that this will remove entries from {@code propertySource}. This is because we want to know 151 * if there are unused properties remaining after all properties have been loaded. 152 */ 153 static ExporterOpenTelemetryProperties load(PropertySource propertySource) 154 throws PrometheusPropertiesException { 155 String protocol = Util.loadString(PREFIX, PROTOCOL, propertySource); 156 String endpoint = Util.loadString(PREFIX, ENDPOINT, propertySource); 157 Map<String, String> headers = Util.loadMap(PREFIX, HEADERS, propertySource); 158 String interval = Util.loadStringAddSuffix(PREFIX, INTERVAL_SECONDS, propertySource, "s"); 159 String timeout = Util.loadStringAddSuffix(PREFIX, TIMEOUT_SECONDS, propertySource, "s"); 160 String serviceName = Util.loadString(PREFIX, SERVICE_NAME, propertySource); 161 String serviceNamespace = Util.loadString(PREFIX, SERVICE_NAMESPACE, propertySource); 162 String serviceInstanceId = Util.loadString(PREFIX, SERVICE_INSTANCE_ID, propertySource); 163 String serviceVersion = Util.loadString(PREFIX, SERVICE_VERSION, propertySource); 164 Map<String, String> resourceAttributes = 165 Util.loadMap(PREFIX, RESOURCE_ATTRIBUTES, propertySource); 166 Boolean preserveNames = Util.loadBoolean(PREFIX, PRESERVE_NAMES, propertySource); 167 return new ExporterOpenTelemetryProperties( 168 protocol, 169 endpoint, 170 headers, 171 interval, 172 timeout, 173 serviceName, 174 serviceNamespace, 175 serviceInstanceId, 176 serviceVersion, 177 resourceAttributes, 178 preserveNames); 179 } 180 181 public static Builder builder() { 182 return new Builder(); 183 } 184 185 public static class Builder { 186 187 @Nullable private String protocol; 188 @Nullable private String endpoint; 189 private final Map<String, String> headers = new HashMap<>(); 190 @Nullable private String interval; 191 @Nullable private String timeout; 192 @Nullable private String serviceName; 193 @Nullable private String serviceNamespace; 194 @Nullable private String serviceInstanceId; 195 @Nullable private String serviceVersion; 196 private final Map<String, String> resourceAttributes = new HashMap<>(); 197 @Nullable private Boolean preserveNames; 198 199 private Builder() {} 200 201 /** 202 * The OTLP protocol to use. 203 * 204 * <p>Supported values: {@code "grpc"} or {@code "http/protobuf"}. 205 * 206 * <p>See OpenTelemetry's <a 207 * href="https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/#otel_exporter_otlp_protocol">OTEL_EXPORTER_OTLP_PROTOCOL</a>. 208 */ 209 public Builder protocol(String protocol) { 210 if (!protocol.equals("grpc") && !protocol.equals("http/protobuf")) { 211 throw new IllegalArgumentException( 212 protocol + ": Unsupported protocol. Expecting grpc or http/protobuf"); 213 } 214 this.protocol = protocol; 215 return this; 216 } 217 218 /** 219 * The OTLP endpoint to send metric data to. 220 * 221 * <p>The default depends on the protocol: 222 * 223 * <ul> 224 * <li>{@code "grpc"}: {@code "http://localhost:4317"} 225 * <li>{@code "http/protobuf"}: {@code "http://localhost:4318/v1/metrics"} 226 * </ul> 227 * 228 * <p>See OpenTelemetry's <a 229 * href="https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/#otel_exporter_otlp_metrics_endpoint">OTEL_EXPORTER_OTLP_METRICS_ENDPOINT</a>. 230 */ 231 public Builder endpoint(String endpoint) { 232 this.endpoint = endpoint; 233 return this; 234 } 235 236 /** 237 * Add an HTTP header to be applied to outgoing requests. Call multiple times to add multiple 238 * headers. 239 * 240 * <p>See OpenTelemetry's <a 241 * href="https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/#otel_exporter_otlp_headers">OTEL_EXPORTER_OTLP_HEADERS</a>. 242 */ 243 public Builder header(String name, String value) { 244 this.headers.put(name, value); 245 return this; 246 } 247 248 /** 249 * The interval between the start of two export attempts. Default is 60 seconds. 250 * 251 * <p>Like OpenTelemetry's <a 252 * href="https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md#periodic-metric-reader">OTEL_METRIC_EXPORT_INTERVAL</a> 253 * (which defaults to 60000 milliseconds), but specified in seconds rather than milliseconds. 254 */ 255 public Builder intervalSeconds(int intervalSeconds) { 256 if (intervalSeconds <= 0) { 257 throw new IllegalArgumentException(intervalSeconds + ": Expecting intervalSeconds > 0"); 258 } 259 this.interval = intervalSeconds + "s"; 260 return this; 261 } 262 263 /** 264 * The timeout for outgoing requests. Default is 10. 265 * 266 * <p>Like OpenTelemetry's <a 267 * href="https://opentelemetry.io/docs/concepts/sdk-configuration/otlp-exporter-configuration/#otel_exporter_otlp_metrics_timeout">OTEL_EXPORTER_OTLP_METRICS_TIMEOUT</a>, 268 * but in seconds rather than milliseconds. 269 */ 270 public Builder timeoutSeconds(int timeoutSeconds) { 271 if (timeoutSeconds <= 0) { 272 throw new IllegalArgumentException(timeoutSeconds + ": Expecting timeoutSeconds > 0"); 273 } 274 this.timeout = timeoutSeconds + "s"; 275 return this; 276 } 277 278 /** 279 * The {@code service.name} resource attribute. 280 * 281 * <p>If not explicitly specified, {@code client_java} will try to initialize it with a 282 * reasonable default, like the JAR file name. 283 * 284 * <p>See {@code service.name} in OpenTelemetry's <a 285 * href="https://opentelemetry.io/docs/specs/otel/resource/semantic_conventions/#service">Resource 286 * Semantic Conventions</a>. 287 */ 288 public Builder serviceName(String serviceName) { 289 this.serviceName = serviceName; 290 return this; 291 } 292 293 /** 294 * The {@code service.namespace} resource attribute. 295 * 296 * <p>See {@code service.namespace} in OpenTelemetry's <a 297 * href="https://opentelemetry.io/docs/specs/otel/resource/semantic_conventions/#service-experimental">Resource 298 * Semantic Conventions</a>. 299 */ 300 public Builder serviceNamespace(String serviceNamespace) { 301 this.serviceNamespace = serviceNamespace; 302 return this; 303 } 304 305 /** 306 * The {@code service.instance.id} resource attribute. 307 * 308 * <p>See {@code service.instance.id} in OpenTelemetry's <a 309 * href="https://opentelemetry.io/docs/specs/otel/resource/semantic_conventions/#service-experimental">Resource 310 * Semantic Conventions</a>. 311 */ 312 public Builder serviceInstanceId(String serviceInstanceId) { 313 this.serviceInstanceId = serviceInstanceId; 314 return this; 315 } 316 317 /** 318 * The {@code service.version} resource attribute. 319 * 320 * <p>See {@code service.version} in OpenTelemetry's <a 321 * href="https://opentelemetry.io/docs/specs/otel/resource/semantic_conventions/#service-experimental">Resource 322 * Semantic Conventions</a>. 323 */ 324 public Builder serviceVersion(String serviceVersion) { 325 this.serviceVersion = serviceVersion; 326 return this; 327 } 328 329 /** 330 * Add a resource attribute. Call multiple times to add multiple resource attributes. 331 * 332 * <p>See OpenTelemetry's <a 333 * href="https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/#general-sdk-configuration">OTEL_RESOURCE_ATTRIBUTES</a>. 334 */ 335 public Builder resourceAttribute(String name, String value) { 336 this.resourceAttributes.put(name, value); 337 return this; 338 } 339 340 /** 341 * When {@code true}, metric names are preserved as-is (including suffixes like {@code _total}). 342 * When {@code false} (default), standard OTel name normalization is applied. 343 */ 344 public Builder preserveNames(boolean preserveNames) { 345 this.preserveNames = preserveNames; 346 return this; 347 } 348 349 public ExporterOpenTelemetryProperties build() { 350 return new ExporterOpenTelemetryProperties( 351 protocol, 352 endpoint, 353 headers, 354 interval, 355 timeout, 356 serviceName, 357 serviceNamespace, 358 serviceInstanceId, 359 serviceVersion, 360 resourceAttributes, 361 preserveNames); 362 } 363 } 364}