Skip to content

Commit

Permalink
Refactor GuidGenerator to work around ILRepack of System.Diagnostics.…
Browse files Browse the repository at this point in the history
…DiagnosticSource
  • Loading branch information
tippmar-nr committed Aug 16, 2024
1 parent a32b438 commit 7e7c482
Showing 1 changed file with 40 additions and 20 deletions.
60 changes: 40 additions & 20 deletions src/Agent/NewRelic/Agent/Core/Utilities/GuidGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
// SPDX-License-Identifier: Apache-2.0

using NewRelic.Agent.Extensions.Logging;
using NewRelic.Reflection;
using System;
using System.Diagnostics;
using System.IO;
using System.Security.Cryptography;
using System.Threading;

Expand All @@ -31,25 +30,16 @@ public static string GenerateNewRelicGuid()

public static string GenerateNewRelicTraceId()
{
try
var retVal = _traceGeneratorFunc();
if (retVal == null)
{
return _traceGeneratorFunc();
}
catch (Exception ex)
{
// If the app does not reference System.Diagnostics.DiagnosticSource then Activity.Current will not be available.
// A FileNotFoundException occurs when System.Diagnostics.DiagnosticSource is unavailble.

if (!(ex is FileNotFoundException))
{
Log.Warn(ex, "Unexpected exception type when attempting to generate a trace ID from Activity.Current");
}

// Fall back to using our standard method of generating traceIds.
Log.Info($"Trace IDs will be generated using the standard generator");
Interlocked.Exchange(ref _traceGeneratorFunc, GenerateTraceId);
return _traceGeneratorFunc();
}

return retVal;
}

private static string GenerateTraceId()
Expand All @@ -63,12 +53,42 @@ private static string GenerateTraceId()

private static string GetTraceIdFromCurrentActivity()
{
if (Activity.Current != default && Activity.Current.IdFormat == ActivityIdFormat.W3C)
{
return Activity.Current.TraceId.ToString();
}
// because we ILRepack System.Diagnostics.DiagnosticSource, we have to look for the app's reference to it (if there is one)
// and use reflection to get the trace id from the current activity

// get list of loaded assemblies
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
// find System.Diagnostics.DiagnosticSource
var diagnosticSourceAssembly = Array.Find(assemblies, a => a.FullName.StartsWith("System.Diagnostics.DiagnosticSource"));
if (diagnosticSourceAssembly == null) // customer app didn't reference the assembly
return null;

// find the Activity class
var activityType = diagnosticSourceAssembly.GetType("System.Diagnostics.Activity");

var fieldReadAccessor = VisibilityBypasser.Instance.GenerateFieldReadAccessor<object>(activityType, "s_current");
if (fieldReadAccessor == null)
return null;

var current = fieldReadAccessor(null);
// get the Value property
var valuePropertyAccessor = VisibilityBypasser.Instance.GeneratePropertyAccessor<object>(current.GetType(), "Value");
if (valuePropertyAccessor == null)
return null;
var value = valuePropertyAccessor(current);

// get IdFormat property
var idFormatGetter = VisibilityBypasser.Instance.GeneratePropertyAccessor<object>(value.GetType(), "IdFormat");
var idFormat = idFormatGetter(value);
if (idFormat == null || Enum.GetName(idFormat.GetType(), idFormat) != "W3C") // make sure it's in W3C trace id format
return null;

return GenerateTraceId();
// get TraceId property
var traceIdGetter = VisibilityBypasser.Instance.GeneratePropertyAccessor<object>(value.GetType(), "TraceId");
if (traceIdGetter == null)
return null;

return traceIdGetter(value).ToString();
}
}
}

0 comments on commit 7e7c482

Please sign in to comment.