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