Skip to content

Commit b66c8f8

Browse files
committed
updated decorator implementation
1 parent b45d5ca commit b66c8f8

File tree

6 files changed

+197
-41
lines changed

6 files changed

+197
-41
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
namespace OpenTelemetry.Instrumentation.Digma.Helpers.Attributes;
2+
3+
/// <summary>
4+
/// An Attribute
5+
/// </summary>
6+
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false)]
7+
public class ActivitiesAttributesAttribute : Attribute
8+
{
9+
public IDictionary<string, string> Attributes { get; }
10+
/// <summary>
11+
/// Define a set of attributes that will be included with every Activity
12+
/// created by the TracingDecorator for this object
13+
/// Example usage:
14+
///
15+
/// ActivitiesAttribute("some_attribute:some_value", "someother_attribute, "some_other_value")
16+
///
17+
/// </summary>
18+
/// <param name="extraAttributes">A set of attributes defined as "key:value"</param>
19+
public ActivitiesAttributesAttribute(params string[] extraAttributes)
20+
{
21+
Attributes =TraceAttributesInputsFormat.ActivityAttributesStringsToDictionary(extraAttributes);
22+
23+
}
24+
}
25+
26+
internal static class TraceAttributesInputsFormat
27+
{
28+
internal static IDictionary<string, string> ActivityAttributesStringsToDictionary(params string[] attributes)
29+
{
30+
if (!attributes.Any())
31+
{
32+
return new Dictionary<string, string>();
33+
}
34+
var attributeFragements = attributes
35+
.Select(x => x.Split(":")).ToArray();
36+
37+
EnsureAttributeSyntax(attributes, attributeFragements);
38+
39+
EnsureUniqueKeys(attributes, attributeFragements);
40+
41+
return attributeFragements.ToDictionary(x => x[0], x => x[1]);
42+
43+
}
44+
45+
private static void EnsureUniqueKeys(string[] attributes, string[][] attributeFragements)
46+
{
47+
var attributeKeys = attributeFragements.Select(x => x[0]).ToArray();
48+
if (attributeKeys.Length != attributeKeys.Distinct().ToArray().Length)
49+
{
50+
throw new ArgumentException($"Attribute keys must be unique. Provided value was: " +
51+
$"{String.Join(',', attributes)}");
52+
}
53+
}
54+
55+
private static void EnsureAttributeSyntax(string[] attributes, string[][] attributeFragements)
56+
{
57+
var illegalFragments = attributeFragements.Where(x => x.Length != 2);
58+
if (illegalFragments.Any())
59+
{
60+
throw new ArgumentException(
61+
$"Illegal attribute values provided in value:{attributes}" +
62+
" The correct syntax is \"key:value\"");
63+
}
64+
}
65+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace OpenTelemetry.Instrumentation.Digma.Helpers.Attributes;
2+
3+
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
4+
public class NoActivityAttribute : Attribute
5+
{
6+
7+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace OpenTelemetry.Instrumentation.Digma.Helpers.Attributes;
2+
3+
4+
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
5+
public class TraceActivityAttribute : Attribute
6+
{
7+
public string? Name { get; }
8+
public bool RecordExceptions { get; }
9+
10+
public TraceActivityAttribute(string? name=null, bool recordExceptions=true)
11+
{
12+
Name = name;
13+
RecordExceptions = recordExceptions;
14+
}
15+
}

Helpers/SpanNamingSchema.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using System.Reflection;
2+
3+
namespace OpenTelemetry.Instrumentation.Digma.Helpers;
4+
5+
public interface ISpanNamingSchema
6+
{
7+
public string GetSpanName(Type classType, MethodInfo method);
8+
}
9+
10+
public class MethodFullNameSchema : ISpanNamingSchema
11+
{
12+
public string GetSpanName(Type classType, MethodInfo method)
13+
{
14+
return $"{classType.FullName}.{method.Name}";
15+
}
16+
}
17+
18+
public class ClassAndMethodNameSchema : ISpanNamingSchema
19+
{
20+
public string GetSpanName(Type classType, MethodInfo method)
21+
{
22+
return $"{classType.Name}.{method.Name}";
23+
}
24+
}

Helpers/TracingDecorator.cs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
using System.Diagnostics;
2+
using System.Reflection;
3+
using OpenTelemetry.Instrumentation.Digma.Helpers.Attributes;
4+
using OpenTelemetry.Trace;
5+
6+
namespace OpenTelemetry.Instrumentation.Digma.Helpers;
7+
8+
public class TraceDecorator<TDecorated> : DispatchProxy where TDecorated : class
9+
{
10+
11+
private ActivitySource _activity;
12+
private TDecorated _decorated;
13+
private ISpanNamingSchema _namingSchema = new MethodFullNameSchema();
14+
private bool _decorateAllMethods = true;
15+
16+
/// <summary>
17+
/// Creates a new TraceDecorator instance wrapping the specific object and implementing the TDecorated interface
18+
/// </summary>
19+
/// <param name="decorated"></param>
20+
/// <param name="spanNamingSchema"></param>
21+
/// <param name="decorateAllMethods"></param>
22+
/// <returns></returns>
23+
public static TDecorated Create(TDecorated decorated, ISpanNamingSchema? spanNamingSchema=null,
24+
bool decorateAllMethods=true)
25+
{
26+
object proxy = Create<TDecorated, TraceDecorator<TDecorated>>()!;
27+
((TraceDecorator<TDecorated>)proxy!).SetParameters(decorated, spanNamingSchema,decorateAllMethods);
28+
29+
return (TDecorated)proxy;
30+
}
31+
32+
private void SetParameters(TDecorated decorated, ISpanNamingSchema? spanNamingSchema, bool decorateAllMethods)
33+
{
34+
_decorated = decorated;
35+
_activity = new(_decorated!.GetType().FullName!);
36+
_decorateAllMethods = decorateAllMethods;
37+
if (spanNamingSchema != null)
38+
{
39+
_namingSchema = spanNamingSchema;
40+
}
41+
}
42+
private object? InvokeDecoratedExecution(MethodInfo? targetMethod, object?[]? args)
43+
{
44+
var result = targetMethod.Invoke(_decorated, args);
45+
return result;
46+
}
47+
48+
private object? WrapWithRecordException(Activity activity, Func<object?> invocation)
49+
{
50+
try
51+
{
52+
var result = invocation();
53+
return result;
54+
}
55+
catch (Exception e)
56+
{
57+
activity.RecordException(e);
58+
throw;
59+
}
60+
}
61+
62+
63+
protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
64+
{
65+
var noActivityAttribute = targetMethod.GetCustomAttribute<NoActivityAttribute>(false);
66+
var activityAttribute = targetMethod.GetCustomAttribute<TraceActivityAttribute>(false);
67+
68+
if (noActivityAttribute == null || (!_decorateAllMethods && activityAttribute==null))
69+
{
70+
var defaultSpanName = _namingSchema.GetSpanName(_decorated!.GetType(), targetMethod);
71+
using var activity = _activity.StartActivity(activityAttribute?.Name ?? defaultSpanName);
72+
if (activityAttribute?.RecordExceptions==false)
73+
{
74+
return InvokeDecoratedExecution(targetMethod, args);
75+
}
76+
77+
return WrapWithRecordException(activity, () => InvokeDecoratedExecution(targetMethod, args));
78+
79+
}
80+
81+
return InvokeDecoratedExecution(targetMethod, args);
82+
83+
}
84+
85+
86+
}

TracingDecorator.cs

Lines changed: 0 additions & 41 deletions
This file was deleted.

0 commit comments

Comments
 (0)