Skip to content

Commit

Permalink
feat: support more Activity props
Browse files Browse the repository at this point in the history
  • Loading branch information
Alxandr committed Nov 21, 2024
1 parent b941a0a commit 559ff13
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,38 +41,34 @@ internal State(ref StateInner inner)
_inner = ref inner;
}

/// <summary>
/// Gets the array of tags.
/// </summary>
public IEnumerable<KeyValuePair<string, object?>> Tags
/// <inheritdoc cref="StateInner.Tags"/>
public IReadOnlyCollection<KeyValuePair<string, object?>> Tags
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _inner.Tags;
}

/// <summary>
/// Adds a tag to the list of tags.
/// </summary>
/// <param name="tags">The tags to add.</param>
/// <inheritdoc cref="StateInner.Links"/>
public IReadOnlyCollection<ActivityLink> Links
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _inner.Links;
}

/// <inheritdoc cref="StateInner.AddTags(ReadOnlySpan{KeyValuePair{string, object?}})"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddTags(ReadOnlySpan<KeyValuePair<string, object?>> tags)
=> _inner.AddTags(tags);

/// <summary>
/// Resets the state of this object to its initial condition.
/// </summary>
/// <inheritdoc cref="StateInner.AddLinks(ReadOnlySpan{ActivityLink})"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddLinks(ReadOnlySpan<ActivityLink> links)
=> _inner.AddLinks(links);

/// <inheritdoc cref="StateInner.Clear()"/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
=> _inner.Clear();

/// <summary>
/// Gets the number of tags currently in this instance.
/// </summary>
public int TagsCount
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _inner.TagsCount;
}
}

/// <summary>
Expand All @@ -81,11 +77,17 @@ public int TagsCount
internal sealed class StateInner
{
private readonly TagList _tags = new();
private readonly LinkList _links = new();

/// <summary>
/// Gets the set of tags.
/// </summary>
public IReadOnlyCollection<KeyValuePair<string, object?>> Tags => _tags;

/// <summary>
/// Gets the array of tags.
/// Gets the set of links.
/// </summary>
public IEnumerable<KeyValuePair<string, object?>> Tags => _tags;
public IReadOnlyCollection<ActivityLink> Links => _links;

/// <summary>
/// Adds a tag to the list of tags.
Expand All @@ -97,21 +99,26 @@ public void AddTags(ReadOnlySpan<KeyValuePair<string, object?>> tags)
}

/// <summary>
/// Resets the state of this object to its initial condition.
/// Adds links to the list of links.
/// </summary>
public void Clear()
/// <param name="links">The links to add.</param>
public void AddLinks(ReadOnlySpan<ActivityLink> links)
{
_tags.Clear();
_links.AddRange(links);
}

/// <summary>
/// Gets the number of tags currently in this instance.
/// Resets the state of this object to its initial condition.
/// </summary>
public int TagsCount => _tags.Count;
public void Clear()
{
_tags.Clear();
_links.Clear();
}
}

private sealed class TagList
: IEnumerable<KeyValuePair<string, object?>>
: IReadOnlyCollection<KeyValuePair<string, object?>>
, IEnumerator<KeyValuePair<string, object?>>
{
private KeyValuePair<string, object?>[] _values = [];
Expand Down Expand Up @@ -228,6 +235,89 @@ IEnumerator IEnumerable.GetEnumerator()
}
}

private sealed class LinkList
: IReadOnlyCollection<ActivityLink>
, IEnumerator<ActivityLink>
{
private ActivityLink[] _values = [];
private int _count;
private int _pos = -1;

/// <summary>
/// Gets the number of links currently in this instance.
/// </summary>
public int Count => _count;

/// <summary>
/// Allocates some room to put some links.
/// </summary>
/// <param name="count">The amount of space to allocate.</param>
private void Reserve(int count)
{
int avail = _values.Length - _count;
if (count > avail)
{
var need = _values.Length + (count - avail);
Array.Resize(ref _values, need);
}
}

/// <summary>
/// Adds links to the list.
/// </summary>
/// <param name="links">The links to add.</param>
public void AddRange(ReadOnlySpan<ActivityLink> links)
{
if (links.Length == 0)
{
return;
}

Reserve(links.Length);
links.CopyTo(_values.AsSpan(_count));
_count += links.Length;
}

/// <summary>
/// Resets the state of this object to its initial condition.
/// </summary>
public void Clear()
{
Array.Clear(_values, 0, _count);
_count = 0;
_pos = -1;
}

ActivityLink IEnumerator<ActivityLink>.Current => _values[_pos];

object IEnumerator.Current => _values[_pos];

bool IEnumerator.MoveNext()
{
return ++_pos < _count;
}

void IDisposable.Dispose()
{
_pos = -1;
}

void IEnumerator.Reset()
{
_pos = -1;
}

IEnumerator<ActivityLink> IEnumerable<ActivityLink>.GetEnumerator()
{
return this;
}

IEnumerator IEnumerable.GetEnumerator()
{
return this;
}
}

