KEMBAR78
[API Proposal]: Api handle Activity.Current value changes · Issue #67276 · dotnet/runtime · GitHub
Skip to content

[API Proposal]: Api handle Activity.Current value changes #67276

@pjanotti

Description

@pjanotti

Background and motivation

[Edit 04/07/2022: Use the event as proposed by @noahfalk]
[Edit 04/07/2022: Updates per conversation with @tarekgh ]

A typical implementation of distributed tracing uses an AsyncLocal<T> to track the "span context" of managed threads. When changes to the span context needs to be tracked this is done by using the AsyncLocal<T> constructor that takes the valueChangedHandler parameter. However, with Activity becoming the standard to represent spans, as used by OpenTelemetry, it is impossible to set the value changed handler since the context is tracked via Activity.Current.

cc @tarekgh @open-telemetry/dotnet-instrumentation-approvers

API Proposal

namespace System.Diagnostics
{
    public readonly struct ActivityNotification
    {
        public Activity? Previous { get; }
        public Activity? Current { get; }
    }

    public partial class Activity : IDisposable
    {
        // Add an event to track updates to the current Activity
        public static event EventHandler<ActivityNotification>? CurrentChanged;
    }
}

API Usage

AsyncLocal<Activity> t_currentActvity = new AsyncLocal<Activity>(ValueChanged);

void Init()
{
    Activity.CurrentChanged += OnCurrentActivityChange;
}

void OnCurrentActivityChange(object sender, ActivityNotification notification)
{
    t_currentActvity = notification.Current;
}

void ValueChanged(AsyncLocalValueChangedArgs<Activity> args)
{
    // Profiler can do whatever it wants with the Activity here. This callback is invoked every time the Activity.Current is set. 
}

Alternative Designs

Using registration methods instead of an Event:

namespace System.Diagnostics
{
    public delegate void ActivityCurrentChangeHandler(Activity? previous, Activity? current);

    public partial class Activity : IDisposable
    {
        public static void RegisterActivityCurrentChangeNotification(ActivityCurrentChangeHandler handler) { }
        public static void CancelActivityCurrentChangeNotification(ActivityCurrentChangeHandler handler) { }
    }
}

Using an Event but using an action that receives only the new current Activity:

namespace System.Diagnostics
{
    public partial class Activity
    {
        public static event Action<Activity?, Activity?>? ActivityChanged;
    }
}
  • Considered the AsyncLocal<Activity> backing Activity.Current to always be created with a valueChangedHandler and exposing a static method on Activity that would allow users to add their own handlers if desired.

  • Implement the support to add value changed handlers directly in AsyncLocal<T> this way any other type of context that needs to be shared can leverage the same implementation and expose similar APIs if desired. However, any implementation still needs to expose an API to add handlers.

Risks

Potentially Activity.Current can change very frequently, especially in a server application, so the implementation should be "pay-for-play" as much as possible.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions