io.jaegertracing.Configuration Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jaeger-core Show documentation
Show all versions of jaeger-core Show documentation
Jaeger Java bindings for OpenTracing API
/*
* Copyright (c) 2018, The Jaeger Authors
* Copyright (c) 2016, Uber Technologies, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package io.jaegertracing;
import io.jaegertracing.internal.JaegerTracer;
import io.jaegertracing.internal.metrics.Metrics;
import io.jaegertracing.internal.metrics.NoopMetricsFactory;
import io.jaegertracing.internal.propagation.B3TextMapCodec;
import io.jaegertracing.internal.propagation.CompositeCodec;
import io.jaegertracing.internal.propagation.TextMapCodec;
import io.jaegertracing.internal.reporters.CompositeReporter;
import io.jaegertracing.internal.reporters.LoggingReporter;
import io.jaegertracing.internal.reporters.RemoteReporter;
import io.jaegertracing.internal.samplers.ConstSampler;
import io.jaegertracing.internal.samplers.HttpSamplingManager;
import io.jaegertracing.internal.samplers.ProbabilisticSampler;
import io.jaegertracing.internal.samplers.RateLimitingSampler;
import io.jaegertracing.internal.samplers.RemoteControlledSampler;
import io.jaegertracing.internal.senders.SenderResolver;
import io.jaegertracing.spi.Codec;
import io.jaegertracing.spi.MetricsFactory;
import io.jaegertracing.spi.Reporter;
import io.jaegertracing.spi.Sampler;
import io.jaegertracing.spi.Sender;
import io.jaegertracing.spi.SenderFactory;
import io.opentracing.propagation.Format;
import io.opentracing.propagation.TextMap;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
/**
* This class is designed to provide {@link JaegerTracer} or {@link JaegerTracer.Builder} when Jaeger client
* configuration is provided in environmental or property variables. It also simplifies creation
* of the client from configuration files.
*/
@Slf4j
public class Configuration {
/**
* Prefix for all properties used to configure the Jaeger tracer.
*/
public static final String JAEGER_PREFIX = "JAEGER_";
/**
* The full URL to the {@code traces} endpoint, like https://jaeger-collector:14268/api/traces
*/
public static final String JAEGER_ENDPOINT = JAEGER_PREFIX + "ENDPOINT";
/**
* The Auth Token to be added as "Bearer" on Authorization headers for requests sent to the endpoint
*/
public static final String JAEGER_AUTH_TOKEN = JAEGER_PREFIX + "AUTH_TOKEN";
/**
* The Basic Auth username to be added on Authorization headers for requests sent to the endpoint
*/
public static final String JAEGER_USER = JAEGER_PREFIX + "USER";
/**
* The Basic Auth password to be added on Authorization headers for requests sent to the endpoint
*/
public static final String JAEGER_PASSWORD = JAEGER_PREFIX + "PASSWORD";
/**
* The host name used to locate the agent.
*/
public static final String JAEGER_AGENT_HOST = JAEGER_PREFIX + "AGENT_HOST";
/**
* The port used to locate the agent.
*/
public static final String JAEGER_AGENT_PORT = JAEGER_PREFIX + "AGENT_PORT";
/**
* Whether the reporter should log the spans.
*/
public static final String JAEGER_REPORTER_LOG_SPANS = JAEGER_PREFIX + "REPORTER_LOG_SPANS";
/**
* The maximum queue size for use when reporting spans remotely.
*/
public static final String JAEGER_REPORTER_MAX_QUEUE_SIZE = JAEGER_PREFIX + "REPORTER_MAX_QUEUE_SIZE";
/**
* The flush interval when reporting spans remotely.
*/
public static final String JAEGER_REPORTER_FLUSH_INTERVAL = JAEGER_PREFIX + "REPORTER_FLUSH_INTERVAL";
/**
* The sampler type.
*/
public static final String JAEGER_SAMPLER_TYPE = JAEGER_PREFIX + "SAMPLER_TYPE";
/**
* The sampler parameter (number).
*/
public static final String JAEGER_SAMPLER_PARAM = "JAEGER_SAMPLER_PARAM";
/**
* The sampler manager host:port.
*/
public static final String JAEGER_SAMPLER_MANAGER_HOST_PORT = JAEGER_PREFIX + "SAMPLER_MANAGER_HOST_PORT";
/**
* The service name.
*/
public static final String JAEGER_SERVICE_NAME = JAEGER_PREFIX + "SERVICE_NAME";
/**
* The tracer level tags.
*/
public static final String JAEGER_TAGS = JAEGER_PREFIX + "TAGS";
/**
* Comma separated list of formats to use for propagating the trace context. Default will the
* standard Jaeger format. Valid values are jaeger and b3.
*/
public static final String JAEGER_PROPAGATION = JAEGER_PREFIX + "PROPAGATION";
/**
* When there are multiple service providers for the {@link SenderFactory} available,
* this var is used to select a {@link SenderFactory} by matching it with
* {@link SenderFactory#getType()}.
*
*/
public static final String JAEGER_SENDER_FACTORY = JAEGER_PREFIX + "SENDER_FACTORY";
/**
* The supported trace context propagation formats.
*/
public enum Propagation {
/**
* The default Jaeger trace context propagation format.
*/
JAEGER,
/**
* The Zipkin B3 trace context propagation format.
*/
B3
}
/**
* The serviceName that the tracer will use
*/
private String serviceName;
private SamplerConfiguration samplerConfig;
private ReporterConfiguration reporterConfig;
private CodecConfiguration codecConfig;
private MetricsFactory metricsFactory;
private Map tracerTags;
/**
* lazy singleton JaegerTracer initialized in getTracer() method.
*/
private JaegerTracer tracer;
public Configuration(String serviceName) {
this.serviceName = JaegerTracer.Builder.checkValidServiceName(serviceName);
}
/**
* @return Configuration object from environmental variables
*/
public static Configuration fromEnv() {
return Configuration.fromEnv(getProperty(JAEGER_SERVICE_NAME));
}
public static Configuration fromEnv(String serviceName) {
return new Configuration(serviceName)
.withTracerTags(tracerTagsFromEnv())
.withReporter(ReporterConfiguration.fromEnv())
.withSampler(SamplerConfiguration.fromEnv())
.withCodec(CodecConfiguration.fromEnv());
}
public JaegerTracer.Builder getTracerBuilder() {
if (reporterConfig == null) {
reporterConfig = new ReporterConfiguration();
}
if (samplerConfig == null) {
samplerConfig = new SamplerConfiguration();
}
if (codecConfig == null) {
codecConfig = new CodecConfiguration();
}
if (metricsFactory == null) {
metricsFactory = loadMetricsFactory();
}
Metrics metrics = new Metrics(metricsFactory);
Reporter reporter = reporterConfig.getReporter(metrics);
Sampler sampler = samplerConfig.createSampler(serviceName, metrics);
JaegerTracer.Builder builder = createTracerBuilder(serviceName)
.withSampler(sampler)
.withReporter(reporter)
.withMetrics(metrics)
.withTags(tracerTags);
codecConfig.apply(builder);
return builder;
}
protected JaegerTracer.Builder createTracerBuilder(String serviceName) {
return new JaegerTracer.Builder(serviceName);
}
public synchronized JaegerTracer getTracer() {
if (tracer != null) {
return tracer;
}
tracer = getTracerBuilder().build();
log.info("Initialized tracer={}", tracer);
return tracer;
}
public synchronized void closeTracer() {
if (tracer != null) {
tracer.close();
}
}
private MetricsFactory loadMetricsFactory() {
ServiceLoader loader = ServiceLoader.load(MetricsFactory.class);
Iterator iterator = loader.iterator();
if (iterator.hasNext()) {
MetricsFactory metricsFactory = iterator.next();
log.debug("Found a Metrics Factory service: {}", metricsFactory.getClass());
return metricsFactory;
}
return new NoopMetricsFactory();
}
/**
* @param metricsFactory the MetricsFactory to use on the JaegerTracer to be built
*/
public Configuration withMetricsFactory(MetricsFactory metricsFactory) {
this.metricsFactory = metricsFactory;
return this;
}
public Configuration withServiceName(String serviceName) {
this.serviceName = JaegerTracer.Builder.checkValidServiceName(serviceName);
return this;
}
public Configuration withReporter(ReporterConfiguration reporterConfig) {
this.reporterConfig = reporterConfig;
return this;
}
public Configuration withSampler(SamplerConfiguration samplerConfig) {
this.samplerConfig = samplerConfig;
return this;
}
public Configuration withCodec(CodecConfiguration codecConfig) {
this.codecConfig = codecConfig;
return this;
}
public Configuration withTracerTags(Map tracerTags) {
if (tracerTags != null) {
this.tracerTags = new HashMap(tracerTags);
}
return this;
}
public String getServiceName() {
return serviceName;
}
public ReporterConfiguration getReporter() {
return reporterConfig;
}
public SamplerConfiguration getSampler() {
return samplerConfig;
}
public CodecConfiguration getCodec() {
return codecConfig;
}
public MetricsFactory getMetricsFactory() {
return metricsFactory;
}
public Map getTracerTags() {
return tracerTags == null ? null : Collections.unmodifiableMap(tracerTags);
}
/**
* SamplerConfiguration allows to configure which sampler the tracer will use.
*/
public static class SamplerConfiguration {
/**
* The type of sampler to use in the tracer. Optional. Valid values: remote (default),
* ratelimiting, probabilistic, const.
*/
private String type;
/**
* The integer or floating point value that makes sense for the correct samplerType. Optional.
*/
private Number param;
/**
* HTTP host:port of the sampling manager that can provide sampling strategy to this service.
* Optional.
*/
private String managerHostPort;
public SamplerConfiguration() {
}
public static SamplerConfiguration fromEnv() {
return new SamplerConfiguration()
.withType(getProperty(JAEGER_SAMPLER_TYPE))
.withParam(getPropertyAsNum(JAEGER_SAMPLER_PARAM))
.withManagerHostPort(getProperty(JAEGER_SAMPLER_MANAGER_HOST_PORT));
}
// for tests
Sampler createSampler(String serviceName, Metrics metrics) {
String samplerType = stringOrDefault(this.getType(), RemoteControlledSampler.TYPE);
Number samplerParam = numberOrDefault(this.getParam(), ProbabilisticSampler.DEFAULT_SAMPLING_PROBABILITY);
String hostPort = stringOrDefault(this.getManagerHostPort(), HttpSamplingManager.DEFAULT_HOST_PORT);
if (samplerType.equals(ConstSampler.TYPE)) {
return new ConstSampler(samplerParam.intValue() != 0);
}
if (samplerType.equals(ProbabilisticSampler.TYPE)) {
return new ProbabilisticSampler(samplerParam.doubleValue());
}
if (samplerType.equals(RateLimitingSampler.TYPE)) {
return new RateLimitingSampler(samplerParam.intValue());
}
if (samplerType.equals(RemoteControlledSampler.TYPE)) {
return new RemoteControlledSampler.Builder(serviceName)
.withSamplingManager(new HttpSamplingManager(hostPort))
.withInitialSampler(new ProbabilisticSampler(samplerParam.doubleValue()))
.withMetrics(metrics)
.build();
}
throw new IllegalStateException(String.format("Invalid sampling strategy %s", samplerType));
}
public String getType() {
return type;
}
public Number getParam() {
return param;
}
public String getManagerHostPort() {
return managerHostPort;
}
public SamplerConfiguration withType(String type) {
this.type = type;
return this;
}
public SamplerConfiguration withParam(Number param) {
this.param = param;
return this;
}
public SamplerConfiguration withManagerHostPort(String managerHostPort) {
this.managerHostPort = managerHostPort;
return this;
}
}
/**
* CodecConfiguration can be used to support additional trace context propagation codec.
*/
public static class CodecConfiguration {
private final Map, List>> codecs;
public CodecConfiguration() {
codecs = new HashMap, List>>();
}
private CodecConfiguration(Map, List>> codecs) {
this.codecs = codecs;
}
public static CodecConfiguration fromEnv() {
Map, List>> codecs = new HashMap, List>>();
String propagation = getProperty(JAEGER_PROPAGATION);
if (propagation != null) {
for (String format : Arrays.asList(propagation.split(","))) {
try {
switch (Configuration.Propagation.valueOf(format.toUpperCase())) {
case JAEGER:
addCodec(codecs, Format.Builtin.HTTP_HEADERS, new TextMapCodec(true));
addCodec(codecs, Format.Builtin.TEXT_MAP, new TextMapCodec(false));
break;
case B3:
addCodec(codecs, Format.Builtin.HTTP_HEADERS, new B3TextMapCodec.Builder().build());
addCodec(codecs, Format.Builtin.TEXT_MAP, new B3TextMapCodec.Builder().build());
break;
default:
log.error("Unhandled propagation format '" + format + "'");
break;
}
} catch (IllegalArgumentException iae) {
log.error("Unknown propagation format '" + format + "'");
}
}
}
return new CodecConfiguration(codecs);
}
public CodecConfiguration withCodec(Format> format, Codec codec) {
addCodec(codecs, format, codec);
return this;
}
public Map, List>> getCodecs() {
return Collections.unmodifiableMap(codecs);
}
private static void addCodec(Map, List>> codecs, Format> format, Codec codec) {
List> codecList = codecs.get(format);
if (codecList == null) {
codecList = new LinkedList>();
codecs.put(format, codecList);
}
codecList.add(codec);
}
public void apply(JaegerTracer.Builder builder) {
// Replace existing TEXT_MAP and HTTP_HEADERS codec with one that represents the
// configured propagation formats
registerCodec(builder, Format.Builtin.HTTP_HEADERS);
registerCodec(builder, Format.Builtin.TEXT_MAP);
}
protected void registerCodec(JaegerTracer.Builder builder, Format format) {
if (codecs.containsKey(format)) {
List> codecsForFormat = codecs.get(format);
Codec codec = codecsForFormat.size() == 1
? codecsForFormat.get(0) : new CompositeCodec(codecsForFormat);
builder.registerInjector(format, codec);
builder.registerExtractor(format, codec);
}
}
}
public static class ReporterConfiguration {
private Boolean logSpans;
private Integer flushIntervalMs;
private Integer maxQueueSize;
private SenderConfiguration senderConfiguration = new SenderConfiguration();
public ReporterConfiguration() {
}
public static ReporterConfiguration fromEnv() {
return new ReporterConfiguration()
.withLogSpans(getPropertyAsBool(JAEGER_REPORTER_LOG_SPANS))
.withFlushInterval(getPropertyAsInt(JAEGER_REPORTER_FLUSH_INTERVAL))
.withMaxQueueSize(getPropertyAsInt(JAEGER_REPORTER_MAX_QUEUE_SIZE))
.withSender(SenderConfiguration.fromEnv());
}
public ReporterConfiguration withLogSpans(Boolean logSpans) {
this.logSpans = logSpans;
return this;
}
public ReporterConfiguration withFlushInterval(Integer flushIntervalMs) {
this.flushIntervalMs = flushIntervalMs;
return this;
}
public ReporterConfiguration withMaxQueueSize(Integer maxQueueSize) {
this.maxQueueSize = maxQueueSize;
return this;
}
public ReporterConfiguration withSender(SenderConfiguration senderConfiguration) {
this.senderConfiguration = senderConfiguration;
return this;
}
private Reporter getReporter(Metrics metrics) {
Reporter reporter = new RemoteReporter.Builder()
.withMetrics(metrics)
.withSender(senderConfiguration.getSender())
.withFlushInterval(numberOrDefault(this.flushIntervalMs, RemoteReporter.DEFAULT_FLUSH_INTERVAL_MS).intValue())
.withMaxQueueSize(numberOrDefault(this.maxQueueSize, RemoteReporter.DEFAULT_MAX_QUEUE_SIZE).intValue())
.build();
if (Boolean.TRUE.equals(this.logSpans)) {
Reporter loggingReporter = new LoggingReporter();
reporter = new CompositeReporter(reporter, loggingReporter);
}
return reporter;
}
public Boolean getLogSpans() {
return logSpans;
}
public Integer getFlushIntervalMs() {
return flushIntervalMs;
}
public Integer getMaxQueueSize() {
return maxQueueSize;
}
public SenderConfiguration getSenderConfiguration() {
return senderConfiguration;
}
}
/**
* Holds the configuration related to the sender. A sender is resolved using a {@link SenderResolver}.
*
*/
@Getter
public static class SenderConfiguration {
/**
* A custom sender set by our consumers. If set, nothing else has effect. Optional.
*/
private Sender sender;
/**
* The Agent Host. Has no effect if the sender is set. Optional.
*/
private String agentHost;
/**
* The Agent Port. Has no effect if the sender is set. Optional.
*/
private Integer agentPort;
/**
* The endpoint, like https://jaeger-collector:14268/api/traces
*/
private String endpoint;
/**
* The Auth Token to be added as "Bearer" on Authorization headers for requests sent to the endpoint
*/
private String authToken;
/**
* The Basic Auth username to be added on Authorization headers for requests sent to the endpoint
*/
private String authUsername;
/**
* The Basic Auth password to be added on Authorization headers for requests sent to the endpoint
*/
private String authPassword;
public SenderConfiguration() {
}
public SenderConfiguration withAgentHost(String agentHost) {
this.agentHost = agentHost;
return this;
}
public SenderConfiguration withAgentPort(Integer agentPort) {
this.agentPort = agentPort;
return this;
}
public SenderConfiguration withEndpoint(String endpoint) {
this.endpoint = endpoint;
return this;
}
public SenderConfiguration withAuthToken(String authToken) {
this.authToken = authToken;
return this;
}
public SenderConfiguration withAuthUsername(String username) {
this.authUsername = username;
return this;
}
public SenderConfiguration withAuthPassword(String password) {
this.authPassword = password;
return this;
}
/**
* Returns a sender if one was given when creating the configuration, or attempts to create a sender based on the
* configuration's state.
* @return the sender passed via the constructor or a properly configured sender
*/
public Sender getSender() {
// if we have a sender, that's the one we return
if (null != sender) {
return sender;
}
return SenderResolver.resolve(this);
}
/**
* Attempts to create a new {@link SenderConfiguration} based on the environment variables
* @return a new sender configuration based on environment variables
*/
public static SenderConfiguration fromEnv() {
String agentHost = getProperty(JAEGER_AGENT_HOST);
Integer agentPort = getPropertyAsInt(JAEGER_AGENT_PORT);
String collectorEndpoint = getProperty(JAEGER_ENDPOINT);
String authToken = getProperty(JAEGER_AUTH_TOKEN);
String authUsername = getProperty(JAEGER_USER);
String authPassword = getProperty(JAEGER_PASSWORD);
return new SenderConfiguration()
.withAgentHost(agentHost)
.withAgentPort(agentPort)
.withEndpoint(collectorEndpoint)
.withAuthToken(authToken)
.withAuthUsername(authUsername)
.withAuthPassword(authPassword);
}
}
private static String stringOrDefault(String value, String defaultValue) {
return value != null && value.length() > 0 ? value : defaultValue;
}
private static Number numberOrDefault(Number value, Number defaultValue) {
return value != null ? value : defaultValue;
}
private static String getProperty(String name) {
return System.getProperty(name, System.getenv(name));
}
private static Integer getPropertyAsInt(String name) {
String value = getProperty(name);
if (value != null) {
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
log.error("Failed to parse integer for property '" + name + "' with value '" + value + "'", e);
}
}
return null;
}
private static Number getPropertyAsNum(String name) {
String value = getProperty(name);
if (value != null) {
try {
return NumberFormat.getInstance().parse(value);
} catch (ParseException e) {
log.error("Failed to parse number for property '" + name + "' with value '" + value + "'", e);
}
}
return null;
}
/**
* Gets the system property defined by the name , and returns a boolean value represented by
* the name. This method defaults to returning false for a name that doesn't exist.
* @param name The name of the system property
*/
private static boolean getPropertyAsBool(String name) {
return Boolean.valueOf(getProperty(name));
}
private static Map tracerTagsFromEnv() {
Map tracerTagMaps = null;
String tracerTags = getProperty(JAEGER_TAGS);
if (tracerTags != null) {
String[] tags = tracerTags.split("\\s*,\\s*");
for (String tag : tags) {
String[] tagValue = tag.split("\\s*=\\s*");
if (tagValue.length == 2) {
if (tracerTagMaps == null) {
tracerTagMaps = new HashMap();
}
tracerTagMaps.put(tagValue[0], resolveValue(tagValue[1]));
} else {
log.error("Tracer tag incorrectly formatted: " + tag);
}
}
}
return tracerTagMaps;
}
private static String resolveValue(String value) {
if (value.startsWith("${") && value.endsWith("}")) {
String[] ref = value.substring(2, value.length() - 1).split("\\s*:\\s*");
if (ref.length > 0) {
String propertyValue = getProperty(ref[0]);
if (propertyValue == null && ref.length > 1) {
propertyValue = ref[1];
}
return propertyValue;
}
}
return value;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy