Skip to content

Commit

Permalink
Feature/support method overloading by adding parameter types (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
arik-dig authored Jul 12, 2022
1 parent d24364c commit 50c2a5f
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 57 deletions.
7 changes: 4 additions & 3 deletions DigmaConfigurationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

namespace OpenTelemetry.Instrumentation.Digma
{
public class DigmaConfigurationOptions
{
private const string DEFAULT_COMMIT_ENV_VAR="DEPLOYMENT_COMMIT_ID";
private const string DEFAULT_ENV_ENV_VAR="DEPLOYMENT_ENV";
private const string DEFAULT_COMMIT_ENV_VAR = "DEPLOYMENT_COMMIT_ID";
private const string DEFAULT_ENV_ENV_VAR = "DEPLOYMENT_ENV";

public string? NamespaceRoot { get; set; } = null;
public string? Environment { get; set; } = null;
Expand All @@ -28,4 +29,4 @@ public class DigmaConfigurationOptions
public string? SpanMappingPattern { get; set; } = "";
public string? SpanMappingReplacement { get; set; } = "";
}
}
}
15 changes: 7 additions & 8 deletions DigmaInstrumentationHelperExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,20 @@
// limitations under the License.
// </copyright>

using System.Diagnostics;
using System.Reflection;
using OpenTelemetry.Resources;

namespace OpenTelemetry.Instrumentation.Digma;

using System.Diagnostics;
using OpenTelemetry.Resources;

public static class DigmaInstrumentationHelperExtensions
{
private static readonly HashSet<string> IgnoreNamespaces = new() {"Microsoft", "System"};
private static readonly HashSet<string> IgnoreNamespaces = new() { "Microsoft", "System" };

public static ResourceBuilder AddDigmaAttributes(this ResourceBuilder builder,
Action<DigmaConfigurationOptions> configure = null)
Action<DigmaConfigurationOptions>? configure = null)
{
var workingDirectory = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
var workingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

DigmaConfigurationOptions options = new DigmaConfigurationOptions();
if (configure != null)
Expand Down Expand Up @@ -61,7 +60,7 @@ public static ResourceBuilder AddDigmaAttributes(this ResourceBuilder builder,
if (options.Environment == null)
{
var env = Environment.GetEnvironmentVariable(options.EnvironmentEnvVariable);
if (env is null)
if (string.IsNullOrWhiteSpace(env))
{
options.Environment = Environment.MachineName + "[local]";
}
Expand All @@ -83,4 +82,4 @@ public static ResourceBuilder AddDigmaAttributes(this ResourceBuilder builder,
});
return builder;
}
}
}
13 changes: 4 additions & 9 deletions EndpointMonitoring.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

Expand Down Expand Up @@ -96,16 +94,13 @@ public void OnNext(KeyValuePair<string, object?> pair)
if (pair.Key != "Microsoft.AspNetCore.Routing.EndpointMatched")
return;

var context = (HttpContext) pair.Value;
var context = (HttpContext)pair.Value;
var endpoint = context?.GetEndpoint();
var descriptor = endpoint?.Metadata.GetMetadata<ControllerActionDescriptor>();
if (descriptor == null)
return;

Activity.Current?.AddTag("code.namespace", descriptor.MethodInfo.DeclaringType?.ToString());
Activity.Current?.AddTag("code.function", descriptor.MethodInfo.Name);
Activity.Current?.AddTag("endpoint.type_full_name", descriptor.MethodInfo.DeclaringType?.ToString());// should be deleted
Activity.Current?.AddTag("endpoint.function", descriptor.MethodInfo.Name); //should be deleted

SpanUtils.AddCommonTags(descriptor.MethodInfo, Activity.Current);
}
}
}
10 changes: 5 additions & 5 deletions Helpers/Attributes/ActivitiesAttributesAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace OpenTelemetry.Instrumentation.Digma.Helpers.Attributes;
public class ActivitiesAttributesAttribute : Attribute
{
public IDictionary<string, string> Attributes { get; }

/// <summary>
/// Define a set of attributes that will be included with every Activity
/// created by the TracingDecorator for this object
Expand All @@ -18,8 +19,7 @@ public class ActivitiesAttributesAttribute : Attribute
/// <param name="extraAttributes">A set of attributes defined as "key:value"</param>
public ActivitiesAttributesAttribute(params string[] extraAttributes)
{
Attributes =TraceAttributesInputsFormat.ActivityAttributesStringsToDictionary(extraAttributes);

Attributes = TraceAttributesInputsFormat.ActivityAttributesStringsToDictionary(extraAttributes);
}
}

