io.servicetalk.opentracing.inmemory.DefaultInMemoryTracer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of servicetalk-opentracing-inmemory Show documentation
Show all versions of servicetalk-opentracing-inmemory Show documentation
A networking framework that evolves with your application
/*
* Copyright © 2018, 2021 Apple Inc. and the ServiceTalk project 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 io.servicetalk.opentracing.inmemory;
import io.servicetalk.opentracing.inmemory.api.InMemoryReference;
import io.servicetalk.opentracing.inmemory.api.InMemoryScopeManager;
import io.servicetalk.opentracing.inmemory.api.InMemorySpan;
import io.servicetalk.opentracing.inmemory.api.InMemorySpanBuilder;
import io.servicetalk.opentracing.inmemory.api.InMemorySpanContext;
import io.servicetalk.opentracing.inmemory.api.InMemorySpanEventListener;
import io.opentracing.Scope;
import io.opentracing.Span;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import static io.servicetalk.opentracing.internal.HexUtils.hexBytesOfLong;
import static java.util.Objects.requireNonNull;
/**
* Tracer implementation that propagates spans in-memory and emits events to listeners.
*/
public final class DefaultInMemoryTracer extends AbstractInMemoryTracer {
private static final Logger logger = LoggerFactory.getLogger(DefaultInMemoryTracer.class);
private final InMemoryScopeManager scopeManager;
private final BiFunction sampler;
private final InMemorySpanEventListener listeners;
private final int maxTagSize;
private final boolean persistLogs;
private final boolean use128BitTraceId;
/**
* Builders for {@link DefaultInMemoryTracer}.
*/
public static final class Builder {
private final InMemoryScopeManager scopeManager;
private final CopyOnWriteInMemorySpanEventListenerSet listeners = new CopyOnWriteInMemorySpanEventListenerSet();
private BiFunction sampler = SamplingStrategies.sampleUnlessFalse();
private int maxTagSize = 16;
private boolean persistLogs;
private boolean use128BitTraceId;
/**
* Constructs a builder for an {@link DefaultInMemoryTracer}.
*
* @param scopeManager a {@link InMemoryScopeManager}.
*/
public Builder(InMemoryScopeManager scopeManager) {
this.scopeManager = requireNonNull(scopeManager);
}
/**
* Sets the sampler.
*
* @param sampler policy which takes a traceId and returns whether the given trace should be sampled
* @return this
*/
public Builder withSampler(Predicate sampler) {
requireNonNull(sampler);
withSampler((traceId, requested) -> requested != null ? requested : sampler.test(traceId));
return this;
}
/**
* Sets the sampler.
*
* @param sampler policy which takes a traceId and the sampling flag specified in carrier (optional,
* could be {@code null}), and returns whether the given trace should be sampled.
* @return this
*/
public Builder withSampler(BiFunction sampler) {
this.sampler = requireNonNull(sampler);
return this;
}
/**
* Add a trace event listener.
*
* @param listener listener to add
* @return this
*/
public Builder addListener(InMemorySpanEventListener listener) {
listeners.add(requireNonNull(listener));
return this;
}
/**
* Sets the maximum number of tags.
*
* @param maxTagSize maximum number of tags
* @return this
*/
public Builder withMaxTagSize(int maxTagSize) {
if (maxTagSize < 0) {
throw new IllegalArgumentException("maxTagSize must be >= 0");
}
this.maxTagSize = maxTagSize;
return this;
}
/**
* Sets whether logs are persisted in the span object. This is necessary when using using
* listeners which sends the span to a backend on span finish.
*
* @param persistLogs whether to persist logs in the span object. Defaults to false.
* @return this
*/
public Builder persistLogs(boolean persistLogs) {
this.persistLogs = persistLogs;
return this;
}
/**
* Sets whether to use 128-bit trace IDs.
*
* @param use128BitTraceId whether to use 128-bit trace IDs.
* @return this
*/
public Builder use128BitTraceId(boolean use128BitTraceId) {
this.use128BitTraceId = use128BitTraceId;
return this;
}
/**
* Builds the {@link DefaultInMemoryTracer}.
*
* @return tracer
*/
public DefaultInMemoryTracer build() {
return new DefaultInMemoryTracer(scopeManager, sampler, listeners, maxTagSize, persistLogs,
use128BitTraceId);
}
}
private DefaultInMemoryTracer(
InMemoryScopeManager scopeManager, BiFunction sampler,
InMemorySpanEventListener listeners, int maxTagSize, boolean persistLogs,
boolean use128BitTraceId) {
this.scopeManager = scopeManager;
this.sampler = sampler;
this.listeners = listeners;
this.maxTagSize = maxTagSize;
this.persistLogs = persistLogs;
this.use128BitTraceId = use128BitTraceId;
}
@Override
public InMemoryScopeManager scopeManager() {
return scopeManager;
}
@Nullable
@Override
public InMemorySpan activeSpan() {
return scopeManager.activeSpan();
}
@Override
public Scope activateSpan(Span span) {
return scopeManager.activate(span);
}
@Override
public InMemorySpanBuilder buildSpan(final String operationName) {
return new DefaultInMemorySpanBuilder(operationName);
}
@Override
public void close() {
//noop
}
private InMemorySpanContext newSpanContext(final String traceId, final String spanId,
@Nullable final String parentSpanId, final boolean sampled) {
return new DefaultInMemorySpanContext(traceId, spanId, parentSpanId, sampled);
}
private final class DefaultInMemorySpanBuilder extends AbstractInMemorySpanBuilder {
DefaultInMemorySpanBuilder(String operationName) {
super(operationName, DefaultInMemoryTracer.this.maxTagSize);
}
@Override
InMemorySpan createSpan(
String operationName, List references,
Map tags, int maxTagSize, boolean ignoreActiveSpan, long startTimestampMicros) {
InMemorySpanContext maybeParent = parent();
if (maybeParent == null && !ignoreActiveSpan) {
// Try to infer the parent based upon the ScopeManager's active Scope.
InMemorySpan span = scopeManager().activeSpan();
if (span != null) {
maybeParent = span.context();
}
}
final String traceIdHex;
final String spanIdHex;
final String parentSpanIdHex;
final boolean sampled;
if (maybeParent != null) {
traceIdHex = maybeParent.toTraceId();
spanIdHex = nextId();
parentSpanIdHex = maybeParent.toSpanId();
sampled = isSampled(traceIdHex, maybeParent.isSampled());
} else {
spanIdHex = nextId();
traceIdHex = use128BitTraceId ? nextId() + spanIdHex : spanIdHex;
parentSpanIdHex = null;
sampled = isSampled(traceIdHex, null);
}
final InMemorySpanContext context = newSpanContext(traceIdHex, spanIdHex, parentSpanIdHex, sampled);
if (sampled) {
SampledInMemorySpan span = new SampledInMemorySpan(operationName, references, context, tags, maxTagSize,
startTimestampMicros, listeners, persistLogs);
span.start();
return span;
} else {
return new UnsampledInMemorySpan(operationName, references, context);
}
}
}
private static String nextId() {
// We should be careful to select a randomly generated ID. If we choose to use a counter, and another
// application also chooses a counter there is a chance we will be synchronized and have a higher probability of
// overlapping IDs.
return hexBytesOfLong(ThreadLocalRandom.current().nextLong());
}
private boolean isSampled(String traceId, @Nullable Boolean requestedByCarrier) {
try {
return sampler.apply(traceId, requestedByCarrier);
} catch (Throwable t) {
logger.warn("Exception from sampler={}, default to not sampling", sampler, t);
return false; // play safe, default to not sampling
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy