System.Diagnostics Tracing APIs (#35220)#35326
Conversation
* System.Diagnostics Tracing APIs * address the feedback
|
Note regarding the This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, to please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change. |
| private static readonly IEnumerable<ActivityLink> s_emptyLinks = new ActivityLink[0]; | ||
| private static readonly IEnumerable<ActivityEvent> s_emptyEvents = new ActivityEvent[0]; | ||
| #pragma warning restore CA1825 | ||
| private static ActivitySource s_defaultSource = new ActivitySource(string.Empty); |
There was a problem hiding this comment.
| private static ActivitySource s_defaultSource = new ActivitySource(string.Empty); | |
| private static readonly ActivitySource s_defaultSource = new ActivitySource(string.Empty); |
| private string? _displayName; | ||
|
|
||
| /// <summary> | ||
| /// Kind describes the relationship between the Activity, its parents, and its children in a Trace. |
There was a problem hiding this comment.
| /// Kind describes the relationship between the Activity, its parents, and its children in a Trace. | |
| /// Gets or sets the relationship between the Activity, its parents, and its children in a Trace. |
| /// <summary> | ||
| /// DisplayName is name mainly intended to be used in the UI and not necessary has to be | ||
| /// same as OperationName. | ||
| /// </summary> |
There was a problem hiding this comment.
| /// <summary> | |
| /// DisplayName is name mainly intended to be used in the UI and not necessary has to be | |
| /// same as OperationName. | |
| /// </summary> | |
| /// <summary>Gets or sets the display name of the Activity</summary> | |
| /// <remarks> | |
| /// DisplayName is intended to be used in a user interface and need not be the same as OperationName. | |
| /// </remarks> |
| public string DisplayName | ||
| { | ||
| get => _displayName ?? OperationName; | ||
| set => _displayName = value; |
There was a problem hiding this comment.
Is null expected / allowed? If you want to permit null, you should add [AllowNull] to the property. If you don't want to permit null, ideally this would throw, e.g. set => _displayName = value ?? throw new ArgumentNullException(nameof(value));
There was a problem hiding this comment.
I'll change this to
set => _displayName = value ?? throw new ArgumentNullException(nameof(value));
| /// <summary> | ||
| /// Get the ActivitySource object associated with this Activity. | ||
| /// All Activities created from public constructors will have a singleton source where the source name is empty string. | ||
| /// Otherwise, the source will be holding the object that created the Activity through ActivitySource.StartActivity. | ||
| /// </summary> |
There was a problem hiding this comment.
| /// <summary> | |
| /// Get the ActivitySource object associated with this Activity. | |
| /// All Activities created from public constructors will have a singleton source where the source name is empty string. | |
| /// Otherwise, the source will be holding the object that created the Activity through ActivitySource.StartActivity. | |
| /// </summary> | |
| /// <summary>Get the ActivitySource object associated with this Activity.</summary> | |
| /// <remarks> | |
| /// All Activities created from public constructors will have a singleton source where the source name is an empty string. | |
| /// Otherwise, the source will hold the object that created the Activity through ActivitySource.StartActivity. | |
| /// </remarks> |
| private static SynchronizedList<ActivityListener> s_allListeners = new SynchronizedList<ActivityListener>(); | ||
| private SynchronizedList<ActivityListener>? _listeners; | ||
|
|
||
| private ActivitySource() { throw new InvalidOperationException(); } |
There was a problem hiding this comment.
I left it when I was doing some internal testing. It was not intended to stay. Thanks for the catch.
| // Causion: We can have the action executed on the same item more than once which is ok in our scenarios. | ||
| internal class SynchronizedList<T> | ||
| { | ||
| private List<T> _list; |
There was a problem hiding this comment.
| private List<T> _list; | |
| private readonly List<T> _list; |
| // SynchronizedList<T> is a helper collection which ensure thread safety on the collection | ||
| // and allow enumerating the collection items and execute some action on the enumerated item and can detect any change in the collection | ||
| // during the enumeration which force restarting the enumeration again. | ||
| // Causion: We can have the action executed on the same item more than once which is ok in our scenarios. |
There was a problem hiding this comment.
| // Causion: We can have the action executed on the same item more than once which is ok in our scenarios. | |
| // Caution: We can have the action executed on the same item more than once which is ok in our scenarios. |
There was a problem hiding this comment.
why English lack having Causion :-) interesting the editor spell checker didn't complain. of course, I'll fix it. thanks.
| { | ||
| lock (_list) | ||
| { | ||
| if (!_list.Contains(item)) |
There was a problem hiding this comment.
How big do we expect these lists to be? And more importantly, what's the worst case? N calls to AddIfNotExist results in O(N^2) work here.
There was a problem hiding this comment.
We are expecting the lists to be short. For activity sources, we expect would be one per component if the component publishing any tracing data. For the listeners, usually, the list will be shorter as in main scenario cases we expect one listener for who is interested to get the Activity notification (e.g. OpenTelemetry for instance). usual apps/Libraries not going to create listeners.
| { | ||
| version = _version; | ||
| index = 0; | ||
| continue; |
There was a problem hiding this comment.
So if there's a mutation we start over? Hence the warning about potentially invoking delegates multiple times? This means if one bad actor was repeatedly adding / removing / adding / removing / etc. while someone else was enumerating, that someone else might never end?
There was a problem hiding this comment.
Yes, it is safe in the calling scenarios. We are calling the listeners to detect if we need to create the Activity. I am not seeing a problem calling the listener multiple times with the same parameters multiple parameters when someone else mutates the list.
The lists we have are the ActivtySource lists and listeners inside the activity source. If there is a bad actor you can do many more bad things creating a lot of Activity sources and listeners. or even in the listening can spend a long time in the execution.
|
@stephentoub could you please tell which of your comments should block getting this to preview 4? I am asking because I can address your comments on master instead and safely catch preview 4. |
|
I didn't realize this was already merged to master. It'd be good to at least reason about the last two comments. |
|
Approved for P4, merging today would be ideal -- cc @Anipik |
|
Talked to @stephentoub offline and learned none of the feedback comments is P4 blocker. So, I'll go ahead and merge this one and will address the feedback on the master. Thanks, @stephentoub for the feedback. |
This change is porting the added System.Diagnostics APIs to preview 4. These APIs are very important to fill the gap between .NET Libraries and OpenTelemtry. There are some teams (application insight and Azure Monitoring) waiting to try the new APIs as early as we can provide it. It will be good if we can have these APIs included in preview 4 for early adaptors.
Original PR merged to master: #35220
Original issue tracking the work: #31373
The design issue for the APIs: dotnet/designs#98
CC @noahfalk