Expand All @@ -31,15 +31,15 @@ internal static IDictionary<string, string> ActivityAttributesStringsToDictionar
{
return new Dictionary<string, string>();
}

var attributeFragements = attributes
.Select(x => x.Split(":")).ToArray();

EnsureAttributeSyntax(attributes, attributeFragements);

EnsureUniqueKeys(attributes, attributeFragements);

return attributeFragements.ToDictionary(x => x[0], x => x[1]);

}

private static void EnsureUniqueKeys(string[] attributes, string[][] attributeFragements)
Expand All @@ -62,4 +62,4 @@ private static void EnsureAttributeSyntax(string[] attributes, string[][] attrib
" The correct syntax is \"key:value\"");
}
}
}
}
1 change: 0 additions & 1 deletion Helpers/Attributes/NoActivityAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@ namespace OpenTelemetry.Instrumentation.Digma.Helpers.Attributes;
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public class NoActivityAttribute : Attribute
{

}
5 changes: 2 additions & 3 deletions Helpers/Attributes/TraceActivityAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
namespace OpenTelemetry.Instrumentation.Digma.Helpers.Attributes;


[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public class TraceActivityAttribute : Attribute
{
public string? Name { get; }
public bool RecordExceptions { get; }
public TraceActivityAttribute(string? name=null, bool recordExceptions=true)

public TraceActivityAttribute(string? name = null, bool recordExceptions = true)
{
Name = name;
RecordExceptions = recordExceptions;
Expand Down
24 changes: 11 additions & 13 deletions Helpers/TracingDecorator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ namespace OpenTelemetry.Instrumentation.Digma.Helpers;

public class TraceDecorator<TDecorated> : DispatchProxy where TDecorated : class
{

private ActivitySource _activity;
private TDecorated _decorated;
private IActivityNamingSchema _namingSchema = new MethodFullNameSchema();
Expand All @@ -20,11 +19,11 @@ public class TraceDecorator<TDecorated> : DispatchProxy where TDecorated : class
/// <param name="activityNamingSchema"></param>
/// <param name="decorateAllMethods"></param>
/// <returns></returns>
public static TDecorated Create(TDecorated decorated, IActivityNamingSchema? activityNamingSchema=null,
bool decorateAllMethods=true)
public static TDecorated Create(TDecorated decorated, IActivityNamingSchema? activityNamingSchema = null,
bool decorateAllMethods = true)
{
object proxy = Create<TDecorated, TraceDecorator<TDecorated>>()!;
((TraceDecorator<TDecorated>)proxy!).SetParameters(decorated, activityNamingSchema,decorateAllMethods);
((TraceDecorator<TDecorated>)proxy!).SetParameters(decorated, activityNamingSchema, decorateAllMethods);

return (TDecorated)proxy;
}
Expand All @@ -39,6 +38,7 @@ private void SetParameters(TDecorated decorated, IActivityNamingSchema? spanNami
_namingSchema = spanNamingSchema;
}
}

private object? InvokeDecoratedExecution(MethodInfo? targetMethod, object?[]? args)
{
var result = targetMethod.Invoke(_decorated, args);
Expand All @@ -58,31 +58,29 @@ private void SetParameters(TDecorated decorated, IActivityNamingSchema? spanNami
throw;
}
}



protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
{
var noActivityAttribute = targetMethod.GetCustomAttribute<NoActivityAttribute>(false);
var activityAttribute = targetMethod.GetCustomAttribute<TraceActivityAttribute>(false);

if (noActivityAttribute == null && (_decorateAllMethods || activityAttribute!=null))
if (noActivityAttribute == null && (_decorateAllMethods || activityAttribute != null))
{
var defaultSpanName = _namingSchema.GetSpanName(_decorated!.GetType(), targetMethod);
using var activity = _activity.StartActivity(activityAttribute?.Name ?? defaultSpanName);


SpanUtils.AddCommonTags(targetMethod, activity);
InjectAttributes(targetMethod, activity);

if (activityAttribute?.RecordExceptions==false)
if (activityAttribute?.RecordExceptions == false)
{
return InvokeDecoratedExecution(targetMethod, args);
}

return WrapWithRecordException(activity, () => InvokeDecoratedExecution(targetMethod, args));

return WrapWithRecordException(activity, () => InvokeDecoratedExecution(targetMethod, args));
}

return InvokeDecoratedExecution(targetMethod, args);

return InvokeDecoratedExecution(targetMethod, args);
}

private void InjectAttributes(MethodInfo targetMethod, Activity? activity)
Expand Down
25 changes: 25 additions & 0 deletions SpanUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System.Diagnostics;
using System.Reflection;

namespace OpenTelemetry.Instrumentation.Digma;

public static class SpanUtils
{
public static void AddCommonTags(MethodInfo methodInfo, Activity? activity)
{
activity?.AddTag("code.namespace", methodInfo.DeclaringType?.ToString());
activity?.AddTag("code.function", methodInfo.Name);
activity?.AddTag("code.function.parameter.types", BuildParameterTypes(methodInfo));
}

static string BuildParameterTypes(MethodInfo methodInfo)
{
ParameterInfo[] paramInfos = methodInfo.GetParameters();
if (paramInfos.Length <= 0)
{
return "";
}

return string.Join('|', paramInfos.Select(pi => pi.ParameterType.FullName));
}
}
8 changes: 2 additions & 6 deletions Tests/Stubs/DecoratedService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,23 @@ public interface IDecoratedService
public void MethodExplicitlyMarkedForTracing(Action stateValidation);

public Task AsyncMethodExplicitlyMarkedForTracing(Action stateValidation);

public void MethodNotExplicitlyMarkedForTracing(Action stateValidation);


public void MethodNotExplicitlyMarkedForTracing(Action stateValidation);
}

public class DecoratedService : IDecoratedService
{

[TraceActivity()]
public void MethodExplicitlyMarkedForTracing(Action stateValidation)
{
var v = Activity.Current;
stateValidation();
}

[TraceActivity()]
public async Task AsyncMethodExplicitlyMarkedForTracing(Action stateValidation)
{
stateValidation();

}

public void MethodNotExplicitlyMarkedForTracing(Action stateValidation)
Expand Down
31 changes: 22 additions & 9 deletions Tests/TestTracingDecorator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ namespace OpenTelemetry.Instrumentation.Digma.Tests;
[TestClass]
public class TestTracingDecorator
{

private static readonly string ServiceInterfaceFqn =
"OpenTelemetry.Instrumentation.Digma.Tests.Stubs.IDecoratedService";

[TestMethod]
public void Activity_Created_For_Attribute_Marked_Method()
{
Expand All @@ -19,10 +21,11 @@ public void Activity_Created_For_Attribute_Marked_Method()
tracingDecorator.MethodExplicitlyMarkedForTracing(() =>
{
Assert.IsNotNull(Activity.Current);
AssertHasCommonTags(Activity.Current, ServiceInterfaceFqn,
"MethodExplicitlyMarkedForTracing", "System.Action");
});
}


[TestInitialize]
public void SetupOtel()
{
Expand All @@ -42,20 +45,30 @@ public async Task Activity_Created_For_Async_Attribute_Marked_Method()
await tracingDecorator.AsyncMethodExplicitlyMarkedForTracing(() =>
{
Assert.IsNotNull(Activity.Current);
AssertHasCommonTags(Activity.Current, ServiceInterfaceFqn,
"AsyncMethodExplicitlyMarkedForTracing", "System.Action");
});
}

[TestMethod]
public void Activity_Not_Created_For_Non_Attribute_Marked_Method_If_All_Methods_False()
{
DecoratedService service = new DecoratedService();
IDecoratedService tracingDecorator = TraceDecorator<IDecoratedService>.Create(service, decorateAllMethods:false);
tracingDecorator.MethodNotExplicitlyMarkedForTracing(() =>
{

Assert.IsNull(Activity.Current);
});
IDecoratedService tracingDecorator =
TraceDecorator<IDecoratedService>.Create(service, decorateAllMethods: false);
tracingDecorator.MethodNotExplicitlyMarkedForTracing(() => { Assert.IsNull(Activity.Current); });
}


private void AssertHasCommonTags(Activity? activity,
string expectedClassName, string expectedMethodName, string expectedParameterTypes)
{
var kvpTags = activity.Tags.ToArray();
CollectionAssert.Contains(kvpTags, new KeyValuePair<string, string>("code.namespace", expectedClassName));
CollectionAssert.Contains(kvpTags, new KeyValuePair<string, string>("code.function", expectedMethodName));
if (!string.IsNullOrWhiteSpace(expectedParameterTypes))
{
CollectionAssert.Contains(kvpTags,
new KeyValuePair<string, string>("code.function.parameter.types", expectedParameterTypes));
}
}
}

0 comments on commit 50c2a5f

Please sign in to comment.