io.jaegertracing.internal.JaegerTracer 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.internal;
import io.jaegertracing.internal.baggage.BaggageSetter;
import io.jaegertracing.internal.baggage.DefaultBaggageRestrictionManager;
import io.jaegertracing.internal.clock.Clock;
import io.jaegertracing.internal.clock.SystemClock;
import io.jaegertracing.internal.exceptions.EmptyIpException;
import io.jaegertracing.internal.exceptions.NotFourOctetsException;
import io.jaegertracing.internal.exceptions.UnsupportedFormatException;
import io.jaegertracing.internal.metrics.Metrics;
import io.jaegertracing.internal.metrics.NoopMetricsFactory;
import io.jaegertracing.internal.propagation.TextMapCodec;
import io.jaegertracing.internal.reporters.RemoteReporter;
import io.jaegertracing.internal.samplers.RemoteControlledSampler;
import io.jaegertracing.internal.samplers.SamplingStatus;
import io.jaegertracing.internal.utils.Utils;
import io.jaegertracing.spi.BaggageRestrictionManager;
import io.jaegertracing.spi.Extractor;
import io.jaegertracing.spi.Injector;
import io.jaegertracing.spi.MetricsFactory;
import io.jaegertracing.spi.Reporter;
import io.jaegertracing.spi.Sampler;
import io.opentracing.References;
import io.opentracing.Scope;
import io.opentracing.ScopeManager;
import io.opentracing.Span;
import io.opentracing.SpanContext;
import io.opentracing.Tracer;
import io.opentracing.propagation.Format;
import io.opentracing.tag.Tags;
import io.opentracing.util.ThreadLocalScopeManager;
import java.io.Closeable;
import java.io.InputStream;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
@ToString
@Slf4j
public class JaegerTracer implements Tracer, Closeable {
private final String version;
private final String serviceName;
private final Reporter reporter;
private final Sampler sampler;
private final Map tags;
private final boolean zipkinSharedRpcSpan;
private final boolean expandExceptionLogs;
@ToString.Exclude private final PropagationRegistry registry;
@ToString.Exclude private final Clock clock;
@ToString.Exclude private final Metrics metrics;
@ToString.Exclude private final ScopeManager scopeManager;
@ToString.Exclude private final BaggageSetter baggageSetter;
@ToString.Exclude private final JaegerObjectFactory objectFactory;
@ToString.Exclude private final int ipv4; // human readable representation is present within the tag map
protected JaegerTracer(JaegerTracer.Builder builder) {
this.serviceName = builder.serviceName;
this.reporter = builder.reporter;
this.sampler = builder.sampler;
this.registry = builder.registry;
this.clock = builder.clock;
this.metrics = builder.metrics;
this.zipkinSharedRpcSpan = builder.zipkinSharedRpcSpan;
this.scopeManager = builder.scopeManager;
this.baggageSetter = new BaggageSetter(builder.baggageRestrictionManager, metrics);
this.expandExceptionLogs = builder.expandExceptionLogs;
this.objectFactory = builder.objectFactory;
this.version = loadVersion();
Map tags = new HashMap(builder.tags);
tags.put(Constants.JAEGER_CLIENT_VERSION_TAG_KEY, this.version);
if (tags.get(Constants.TRACER_HOSTNAME_TAG_KEY) == null) {
String hostname = getHostName();
if (hostname != null) {
tags.put(Constants.TRACER_HOSTNAME_TAG_KEY, hostname);
}
}
int ipv4;
Object ipTag = tags.get(Constants.TRACER_IP_TAG_KEY);
if (ipTag == null) {
try {
tags.put(Constants.TRACER_IP_TAG_KEY, InetAddress.getLocalHost().getHostAddress());
ipv4 = Utils.ipToInt(Inet4Address.getLocalHost().getHostAddress());
} catch (UnknownHostException e) {
ipv4 = 0;
}
} else {
try {
ipv4 = Utils.ipToInt(ipTag.toString());
} catch (EmptyIpException e) {
ipv4 = 0;
} catch (NotFourOctetsException e) {
ipv4 = 0;
}
}
this.ipv4 = ipv4;
this.tags = Collections.unmodifiableMap(tags);
}
public String getVersion() {
return version;
}
public Metrics getMetrics() {
return metrics;
}
public String getServiceName() {
return serviceName;
}
public Map tags() {
return tags;
}
public int getIpv4() {
return ipv4;
}
Clock clock() {
return clock;
}
Reporter getReporter() {
return reporter;
}
void reportSpan(JaegerSpan span) {
reporter.report(span);
metrics.spansFinished.inc(1);
}
@Override
public ScopeManager scopeManager() {
return scopeManager;
}
/**
* Retrieves the currently active span from the {@link ScopeManager}. It cannot be guaranteed that this span
* will be a {@link JaegerSpan}, as other libraries might have set this active span there. Consumers expecting
* this to return a {@link JaegerSpan} should always check the type of the return and act accordingly.
*
* @return the currently active span from the {@link ScopeManager}
*/
@Override
public Span activeSpan() {
// the active scope might have been added there through an API extension, similar to what the OT java-metrics
// library does -- therefore, we can't guarantee that we are returning a JaegerSpan here.
Scope scope = this.scopeManager.active();
return scope == null ? null : scope.span();
}
@Override
public JaegerTracer.SpanBuilder buildSpan(String operationName) {
return objectFactory.createSpanBuilder(this, operationName);
}
@Override
public void inject(SpanContext spanContext, Format format, T carrier) {
Injector injector = registry.getInjector(format);
if (injector == null) {
throw new UnsupportedFormatException(format);
}
injector.inject((JaegerSpanContext) spanContext, carrier);
}
@Override
public JaegerSpanContext extract(Format format, T carrier) {
Extractor extractor = registry.getExtractor(format);
if (extractor == null) {
throw new UnsupportedFormatException(format);
}
return extractor.extract(carrier);
}
/**
* Shuts down the {@link Reporter} and {@link Sampler}
*/
@Override
public void close() {
reporter.close();
sampler.close();
}
public class SpanBuilder implements Tracer.SpanBuilder {
private String operationName;
private long startTimeMicroseconds;
/**
* In 99% situations there is only one parent (childOf), so we do not want to allocate
* a collection of references.
*/
private List references = Collections.emptyList();
private final Map tags = new HashMap();
private boolean ignoreActiveSpan = false;
protected SpanBuilder(String operationName) {
this.operationName = operationName;
}
@Override
public JaegerTracer.SpanBuilder asChildOf(SpanContext parent) {
return addReference(References.CHILD_OF, parent);
}
@Override
public JaegerTracer.SpanBuilder asChildOf(Span parent) {
return addReference(References.CHILD_OF, parent != null ? parent.context() : null);
}
@Override
public JaegerTracer.SpanBuilder addReference(String referenceType, SpanContext reference) {
if (reference == null) {
return this;
}
if (!(reference instanceof JaegerSpanContext)) {
log.warn("Expected to have a JaegerSpanContext but got " + reference.getClass().getName());
return this;
}
JaegerSpanContext referencedContext = (JaegerSpanContext) reference;
// Jaeger thrift currently does not support other reference types
if (!References.CHILD_OF.equals(referenceType)
&& !References.FOLLOWS_FROM.equals(referenceType)) {
return this;
}
if (references.isEmpty()) {
// Optimization for 99% situations, when there is only one parent
references = Collections.singletonList(new Reference(referencedContext, referenceType));
} else {
if (references.size() == 1) {
references = new ArrayList(references);
}
references.add(new Reference(referencedContext, referenceType));
}
return this;
}
@Override
public JaegerTracer.SpanBuilder withTag(String key, String value) {
tags.put(key, value);
return this;
}
@Override
public JaegerTracer.SpanBuilder withTag(String key, boolean value) {
tags.put(key, value);
return this;
}
@Override
public JaegerTracer.SpanBuilder withTag(String key, Number value) {
tags.put(key, value);
return this;
}
@Override
public JaegerTracer.SpanBuilder withStartTimestamp(long microseconds) {
this.startTimeMicroseconds = microseconds;
return this;
}
private JaegerSpanContext createNewContext(String debugId) {
long id = Utils.uniqueId();
byte flags = 0;
if (debugId != null) {
flags = (byte) (flags | JaegerSpanContext.flagSampled | JaegerSpanContext.flagDebug);
tags.put(Constants.DEBUG_ID_HEADER_KEY, debugId);
metrics.traceStartedSampled.inc(1);
} else {
//TODO(prithvi): Don't assume operationName is set on creation
SamplingStatus samplingStatus = sampler.sample(operationName, id);
if (samplingStatus.isSampled()) {
flags |= JaegerSpanContext.flagSampled;
tags.putAll(samplingStatus.getTags());
metrics.traceStartedSampled.inc(1);
} else {
metrics.traceStartedNotSampled.inc(1);
}
}
return getObjectFactory().createSpanContext(
id,
id,
0,
flags,
Collections.emptyMap(),
debugId);
}
private Map createChildBaggage() {
Map baggage = null;
// optimization for 99% use cases, when there is only one parent
if (references.size() == 1) {
return references.get(0).getSpanContext().baggage();
}
for (Reference reference: references) {
if (reference.getSpanContext().baggage() != null) {
if (baggage == null) {
baggage = new HashMap();
}
baggage.putAll(reference.getSpanContext().baggage());
}
}
return baggage;
}
private JaegerSpanContext createChildContext() {
JaegerSpanContext preferredReference = preferredReference();
if (isRpcServer()) {
if (isSampled()) {
metrics.tracesJoinedSampled.inc(1);
} else {
metrics.tracesJoinedNotSampled.inc(1);
}
// Zipkin server compatibility
if (zipkinSharedRpcSpan) {
return preferredReference;
}
}
return getObjectFactory().createSpanContext(
preferredReference.getTraceId(),
Utils.uniqueId(),
preferredReference.getSpanId(),
// should we do OR across passed references?
preferredReference.getFlags(),
createChildBaggage(),
null);
}
//Visible for testing
boolean isRpcServer() {
return Tags.SPAN_KIND_SERVER.equals(tags.get(Tags.SPAN_KIND.getKey()));
}
private JaegerSpanContext preferredReference() {
Reference preferredReference = references.get(0);
for (Reference reference: references) {
// childOf takes precedence as a preferred parent
if (References.CHILD_OF.equals(reference.getType())
&& !References.CHILD_OF.equals(preferredReference.getType())) {
preferredReference = reference;
break;
}
}
return preferredReference.getSpanContext();
}
private boolean isSampled() {
if (references != null) {
for (Reference reference : references) {
if (reference.getSpanContext().isSampled()) {
return true;
}
}
}
return false;
}
private String debugId() {
if (references.size() == 1 && references.get(0).getSpanContext().isDebugIdContainerOnly()) {
return references.get(0).getSpanContext().getDebugId();
}
return null;
}
@Override
public JaegerSpan start() {
JaegerSpanContext context;
// Check if active span should be established as CHILD_OF relationship
if (references.isEmpty() && !ignoreActiveSpan && null != scopeManager.active()) {
asChildOf(scopeManager.active().span());
}
String debugId = debugId();
if (references.isEmpty() || debugId != null) {
context = createNewContext(debugId);
} else {
context = createChildContext();
}
long startTimeNanoTicks = 0;
boolean computeDurationViaNanoTicks = false;
if (startTimeMicroseconds == 0) {
startTimeMicroseconds = clock.currentTimeMicros();
if (!clock.isMicrosAccurate()) {
startTimeNanoTicks = clock.currentNanoTicks();
computeDurationViaNanoTicks = true;
}
}
JaegerSpan jaegerSpan = getObjectFactory().createSpan(
JaegerTracer.this,
operationName,
context,
startTimeMicroseconds,
startTimeNanoTicks,
computeDurationViaNanoTicks,
tags,
references);
if (context.isSampled()) {
metrics.spansStartedSampled.inc(1);
} else {
metrics.spansStartedNotSampled.inc(1);
}
return jaegerSpan;
}
@Override
public Scope startActive(boolean finishSpanOnClose) {
return scopeManager.activate(start(), finishSpanOnClose);
}
@Override
public JaegerTracer.SpanBuilder ignoreActiveSpan() {
ignoreActiveSpan = true;
return this;
}
@Override
@Deprecated
public JaegerSpan startManual() {
return start();
}
private JaegerObjectFactory getObjectFactory() {
return JaegerTracer.this.objectFactory;
}
}
/**
* Builds a {@link JaegerTracer} with options.
*/
public static class Builder {
private Sampler sampler;
private Reporter reporter;
private final PropagationRegistry registry = new PropagationRegistry();
private Metrics metrics = new Metrics(new NoopMetricsFactory());
private final String serviceName;
private Clock clock = new SystemClock();
private Map tags = new HashMap();
private boolean zipkinSharedRpcSpan;
private ScopeManager scopeManager = new ThreadLocalScopeManager();
private BaggageRestrictionManager baggageRestrictionManager = new DefaultBaggageRestrictionManager();
private boolean expandExceptionLogs;
private final JaegerObjectFactory objectFactory;
public Builder(String serviceName) {
this(serviceName, new JaegerObjectFactory());
}
protected Builder(String serviceName, JaegerObjectFactory objectFactory) {
this.serviceName = checkValidServiceName(serviceName);
this.objectFactory = objectFactory;
TextMapCodec textMapCodec =
TextMapCodec.builder()
.withUrlEncoding(false)
.withObjectFactory(this.objectFactory)
.build();
this.registerInjector(Format.Builtin.TEXT_MAP, textMapCodec);
this.registerExtractor(Format.Builtin.TEXT_MAP, textMapCodec);
TextMapCodec httpCodec =
TextMapCodec.builder()
.withUrlEncoding(true)
.withObjectFactory(this.objectFactory)
.build();
this.registerInjector(Format.Builtin.HTTP_HEADERS, httpCodec);
this.registerExtractor(Format.Builtin.HTTP_HEADERS, httpCodec);
// TODO binary codec not implemented
}
/**
* @param reporter reporter.
*/
public Builder withReporter(Reporter reporter) {
this.reporter = reporter;
return this;
}
/**
* @param sampler sampler.
*/
public Builder withSampler(Sampler sampler) {
this.sampler = sampler;
return this;
}
public Builder registerInjector(Format format, Injector injector) {
this.registry.register(format, injector);
return this;
}
public Builder registerExtractor(Format format, Extractor extractor) {
this.registry.register(format, extractor);
return this;
}
/**
* Creates a new {@link Metrics} to be used with the tracer, backed by the given {@link MetricsFactory}
* @param metricsFactory the metrics factory to use
* @return this instance of the builder
*/
public Builder withMetricsFactory(MetricsFactory metricsFactory) {
this.metrics = new Metrics(metricsFactory);
return this;
}
public Builder withScopeManager(ScopeManager scopeManager) {
this.scopeManager = scopeManager;
return this;
}
public Builder withClock(Clock clock) {
this.clock = clock;
return this;
}
public Builder withZipkinSharedRpcSpan() {
zipkinSharedRpcSpan = true;
return this;
}
public Builder withExpandExceptionLogs() {
this.expandExceptionLogs = true;
return this;
}
public Builder withMetrics(Metrics metrics) {
this.metrics = metrics;
return this;
}
public Builder withTag(String key, String value) {
tags.put(key, value);
return this;
}
public Builder withTag(String key, boolean value) {
tags.put(key, value);
return this;
}
public Builder withTag(String key, Number value) {
tags.put(key, value);
return this;
}
public Builder withTags(Map tags) {
if (tags != null) {
this.tags.putAll(tags);
}
return this;
}
public Builder withBaggageRestrictionManager(BaggageRestrictionManager baggageRestrictionManager) {
this.baggageRestrictionManager = baggageRestrictionManager;
return this;
}
public JaegerTracer build() {
if (reporter == null) {
reporter = new RemoteReporter.Builder()
.withMetrics(metrics)
.build();
}
if (sampler == null) {
sampler = new RemoteControlledSampler.Builder(serviceName)
.withMetrics(metrics)
.build();
}
return createTracer();
}
protected JaegerTracer createTracer() {
return new JaegerTracer(this);
}
public static String checkValidServiceName(String serviceName) {
if (serviceName == null || serviceName.trim().length() == 0) {
throw new IllegalArgumentException("Service name must not be null or empty");
}
return serviceName;
}
}
private static String loadVersion() {
return "Java-" + getVersionFromProperties();
}
public static String getVersionFromProperties() {
String version;
try {
InputStream is = JaegerTracer.class.getResourceAsStream("jaeger.properties");
try {
Properties prop = new Properties();
prop.load(is);
version = prop.getProperty(Constants.JAEGER_CLIENT_VERSION_TAG_KEY);
} finally {
is.close();
}
} catch (Exception e) {
throw new RuntimeException("Cannot read jaeger.properties", e);
}
if (version == null) {
throw new RuntimeException("Cannot read " + Constants.JAEGER_CLIENT_VERSION_TAG_KEY + " from jaeger.properties");
}
return version;
}
String getHostName() {
try {
return InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) {
log.error("Cannot obtain host name", e);
return null;
}
}
JaegerSpanContext setBaggage(JaegerSpan jaegerSpan, String key, String value) {
return baggageSetter.setBaggage(jaegerSpan, key, value);
}
boolean isExpandExceptionLogs() {
return this.expandExceptionLogs;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy