Add Activity to DiagnosticSource #15103
Conversation
|
Hi @lmolkova, I'm your friendly neighborhood .NET Foundation Pull Request Bot (You can call me DNFBOT). Thanks for your contribution! TTYL, DNFBOT; |
|
@vancem PTLA |
|
@lmolkova Could you please sign the contribution agreement using the above link? |
|
It adds new API -- @vancem do we need API review first? Or is it porting of Desktop API? |
|
cc: @valenis |
|
Hi @lmolkova, I'm your friendly neighborhood .NET Foundation Pull Request Bot (You can call me DNFBOT). Thanks for your contribution! TTYL, DNFBOT; |
|
To answer @karelz, this is new surface area, and needs a API review. @lmolkova previously created issue #15023 to track that API review. I suggested that she create this pull request because it will have many of the details (e.g. descriptions of each API and the guidance on how to use the class) which would be helpful in doing the review. We should of course wait for the review to be complete before merging this pull request. |
|
Got it, that makes sense - folks usually add [WIP] prefix to PRs like that. Let me do that. |
|
@lmolkova, Thanks for signing the contribution license agreement so quickly! Actual humans will now validate the agreement and then evaluate the PR. |
1de6e8c to
365361f
Compare
5562c65 to
cef093b
Compare
| get | ||
| { | ||
| if (ParentId != null) | ||
| yield return new KeyValuePair<string, string>("ParentId", ParentId); |
There was a problem hiding this comment.
ParentId shouldn't be returned as a tag because it is a property.
If it stays as a tag, what's the reason for having ParentId but not Id, OperationName, ... as tags?!?
There was a problem hiding this comment.
ParentId is returned in Tags to simplify activity logging: we include ParentId to make sure it will be logged. It's good idea to apply the same behavior to Id and operation name.
There was a problem hiding this comment.
The user didn't add the ParentId tag so why should it be returned on a call to Tags?! "ParentId" is a magic string that could easily be avoided by accessing the property.
Therefore I vote for not including it as a tag.
| } | ||
|
|
||
| /// <summary> | ||
| /// Tags are string-string key-value pairs that represent information that will |
| { | ||
| get | ||
| { | ||
| for (var activity = this; activity != null; activity = activity.Parent) |
There was a problem hiding this comment.
In OpenTracing, baggage is copied to child spans/activities, so later changes to a parent don't affect children. This has advantages (immutability) and disadvantages though.
There was a problem hiding this comment.
We would like to avoid copying baggage.
Code which adds baggage to parent activity should expect that new child activity could be created/sent outside of process at any moment, so for this case there is no difference if Baggage/SpanContext is mutable or not: any child activity may get or may not get parent baggage item.
There is a slight difference for in-proc children, that they will receive any parent update, but for the sake of performance, we allow it.
There was a problem hiding this comment.
@lmolkova I believe we should preserve the semantics that there are COPIES made when children are made. The current implementation has this property because we don't allow mutation of EXISTING entries (we allow people to add a existing entry (which has the effect of overwriting that entry in 'GetBaggageItem' but the semantics is copy. If we every allowed update, we would want to copy in that case. (not an issue right now...).
| /// </summary> | ||
| /// <param name="operationName">Operations name <see cref="OperationName"/></param> | ||
| public Activity(string operationName) | ||
| { |
There was a problem hiding this comment.
Null/empty/whitespace check for operationName?
There was a problem hiding this comment.
I'm uncertain about it. Why it is not valid? It does not seem to break anything. I also see opentracing-java does not require it.
OperationName is something which makes sense in client code which assigns and uses it.
There was a problem hiding this comment.
You use it for the identifier in the DiagnosticSource.Write call. Is there any valid case for an empty/null operationName? I can't think of one
There was a problem hiding this comment.
There is no good reason for a null operation name. However ideally logging is as lean as possible so if we can get away without some run time checking (do the checking later), that is better. If we believe this is a common error, then I would add it, but my intuition suggests that it won't be common and is not catastrophic even if it does happen, so we can do nothing.
| /// <returns>'this' for convinient chaining</returns> | ||
| public Activity AddTag(string key, string value) | ||
| { | ||
| _tags = new KeyValueListNode() { keyValue = new KeyValuePair<string, string>(key, value), Next = _tags }; |
There was a problem hiding this comment.
Since tags and baggage are linked lists right now, there's no way to overwrite existing keys. However, I think this is a necessary and very important feature!
There was a problem hiding this comment.
Could you share scenarios for this feature?
I can imagine there is some part of baggage which in fact describes some details about caller rather than global context. And should be updated on every child, e.g. caller version, user, device, whatever. We are considering such scenarios as a future step and it would be great to know if opentracing has a story for them.
There was a problem hiding this comment.
Note that the implementation of baggage as a linked list does allow overwriting keys (after all you can call 'AddBaggage' at any time and it will add the new value to the list.
We chose this data structure because it was efficient it the most likely scenarios (a very small number of unique entries which can be passed very efficiently (no copy, simply reference), to any sub-activities).
It is true, however that the current implementation leaves the old value in the list. This does not affect the GetBaggageItem() (which gets the most recently added key), (which is the primary use for baggage, control at runtime), but does affect the 'Baggage' IEnumerable. This can of course be fixed, but having but does complicate the implementation. It is worth consideration...
| /// <returns>'this' for convinient chaining</returns> | ||
| public Activity SetStartTime(DateTime startTimeUtc) | ||
| { | ||
| StartTimeUtc = startTimeUtc; |
There was a problem hiding this comment.
What should happen if Kind is Unspecified or Local?
| /// <returns>'this' for convinient chaining</returns> | ||
| public Activity SetEndTime(DateTime endTimeUtc) | ||
| { | ||
| Duration = endTimeUtc - StartTimeUtc; |
There was a problem hiding this comment.
What if Duration ends up negative? What if endTime has a different Kind?
Why not have a Stop overload that accepts the endTimeUtc?
There was a problem hiding this comment.
Stop will be called after DiagnosticSource.Write will notify user about activity being stopping.
Here I'd like to give control to user: if he wants to set stop time in the past, I don't see reasons to prevent it. I agree though we should insist on Utc Kind.
|
|
||
| Id = GenerateId(); | ||
|
|
||
| Current = this; |
There was a problem hiding this comment.
I'm not sure if this is a good idea. Setting static properties in instance methods is bad practice and unexpected. Would it be better to have this in DiagnosticSource.StartActivity?!
| #region private | ||
|
|
||
| private string GenerateId() | ||
| { |
There was a problem hiding this comment.
Weird algorithm.
As far as I've seen, most tracers use e.g. 128bit random numbers to be able to easily compute sampling rates. (1% of all requests means that all numbers below a threshold will be tracked)
Also, why have different logic for DEBUG/RELEASE?
| /// reasonable, but arguments (e.g. specific accounts etc), should not be in | ||
| /// the name but rather in the tags. | ||
| /// </summary> | ||
| public string OperationName { get; } |
There was a problem hiding this comment.
In OpenTracing there are cases where you want to overwrite the operationName. E.g the hosting layer uses a generic "http-in" and the MVC layer overwrites it with "controller/action".
Here this is problematic though because you use the operationName for the DiagnosticSource events as well. Not sure if/how to cover this?!
There was a problem hiding this comment.
I see there may be a case for it. Another approach could be to start new Activity (Span) In MVC with a new name, however Tags won't flow.
I would prefer application to add Tag to activity describing controller/action rather than changing operationName. From the logs prospective, it's useful to see same operation name on start and stop events.
|
@lmolkova have you measured the performance impact of this on a typical http request? |
cef093b to
e054224
Compare
Outgoinghere are the measurements for a very short outgoing request to localhost, 10000 runs:
Difference is around 23 microseconds per request in average. Impact must not depend on outgoing request parameters, but may depend on DiagnosticSource event filter which is under user control. IncomingOn a simple AspNet Core WebApi app with instrumented incoming request latency standard deviation is quite high comparing to outgoing requests even on 10000 runs. |
|
@karelz, the API #15023 has been approved and PR is ready for review. I wonder if it should still be labeled as no-merge and blocked? |
|
I just looked over all the feedback, and none of it feels blocking to me. However would like to take a look at exactly where we landed I will do this by tomorrow morning. |
|
LGTM. While there still might be changes, I am OK with what IS present (it may be missing things, but they can be added compatibly). This allows us to check this in, and build an end-to-end system with it and be in a better position to add any missing things later. |
1a29d13 to
ca3ce3b
Compare
6cd68af to
3a0882c
Compare
| <Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
| <PropertyGroup> | ||
| <BuildConfigurations> | ||
| net45-Windows_NT; |
There was a problem hiding this comment.
Does this configuration even build? We haven't even enabled net45 builds yet.
There was a problem hiding this comment.
Correct, I cannot build it on net45. Do you suggest to remove build configuration for net45 until it's enabled?
There was a problem hiding this comment.
I think @tarekgh was hitting this while trying to make netfx build configurations work so I suspect he will take care of removing this. However in general we should avoid adding configurations that don't work yet unless we have follow-up items to make them work.
For this library what is the goal of adding the net45 configuration?
There was a problem hiding this comment.
The feature implemented here will be used in ASP.NET Core, which also targets .NET 4.5.1
Some APIs (CallContext, which is crucial for the feature) are not available on netstandard1.1 and available on .NET 4.5.
There was a problem hiding this comment.
We are in the process of bringing up build support for netfx versions I would highly suggest you file a follow-up item to get that configuration enabled for this library if it is a desired configuration you want to support in the package.
| <OSGroup>Windows_NT</OSGroup> | ||
| <TestTFMs>netcore50;net46</TestTFMs> | ||
| </Project> | ||
| <Project Include="System.Diagnostics.DiagnosticSource.Tests.csproj"> |
There was a problem hiding this comment.
The .builds files are no longer used. I suspect you didn't test any of these changes on desktop because I don't expect your desktop configuration even builds today.
There was a problem hiding this comment.
Are .build files are no longer user for tests as well? I see many projects still have them.
Can I assume tests will run for the targets provided in .csproj and safely remove .build file from the repo?
There was a problem hiding this comment.
Correct .builds files are no longer used. We just haven't deleted them all yet, we have a clean-up item to delete them all.
…urce_activity Add Activity to DiagnosticSource Commit migrated from dotnet/corefx@3290d33
This change introduces Activity class which provides operation context for DiagnosticSource, see #15023 and User Guide for more details.