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