-
Notifications
You must be signed in to change notification settings - Fork 848
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
[Do not merge] Initial prototype for context-prop overhaul. #655
[Do not merge] Initial prototype for context-prop overhaul. #655
Conversation
Codecov Report
@@ Coverage Diff @@
## master #655 +/- ##
============================================
- Coverage 79.39% 78.73% -0.66%
Complexity 712 712
============================================
Files 88 90 +2
Lines 2514 2535 +21
Branches 238 238
============================================
Hits 1996 1996
- Misses 418 439 +21
Partials 100 100
Continue to review full report at Codecov.
|
import io.opentelemetry.context.propagation.HttpInjector; | ||
import java.util.List; | ||
|
||
public interface CorrelationsManager { |
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.
Is this what we called Tags in opencensus? I like Tags better.
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.
Yes - I called this Correlations
as this is the used name in the related OTEPS ;)
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.
Please tell @tedsuo that Tags is a better name :)
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.
@bogdandrutu you and @yurishkuro have a jello wrestling match, the winner can pick the name. :)
|
||
package io.opentelemetry.context; | ||
|
||
public final class Context { |
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.
If we add this class we also need all the helpers:
- Wrap callable/runnable
- Wrapper for an Executor
- Run/Call helper to run a Runnable/Callable with the context.
Also we need to make sure somehow that users are not double wrapping things with this Context and io.grpc.Context when they use both opentelemetry and io.grpc.Context.
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.
If we add this class we also need all the helpers:
Good point, yes. Although I wonder if we could postpone them (for a second version), or you think we would need them right from the start?
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.
Without them users cannot instrument their code. That is the main reason I actually used directly the grpc one, to not duplicate code unnecessary.
I would like to see how an RPC integration will look like with the new Injector/Extractor
Is baggage expected to be attached to and reported with each span? If not, maybe we should consider separating baggage out to a separate project as well. (Sorry, I don’t have a great intuition around baggage.) |
No, it won't be attached (by default). And yes, there has been some talk about separating this one (as well as context prop itself ;) ) |
cc @yurishkuro (probably this can be of interest to you ;) ) |
Not to complicate things, but having the SDK be able to access the entire context during span calls was my solution for having baggage be an independent system, which could still optionally be reported by the observability system. |
So, as long as Span getCurrentSpan() {
String baggageVal1 = OpenTelemetry.baggageManager().getValue(
Context.current(), "mykey");
...
return (Span) Context.current().getValue(CURRENT_SPAN_KEY);
} |
This concern raised by @bogdandrutu makes me wonder about not having an intermediate layer abstracting the Context concept in every language - for Java, this would simplify things, but it would force all implementations to have to use |
In languages like Go, Python that have a Context already we should just use that. In other languages we should decide between implement our own or use an existing one. |
Hey @arminru Was wondering about your opinion here, regarding using |
Hi @carlosalberto! Also see #575 against having api dependency on |
@carlosalberto I don't see any issue for us in particular but I am wondering if adding a hard dependency to |
Hey all - the latest iteration does the following:
// Extract.
Context ctx = extractor.extract(Context.current(), inboundCarrier, new MapGetter());
try (Scope scope = Context.setCurrent(ctx)) {
... Feel free to review it and comment the ongoing prototype/API. Notes/questions
PS - I still have the |
I don't know of any necessity either but it doesn't really hurt since one can just implement both interfaces in a single propagator class and share that instance for both injecting and extracting. |
Btw, as inspecting the dummy example in the actual code changes, I'm pasting it a link to a gist of it ;) https://gist.github.com/carlosalberto/1d91dc23967ff6442758d858b0d0fe25 |
private static final Context.Key<Object> BAGGAGE_KEY = Context.createKey("baggage"); | ||
|
||
public static Context.Key<Object> getSpanContextKey() { | ||
return BAGGAGE_KEY; |
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.
Should there be a separate key for span context or is it actually being stored in baggage?
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.
Separate key for SpanContext
;)
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.
I see, there is a getSpanContextKey
for each context (key).
<C> Context extract(Context ctx, C carrier, Getter<C> getter); | ||
|
||
interface Getter<C> { | ||
@Nullable |
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.
As discussed in our meeting today, This interface should have another method that provides access to all the headers:
Iterable<String> headers();
This is done in order to show how extraction would work given the prototype changes.
@bogdandrutu I've 'ported' a super small integration for web-servlet (from OT, as that's the one I was slightly familiar with), which shows how extraction would work (even if only (I'm also wondering if you need something more specific, such as Thrift or gRPC - let me know if that's the case ;)) The important code lives in /* In a real world scenario, a single instance would be shared/used. */
HttpTraceContextExtractor extractor = new HttpTraceContextExtractor();
/**
* SpanContext *and* other members (such as correlationcontext) would be extracted here, and
* make it available in the returned Context object.
*
* For further consumption, the returned Context object would need to be explicitly passed
* to DistributedContext/Baggage handlers, or else set it automatically as the current instance.
*/
Context ctx =
extractor.extract(Context.current(), httpRequest, HttpServletRequestGetter.getInstance());
SpanContext extractedContext = ctx.getValue(ContextKeys.getSpanContextKey());
Span span =
tracer
.spanBuilder(httpRequest.getMethod())
.setParent(extractedContext)
.setSpanKind(Span.Kind.SERVER)
.startSpan(); |
8cc6b84
to
d6c9dfa
Compare
Hey all - I've added the Global Propagators part from the referenced prototype. This is now implemented using a class (should we do interface?) called public final class Propagators {
public static Propagators create(HttpInjector injector, HttpExtractor extractor);
public HttpExtractor getHttpExtractor();
public HttpInjector getHttpInjector();
// Add here binary propagators and other members.
} Then it can registered in HttpInjector injector = ChainedPropagators.chain(...);
HttpExtractor extractor = ChainedPropagators.chain(...);
OpenTelemetry.setPropagators(Propagators.create(injector, extractor));
...
OpenTelemetry.getPropagators().extract(Context.current(), carrier, carrierGetter); This is a very simple design that, I think, should work without many problems. Users would consume such Putting all propagators in a single class could also be used to remove the chained registration if needed, as we could even have something like: Propagators props = Propagators.newBuilder()
.addHttpExtractor(TraceContextExtractor())
.addHttpExtractor(DefaultCorrelationContextExtractor())
.addHttpExtractor(DefaultBaggageExtractor())
.build();
OpenTelemetry.setPropagators(props); |
Closing this on behalf of #720 |
This is an initial prototype for the context-prop changes - below is a summary of the changes, in addition to a possible alternative approach for this (also, this prototype is meant to be reviewed by non-Javaers, in order to validate the contest-prop proposal).
Observe that the main code is not touched, and only the new classes have been added (I had intended to re-adjust the entire code base, which I'd be happy to do once we decide the path to take).
Also,
DistributedContext
and its related class would be removed under this initial protoype.Summary
Context
The current effort makes
Context
an explicit parameter for all related operations - with the help ofContext.current()
, which makes the current instance available all time. ThisContext
class acts as layer on top ofio.grpc.Context
, in case we/some vendor want to replace with something else (while also hiding advanced features, such as cancellation).Actual top-level objects (active
Span
, the container for baggage, the container for correlations, etc) all live in its own bucket withinContext
, which can be set/accessed through a key object, which is kept privately by each owner (and accessible to theInjector
/Extractor
in turn):Simple string keys cannot be used to access the underlying object, as a limitation imposed by
io.grpc.Context
. But direct access to the objects inContext
could be offered (as opposed to have convenience methods such asTracer.getCurrentSpan()
), through exposing a set of predefined keys:Question: Should we make this interface, and make it pluggable as part of the
OpenTelemetry
object? Then it could be accessed throughOpenTelemetry.getContextManager().current()
.Baggage/Correlation managers
No direct access would exist to the baggage/correlation containers, and such values would have to be fetched through their respective managers:
Propagators
Injectors/extractors would work directly with a
Context
object, fetching each own the bucket they want and work with it:Alternative approach
During playing with the approach above, I though about an alternative approach:
Context
an explicit component (just like above) which works mostly behind the scenes.DistributedContext
toCorrelationContext
and adjust the operations (removegetEntryValue()
, etc).BaggageContext
object, with the set/clear operations (BagaggeContext.removeValue()
, for example).Tracer.getCurrentSpan()
andCorrelationContext.setCurrent()
stay, which would work withContext
behind the scenes, implicitly (so the user does not have to think of the context all the time).Context
could, however, be accessed all the time as an advanced feature.Context
directly, so Injection/Extraction can be done in a single step.Code with this design would look like this: