Upgrade Telemetry to V2#3551
Conversation
There was a problem hiding this comment.
Telemetry doesn't have to start inside the global. In fact, while we're factoring it, I triggered a deadlock a few times while trying to read global information for telemetry purposes.
There was a problem hiding this comment.
The time has come for someone besides tests to use this data!
There was a problem hiding this comment.
The internal worker instance is now private: it's an implementation concern.
| # @public_api | ||
| class Option | ||
| attr_reader :definition | ||
| attr_reader :definition, :precedence_set |
There was a problem hiding this comment.
This is the effective precedence of the value this option is currently set to.
For example, if this option was never set, the precedence will be "default".
If the same option is then set by programmatic configuration (Datadog.configure{}), it will change to "programmatic".
Then, again, if it is over, written by remote configuration, it will change to "remote configuration".
Now, the interesting part is that same option is then again remote configuration by programmatic configuration, it will not change value, given remote configuration has a higher precedence.
Let me know if this makes sense. Also, I expanded the in-line documentation of this class regarding precedence.
On another topic, it probably makes sense to simply rename me to precedence, what do you think? I would probably do that in a separate PR as, it will touch many files and related to this PR, but it's a very simple rename to do as a follow up.
There was a problem hiding this comment.
Gotcha. Was just curious. No strong opinions at this point, so if you're confident on this, I'm interested to see how it works.
| return if !@enabled || forked? | ||
|
|
||
| res = @emitter.request(:'app-started') | ||
| res = @emitter.request(Event::AppStarted.new) |
There was a problem hiding this comment.
I like the use of a class. Nit, but does it make sense to initialize an object?
There was a problem hiding this comment.
This pattern of initializing an object makes sense for more complex the telemetry events like AppClientConfigurationChange:
AppClientConfigurationChange needs information to be provided in two different contexts: first it needs to know what has changed in the source of that change (changes, 'remote_config'), then later it needs to receive the auto-incrementing seq_id:
Because these two sets of information exist in different contexts, I need a way to store the first set (changes, 'remote_config') and then merge it with the second set (seq_id).
So in this case, it makes sense to initialize the class and then call a method accepting a parameter with the remaining information.
That being said, most events are simple, like you mention for AppStarted, and don't actually need two sets of data to build the event, but in order to keep the API consistent, I had to add this flexibility to all events.
Let me know if this makes sense or if you have further suggestions.
There was a problem hiding this comment.
I see: sometimes it has state attached. I suppose most of these events don't fire often, but if they ever fired as often as traces (crazy possibly, I know), then I was imagining this would apply more pressure to memory.
Still I do like the event as a parameter from a flexibility point of view.
Feedback is not blocking.
| class Event | ||
| include Telemetry::Collector | ||
| # Base class for all Telemetry V2 events. | ||
| class Base |
There was a problem hiding this comment.
Should this be a class? Or could it be a module? (Given it doesn't introduce state.)
There was a problem hiding this comment.
I think the hierarchy here is that events are true children of a base class: an event is a subtype of a Base event.
It can technically work as a module, but I think that it would create the misleading impression that events just so happen to respect the Base interface, when in fact all events are a subtype of a Base event.
There was a problem hiding this comment.
I see, this got me thinking about Sandi Metz's take on inheritance/composition. Some notes surrounding her discourse on the subject that I could find:
Deciding Between Inheritance and Composition
Inheritance gives you message delegation for free at the cost of maintaining a class hierarchy. Composition allows objects to have structural independence at the cost of explicit message delegation.
Composition contains far few built-in dependencies than inheritance; it is very often the best choice.
With inheritance, a correctly modeled hierarchy will give you the benefit of propogating changes in the base class to all subclasses. This can also be a disadvantage when the hierarchy is modeled incorrectly, as a dramatic change to the base class due to a change in requirements will break sub-classes. Inheritance = built-in dependencies.
Enormous, broad-reaching changes of behavior can be achieved with very small changes in code. This is true, for better or for worse, whether you come to regret it or not.
Avoid writing frameworks that require users of your code to subclass your objects in order to gain your behavior. Their application's objects may already be arranged in a hierarchy; inheriting from your framework may not be possible.
On that very last point: is there a case where we might want to have independent trees of event behavior? I don't think immediately, no, but that would be a strong case for composition over inheritance. I suppose to me, it would be more a factor of keeping our options open in accordance to the principle of least harm: that composition would be less restraining.
Another way I think about the reason to do one over the other is state (e.g. instance variables).
- Does the base class define or maintain state? (Internal or otherwise?)
- Does it need some initialization (constructor or otherwise)?
If yes to either, then probably should be inheritance, because modules are not great at maintaining state. Otherwise consider using a module.
Some food for thought. Ultimately, we can always refactor this if it doesn't constitute a breaking change, so no blocker on my side if we can always change later without much pain.
There was a problem hiding this comment.
Deciding Between Inheritance and Composition
It seems like the composition that she is discussing is about having a delegator object. And I'm not sure that that applies here because the need is for a consistent interface. There's almost no behaviour being delegated to the base class, is for subclasses to respect interface.
Both classes and modules would fall under inheritance, based on her definition above I believe.
There was a problem hiding this comment.
Also, this whole thing is internal and constraint inside a single file, so factoring is trivial, and will likely not even affect unit tests.
| 'app-started' | ||
| end | ||
|
|
||
| API_VERSION = 'v1' |
There was a problem hiding this comment.
Is there any versioning of the API within our code?
There was a problem hiding this comment.
Yes, it's here:
| Ext::HEADER_DD_TELEMETRY_API_VERSION => api_version, | ||
| Ext::HEADER_DD_TELEMETRY_REQUEST_TYPE => request_type, | ||
| Ext::HEADER_CLIENT_LIBRARY_LANGUAGE => Core::Environment::Ext::LANG, | ||
| Ext::HEADER_CLIENT_LIBRARY_VERSION => DDTrace::VERSION::STRING, |
There was a problem hiding this comment.
Just FYI, when ported to 2.x, this will need to be updated.
|
|
|
System tests should now pass. |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #3551 +/- ##
==========================================
- Coverage 98.27% 98.23% -0.04%
==========================================
Files 1274 1254 -20
Lines 75205 74310 -895
Branches 3544 3526 -18
==========================================
- Hits 73906 73001 -905
- Misses 1299 1309 +10 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
The last failing system test ( |
This PR upgrades
ddtraceto send Telemetry in the V2 schema version, instead of V1.Also, this PR cleans up a lot of the existing telemetry code, simplifying its implementation and increasing type checking.
Done
Additional Notes:
How to test the change?
For Datadog employees:
credentials of any kind, I've requested a review from
@DataDog/security-design-and-guidance.Unsure? Have a question? Request a review!