-
Notifications
You must be signed in to change notification settings - Fork 321
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce TraceContextHolder, unifying context propagation #417
Conversation
Introducing `TraceContextHolder` in the internal API as a common superclass of both `TraceContext` and `AbstractSpan`. Given an instance of this class, you can create child spans, capture exceptions and manage activations/scopes. This abstraction reliefs clients from having to differ between the case when the current activation is a `TraceContext` vs an `AbstractSpan`. A `TraceContext` would be active when the current thread does not own the lifecycle of the parent span. Otherwise an `AbstractSpan` would be active. There is rarely a case where a client would need to get the currently active span to set the name or tags. This propagation is normally done with `@Advice.Local` variables from enter to exit advices. Should the need for that arise however, and the client can ensure that the current activation is actually an `AbstractSpan`, all they need to do is to cast `tracer.getActive()` from `TraceContextHolder` to `AbstractSpan`. The public API does not differ between `Span` and `TraceContextHolder`. `ElasticApm.currentSpan()` always returns a `Span`. In case the current activation is a `TraceContextHolder`, methods like `Span#setTag` are silent noops. Calling those methods on a `Span` whose lifecycle is not owned by the caller is illegal anyway. However, calling `ElasticApm.currentSpan().createSpan()` is always allowed. Taking away `ElasticApm.currentSpan()` is not an option, as a common use case is to rename the spans created by the auto instrumentation or to add custom tags. Also, `ElasticApm.currentSpan().createSpan()` makes for a much nicer and more object-oriented API than starting a span on a tracer, passing in the parent as a start option. In preparation of elastic#145
Codecov Report
@@ Coverage Diff @@
## master #417 +/- ##
===========================================
+ Coverage 66.31% 71.7% +5.39%
+ Complexity 1419 1315 -104
===========================================
Files 158 146 -12
Lines 6103 5104 -999
Branches 684 521 -163
===========================================
- Hits 4047 3660 -387
+ Misses 1797 1210 -587
+ Partials 259 234 -25
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Makes a cleaner API. Few general comments:
- If I understand correctly,
AbstractSpanImpl
can now hold a ref to anyTraceContextHolder
- I understand that the name needs to stayspan
so not to not break API, but please change that inAbstractSpanImpl
documentation and update theAbstractSpanInstrumentation
to utilize the full benefits of this - I saw you removed the binary serialization option. However, this PR is an infrastructure that enables activating a context in a different thread and we can't use the same instance due to recycling issues. Maybe use this PR to also pool
TraceContext
s that are used outside of Spans, so that when you want to activate aTraceContext
on a different thread- take one from the pool and usecopyFrom
. Then we are allocation-free and still pool-safe.
apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java
Show resolved
Hide resolved
apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java
Show resolved
Hide resolved
apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java
Outdated
Show resolved
Hide resolved
apm-agent-core/src/main/java/co/elastic/apm/agent/impl/SpanListener.java
Outdated
Show resolved
Hide resolved
apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/TraceContextHolder.java
Outdated
Show resolved
Hide resolved
...pm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/AbstractSpanInstrumentation.java
Outdated
Show resolved
Hide resolved
apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/TraceContextHolder.java
Outdated
Show resolved
Hide resolved
...pm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/AbstractSpanInstrumentation.java
Outdated
Show resolved
Hide resolved
...pm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/AbstractSpanInstrumentation.java
Outdated
Show resolved
Hide resolved
...pm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/AbstractSpanInstrumentation.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One very minor suggestion
...pm-api-plugin/src/main/java/co/elastic/apm/agent/plugin/api/AbstractSpanInstrumentation.java
Outdated
Show resolved
Hide resolved
Thanks for the excellent review! 👌 |
Introducing
TraceContextHolder
in the internal API as a commonsuperclass of both
TraceContext
andAbstractSpan
. Given an instanceof this class, you can create child spans, capture exceptions and manage
activations/scopes.
This abstraction reliefs clients from having to differ between the case
when the current activation is a
TraceContext
vs anAbstractSpan
.A
TraceContext
would be active when the current thread does not ownthe lifecycle of the parent span. Otherwise an
AbstractSpan
would beactive.
There is rarely a case where a client would need to get the currently
active span to set the name or tags. This propagation is normally done
with
@Advice.Local
variables from enter to exit advices. Should theneed for that arise however, and the client can ensure that the current
activation is actually an
AbstractSpan
, all they need to do is to casttracer.getActive()
fromTraceContextHolder
toAbstractSpan
.The public API does not differ between
Span
andTraceContextHolder
.ElasticApm.currentSpan()
always returns aSpan
. In case the currentactivation is a
TraceContextHolder
, methods likeSpan#setTag
aresilent noops. Calling those methods on a
Span
whose lifecycle is notowned by the caller is illegal anyway. However, calling
ElasticApm.currentSpan().createSpan()
is always allowed.Taking away
ElasticApm.currentSpan()
is not an option, as a common usecase is to rename the spans created by the auto instrumentation or to
add custom tags.
Also,
ElasticApm.currentSpan().createSpan()
makes for a much nicer andmore object-oriented API than starting a span on a tracer, passing in
the parent as a start option.
In preparation of #145