未验证 提交 970d347a 编写于 作者: T Tarek Mahmoud Sayed 提交者: GitHub

Add ActivitySource.CreateActivity methods (#48374)

* Add ActivitySource.CreateActivity methods

* Address the feedback

* Add IdFormat parameter to ActivitySource.Create and fix the whole IdFormat logic

* Address @noahfalk feedback
上级 503a7725
......@@ -146,6 +146,9 @@ public sealed class ActivitySource : IDisposable
public string Name { get { throw null; } }
public string? Version { get { throw null; } }
public bool HasListeners() { throw null; }
public System.Diagnostics.Activity? CreateActivity(string name, System.Diagnostics.ActivityKind kind) { throw null; }
public System.Diagnostics.Activity? CreateActivity(string name, System.Diagnostics.ActivityKind kind, System.Diagnostics.ActivityContext parentContext, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object?>>? tags = null, System.Collections.Generic.IEnumerable<System.Diagnostics.ActivityLink>? links = null, System.Diagnostics.ActivityIdFormat idFormat = System.Diagnostics.ActivityIdFormat.Unknown) { throw null; }
public System.Diagnostics.Activity? CreateActivity(string name, System.Diagnostics.ActivityKind kind, string parentId, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object?>>? tags = null, System.Collections.Generic.IEnumerable<System.Diagnostics.ActivityLink>? links = null, System.Diagnostics.ActivityIdFormat idFormat = System.Diagnostics.ActivityIdFormat.Unknown) { throw null; }
public System.Diagnostics.Activity? StartActivity([System.Runtime.CompilerServices.CallerMemberName] string name = "", System.Diagnostics.ActivityKind kind = ActivityKind.Internal) { throw null; }
public System.Diagnostics.Activity? StartActivity(string name, System.Diagnostics.ActivityKind kind, System.Diagnostics.ActivityContext parentContext, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object?>>? tags = null, System.Collections.Generic.IEnumerable<System.Diagnostics.ActivityLink>? links = null, System.DateTimeOffset startTime = default) { throw null; }
public System.Diagnostics.Activity? StartActivity(string name, System.Diagnostics.ActivityKind kind, string parentId, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string, object?>>? tags = null, System.Collections.Generic.IEnumerable<System.Diagnostics.ActivityLink>? links = null, System.DateTimeOffset startTime = default) { throw null; }
......
......@@ -1007,55 +1007,15 @@ public void SetCustomProperty(string propertyName, object? propertyValue)
return ret;
}
internal static Activity CreateAndStart(ActivitySource source, string name, ActivityKind kind, string? parentId, ActivityContext parentContext,
IEnumerable<KeyValuePair<string, object?>>? tags, IEnumerable<ActivityLink>? links,
DateTimeOffset startTime, ActivityTagsCollection? samplerTags, ActivitySamplingResult request)
internal static Activity Create(ActivitySource source, string name, ActivityKind kind, string? parentId, ActivityContext parentContext,
IEnumerable<KeyValuePair<string, object?>>? tags, IEnumerable<ActivityLink>? links, DateTimeOffset startTime,
ActivityTagsCollection? samplerTags, ActivitySamplingResult request, bool startIt, ActivityIdFormat idFormat)
{
Activity activity = new Activity(name);
activity.Source = source;
activity.Kind = kind;
activity._previousActiveActivity = Current;
if (parentId != null)
{
activity._parentId = parentId;
}
else if (parentContext != default)
{
activity._traceId = parentContext.TraceId.ToString();
if (parentContext.SpanId != default)
{
activity._parentSpanId = parentContext.SpanId.ToString();
}
activity.ActivityTraceFlags = parentContext.TraceFlags;
activity._traceState = parentContext.TraceState;
}
else
{
if (activity._previousActiveActivity != null)
{
// The parent change should not form a loop. We are actually guaranteed this because
// 1. Un-started activities can't be 'Current' (thus can't be 'parent'), we throw if you try.
// 2. All started activities have a finite parent change (by inductive reasoning).
activity.Parent = activity._previousActiveActivity;
}
}
activity.IdFormat =
ForceDefaultIdFormat ? DefaultIdFormat :
activity.Parent != null ? activity.Parent.IdFormat :
activity._parentSpanId != null ? ActivityIdFormat.W3C :
activity._parentId == null ? DefaultIdFormat :
IsW3CId(activity._parentId) ? ActivityIdFormat.W3C :
ActivityIdFormat.Hierarchical;
if (activity.IdFormat == ActivityIdFormat.W3C)
activity.GenerateW3CId();
else
activity._id = activity.GenerateHierarchicalId();
activity.IdFormat = idFormat;
if (links != null)
{
......@@ -1091,8 +1051,6 @@ public void SetCustomProperty(string propertyName, object? propertyValue)
}
}
activity.StartTimeUtc = startTime == default ? GetUtcNow() : startTime.UtcDateTime;
activity.IsAllDataRequested = request == ActivitySamplingResult.AllData || request == ActivitySamplingResult.AllDataAndRecorded;
if (request == ActivitySamplingResult.AllDataAndRecorded)
......@@ -1100,7 +1058,32 @@ public void SetCustomProperty(string propertyName, object? propertyValue)
activity.ActivityTraceFlags |= ActivityTraceFlags.Recorded;
}
SetCurrent(activity);
if (parentId != null)
{
activity._parentId = parentId;
}
else if (parentContext != default)
{
activity._traceId = parentContext.TraceId.ToString();
if (parentContext.SpanId != default)
{
activity._parentSpanId = parentContext.SpanId.ToString();
}
activity.ActivityTraceFlags = parentContext.TraceFlags;
activity._traceState = parentContext.TraceState;
}
if (startTime != default)
{
activity.StartTimeUtc = startTime.UtcDateTime;
}
if (startIt)
{
activity.Start();
}
return activity;
}
......
......@@ -23,7 +23,8 @@ namespace System.Diagnostics
/// <param name="kind"><see cref="ActivityKind"/> to create the Activity object with.</param>
/// <param name="tags">Key-value pairs list for the tags to create the Activity object with.<see cref="ActivityContext"/></param>
/// <param name="links"><see cref="ActivityLink"/> list to create the Activity object with.</param>
internal ActivityCreationOptions(ActivitySource source, string name, T parent, ActivityKind kind, IEnumerable<KeyValuePair<string, object?>>? tags, IEnumerable<ActivityLink>? links)
/// <param name="idFormat">The default Id format to use.</param>
internal ActivityCreationOptions(ActivitySource source, string name, T parent, ActivityKind kind, IEnumerable<KeyValuePair<string, object?>>? tags, IEnumerable<ActivityLink>? links, ActivityIdFormat idFormat)
{
Source = source;
Name = name;
......@@ -31,21 +32,49 @@ internal ActivityCreationOptions(ActivitySource source, string name, T parent, A
Parent = parent;
Tags = tags;
Links = links;
IdFormat = idFormat;
if (IdFormat == ActivityIdFormat.Unknown && Activity.ForceDefaultIdFormat)
{
IdFormat = Activity.DefaultIdFormat;
}
_samplerTags = null;
if (parent is ActivityContext ac)
if (parent is ActivityContext ac && ac != default)
{
_context = ac;
if (IdFormat == ActivityIdFormat.Unknown)
{
IdFormat = ActivityIdFormat.W3C;
}
}
else if (parent is string p && p != null)
{
// We don't care about the return value. we care if _context is initialized accordingly.
ActivityContext.TryParse(p, null, out _context);
if (IdFormat != ActivityIdFormat.Hierarchical)
{
if (ActivityContext.TryParse(p, null, out _context))
{
IdFormat = ActivityIdFormat.W3C;
}
if (IdFormat == ActivityIdFormat.Unknown)
{
IdFormat =ActivityIdFormat.Hierarchical;
}
}
else
{
_context = default;
}
}
else
{
_context = default;
if (IdFormat == ActivityIdFormat.Unknown)
{
IdFormat = Activity.Current != null ? Activity.Current.IdFormat : Activity.DefaultIdFormat;
}
}
}
......@@ -103,16 +132,24 @@ public ActivityTraceId TraceId
#endif
get
{
if (Parent is ActivityContext && _context == default)
if (Parent is ActivityContext && IdFormat == ActivityIdFormat.W3C && _context == default)
{
Func<ActivityTraceId>? traceIdGenerator = Activity.TraceIdGenerator;
ActivityTraceId id = traceIdGenerator == null ? ActivityTraceId.CreateRandom() : traceIdGenerator();
// Because the struct is readonly, we cannot directly assign _context. We have to workaround it by calling Unsafe.AsRef
Unsafe.AsRef(in _context) = new ActivityContext(ActivityTraceId.CreateRandom(), default, ActivityTraceFlags.None);
Unsafe.AsRef(in _context) = new ActivityContext(id, default, ActivityTraceFlags.None);
}
return _context.TraceId;
}
}
/// <summary>
/// Retrieve Id format of to use for the Activity we may create.
/// </summary>
internal ActivityIdFormat IdFormat { get; }
// Helper to access the sampling tags. The SamplingTags Getter can allocate when not necessary.
internal ActivityTagsCollection? GetSamplingTags() => _samplerTags;
......
......@@ -77,11 +77,56 @@ public bool HasListeners()
/// <param name="name">The operation name of the Activity</param>
/// <param name="kind">The <see cref="ActivityKind"/></param>
/// <returns>The created <see cref="Activity"/> object or null if there is no any event listener.</returns>
/// <remarks>
/// If the Activity object is created, it will not start automatically. Callers need to call <see cref="Activity.Start()"/> to start it.
/// </remarks>
public Activity? CreateActivity(string name, ActivityKind kind)
=> CreateActivity(name, kind, default, null, null, null, default, startIt: false);
/// <summary>
/// Creates a new <see cref="Activity"/> object if there is any listener to the Activity, returns null otherwise.
/// If the Activity object is created, it will not automatically start. Callers will need to call <see cref="Activity.Start()"/> to start it.
/// </summary>
/// <param name="name">The operation name of the Activity.</param>
/// <param name="kind">The <see cref="ActivityKind"/></param>
/// <param name="parentContext">The parent <see cref="ActivityContext"/> object to initialize the created Activity object with.</param>
/// <param name="tags">The optional tags list to initialize the created Activity object with.</param>
/// <param name="links">The optional <see cref="ActivityLink"/> list to initialize the created Activity object with.</param>
/// <param name="idFormat">The default Id format to use.</param>
/// <returns>The created <see cref="Activity"/> object or null if there is no any listener.</returns>
/// <remarks>
/// If the Activity object is created, it will not start automatically. Callers need to call <see cref="Activity.Start()"/> to start it.
/// </remarks>
public Activity? CreateActivity(string name, ActivityKind kind, ActivityContext parentContext, IEnumerable<KeyValuePair<string, object?>>? tags = null, IEnumerable<ActivityLink>? links = null, ActivityIdFormat idFormat = ActivityIdFormat.Unknown)
=> CreateActivity(name, kind, parentContext, null, tags, links, default, startIt: false, idFormat);
/// <summary>
/// Creates a new <see cref="Activity"/> object if there is any listener to the Activity, returns null otherwise.
/// </summary>
/// <param name="name">The operation name of the Activity.</param>
/// <param name="kind">The <see cref="ActivityKind"/></param>
/// <param name="parentId">The parent Id to initialize the created Activity object with.</param>
/// <param name="tags">The optional tags list to initialize the created Activity object with.</param>
/// <param name="links">The optional <see cref="ActivityLink"/> list to initialize the created Activity object with.</param>
/// <param name="idFormat">The default Id format to use.</param>
/// <returns>The created <see cref="Activity"/> object or null if there is no any listener.</returns>
/// <remarks>
/// If the Activity object is created, it will not start automatically. Callers need to call <see cref="Activity.Start()"/> to start it.
/// </remarks>
public Activity? CreateActivity(string name, ActivityKind kind, string parentId, IEnumerable<KeyValuePair<string, object?>>? tags = null, IEnumerable<ActivityLink>? links = null, ActivityIdFormat idFormat = ActivityIdFormat.Unknown)
=> CreateActivity(name, kind, default, parentId, tags, links, default, startIt: false, idFormat);
/// <summary>
/// Creates and starts a new <see cref="Activity"/> object if there is any listener to the Activity, returns null otherwise.
/// </summary>
/// <param name="name">The operation name of the Activity</param>
/// <param name="kind">The <see cref="ActivityKind"/></param>
/// <returns>The created <see cref="Activity"/> object or null if there is no any event listener.</returns>
public Activity? StartActivity([CallerMemberName] string name = "", ActivityKind kind = ActivityKind.Internal)
=> StartActivity(name, kind, default, null, null, null, default);
=> CreateActivity(name, kind, default, null, null, null, default);
/// <summary>
/// Creates a new <see cref="Activity"/> object if there is any listener to the Activity events, returns null otherwise.
/// Creates and starts a new <see cref="Activity"/> object if there is any listener to the Activity events, returns null otherwise.
/// </summary>
/// <param name="name">The operation name of the Activity.</param>
/// <param name="kind">The <see cref="ActivityKind"/></param>
......@@ -91,10 +136,10 @@ public bool HasListeners()
/// <param name="startTime">The optional start timestamp to set on the created Activity object.</param>
/// <returns>The created <see cref="Activity"/> object or null if there is no any listener.</returns>
public Activity? StartActivity(string name, ActivityKind kind, ActivityContext parentContext, IEnumerable<KeyValuePair<string, object?>>? tags = null, IEnumerable<ActivityLink>? links = null, DateTimeOffset startTime = default)
=> StartActivity(name, kind, parentContext, null, tags, links, startTime);
=> CreateActivity(name, kind, parentContext, null, tags, links, startTime);
/// <summary>
/// Creates a new <see cref="Activity"/> object if there is any listener to the Activity events, returns null otherwise.
/// Creates and starts a new <see cref="Activity"/> object if there is any listener to the Activity events, returns null otherwise.
/// </summary>
/// <param name="name">The operation name of the Activity.</param>
/// <param name="kind">The <see cref="ActivityKind"/></param>
......@@ -104,10 +149,10 @@ public bool HasListeners()
/// <param name="startTime">The optional start timestamp to set on the created Activity object.</param>
/// <returns>The created <see cref="Activity"/> object or null if there is no any listener.</returns>
public Activity? StartActivity(string name, ActivityKind kind, string parentId, IEnumerable<KeyValuePair<string, object?>>? tags = null, IEnumerable<ActivityLink>? links = null, DateTimeOffset startTime = default)
=> StartActivity(name, kind, default, parentId, tags, links, startTime);
=> CreateActivity(name, kind, default, parentId, tags, links, startTime);
/// <summary>
/// Creates a new <see cref="Activity"/> object if there is any listener to the Activity events, returns null otherwise.
/// Creates and starts a new <see cref="Activity"/> object if there is any listener to the Activity events, returns null otherwise.
/// </summary>
/// <param name="kind">The <see cref="ActivityKind"/></param>
/// <param name="parentContext">The parent <see cref="ActivityContext"/> object to initialize the created Activity object with.</param>
......@@ -117,9 +162,10 @@ public bool HasListeners()
/// <param name="name">The operation name of the Activity.</param>
/// <returns>The created <see cref="Activity"/> object or null if there is no any listener.</returns>
public Activity? StartActivity(ActivityKind kind, ActivityContext parentContext = default, IEnumerable<KeyValuePair<string, object?>>? tags = null, IEnumerable<ActivityLink>? links = null, DateTimeOffset startTime = default, [CallerMemberName] string name = "")
=> StartActivity(name, kind, parentContext, null, tags, links, startTime);
=> CreateActivity(name, kind, parentContext, null, tags, links, startTime);
private Activity? StartActivity(string name, ActivityKind kind, ActivityContext context, string? parentId, IEnumerable<KeyValuePair<string, object?>>? tags, IEnumerable<ActivityLink>? links, DateTimeOffset startTime)
private Activity? CreateActivity(string name, ActivityKind kind, ActivityContext context, string? parentId, IEnumerable<KeyValuePair<string, object?>>? tags,
IEnumerable<ActivityLink>? links, DateTimeOffset startTime, bool startIt = true, ActivityIdFormat idFormat = ActivityIdFormat.Unknown)
{
// _listeners can get assigned to null in Dispose.
SynchronizedList<ActivityListener>? listeners = _listeners;
......@@ -135,8 +181,15 @@ public bool HasListeners()
if (parentId != null)
{
var aco = new ActivityCreationOptions<string>(this, name, parentId, kind, tags, links);
var acoContext = new ActivityCreationOptions<ActivityContext>(this, name, aco.GetContext(), kind, tags, links);
ActivityCreationOptions<string> aco = default;
ActivityCreationOptions<ActivityContext> acoContext = default;
aco = new ActivityCreationOptions<string>(this, name, parentId, kind, tags, links, idFormat);
if (aco.IdFormat == ActivityIdFormat.W3C)
{
// acoContext is used only in the Sample calls which called only when we have W3C Id format.
acoContext = new ActivityCreationOptions<ActivityContext>(this, name, aco.GetContext(), kind, tags, links, ActivityIdFormat.W3C);
}
listeners.EnumWithFunc((ActivityListener listener, ref ActivityCreationOptions<string> data, ref ActivitySamplingResult result, ref ActivityCreationOptions<ActivityContext> dataWithContext) => {
SampleActivity<string>? sampleUsingParentId = listener.SampleUsingParentId;
......@@ -148,14 +201,14 @@ public bool HasListeners()
result = sr;
}
}
else
else if (data.IdFormat == ActivityIdFormat.W3C)
{
// In case we have a parent Id and the listener not providing the SampleUsingParentId, we'll try to find out if the following conditions are true:
// - The listener is providing the Sample callback
// - Can convert the parent Id to a Context. ActivityCreationOptions.TraceId != default means parent id converted to a valid context.
// Then we can call the listener Sample callback with the constructed context.
SampleActivity<ActivityContext>? sample = listener.Sample;
if (sample != null && data.GetContext() != default) // data.GetContext() != default means parent Id parsed correctly to a context
if (sample != null)
{
ActivitySamplingResult sr = sample(ref dataWithContext);
if (sr > result)
......@@ -166,10 +219,18 @@ public bool HasListeners()
}
}, ref aco, ref samplingResult, ref acoContext);
if (context == default && aco.GetContext() != default)
if (context == default)
{
context = aco.GetContext();
parentId = null;
if (aco.GetContext() != default)
{
context = aco.GetContext();
parentId = null;
}
else if (acoContext.GetContext() != default)
{
context = acoContext.GetContext();
parentId = null;
}
}
samplerTags = aco.GetSamplingTags();
......@@ -188,11 +249,13 @@ public bool HasListeners()
}
}
}
idFormat = aco.IdFormat;
}
else
{
bool useCurrentActivityContext = context == default && Activity.Current != null;
var aco = new ActivityCreationOptions<ActivityContext>(this, name, useCurrentActivityContext ? Activity.Current!.Context : context, kind, tags, links);
var aco = new ActivityCreationOptions<ActivityContext>(this, name, useCurrentActivityContext ? Activity.Current!.Context : context, kind, tags, links, idFormat);
listeners.EnumWithFunc((ActivityListener listener, ref ActivityCreationOptions<ActivityContext> data, ref ActivitySamplingResult result, ref ActivityCreationOptions<ActivityContext> unused) => {
SampleActivity<ActivityContext>? sample = listener.Sample;
if (sample != null)
......@@ -214,12 +277,12 @@ public bool HasListeners()
}
samplerTags = aco.GetSamplingTags();
idFormat = aco.IdFormat;
}
if (samplingResult != ActivitySamplingResult.None)
{
activity = Activity.CreateAndStart(this, name, kind, parentId, context, tags, links, startTime, samplerTags, samplingResult);
listeners.EnumWithAction((listener, obj) => listener.ActivityStarted?.Invoke((Activity) obj), activity);
activity = Activity.Create(this, name, kind, parentId, context, tags, links, startTime, samplerTags, samplingResult, startIt, idFormat);
}
return activity;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册