Skip to content
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

Added ActivityEvent.EnumerateTags & ActivityLink.EnumerateTags extensions #1320

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/OpenTelemetry.Api/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
* Added `EnumerateEvents` extension method on `Activity` for retrieving events
efficiently
([#1319](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1319))
* Added `EnumerateTags` extension methods on `ActivityLink` & `ActivityEvent`
for retrieving tags efficiently. Renamed `Activity.EnumerateTagValues` ->
`Activity.EnumerateTags`.
([#1320](https://github.com/open-telemetry/opentelemetry-dotnet/pull/1320))

## 0.6.0-beta.1

Expand Down
75 changes: 70 additions & 5 deletions src/OpenTelemetry.Api/Trace/ActivityExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public static Status GetStatus(this Activity activity)

ActivityStatusTagEnumerator state = default;

ActivityTagObjectsEnumeratorFactory<ActivityStatusTagEnumerator>.Enumerate(activity, ref state);
ActivityTagsEnumeratorFactory<ActivityStatusTagEnumerator>.Enumerate(activity, ref state);

var status = SpanHelper.ResolveCanonicalCodeToStatus(state.StatusCode);

Expand All @@ -90,7 +90,7 @@ public static object GetTagValue(this Activity activity, string tagName)

ActivitySingleTagEnumerator state = new ActivitySingleTagEnumerator(tagName);

ActivityTagObjectsEnumeratorFactory<ActivitySingleTagEnumerator>.Enumerate(activity, ref state);
ActivityTagsEnumeratorFactory<ActivitySingleTagEnumerator>.Enumerate(activity, ref state);

return state.Value;
}
Expand All @@ -103,12 +103,12 @@ public static object GetTagValue(this Activity activity, string tagName)
/// <param name="tagEnumerator">Tag enumerator.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")]
public static void EnumerateTagValues<T>(this Activity activity, ref T tagEnumerator)
public static void EnumerateTags<T>(this Activity activity, ref T tagEnumerator)
where T : struct, IActivityEnumerator<KeyValuePair<string, object>>
{
Debug.Assert(activity != null, "Activity should not be null");

ActivityTagObjectsEnumeratorFactory<T>.Enumerate(activity, ref tagEnumerator);
ActivityTagsEnumeratorFactory<T>.Enumerate(activity, ref tagEnumerator);
}

/// <summary>
Expand All @@ -127,6 +127,20 @@ public static void EnumerateLinks<T>(this Activity activity, ref T linkEnumerato
ActivityLinksEnumeratorFactory<T>.Enumerate(activity, ref linkEnumerator);
}

/// <summary>
/// Enumerates all the key/value pairs on an <see cref="ActivityLink"/> without performing an allocation.
/// </summary>
/// <typeparam name="T">The struct <see cref="IActivityEnumerator{T}"/> implementation to use for the enumeration.</typeparam>
/// <param name="activityLink">ActivityLink instance.</param>
/// <param name="tagEnumerator">Tag enumerator.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")]
public static void EnumerateTags<T>(this ActivityLink activityLink, ref T tagEnumerator)
where T : struct, IActivityEnumerator<KeyValuePair<string, object>>
{
ActivityTagsEnumeratorFactory<T>.Enumerate(activityLink, ref tagEnumerator);
}

/// <summary>
/// Enumerates all the <see cref="ActivityEvent"/>s on an <see cref="Activity"/> without performing an allocation.
/// </summary>
Expand All @@ -143,6 +157,20 @@ public static void EnumerateEvents<T>(this Activity activity, ref T eventEnumera
ActivityEventsEnumeratorFactory<T>.Enumerate(activity, ref eventEnumerator);
}

/// <summary>
/// Enumerates all the key/value pairs on an <see cref="ActivityEvent"/> without performing an allocation.
/// </summary>
/// <typeparam name="T">The struct <see cref="IActivityEnumerator{T}"/> implementation to use for the enumeration.</typeparam>
/// <param name="activityEvent">ActivityEvent instance.</param>
/// <param name="tagEnumerator">Tag enumerator.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "ActivityProcessor is hot path")]
public static void EnumerateTags<T>(this ActivityEvent activityEvent, ref T tagEnumerator)
where T : struct, IActivityEnumerator<KeyValuePair<string, object>>
{
ActivityTagsEnumeratorFactory<T>.Enumerate(activityEvent, ref tagEnumerator);
}

/// <summary>
/// Record Exception.
/// </summary>
Expand Down Expand Up @@ -216,15 +244,20 @@ public bool ForEach(KeyValuePair<string, object> item)
}
}

private static class ActivityTagObjectsEnumeratorFactory<TState>
private static class ActivityTagsEnumeratorFactory<TState>
where TState : struct, IActivityEnumerator<KeyValuePair<string, object>>
{
private static readonly object EmptyActivityTagObjects = typeof(Activity).GetField("s_emptyTagObjects", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null);

private static readonly object EmptyActivityEventTags = typeof(ActivityEvent).GetField("s_emptyTags", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null);

private static readonly DictionaryEnumerator<string, object, TState>.AllocationFreeForEachDelegate
ActivityTagObjectsEnumerator = DictionaryEnumerator<string, object, TState>.BuildAllocationFreeForEachDelegate(
typeof(Activity).GetField("_tags", BindingFlags.Instance | BindingFlags.NonPublic).FieldType);

private static readonly DictionaryEnumerator<string, object, TState>.AllocationFreeForEachDelegate
ActivityTagsCollectionEnumerator = DictionaryEnumerator<string, object, TState>.BuildAllocationFreeForEachDelegate(typeof(ActivityTagsCollection));

private static readonly DictionaryEnumerator<string, object, TState>.ForEachDelegate ForEachTagValueCallbackRef = ForEachTagValueCallback;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -243,6 +276,38 @@ public static void Enumerate(Activity activity, ref TState state)
ForEachTagValueCallbackRef);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Enumerate(ActivityLink activityLink, ref TState state)
{
var tags = activityLink.Tags;

if (tags is null)
{
return;
}

ActivityTagsCollectionEnumerator(
tags,
ref state,
ForEachTagValueCallbackRef);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Enumerate(ActivityEvent activityEvent, ref TState state)
{
var tags = activityEvent.Tags;

if (ReferenceEquals(tags, EmptyActivityEventTags))
{
return;
}

ActivityTagsCollectionEnumerator(
tags,
ref state,
ForEachTagValueCallbackRef);
}

private static bool ForEachTagValueCallback(ref TState state, KeyValuePair<string, object> item)
=> state.ForEach(item);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,14 @@ internal static class JaegerActivityExtensions
[SemanticConventions.AttributeDbInstance] = 2, // peer.service for Redis.
};

private static readonly DictionaryEnumerator<string, object, PooledListState<JaegerTag>>.ForEachDelegate ProcessTagRef = ProcessTag;

public static JaegerSpan ToJaegerSpan(this Activity activity)
{
var jaegerTags = new TagEnumerationState
{
Tags = PooledList<JaegerTag>.Create(),
};

activity.EnumerateTagValues(ref jaegerTags);
activity.EnumerateTags(ref jaegerTags);

string peerServiceName = null;
if (activity.Kind == ActivityKind.Client || activity.Kind == ActivityKind.Producer)
Expand Down Expand Up @@ -178,24 +176,20 @@ public static PooledList<JaegerLog> ToJaegerLogs(this Activity activity)

public static JaegerLog ToJaegerLog(this ActivityEvent timedEvent)
{
var tags = new PooledListState<JaegerTag>
var jaegerTags = new EventTagsEnumerationState
{
Created = true,
List = PooledList<JaegerTag>.Create(),
Tags = PooledList<JaegerTag>.Create(),
};

DictionaryEnumerator<string, object, PooledListState<JaegerTag>>.AllocationFreeForEach(
timedEvent.Tags,
ref tags,
ProcessTagRef);
timedEvent.EnumerateTags(ref jaegerTags);

// Matches what OpenTracing and OpenTelemetry defines as the event name.
// https://github.com/opentracing/specification/blob/master/semantic_conventions.md#log-fields-table
// https://github.com/open-telemetry/opentelemetry-specification/pull/397/files
PooledList<JaegerTag>.Add(ref tags.List, new JaegerTag("message", JaegerTagType.STRING, vStr: timedEvent.Name));
PooledList<JaegerTag>.Add(ref jaegerTags.Tags, new JaegerTag("message", JaegerTagType.STRING, vStr: timedEvent.Name));

// TODO: Use the same function as JaegerConversionExtensions or check that the perf here is acceptable.
return new JaegerLog(timedEvent.Timestamp.ToEpochMicroseconds(), tags.List);
return new JaegerLog(timedEvent.Timestamp.ToEpochMicroseconds(), jaegerTags.Tags);
}

public static JaegerSpanRef ToJaegerSpanRef(this in ActivityLink link)
Expand Down Expand Up @@ -306,20 +300,6 @@ private static void ProcessJaegerTag(ref TagEnumerationState state, string key,
PooledList<JaegerTag>.Add(ref state.Tags, jaegerTag);
}

private static bool ProcessTag(ref PooledListState<JaegerTag> state, KeyValuePair<string, object> attribute)
{
if (attribute.Value is Array)
{
ProcessJaegerTagArray(ref state.List, attribute);
}
else if (attribute.Value != null)
{
PooledList<JaegerTag>.Add(ref state.List, attribute.ToJaegerTag());
}

return true;
}

private struct TagEnumerationState : IActivityEnumerator<KeyValuePair<string, object>>
{
public PooledList<JaegerTag> Tags;
Expand Down Expand Up @@ -389,11 +369,23 @@ public bool ForEach(ActivityEvent activityEvent)
}
}

private struct PooledListState<T>
private struct EventTagsEnumerationState : IActivityEnumerator<KeyValuePair<string, object>>
{
public bool Created;
public PooledList<JaegerTag> Tags;

public bool ForEach(KeyValuePair<string, object> tag)
{
if (tag.Value is Array)
{
ProcessJaegerTagArray(ref this.Tags, tag);
}
else if (tag.Value != null)
{
PooledList<JaegerTag>.Add(ref this.Tags, tag.ToJaegerTag());
}

public PooledList<T> List;
return true;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ internal static ZipkinSpan ToZipkinSpan(this Activity activity, ZipkinEndpoint d
Tags = PooledList<KeyValuePair<string, object>>.Create(),
};

activity.EnumerateTagValues(ref tagState);
activity.EnumerateTags(ref tagState);

var activitySource = activity.Source;
if (!string.IsNullOrEmpty(activitySource.Name))
Expand Down
21 changes: 2 additions & 19 deletions src/OpenTelemetry/Internal/EnumerationHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <auto-generated>
// <auto-generated>
// <copyright file="EnumerationHelper.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
Expand All @@ -16,9 +16,7 @@
// </copyright>
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;

Expand Down Expand Up @@ -55,8 +53,6 @@ internal class Enumerator<TEnumerable, TItem, TState>
private static readonly MethodInfo GeneircCurrentGetMethod = typeof(IEnumerator<TItem>).GetProperty("Current").GetMethod;
private static readonly MethodInfo MoveNextMethod = typeof(IEnumerator).GetMethod("MoveNext");
private static readonly MethodInfo DisposeMethod = typeof(IDisposable).GetMethod("Dispose");
private static readonly ConcurrentDictionary<Type, AllocationFreeForEachDelegate> AllocationFreeForEachDelegates = new ConcurrentDictionary<Type, AllocationFreeForEachDelegate>();
private static readonly Func<Type, AllocationFreeForEachDelegate> BuildAllocationFreeForEachDelegateRef = BuildAllocationFreeForEachDelegate;

public delegate void AllocationFreeForEachDelegate(TEnumerable instance, ref TState state, ForEachDelegate itemCallback);

Expand All @@ -66,19 +62,6 @@ protected Enumerator()
{
}

public static void AllocationFreeForEach(TEnumerable instance, ref TState state, ForEachDelegate itemCallback)
{
Debug.Assert(instance != null && itemCallback != null);

var type = instance.GetType();

var allocationFreeForEachDelegate = AllocationFreeForEachDelegates.GetOrAdd(
type,
BuildAllocationFreeForEachDelegateRef);

allocationFreeForEachDelegate(instance, ref state, itemCallback);
}

/* We want to do this type of logic...
public static void AllocationFreeForEach(Dictionary<string, int> dictionary, ref TState state, ForEachDelegate itemCallback)
{
Expand Down Expand Up @@ -108,7 +91,7 @@ public static AllocationFreeForEachDelegate BuildAllocationFreeForEachDelegate(T
var enumeratorType = getEnumeratorMethod.ReturnType;

var dynamicMethod = new DynamicMethod(
nameof(AllocationFreeForEach),
nameof(AllocationFreeForEachDelegate),
null,
new[] { typeof(TEnumerable), typeof(TState).MakeByRefType(), itemCallbackType },
typeof(AllocationFreeForEachDelegate).Module,
Expand Down
Loading