brave.opentracing.BraveSpanBuilder Maven / Gradle / Ivy
Show all versions of brave-opentracing Show documentation
/*
* Copyright 2016-2019 The OpenZipkin Authors
*
* 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 brave.opentracing;
import brave.Tracing;
import brave.propagation.CurrentTraceContext;
import brave.propagation.TraceContext;
import brave.propagation.TraceContextOrSamplingFlags;
import io.opentracing.References;
import io.opentracing.Span;
import io.opentracing.SpanContext;
import io.opentracing.Tracer;
import io.opentracing.tag.Tag;
import io.opentracing.tag.Tags;
import java.util.LinkedHashMap;
import java.util.Map;
import static io.opentracing.tag.Tags.SAMPLING_PRIORITY;
/**
* Uses by the underlying {@linkplain brave.Tracer} to create a {@linkplain BraveSpan} wrapped
* {@linkplain brave.Span}
*
* Indicate {@link Tags#SPAN_KIND} before calling {@link #start()} to ensure RPC spans are
* identified properly.
*
*
Brave does not support multiple parents so this has been implemented to use the first parent
* defined.
*/
public class BraveSpanBuilder implements Tracer.SpanBuilder {
final brave.Tracer tracer;
final CurrentTraceContext currentTraceContext;
final Map tags = new LinkedHashMap<>();
String operationName;
long timestamp;
int remotePort;
BraveSpanContext reference;
boolean ignoreActiveSpan = false;
BraveSpanBuilder(Tracing tracing, String operationName) {
this.tracer = tracing.tracer();
this.currentTraceContext = tracing.currentTraceContext();
this.operationName = operationName;
}
@Override public BraveSpanBuilder asChildOf(SpanContext parent) {
return addReference(References.CHILD_OF, parent);
}
@Override public BraveSpanBuilder asChildOf(Span parent) {
return asChildOf(parent != null ? parent.context() : null);
}
@Override public BraveSpanBuilder addReference(String type, SpanContext context) {
if (reference != null || context == null) return this;
if (References.CHILD_OF.equals(type) || References.FOLLOWS_FROM.equals(type)) {
this.reference = (BraveSpanContext) context;
}
return this;
}
@Override public BraveSpanBuilder withTag(String key, String value) {
tags.put(key, value);
return this;
}
@Override public BraveSpanBuilder withTag(String key, boolean value) {
if (Tags.ERROR.getKey().equals(key) && !value) return this;
return withTag(key, Boolean.toString(value));
}
@Override public BraveSpanBuilder withTag(String key, Number value) {
if (Tags.PEER_PORT.getKey().equals(key)) {
remotePort = value.intValue();
return this;
}
return withTag(key, value.toString());
}
@Override public BraveSpanBuilder withTag(Tag tag, T value) {
if (tag == null) throw new NullPointerException("tag == null");
if (value == null) throw new NullPointerException("value == null");
if (value instanceof String) return withTag(tag.getKey(), (String) value);
if (value instanceof Number) return withTag(tag.getKey(), (Number) value);
if (value instanceof Boolean) return withTag(tag.getKey(), (Boolean) value);
throw new IllegalArgumentException("tag value not a string, number or boolean: " + value);
}
@Override public BraveSpanBuilder withStartTimestamp(long microseconds) {
this.timestamp = microseconds;
return this;
}
@Override public BraveSpanBuilder ignoreActiveSpan() {
ignoreActiveSpan = true;
return this;
}
@Override public BraveSpan start() {
boolean server = Tags.SPAN_KIND_SERVER.equals(tags.get(Tags.SPAN_KIND.getKey()));
// Handle active span ignoring
CurrentTraceContext.Scope scope = ignoreActiveSpan ?
currentTraceContext.newScope(null) :
CurrentTraceContext.Scope.NOOP;
brave.Span span;
try {
// Check if active span should be established as CHILD_OF relationship
if (reference == null) {
brave.Span parent = tracer.currentSpan();
if (parent != null) asChildOf(BraveSpanContext.create(parent.context()));
}
TraceContext context;
if (reference == null) {
// adjust sampling decision, this reflects Zipkin's "before the fact" sampling policy
// https://github.com/openzipkin/brave/tree/master/brave#sampling
span = tracer.nextSpan(flagsFromSamplingPriority(tags.get(SAMPLING_PRIORITY.getKey())));
} else if ((context = reference.unwrap()) != null) {
// Zipkin's default is to share a span ID between the client and the server in an RPC.
// When we start a server span with a parent, we assume the "parent" is actually the
// client on the other side of the RPC. Accordingly, we join that span instead of fork.
// TODO: this is incorrect as we don't know if this was an incoming server or consumer request
span = server ? tracer.joinSpan(context) : tracer.newChild(context);
} else {
span = tracer.nextSpan(((BraveSpanContext.Incomplete) reference).extractionResult());
}
} finally {
scope.close();
}
if (operationName != null) span.name(operationName);
BraveSpan result = new BraveSpan(tracer, span);
result.remotePort = remotePort;
for (Map.Entry tag : tags.entrySet()) {
result.setTag(tag.getKey(), tag.getValue());
}
if (timestamp != 0) {
span.start(timestamp);
} else {
span.start();
}
return result;
}
/* @Override deprecated 0.32 method: Intentionally no override to ensure 0.33 works! */
@Deprecated public BraveSpan startManual() {
throw new UnsupportedOperationException("Not supported in OpenTracing 0.33+");
}
/* @Override deprecated 0.32 method: Intentionally no override to ensure 0.33 works! */
@Deprecated public BraveScope startActive(boolean finishSpanOnClose) {
throw new UnsupportedOperationException("Not supported in OpenTracing 0.33+");
}
static TraceContextOrSamplingFlags flagsFromSamplingPriority(String samplingPriorityString) {
if (samplingPriorityString == null) return TraceContextOrSamplingFlags.EMPTY;
try {
int samplingPriority = Integer.parseInt(samplingPriorityString);
if (samplingPriority == 0) {
return TraceContextOrSamplingFlags.NOT_SAMPLED;
} else if (samplingPriority > 0) {
return TraceContextOrSamplingFlags.SAMPLED;
}
} catch (NumberFormatException ex) {
// ignore
}
return TraceContextOrSamplingFlags.EMPTY;
}
}