-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Proposed new System.Diagnostics APIs #32660
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
Changes from all commits
0033a42
502ea4c
ed8e05a
38e13df
4d401dc
fb12851
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,9 +5,11 @@ | |
| // Changes to this file must follow the https://aka.ms/api-review process. | ||
| // ------------------------------------------------------------------------------ | ||
|
|
||
| using System.Collections.Generic; | ||
|
|
||
| namespace System.Diagnostics | ||
| { | ||
| public partial class Activity | ||
| public partial class Activity : IDisposable | ||
| { | ||
| public Activity(string operationName) { } | ||
| public System.Diagnostics.ActivityTraceFlags ActivityTraceFlags { get { throw null; } set { } } | ||
|
|
@@ -34,6 +36,7 @@ public string? Id | |
| get { throw null; } | ||
| } | ||
| public System.Diagnostics.ActivityIdFormat IdFormat { get { throw null; } } | ||
| public System.Diagnostics.ActivityKind Kind { get; set; } | ||
| public string OperationName { get { throw null; } } | ||
| public System.Diagnostics.Activity? Parent { get { throw null; } } | ||
| public string? ParentId { get { throw null; } } | ||
|
|
@@ -43,9 +46,11 @@ public string? Id | |
| public System.Diagnostics.ActivitySpanId SpanId { get { throw null; } } | ||
| public System.DateTime StartTimeUtc { get { throw null; } } | ||
| public System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, string?>> Tags { get { throw null; } } | ||
| public System.Collections.Generic.IEnumerable<ActivityLink> Links { get { throw null; } } | ||
| public System.Diagnostics.ActivityTraceId TraceId { get { throw null; } } | ||
| public string? TraceStateString { get { throw null; } set { } } | ||
| public System.Diagnostics.Activity AddBaggage(string key, string? value) { throw null; } | ||
| public System.Diagnostics.Activity AddLink(ActivityLink link) { throw null; } | ||
| public System.Diagnostics.Activity AddTag(string key, string? value) { throw null; } | ||
| public string? GetBaggageItem(string key) { throw null; } | ||
| public System.Diagnostics.Activity SetEndTime(System.DateTime endTimeUtc) { throw null; } | ||
|
|
@@ -55,6 +60,9 @@ public string? Id | |
| public System.Diagnostics.Activity SetStartTime(System.DateTime startTimeUtc) { throw null; } | ||
| public System.Diagnostics.Activity Start() { throw null; } | ||
| public void Stop() { } | ||
| public void Dispose() { } | ||
| public void SetCustomProperty(string propertyName, object? propertyValue) { } | ||
|
tarekgh marked this conversation as resolved.
Outdated
|
||
| public object? GetCustomProperty(string propertyName) { throw null; } | ||
| } | ||
| public enum ActivityIdFormat | ||
| { | ||
|
|
@@ -121,4 +129,51 @@ public virtual void OnActivityImport(System.Diagnostics.Activity activity, objec | |
| public System.Diagnostics.Activity StartActivity(System.Diagnostics.Activity activity, object? args) { throw null; } | ||
| public void StopActivity(System.Diagnostics.Activity activity, object? args) { } | ||
| } | ||
| public enum ActivityKind | ||
| { | ||
| Internal = 1, | ||
|
noahfalk marked this conversation as resolved.
Outdated
|
||
| Server = 2, | ||
| Client = 3, | ||
| Producer = 4, | ||
| Consumer = 5, | ||
| } | ||
| public readonly struct ActivityContext : IEquatable<ActivityContext> | ||
| { | ||
| public ActivityContext(System.Diagnostics.ActivityTraceId traceId, System.Diagnostics.ActivitySpanId spanId, System.Diagnostics.ActivityTraceFlags traceOptions, string? traceState = null) { throw null; } | ||
| public System.Diagnostics.ActivityTraceId TraceId { get; } | ||
| public System.Diagnostics.ActivitySpanId SpanId { get; } | ||
| public System.Diagnostics.ActivityTraceFlags TraceFlags { get; } | ||
| public string? TraceState { get; } | ||
| public static bool operator ==(System.Diagnostics.ActivityContext context1, System.Diagnostics.ActivityContext context2) { throw null; } | ||
| public static bool operator !=(System.Diagnostics.ActivityContext context1, System.Diagnostics.ActivityContext context2) { throw null; } | ||
| public bool Equals(System.Diagnostics.ActivityContext context) { throw null; } | ||
| public override bool Equals(object? obj) { throw null; } | ||
| public override int GetHashCode() { throw null; } | ||
| } | ||
| public readonly struct ActivityLink | ||
| { | ||
| public ActivityLink(System.Diagnostics.ActivityContext context) { throw null; } | ||
| public ActivityLink(System.Diagnostics.ActivityContext context, System.Collections.Generic.IDictionary<string, object>? attributes) { throw null; } | ||
| public System.Diagnostics.ActivityContext Context { get; } | ||
| public System.Collections.Generic.IDictionary<string, object>? Attributes { get; } | ||
| } | ||
| public sealed class ActivitySource : IDisposable | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we discussed that for libs (Azure SDK) it's not a problem to create E.g. imagine in the controller you want to track custom Activities. Developers would have to create static using (var s = new ActivitySource("foo"))
using (var a = s.CreateActivity())
{
// do stuff
}Can we think about it from app dev convenience?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does the concern here ActivitySource is Disposable? if so, we can think in another way to dispose it. but wouldn't anyone writing a code as you showed will not work for them anyway and will it be obvious there is a problem? static ActivitySource s = new ActivitySource("foo");
...
using (var a = s.CreateActivity())
{
// do stuff
}
...
// later if the app want to get rid of it will do
s.Untrack();what you think?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If an app wrote @lmolkova's example I don't see any reason that it wouldn't function properly? The only downside of that pattern is the app author is paying the cost to create the ActivitySource, publish it, and have telemetry agents decide if they want to subscribe to it on each Activity generated.
If the concern is being able to write simple one liners or not reading the docs then I'd guess the most likely risk is not calling Dispose() on the ActivitySource. For example: Looking at it a bit I believe we could eliminate the need for Dispose() by making a global list of weak GCHandles rather than a global list of strong ActivitySource references. We pay one additional pointer of memory per ActivitySource for the GCHandle and need a little more complicated implementation. ActivitySource is already around 100 bytes each (~4 pointers + 3 pointers for string + ~20*2 characters) so another pointer doesn't appear to be a significant relative overhead. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is exactly my concern: the easiest and most convenient way to use this API requires 2 allocations and listener to start/stop subscription in e.g. per-request basis (imagine concurrency issues and locks in listener). Assuming source is less granular that activity, it's possible to have 1) default source and you create one per app or web host 2) source per e.g. controller that you can register as singleton in your container. With current implementation it's not even possible to register source in DI container (if you want more than one) and users will be forced to have static fields to keep each source.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A couple of clarification questions to get the whole picture:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think similar to @tarekgh I am trying to understand your goal : ) Is the goal of the default source to allow for simpler usage like this? The source per controller I am less sure what the goal is there?
Is your concern that if we don't then it won't appeal to developers writing ASP.Net apps? Something else? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. with the design with so many allocations - is the assumption that the span name for ASP.NET incoming requests will be a constant value like
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are going to get rid of ActivitySource all together. I am going to share the new final proposal by tomorrow or so. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we call ActivitySource
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are going to get rid of ActivitySource all together. |
||
| { | ||
| private ActivitySource() { throw null; } | ||
| public ActivitySource(string name) { throw null; } | ||
| public string Name { get; } | ||
| public Activity? StartActivity() { throw null; } | ||
| public Activity? StartActivity(System.Diagnostics.ActivityContext context, System.Collections.Generic.IEnumerable<ActivityLink>? links = null, System.DateTimeOffset startTime = default) { throw null; } | ||
| public void Dispose() { } | ||
| } | ||
| public abstract class ActivityListener : IDisposable | ||
| { | ||
| public void Start() { throw null; } | ||
| public virtual bool EnableListening(string activitySourceName) { throw null; } | ||
| public virtual bool ShouldCreateActivity(string activitySourceName, ActivityContext context, IEnumerable<ActivityLink>? links) { throw null; } | ||
| public virtual void OnActivityStarted(Activity a) { throw null; } | ||
| public virtual void OnActivityStopped(Activity a) { throw null; } | ||
| public void Dispose() { throw null; } | ||
| protected virtual void Dispose(bool disposing) { throw null; } | ||
| } | ||
| } | ||
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 does this mean for all activity use today where it's not being disposed? Are we introducing leaks?
Uh oh!
There was an error while loading. Please reload this page.
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.
We are not introducing a leak. This is added for simplifying the pattern of using Activity. I am going to add some code samples in the issue we'll use for the design review. here is some example quick example for what we currently have and what we are proposing:
Here is what the new pattern will look like:
I am working to polish the sample code and add more samples.