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 PrometheusMetricProducer.builder( 044 registry, instrumentationScopeInfo, getResourceField(sdk), preserveNames) 045 .exporterFilterProperties(config.getExporterFilterProperties()) 046 .build()); 047 return reader; 048 } 049 050 static AutoConfiguredOpenTelemetrySdk createAutoConfiguredOpenTelemetrySdk( 051 OpenTelemetryExporter.Builder builder, 052 AtomicReference<MetricReader> readerRef, 053 ExporterOpenTelemetryProperties properties, 054 InstrumentationScopeInfo instrumentationScopeInfo) { 055 PropertyMapper propertyMapper = PropertyMapper.create(properties, builder); 056 057 return AutoConfiguredOpenTelemetrySdk.builder() 058 .addPropertiesSupplier(() -> propertyMapper.configLowPriority) 059 .addPropertiesCustomizer( 060 c -> PropertyMapper.customizeProperties(propertyMapper.configHighPriority, c)) 061 .addMetricReaderCustomizer( 062 (reader, unused) -> { 063 readerRef.set(reader); 064 return reader; 065 }) 066 .addResourceCustomizer( 067 (resource, c) -> 068 getResource(builder, resource, instrumentationScopeInfo, c, properties)) 069 .build(); 070 } 071 072 private static Resource getResource( 073 OpenTelemetryExporter.Builder builder, 074 Resource resource, 075 InstrumentationScopeInfo instrumentationScopeInfo, 076 ConfigProperties configProperties, 077 ExporterOpenTelemetryProperties properties) { 078 return resource 079 .merge( 080 PropertiesResourceProvider.mergeResource( 081 builder.resourceAttributes, 082 builder.serviceName, 083 builder.serviceNamespace, 084 builder.serviceInstanceId, 085 builder.serviceVersion)) 086 .merge(ResourceConfiguration.createEnvironmentResource(configProperties)) 087 .merge( 088 PropertiesResourceProvider.mergeResource( 089 properties.getResourceAttributes(), 090 properties.getServiceName(), 091 properties.getServiceNamespace(), 092 properties.getServiceInstanceId(), 093 properties.getServiceVersion())) 094 .merge(Resource.create(otelResourceAttributes(instrumentationScopeInfo))); 095 } 096 097 /** 098 * Only copy the service instance id from the Otel agent resource attributes. 099 * 100 * <p>All other attributes are calculated from the configuration using OTel SDK AutoConfig. 101 */ 102 private static Attributes otelResourceAttributes( 103 InstrumentationScopeInfo instrumentationScopeInfo) { 104 AttributesBuilder builder = Attributes.builder(); 105 Map<String, String> attributes = 106 ResourceAttributesFromOtelAgent.getResourceAttributes(instrumentationScopeInfo.getName()); 107 String id = attributes.get(SERVICE_INSTANCE_ID); 108 if (id != null) { 109 builder.put(SERVICE_INSTANCE_ID, id); 110 } 111 return builder.build(); 112 } 113 114 static boolean resolvePreserveNames( 115 OpenTelemetryExporter.Builder builder, PrometheusProperties config) { 116 if (builder.preserveNames != null) { 117 return builder.preserveNames; 118 } 119 Boolean fromConfig = config.getExporterOpenTelemetryProperties().getPreserveNames(); 120 return fromConfig != null && fromConfig; 121 } 122 123 static Resource getResourceField(AutoConfiguredOpenTelemetrySdk sdk) { 124 try { 125 Method method = AutoConfiguredOpenTelemetrySdk.class.getDeclaredMethod("getResource"); 126 method.setAccessible(true); 127 return (Resource) method.invoke(sdk); 128 } catch (ReflectiveOperationException e) { 129 throw new RuntimeException(e); 130 } 131 } 132}