[DebuggerDisplay("{ToString(),nq}")]
[EditorBrowsable(EditorBrowsableState.Never)]
private sealed class UnsetTagValueMarker
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,37 @@ public static class AltinnActivityExtensions
/// <param name="name">The operation name of the Activity.</param>
/// <param name="tags">The operation tags.</param>
/// <returns>The created <see cref="Activity"/> object or null if there is no any event listener.</returns>
public static Activity? StartActivity(this ActivitySource source, ActivityKind kind, string name, ReadOnlySpan<KeyValuePair<string, object?>> tags)
public static Activity? StartActivity(
this ActivitySource source,
ActivityKind kind,
string name,
ReadOnlySpan<KeyValuePair<string, object?>> tags)
=> StartActivity(source, name, kind, parentContext: default, tags, links: default, startTime: default);

/// <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="source">The <see cref="ActivitySource"/>.</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="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 event listener.</returns>
public static Activity? StartActivity(
this ActivitySource source,
string name,
ActivityKind kind = ActivityKind.Internal,
ActivityContext parentContext = default,
ReadOnlySpan<KeyValuePair<string, object?>> tags = default,
ReadOnlySpan<ActivityLink> links = default,
DateTimeOffset startTime = default)
{
using var state = ActivityHelper.ThreadLocalState;
state.AddTags(tags);
state.AddLinks(links);

return source.StartActivity(kind, tags: state.Tags, name: name);
return source.StartActivity(kind, parentContext: parentContext, tags: state.Tags, links: state.Links, name: name);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public void ThreadLocalState_Works_InMultithreaded()

ActivitySource.AddActivityListener(listener);

var linkContext = new ActivityContext(ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom(), ActivityTraceFlags.None);
for (var i = 0; i < count; i++)
{
var index = i;
Expand All @@ -36,11 +37,14 @@ public void ThreadLocalState_Works_InMultithreaded()
new("thread.id", Environment.CurrentManagedThreadId),
new("test.iteration", index),
];
Span<ActivityLink> links = [
new(linkContext),
];

ready.WaitOne();

RunOuterActivity($"part1-{index}", source, tags);
RunOuterActivity($"part2-{index}", source, tags);
RunOuterActivity($"part1-{index}", source, tags, links);
RunOuterActivity($"part2-{index}", source, tags, links);
});
threads.Add(thread);
}
Expand All @@ -63,12 +67,19 @@ public void ThreadLocalState_Works_InMultithreaded()
activity.Duration.Should().BeGreaterThan(TimeSpan.Zero);
var testIteration = (int)activity.TagObjects.Single(tag => tag.Key == "test.iteration").Value!;
var threadId = (int)activity.TagObjects.Single(tag => tag.Key == "thread.id").Value!;

if (activity.OperationName.EndsWith("-outer"))
{
activity.Links.Should().HaveCount(1);
activity.Links.First().Context.Should().Be(linkContext);
}

threadMap[testIteration].ThreadId.Should().Be(threadId);
}

static void RunOuterActivity(string name, ActivitySource source, ReadOnlySpan<KeyValuePair<string, object?>> tags)
static void RunOuterActivity(string name, ActivitySource source, ReadOnlySpan<KeyValuePair<string, object?>> tags, ReadOnlySpan<ActivityLink> links)
{
using var activity = StartActivity($"{name}-outer", source, tags);
using var activity = StartActivity($"{name}-outer", source, tags, links);
Thread.Sleep(25); // Simulate tiny work
RunInnerActivity(name, source, tags);
Thread.Sleep(25); // Simulate tiny work
Expand All @@ -80,12 +91,13 @@ static void RunInnerActivity(string name, ActivitySource source, ReadOnlySpan<Ke
Thread.Sleep(50); // Simulate small work
}

static Activity? StartActivity(string name, ActivitySource source, ReadOnlySpan<KeyValuePair<string, object?>> tags)
static Activity? StartActivity(string name, ActivitySource source, ReadOnlySpan<KeyValuePair<string, object?>> tags, ReadOnlySpan<ActivityLink> links = default)
{
using var state = ActivityHelper.ThreadLocalState;
state.AddTags(tags);
state.AddLinks(links);

return source.StartActivity(ActivityKind.Internal, tags: state.Tags, name: name);
return source.StartActivity(ActivityKind.Internal, tags: state.Tags, links: state.Links, name: name);
}
}

Expand Down

0 comments on commit 559ff13

Please sign in to comment.