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