Is your feature request related to a problem? Please describe.
In quickstart, I know how to create new span context in filter: https://github.com/open-telemetry/opentelemetry-java/blob/master/QUICKSTART.md#context-propagation. But how can I get this context in controller, service and others?
Describe the solution you'd like
A theadlocal context resolver. Perhaps like that:
public class ThreadContextHolder {
private static final ThreadLocal<Span> OPEN_TRACING_SPAN_HOLDER = new ThreadLocal<>();
public static Span getOpenTracingSpan() {
return OPEN_TRACING_SPAN_HOLDER.get();
}
public static void setOpenTracingSpan(Span span) {
OPEN_TRACING_SPAN_HOLDER.set(span);
}
}
public void handle(HttpExchange httpExchange) {
// Extract the SpanContext and other elements from the request.
Context extractedContext = OpenTelemetry.getPropagators().getTextMapPropagator()
.extract(Context.current(), httpExchange, getter);
Span serverSpan = null;
try (Scope scope = ContextUtils.withScopedContext(extractedContext)) {
// Automatically use the extracted SpanContext as parent.
serverSpan = tracer.spanBuilder("/resource").setSpanKind(Span.Kind.SERVER)
.startSpan();
// Add the attributes defined in the Semantic Conventions
serverSpan.setAttribute("http.method", "GET");
ThreadContextHolder.setOpenTracingSpan(serverSpan); // set to threadlocal
// Serve the request
...
} finally {
if (serverSpan != null) {
serverSpan.end();
}
}
}
Then we can access this span in the same thread. I guess the api would provide this util?
This is exactly what scope is for. You create you server span as in your example, then you create a new scope with that span:
public void handle(HttpExchange httpExchange) {
// Extract the SpanContext and other elements from the request.
Context extractedContext = OpenTelemetry.getPropagators().getTextMapPropagator()
.extract(Context.current(), httpExchange, getter);
Span serverSpan = tracer.spanBuilder("/resource").setSpanKind(Span.Kind.SERVER).setParent(extractedContext)
.startSpan();
//This installs the context with serverSpan as the active one
try (Scope scope = tracer.withSpan(serverSpan)) {
// Add the attributes defined in the Semantic Conventions
serverSpan.setAttribute("http.method", "GET");
// Serve the request
...
} finally {
serverSpan.end();
}
}
@iNikem Sorry I didn't get you point. In the controller how can I get the scope?
@RequestMapping(value = "/test")
public boolean test() {
// how to get scope?
}
Hi @wwulfric, in your code you should be able to use Context.current() to get the current context, or alternatively to access the span directly, there is TracingContextUtils.getCurrentSpan(). Does that work for you?
@anuraaga It seems literally right but the return span context trace id is 000...0, span id is 00...0.
```java
@RequestMapping(value = "/test")
public boolean test() {
Span span = TracingContextUtils.getCurrentSpan();
// span context trace id is 000...0, span id is 00...0.
}
Can you share your full code?
@iNikem
ThreadContextHolder holds threadlocal span.
public class ThreadContextHolder {
private static final ThreadLocal<Span> OPEN_TRACING_SPAN_HOLDER = new ThreadLocal<>();
public static Span getOpenTracingSpan() {
return OPEN_TRACING_SPAN_HOLDER.get();
}
public static void setOpenTracingSpan(Span span) {
OPEN_TRACING_SPAN_HOLDER.set(span);
}
}
OpenTracingFilter is a web filter. Request scope span is generated here.
public class OpenTracingFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(OpenTracingFilter.class);
private Tracer tracer;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
tracer = OpenTelemetry.getTracer(OpenTracingFilter.class.getName());
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
TextMapPropagator.Getter<HttpServletRequest> getter =
HttpServletRequest::getHeader;
Context extractedContext = OpenTelemetry.getPropagators().getTextMapPropagator()
.extract(Context.current(), httpRequest, getter);
Span serverSpan = null;
try (Scope scope = ContextUtils.withScopedContext(extractedContext)) {
// Automatically use the extracted SpanContext as parent.
serverSpan = tracer.spanBuilder(httpRequest.getRequestURI()).setSpanKind(Span.Kind.SERVER)
.startSpan();
// Add the attributes defined in the Semantic Conventions
serverSpan.setAttribute("http.method", httpRequest.getMethod());
serverSpan.setAttribute("http.scheme", httpRequest.getProtocol());
serverSpan.setAttribute("http.host", httpRequest.getRemoteHost());
serverSpan.setAttribute("http.target", httpRequest.getRequestURI());
ThreadContextHolder.setOpenTracingSpan(serverSpan);
// Serve the request
filterChain.doFilter(servletRequest, servletResponse);
} finally {
if (serverSpan != null) {
ThreadContextHolder.removeOpenTracingSpan();
serverSpan.end();
}
}
}
@Override
public void destroy() {
}
}
I wanna access this span in controller method(and other same-thread logic block). I can access it by ThreadContextHolder. But I guess opentelemetry would provide this kind of util.
@RestController
public class ProjectController {
@RequestMapping(value = "/test")
public boolean test() {
Span span = ThreadContextHolder.getOpenTracingSpan();
}
}
Ah - you want to make the Scope with the new span, not the extracted context. The scope, and TracingContextUtils is the utility opentelemetry provides for this :)
Context extractedContext = OpenTelemetry.getPropagators().getTextMapPropagator()
.extract(Context.current(), httpRequest, getter);
Span serverSpan = tracer.spanBuilder(httpRequest.getRequestURI()).setSpanKind(Span.Kind.SERVER)
.setParent(extractedContext).startSpan();
...
try (Scope scope = TracingContextUtils.currentContextWith(serverSpan) {
filterChain.doFilter(servletRequest, servletResponse);
}
Hi @wwulfric - judging by your emoji it sounds like your issue was solved :) Let me close this issue but if you still have any please feel free to reopen it.
Most helpful comment
Ah - you want to make the
Scopewith the new span, not the extracted context. The scope, andTracingContextUtilsis the utility opentelemetry provides for this :)