blob: db6a355576246ce4cc71f91454077671096131d4 [file] [log] [blame]
/*
* Copyright 2018, OpenCensus 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.opencensus.contrib.spring.sleuth.v1x;
import io.grpc.Context;
import io.opencensus.common.ExperimentalApi;
import io.opencensus.trace.unsafe.ContextUtils;
import org.apache.commons.logging.Log;
import org.springframework.cloud.sleuth.Span;
import org.springframework.core.NamedThreadLocal;
/*>>>
import org.checkerframework.checker.nullness.qual.Nullable;
*/
/** Inspired by the Sleuth's {@code SpanContextHolder}. */
@ExperimentalApi
final class OpenCensusSleuthSpanContextHolder {
private static final Log log =
org.apache.commons.logging.LogFactory.getLog(OpenCensusSleuthSpanContextHolder.class);
private static final ThreadLocal</*@Nullable*/ SpanContext> CURRENT_SPAN =
new NamedThreadLocal</*@Nullable*/ SpanContext>("Trace Context");
// Get the current span out of the thread context.
@javax.annotation.Nullable
static Span getCurrentSpan() {
SpanContext currentSpanContext = CURRENT_SPAN.get();
return currentSpanContext != null ? currentSpanContext.span : null;
}
// Set the current span in the thread context
static void setCurrentSpan(Span span) {
if (log.isTraceEnabled()) {
log.trace("Setting current span " + span);
}
push(span, /* autoClose= */ false);
}
// Remove all thread context relating to spans (useful for testing).
// See close() for a better alternative in instrumetation
static void removeCurrentSpan() {
removeCurrentSpanInternal(null);
}
@SuppressWarnings("CheckReturnValue")
@javax.annotation.Nullable
private static SpanContext removeCurrentSpanInternal(
@javax.annotation.Nullable SpanContext toRestore) {
if (toRestore != null) {
setSpanContextInternal(toRestore);
} else {
CURRENT_SPAN.remove();
// This is a big hack and can cause other data in the io.grpc.Context to be lost. But
// Spring 1.5 does not use io.grpc.Context and because the framework does not accept any
// gRPC context, the context will always be ROOT anyway.
Context.ROOT.attach();
}
return toRestore;
}
// Check if there is already a span in the current thread.
static boolean isTracing() {
return CURRENT_SPAN.get() != null;
}
// Close the current span and all parents that can be auto closed. On every iteration a function
// will be applied on the closed Span.
static void close(SpanFunction spanFunction) {
SpanContext current = CURRENT_SPAN.get();
while (current != null) {
spanFunction.apply(current.span);
current = removeCurrentSpanInternal(current.parent);
if (current == null || !current.autoClose) {
return;
}
}
}
// Close the current span and all parents that can be auto closed.
static void close() {
close(NO_OP_FUNCTION);
}
/**
* Push a span into the thread context, with the option to have it auto close if any child spans
* are themselves closed. Use autoClose=true if you start a new span with a parent that wasn't
* already in thread context.
*/
static void push(Span span, boolean autoClose) {
if (isCurrent(span)) {
return;
}
setSpanContextInternal(new SpanContext(span, autoClose));
}
interface SpanFunction {
void apply(Span span);
}
private static final SpanFunction NO_OP_FUNCTION =
new SpanFunction() {
@Override
public void apply(Span span) {}
};
@SuppressWarnings("CheckReturnValue")
private static void setSpanContextInternal(SpanContext spanContext) {
CURRENT_SPAN.set(spanContext);
spanContext.ocCurrentContext.attach();
}
private static boolean isCurrent(Span span) {
if (span == null) {
return false;
}
SpanContext currentSpanContext = CURRENT_SPAN.get();
return currentSpanContext != null && span.equals(currentSpanContext.span);
}
private static class SpanContext {
final Span span;
final boolean autoClose;
@javax.annotation.Nullable final SpanContext parent;
final OpenCensusSleuthSpan ocSpan;
final Context ocCurrentContext;
private SpanContext(Span span, boolean autoClose) {
this.span = span;
this.autoClose = autoClose;
this.parent = CURRENT_SPAN.get();
this.ocSpan = new OpenCensusSleuthSpan(span);
this.ocCurrentContext =
Context.current().withValue(ContextUtils.CONTEXT_SPAN_KEY, this.ocSpan);
}
}
private OpenCensusSleuthSpanContextHolder() {}
}