opentelemetry-java-instrume.../javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/TracerInstaller.java

220 lines
8.6 KiB
Java

/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.tooling;
import io.opentelemetry.instrumentation.api.config.Config;
import io.opentelemetry.javaagent.spi.TracerCustomizer;
import io.opentelemetry.javaagent.spi.exporter.MetricExporterFactory;
import io.opentelemetry.javaagent.spi.exporter.MetricServer;
import io.opentelemetry.javaagent.spi.exporter.SpanExporterFactory;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.metrics.export.IntervalMetricReader;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import io.opentelemetry.sdk.metrics.export.MetricProducer;
import io.opentelemetry.sdk.trace.TracerSdkManagement;
import io.opentelemetry.sdk.trace.config.TraceConfig;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.ServiceLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TracerInstaller {
private static final Logger log = LoggerFactory.getLogger(TracerInstaller.class);
private static final String EXPORTER_JAR_CONFIG = "otel.exporter.jar";
private static final String EXPORTERS_CONFIG = "otel.exporter";
private static final String PROPAGATORS_CONFIG = "otel.propagators";
private static final String TRACE_ENABLED_CONFIG = "otel.trace.enabled";
private static final List<String> DEFAULT_EXPORTERS = Collections.singletonList("otlp");
/** Register agent tracer if no agent tracer is already registered. */
@SuppressWarnings("unused")
public static synchronized void installAgentTracer() {
if (Config.get().getBooleanProperty(TRACE_ENABLED_CONFIG, true)) {
Properties config = Config.get().asJavaProperties();
configure(config);
// Try to create an exporter from external jar file
String exporterJar = Config.get().getProperty(EXPORTER_JAR_CONFIG);
if (exporterJar != null) {
installExportersFromJar(exporterJar, config);
} else {
// Try to create embedded exporter
installExporters(Config.get().getListProperty(EXPORTERS_CONFIG, DEFAULT_EXPORTERS), config);
}
} else {
log.info("Tracing is disabled.");
}
PropagatorsInitializer.initializePropagators(Config.get().getListProperty(PROPAGATORS_CONFIG));
}
private static synchronized void installExporters(List<String> exporters, Properties config) {
for (String exporterName : exporters) {
SpanExporterFactory spanExporterFactory = findSpanExporterFactory(exporterName);
if (spanExporterFactory != null) {
installExporter(spanExporterFactory, config);
} else {
log.warn("No {} span exporter found", exporterName);
}
MetricExporterFactory metricExporterFactory = findMetricExporterFactory(exporterName);
if (metricExporterFactory != null) {
installExporter(metricExporterFactory, config);
} else {
log.debug("No {} metric exporter found", exporterName);
}
MetricServer metricServer = findMetricServer(exporterName);
if (metricServer != null) {
installMetricServer(metricServer, config);
} else {
log.debug("No {} metric server found", exporterName);
}
}
}
private static MetricExporterFactory findMetricExporterFactory(String exporterName) {
ServiceLoader<MetricExporterFactory> serviceLoader =
ServiceLoader.load(MetricExporterFactory.class, TracerInstaller.class.getClassLoader());
for (MetricExporterFactory metricExporterFactory : serviceLoader) {
if (metricExporterFactory.getNames().contains(exporterName)) {
return metricExporterFactory;
}
}
return null;
}
private static MetricServer findMetricServer(String exporterName) {
ServiceLoader<MetricServer> serviceLoader =
ServiceLoader.load(MetricServer.class, TracerInstaller.class.getClassLoader());
for (MetricServer metricServer : serviceLoader) {
if (metricServer.getNames().contains(exporterName)) {
return metricServer;
}
}
return null;
}
private static SpanExporterFactory findSpanExporterFactory(String exporterName) {
ServiceLoader<SpanExporterFactory> serviceLoader =
ServiceLoader.load(SpanExporterFactory.class, TracerInstaller.class.getClassLoader());
for (SpanExporterFactory spanExporterFactory : serviceLoader) {
if (spanExporterFactory.getNames().contains(exporterName)) {
return spanExporterFactory;
}
}
return null;
}
private static synchronized void installExportersFromJar(String exporterJar, Properties config) {
URL url;
try {
url = new File(exporterJar).toURI().toURL();
} catch (MalformedURLException e) {
log.warn("Filename could not be parsed: " + exporterJar + ". Exporter is not installed");
log.warn("No valid exporter found. Tracing will run but spans are dropped");
return;
}
ExporterClassLoader exporterLoader =
new ExporterClassLoader(url, TracerInstaller.class.getClassLoader());
SpanExporterFactory spanExporterFactory =
getExporterFactory(SpanExporterFactory.class, exporterLoader);
if (spanExporterFactory != null) {
installExporter(spanExporterFactory, config);
} else {
log.warn("No span exporter found in {}", exporterJar);
log.warn("No valid exporter found. Tracing will run but spans are dropped");
}
MetricExporterFactory metricExporterFactory =
getExporterFactory(MetricExporterFactory.class, exporterLoader);
if (metricExporterFactory != null) {
installExporter(metricExporterFactory, config);
}
}
private static void installExporter(
MetricExporterFactory metricExporterFactory, Properties config) {
MetricExporter metricExporter = metricExporterFactory.fromConfig(config);
IntervalMetricReader.builder()
.readProperties(config)
.setMetricExporter(metricExporter)
.setMetricProducers(
Collections.singleton(OpenTelemetrySdk.getGlobalMeterProvider().getMetricProducer()))
.build();
log.info("Installed metric exporter: " + metricExporter.getClass().getName());
}
private static void installExporter(SpanExporterFactory spanExporterFactory, Properties config) {
SpanExporter spanExporter = spanExporterFactory.fromConfig(config);
BatchSpanProcessor spanProcessor =
BatchSpanProcessor.builder(spanExporter).readProperties(config).build();
OpenTelemetrySdk.getGlobalTracerManagement().addSpanProcessor(spanProcessor);
log.info("Installed span exporter: " + spanExporter.getClass().getName());
}
private static void installMetricServer(MetricServer metricServer, Properties config) {
MetricProducer metricProducer = OpenTelemetrySdk.getGlobalMeterProvider().getMetricProducer();
metricServer.start(metricProducer, config);
log.info("Installed metric server: " + metricServer.getClass().getName());
}
private static <F> F getExporterFactory(Class<F> service, ExporterClassLoader exporterLoader) {
ServiceLoader<F> serviceLoader = ServiceLoader.load(service, exporterLoader);
Iterator<F> i = serviceLoader.iterator();
if (i.hasNext()) {
F factory = i.next();
if (i.hasNext()) {
log.warn(
"Exporter JAR defines more than one {}. Only the first one found will be used",
service.getName());
}
return factory;
}
return null;
}
private static void configure(Properties config) {
TracerSdkManagement tracerManagement = OpenTelemetrySdk.getGlobalTracerManagement();
// Register additional thread details logging span processor
tracerManagement.addSpanProcessor(new AddThreadDetailsSpanProcessor());
// Execute any user-provided (usually vendor-provided) configuration logic.
ServiceLoader<TracerCustomizer> serviceLoader =
ServiceLoader.load(TracerCustomizer.class, TracerInstaller.class.getClassLoader());
for (TracerCustomizer customizer : serviceLoader) {
customizer.configure(tracerManagement);
}
/* Update trace config from env vars or sys props */
TraceConfig activeTraceConfig = tracerManagement.getActiveTraceConfig();
tracerManagement.updateActiveTraceConfig(
activeTraceConfig.toBuilder().readProperties(config).build());
}
@SuppressWarnings("unused")
public static void logVersionInfo() {
VersionLogger.logAllVersions();
log.debug(
AgentInstaller.class.getName() + " loaded on " + AgentInstaller.class.getClassLoader());
}
}