-
Notifications
You must be signed in to change notification settings - Fork 373
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
[OpenTracing] Span and SpanContext implementation #472
[OpenTracing] Span and SpanContext implementation #472
Conversation
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.
Just a couple of things.
lib/ddtrace/opentracer/span.rb
Outdated
# @param key [String] the key of the baggage item | ||
# @param value [String] the value of the baggage item | ||
def set_baggage_item(key, value) | ||
tap { context.baggage[key] = value } |
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.
Do we want the span to be in charge of manipulating the data within the baggage?
By having the span context take care of manipulating the baggage we can abstract away the implementation details of the baggage.
Although it is unlikely that the implementation of the baggage will change.
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 agree, I don't think the span should be responsible for knowing details about baggage on the span context. That's why I'm a bit confused about this: if the span context manages the baggage, why is there a baggage function on the span at all?
We could implement baggage functions on the span context object that mirror this, and have this span delegate to those. I guess I'm hesitant to do that only because it seems like a modification of the OT spec. But since its all internal anyways, maybe it's okay.
@Kyle-Verhoog What are your thoughts: move this to SpanContext and delegate from Span, or keep as is?
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 think the delegate makes sense. Also, I forgot to mention this earlier, but we gotta keep in mind the span context instance should be immutable[0]. So setting a key in the baggage for a span context should produce a new span context instance with the key which the span will use.
[0] https://github.com/opentracing/specification/blob/master/specification.md#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 think what this could look like is spancontext.set_baggage_item returns a new span context instance with the new item added and span.set_baggage_item will update its span context reference with this new instance.
(This is a change I have to make in the python tracer still).
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.
@Kyle-Verhoog Okay, I think you're right about the immutable part. I think the implementation might need to be a little different: how about we don't implement SpanContext#set_baggage_item
and force the use of SpanContext#new
constructor which accepts baggage? Then do as you said, and have the Span#set_baggage_item
build a new SpanContext
from an old one?
The more complicated this SpanContext
management gets, the more it screams "factory pattern" to me. Especially if the tracer has to build these too.
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.
Sounds good to me! The SpanContext#new
should duplicate the baggage that it receives. 👍
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.
Alright, I'll go ahead and make this change then.
# Add a log entry to this span | ||
# @param timestamp [Time] time of the log | ||
# @param fields [Hash] Additional information to log | ||
def log_kv(timestamp: Time.now, **fields) |
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.
What about the other error fields (stack
, event
, etc)? Or does error.object
basically encapsulate all of these?
All standard log fields:
https://github.com/opentracing/specification/blob/master/semantic_conventions.md#log-fields-table
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.
Datadog::Span#set_error
only accepts an StandardError object, which contains the message, stack, and type. That function then pulls those parts out and tags the span with them appropriately.
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 tiny comment otherwise LGTM!
@@ -47,23 +61,30 @@ | |||
let(:timestamp) { Time.now } | |||
let(:fields) { { time_started: Time.now, account_id: '1234' } } | |||
|
|||
before(:each) do | |||
# Expect a deprecation warning to be output. | |||
it do |
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.
Would it 'expects a deprecation warning to be output.'
work here?
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.
You could do that, and that would be okay. What matters is that the test is easy to read and understand. The language as its written reads as "it expects log to output 'Span#log is deprecated.' to stderr." In my mind, I think this is an abundantly clear statement, that does not require clarification by naming the example.
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.
Not to bikesched too much. I have mixed feelings about using nameless examples. Having explicit name to a example most often than not makes it more clearer than the code itself.
And while 1 line and relatively short nameless examples are fine and easy to read most of the time.
However examples like:
it do
expect {x}.to change {z}
end
look like a exception from this rule.
However since beauty lies in the eye of beholder I'm fine with both :).
Lets come back ot this discussion a bit later when working on Rubocop rules
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.
For what its worth, the style choice here isn't arbitrary, it's an implementation of the http://www.betterspecs.org/ standard, which I think does a better job of explaining the reasoning than I can here.
This all comes down to style ultimately I think. Sometimes it's worth discussing the possible merits, and it's totally reasonable to suggest alternatives, but given that both suggested styles are clear and understandable, it doesn't seem like there's much to gain by dwelling too much on things like this. The same would apply if the circumstances were reversed, and it was the named example in the PR instead of the nameless one.
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.
Looks good.
tap do | ||
# SpanContext is immutable, so to make changes | ||
# build a new span context. | ||
@span_context = SpanContextFactory.clone( |
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.
👍
:datadog_context | ||
|
||
def initialize(datadog_context:, baggage: {}) | ||
@datadog_context = datadog_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.
👍
* Added: Datadog::OpenTracer::SpanContext implementation. * Added: Datadog::OpenTracer::Span implementation. * Changed: Bump required Ruby version for OpenTracing to 2.1. * Added: Datadog::OpenTracer::SpanContextFactory. * Changed: OpenTracer::Span#set_baggage_item to create new SpanContexts. * Changed: Datadog::OpenTracer::SpanContext#baggage to be immutable. * Added: span_id, trace_id, parent_id to Datadog::OpenTracer::SpanContext. * Changed: Replaced span_id, trace_id, parent_id with reference to Datadog::Context.
* Added: Datadog::OpenTracer::SpanContext implementation. * Added: Datadog::OpenTracer::Span implementation. * Changed: Bump required Ruby version for OpenTracing to 2.1. * Added: Datadog::OpenTracer::SpanContextFactory. * Changed: OpenTracer::Span#set_baggage_item to create new SpanContexts. * Changed: Datadog::OpenTracer::SpanContext#baggage to be immutable. * Added: span_id, trace_id, parent_id to Datadog::OpenTracer::SpanContext. * Changed: Replaced span_id, trace_id, parent_id with reference to Datadog::Context.
* Added: Datadog::OpenTracer::SpanContext implementation. * Added: Datadog::OpenTracer::Span implementation. * Changed: Bump required Ruby version for OpenTracing to 2.1. * Added: Datadog::OpenTracer::SpanContextFactory. * Changed: OpenTracer::Span#set_baggage_item to create new SpanContexts. * Changed: Datadog::OpenTracer::SpanContext#baggage to be immutable. * Added: span_id, trace_id, parent_id to Datadog::OpenTracer::SpanContext. * Changed: Replaced span_id, trace_id, parent_id with reference to Datadog::Context.
* Added: Datadog::OpenTracer::SpanContext implementation. * Added: Datadog::OpenTracer::Span implementation. * Changed: Bump required Ruby version for OpenTracing to 2.1. * Added: Datadog::OpenTracer::SpanContextFactory. * Changed: OpenTracer::Span#set_baggage_item to create new SpanContexts. * Changed: Datadog::OpenTracer::SpanContext#baggage to be immutable. * Added: span_id, trace_id, parent_id to Datadog::OpenTracer::SpanContext. * Changed: Replaced span_id, trace_id, parent_id with reference to Datadog::Context.
* Added: Datadog::OpenTracer::SpanContext implementation. * Added: Datadog::OpenTracer::Span implementation. * Changed: Bump required Ruby version for OpenTracing to 2.1. * Added: Datadog::OpenTracer::SpanContextFactory. * Changed: OpenTracer::Span#set_baggage_item to create new SpanContexts. * Changed: Datadog::OpenTracer::SpanContext#baggage to be immutable. * Added: span_id, trace_id, parent_id to Datadog::OpenTracer::SpanContext. * Changed: Replaced span_id, trace_id, parent_id with reference to Datadog::Context.
* Added: Datadog::OpenTracer::SpanContext implementation. * Added: Datadog::OpenTracer::Span implementation. * Changed: Bump required Ruby version for OpenTracing to 2.1. * Added: Datadog::OpenTracer::SpanContextFactory. * Changed: OpenTracer::Span#set_baggage_item to create new SpanContexts. * Changed: Datadog::OpenTracer::SpanContext#baggage to be immutable. * Added: span_id, trace_id, parent_id to Datadog::OpenTracer::SpanContext. * Changed: Replaced span_id, trace_id, parent_id with reference to Datadog::Context.
* Added: Datadog::OpenTracer::SpanContext implementation. * Added: Datadog::OpenTracer::Span implementation. * Changed: Bump required Ruby version for OpenTracing to 2.1. * Added: Datadog::OpenTracer::SpanContextFactory. * Changed: OpenTracer::Span#set_baggage_item to create new SpanContexts. * Changed: Datadog::OpenTracer::SpanContext#baggage to be immutable. * Added: span_id, trace_id, parent_id to Datadog::OpenTracer::SpanContext. * Changed: Replaced span_id, trace_id, parent_id with reference to Datadog::Context.
This pull request implements the OpenTracing
Span
andSpanContext
. In their current forms, they are very simple, and may not represent the full scope of what will ultimately be supported.Some worthwhile notes:
Span
andSpanContext
are still somewhat undetermined. They may require changes depending on how and where we actually construct and configure spans. The current plan is that they are treated like simple data structs, that are not responsible for building sophisticated Spans or SpanContexts. That logic is anticipated to be a part of the Tracer, or some kind of factory pattern.log
andlog_kv
should be implemented. For now, they simply set errors, if provided with one. Otherwise they do nothing. We didn't interpret these functions to have responsibilities beyond setting some "debug tags", as opposed to doing logging in the traditional sense.span_id
andtrace_id
to theSpanContext
, since this object is meant to be a serializable layer of data for sharing trace data across process boundaries (particularly for the purposes of distributed tracing.)