All Downloads are FREE. Search and download functionalities are using the official Maven repository.

brave.propagation.ThreadLocalSpan Maven / Gradle / Ivy

There is a newer version: 6.0.3
Show newest version
/*
 * Copyright 2013-2020 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.propagation;

import brave.Span;
import brave.Tracer;
import brave.Tracer.SpanInScope;
import brave.Tracing;
import brave.internal.Nullable;
import java.util.ArrayDeque;

/**
 * This type allows you to place a span in scope in one method and access it in another without
 * using an explicit request parameter.
 *
 * 

Many libraries expose a callback model as opposed to an interceptor one. When creating new * instrumentation, you may find places where you need to place a span in scope in one callback * (like `onStart()`) and end the scope in another callback (like `onFinish()`). * *

Provided the library guarantees these run on the same thread, you can simply propagate the * result of {@link Tracer#startScopedSpan(String)} or {@link Tracer#withSpanInScope(Span)} from the * starting callback to the closing one. This is typically done with a request-scoped attribute. * * Here's an example: *

{@code
 * class MyFilter extends Filter {
 *   public void onStart(Request request, Attributes attributes) {
 *     // Assume you have code to start the span and add relevant tags...
 *
 *     // We now set the span in scope so that any code between here and
 *     // the end of the request can see it with Tracer.currentSpan()
 *     SpanInScope spanInScope = tracer.withSpanInScope(span);
 *
 *     // We don't want to leak the scope, so we place it somewhere we can
 *     // lookup later
 *     attributes.put(SpanInScope.class, spanInScope);
 *   }
 *
 *   public void onFinish(Response response, Attributes attributes) {
 *     // as long as we are on the same thread, we can read the span started above
 *     Span span = tracer.currentSpan();
 *
 *     // Assume you have code to complete the span
 *
 *     // We now remove the scope (which implicitly detaches it from the span)
 *     attributes.remove(SpanInScope.class).close();
 *   }
 * }
 * }
* *

Sometimes you have to instrument a library where There's no attribute namespace shared across * request and response. For this scenario, you can use {@link ThreadLocalSpan} to temporarily store * the span between callbacks. * * Here's an example: *

{@code
 * class MyFilter extends Filter {
 *   final ThreadLocalSpan threadLocalSpan;
 *
 *   public void onStart(Request request) {
 *     // Allocates a span and places it in scope so that code between here and onFinish can see it
 *     Span span = threadLocalSpan.next();
 *     if (span == null || span.isNoop()) return; // skip below logic on noop
 *
 *     // Assume you have code to start the span and add relevant tags...
 *   }
 *
 *   public void onFinish(Response response, Attributes attributes) {
 *     // as long as we are on the same thread, we can read the span started above
 *     Span span = threadLocalSpan.remove();
 *     if (span == null || span.isNoop()) return; // skip below logic on noop
 *
 *     // Assume you have code to complete the span
 *   }
 * }
 * }
*/ public class ThreadLocalSpan { /** * This uses the {@link Tracing#currentTracer()}, which means calls to {@link #next()} may return * null. Use this when you have no other means to get a reference to the tracer. For example, JDBC * connections, as they often initialize prior to the tracing component. */ public static final ThreadLocalSpan CURRENT_TRACER = new ThreadLocalSpan(null); public static ThreadLocalSpan create(Tracer tracer) { if (tracer == null) throw new NullPointerException("tracer == null"); return new ThreadLocalSpan(tracer); } @Nullable final Tracer tracer; ThreadLocalSpan(Tracer tracer) { this.tracer = tracer; } Tracer tracer() { return tracer != null ? tracer : Tracing.currentTracer(); } /** * Returns the {@link Tracer#nextSpan(TraceContextOrSamplingFlags)} or null if {@link * #CURRENT_TRACER} and tracing isn't available. */ @Nullable public Span next(TraceContextOrSamplingFlags extracted) { Tracer tracer = tracer(); if (tracer == null) return null; Span next = tracer.nextSpan(extracted); SpanAndScope spanAndScope = new SpanAndScope(next, tracer.withSpanInScope(next)); getCurrentSpanInScopeStack().addFirst(spanAndScope); return next; } /** * Returns the {@link Tracer#nextSpan()} or null if {@link #CURRENT_TRACER} and tracing isn't * available. */ @Nullable public Span next() { Tracer tracer = tracer(); if (tracer == null) return null; Span next = tracer.nextSpan(); SpanAndScope spanAndScope = new SpanAndScope(next, tracer.withSpanInScope(next)); getCurrentSpanInScopeStack().addFirst(spanAndScope); return next; } static final class SpanAndScope { final Span span; final SpanInScope spanInScope; SpanAndScope(Span span, SpanInScope spanInScope) { this.span = span; this.spanInScope = spanInScope; } } /** * Returns the span set in scope via {@link #next()} or null if there was none. * *

When assertions are on, this will throw an assertion error if the span returned was not the * one currently in context. This could happen if someone called {@link * Tracer#withSpanInScope(Span)} or {@link CurrentTraceContext#newScope(TraceContext)} outside a * try/finally block. */ @Nullable public Span remove() { Tracer tracer = tracer(); Span currentSpan = tracer != null ? tracer.currentSpan() : null; SpanAndScope spanAndScope = getCurrentSpanInScopeStack().pollFirst(); if (spanAndScope == null) return currentSpan; Span span = spanAndScope.span; spanAndScope.spanInScope.close(); assert span.equals(currentSpan) : "Misalignment: scoped span " + span + " != current span " + currentSpan; return currentSpan; } /** * This keeps track of a stack with a normal array dequeue. Redundant stacking of the same span is * not possible because there is no api to place an arbitrary span in scope using this api. */ @SuppressWarnings("ThreadLocalUsage") // intentional: to support multiple Tracer instances final ThreadLocal> currentSpanInScopeStack = new ThreadLocal<>(); ArrayDeque getCurrentSpanInScopeStack() { ArrayDeque stack = currentSpanInScopeStack.get(); if (stack == null) { stack = new ArrayDeque<>(); currentSpanInScopeStack.set(stack); } return stack; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy