001package io.prometheus.metrics.exporter.opentelemetry; 002 003import io.opentelemetry.api.common.Attributes; 004import io.opentelemetry.api.common.AttributesBuilder; 005import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; 006import io.opentelemetry.sdk.autoconfigure.ResourceConfiguration; 007import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; 008import io.opentelemetry.sdk.common.InstrumentationScopeInfo; 009import io.opentelemetry.sdk.metrics.export.MetricReader; 010import io.opentelemetry.sdk.resources.Resource; 011import io.prometheus.metrics.config.ExporterOpenTelemetryProperties; 012import io.prometheus.metrics.config.PrometheusProperties; 013import io.prometheus.metrics.model.registry.PrometheusRegistry; 014import io.prometheus.otelagent.ResourceAttributesFromOtelAgent; 015import java.lang.reflect.Method; 016import java.util.Map; 017import java.util.concurrent.atomic.AtomicReference; 018 019public class OtelAutoConfig { 020 021 private static final String SERVICE_INSTANCE_ID = "service.instance.id"; 022 023 static MetricReader createReader( 024 OpenTelemetryExporter.Builder builder, 025 PrometheusProperties config, 026 PrometheusRegistry registry) { 027 AtomicReference<MetricReader> readerRef = new AtomicReference<>(); 028 InstrumentationScopeInfo instrumentationScopeInfo = 029 PrometheusInstrumentationScope.loadInstrumentationScopeInfo(); 030 031 AutoConfiguredOpenTelemetrySdk sdk = 032 createAutoConfiguredOpenTelemetrySdk( 033 builder, 034 readerRef, 035 config.getExporterOpenTelemetryProperties(), 036 instrumentationScopeInfo); 037 038 MetricReader reader = readerRef.get(); 039 reader.register( 040 new PrometheusMetricProducer(registry, instrumentationScopeInfo, getResourceField(sdk))); 041 return reader; 042 } 043 044 static AutoConfiguredOpenTelemetrySdk createAutoConfiguredOpenTelemetrySdk( 045 OpenTelemetryExporter.Builder builder, 046 AtomicReference<MetricReader> readerRef, 047 ExporterOpenTelemetryProperties properties, 048 InstrumentationScopeInfo instrumentationScopeInfo) { 049 PropertyMapper propertyMapper = PropertyMapper.create(properties, builder); 050 051 return AutoConfiguredOpenTelemetrySdk.builder() 052 .addPropertiesSupplier(() -> propertyMapper.configLowPriority) 053 .addPropertiesCustomizer( 054 c -> PropertyMapper.customizeProperties(propertyMapper.configHighPriority, c)) 055 .addMetricReaderCustomizer( 056 (reader, unused) -> { 057 readerRef.set(reader); 058 return reader; 059 }) 060 .addResourceCustomizer( 061 (resource, c) -> 062 getResource(builder, resource, instrumentationScopeInfo, c, properties)) 063 .build(); 064 } 065 066 private static Resource getResource( 067 OpenTelemetryExporter.Builder builder, 068 Resource resource, 069 InstrumentationScopeInfo instrumentationScopeInfo, 070 ConfigProperties configProperties, 071 ExporterOpenTelemetryProperties properties) { 072 return resource 073 .merge( 074 PropertiesResourceProvider.mergeResource( 075 builder.resourceAttributes, 076 builder.serviceName, 077 builder.serviceNamespace, 078 builder.serviceInstanceId, 079 builder.serviceVersion)) 080 .merge(ResourceConfiguration.createEnvironmentResource(configProperties)) 081 .merge( 082 PropertiesResourceProvider.mergeResource( 083 properties.getResourceAttributes(), 084 properties.getServiceName(), 085 properties.getServiceNamespace(), 086 properties.getServiceInstanceId(), 087 properties.getServiceVersion())) 088 .merge(Resource.create(otelResourceAttributes(instrumentationScopeInfo))); 089 } 090 091 /** 092 * Only copy the service instance id from the Otel agent resource attributes. 093 * 094 * <p>All other attributes are calculated from the configuration using OTel SDK AutoConfig. 095 */ 096 private static Attributes otelResourceAttributes( 097 InstrumentationScopeInfo instrumentationScopeInfo) { 098 AttributesBuilder builder = Attributes.builder(); 099 Map<String, String> attributes = 100 ResourceAttributesFromOtelAgent.getResourceAttributes(instrumentationScopeInfo.getName()); 101 String id = attributes.get(SERVICE_INSTANCE_ID); 102 if (id != null) { 103 builder.put(SERVICE_INSTANCE_ID, id); 104 } 105 return builder.build(); 106 } 107 108 static Resource getResourceField(AutoConfiguredOpenTelemetrySdk sdk) { 109 try { 110 Method method = AutoConfiguredOpenTelemetrySdk.class.getDeclaredMethod("getResource"); 111 method.setAccessible(true); 112 return (Resource) method.invoke(sdk); 113 } catch (Exception e) { 114 throw new RuntimeException(e); 115 } 116 } 117}