When using the OTel Java API, manually instrumented code typically looks like the following:
@Inject
Tracer tracer;
public String instrumentedMethod() {
// ...
Span span = tracer.spanBuilder("interesting operation").startSpan();
span.setAttribute("custom.info", "some info");
try (Scope scope = span.makeCurrent()) {
// logic
}
finally {
span.end();
}
// ...
}
From my point of view, the lifespan of the span is defined twice:
tracer.spanBuilder("...").startSpan() and span.end() demarcate the beginning and the end of the span,span.makeCurrent() and scope.end() does this in some wayWhat's the purpose of startSpan()/span.end() in contrast to span.makeCurrent()/scope.end(). Can scope be omitted? Is span.end() superfluous when scope is used?
Starting and ending a span does not make the rest of your application aware of the current Context - i.e. what span is currently "active".
If you didn't open the Scope, any code in your //logic section would not be able to know the Context, and so it can't know what the parent span is. You would end up with a bunch of individual spans that aren't linked together by a common trace.
When you open a Scope, OpenTelemetry sets a ThreadLocal, and so any new span started within that scope (the try block) will be able to know what its parent span is.
There are cases where you may have a Context but not an entire Span, so those concepts are separated for that reason (and I assume a handful of others reasons).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With