Skip to content

Commit 408fe14

Browse files
authored
Merge pull request #44 from digma-ai/configuration-file
Configuration file
2 parents 5149cbd + c9a2143 commit 408fe14

22 files changed

+1497
-151
lines changed

.github/workflows/dotnet.yml

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,24 @@ jobs:
1313

1414
steps:
1515
- uses: actions/checkout@v3
16-
- name: Setup .NET 6.0
17-
uses: actions/setup-dotnet@v2
18-
with:
19-
dotnet-version: 6.0.x
20-
- name: Setup .NET 9.0
16+
17+
- name: Setup .NET
2118
uses: actions/setup-dotnet@v2
2219
with:
23-
dotnet-version: 9.0.x
20+
dotnet-version: |
21+
6.0.x
22+
9.0.x
23+
2424
- name: Restore dependencies
2525
run: dotnet restore
26+
2627
- name: Build
2728
run: dotnet build --no-restore
28-
- name: Test
29-
run: dotnet test --no-build --verbosity normal
29+
30+
- name: Test OpenTelemetry.Instrumentation.Digma.Tests
31+
run: |
32+
dotnet test ./src/OpenTelemetry.Instrumentation.Digma.Tests --no-build --verbosity normal
33+
34+
- name: Test AutoInstrumentation.UnitTests
35+
run: |
36+
dotnet test ./src/Tests/AutoInstrumentation.UnitTests --framework net9.0 --no-build --verbosity normal

OpenTelemetry.Instrumentation.Digma.sln

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTelemetry.Instrumentati
1111
EndProject
1212
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenTelemetry.AutoInstrumentation.Digma", "src\OpenTelemetry.AutoInstrumentation.Digma\OpenTelemetry.AutoInstrumentation.Digma.csproj", "{90C5C062-4E24-4C16-A1D1-77BDE5907267}"
1313
EndProject
14+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{27F80A25-54CF-454F-857B-A0A5782D5A1C}"
15+
EndProject
16+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoInstrumentation.UnitTests", "src\Tests\AutoInstrumentation.UnitTests\AutoInstrumentation.UnitTests.csproj", "{5C3CC7F6-09C4-40D4-866C-6F7C0A7A0BC9}"
17+
EndProject
18+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoInstrumentation.IntegrationTests", "src\Tests\AutoInstrumentation.IntegrationTests\AutoInstrumentation.IntegrationTests.csproj", "{E4E70C20-C27F-4B8A-AD67-AD6070D3E217}"
19+
EndProject
1420
Global
1521
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1622
Debug|Any CPU = Debug|Any CPU
@@ -33,11 +39,23 @@ Global
3339
{90C5C062-4E24-4C16-A1D1-77BDE5907267}.Debug|Any CPU.Build.0 = Debug|Any CPU
3440
{90C5C062-4E24-4C16-A1D1-77BDE5907267}.Release|Any CPU.ActiveCfg = Release|Any CPU
3541
{90C5C062-4E24-4C16-A1D1-77BDE5907267}.Release|Any CPU.Build.0 = Release|Any CPU
42+
{5C3CC7F6-09C4-40D4-866C-6F7C0A7A0BC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
43+
{5C3CC7F6-09C4-40D4-866C-6F7C0A7A0BC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
44+
{5C3CC7F6-09C4-40D4-866C-6F7C0A7A0BC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
45+
{5C3CC7F6-09C4-40D4-866C-6F7C0A7A0BC9}.Release|Any CPU.Build.0 = Release|Any CPU
46+
{E4E70C20-C27F-4B8A-AD67-AD6070D3E217}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47+
{E4E70C20-C27F-4B8A-AD67-AD6070D3E217}.Debug|Any CPU.Build.0 = Debug|Any CPU
48+
{E4E70C20-C27F-4B8A-AD67-AD6070D3E217}.Release|Any CPU.ActiveCfg = Release|Any CPU
49+
{E4E70C20-C27F-4B8A-AD67-AD6070D3E217}.Release|Any CPU.Build.0 = Release|Any CPU
3650
EndGlobalSection
3751
GlobalSection(SolutionProperties) = preSolution
3852
HideSolutionNode = FALSE
3953
EndGlobalSection
4054
GlobalSection(ExtensibilityGlobals) = postSolution
4155
SolutionGuid = {1F556E97-6654-4720-AA94-2B3AB1FC8983}
4256
EndGlobalSection
57+
GlobalSection(NestedProjects) = preSolution
58+
{5C3CC7F6-09C4-40D4-866C-6F7C0A7A0BC9} = {27F80A25-54CF-454F-857B-A0A5782D5A1C}
59+
{E4E70C20-C27F-4B8A-AD67-AD6070D3E217} = {27F80A25-54CF-454F-857B-A0A5782D5A1C}
60+
EndGlobalSection
4361
EndGlobal
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using HarmonyLib;
6+
using OpenTelemetry.AutoInstrumentation.Digma.Instrumentation;
7+
using OpenTelemetry.AutoInstrumentation.Digma.Utils;
8+
9+
namespace OpenTelemetry.AutoInstrumentation.Digma;
10+
11+
public class AutoInstrumentor : IDisposable
12+
{
13+
private readonly Harmony _harmony;
14+
private readonly SqlClientInstrumentation _sqlClientInstrumentation;
15+
private readonly VerticaInstrumentation _verticaInstrumentation;
16+
private readonly UserCodeInstrumentation _userCodeInstrumentation;
17+
private readonly HashSet<Assembly> _scannedAssemblies = new();
18+
19+
public AutoInstrumentor(Configuration configuration = null)
20+
{
21+
_harmony = new Harmony("OpenTelemetry.AutoInstrumentation.Digma");
22+
_sqlClientInstrumentation = new SqlClientInstrumentation(_harmony);
23+
_verticaInstrumentation = new VerticaInstrumentation(_harmony);
24+
_userCodeInstrumentation = new UserCodeInstrumentation(_harmony, configuration);
25+
}
26+
27+
public AutoInstrumentor Instrument()
28+
{
29+
Logger.LogInfo("Sync Initialization started");
30+
Logger.LogInfo("Env vars:\n"+string.Join("\n", EnvVars.GetAll().Select(x => $"{x.Key}={x.Value}")));
31+
32+
AppDomain.CurrentDomain.AssemblyLoad += (sender, args) =>
33+
{
34+
Logger.LogDebug($"Processing lazy-loaded {args.LoadedAssembly.FullName}");
35+
ProcessAssembly(args.LoadedAssembly);
36+
};
37+
38+
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
39+
{
40+
Logger.LogDebug($"Processing pre-loaded assembly {assembly.FullName}");
41+
ProcessAssembly(assembly);
42+
}
43+
44+
return this;
45+
}
46+
47+
private void ProcessAssembly(Assembly assembly)
48+
{
49+
lock (_scannedAssemblies)
50+
{
51+
if(!_scannedAssemblies.Add(assembly))
52+
return;
53+
}
54+
55+
var name = assembly.GetName().Name;
56+
if (name == "System.Data")
57+
{
58+
_sqlClientInstrumentation.Instrument(assembly);
59+
return;
60+
}
61+
62+
if (name == "Vertica.Data")
63+
{
64+
_verticaInstrumentation.Instrument(assembly);
65+
return;
66+
}
67+
68+
_userCodeInstrumentation.Instrument(assembly);
69+
}
70+
71+
public void Dispose()
72+
{
73+
_harmony.UnpatchAll(_harmony.Id);
74+
}
75+
}

src/OpenTelemetry.AutoInstrumentation.Digma/Instrumentation/UserCodeInstrumentation.cs

Lines changed: 23 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Diagnostics;
3-
using System.Linq;
43
using System.Reflection;
54
using HarmonyLib;
65
using OpenTelemetry.AutoInstrumentation.Digma.Utils;
@@ -14,40 +13,32 @@ public class UserCodeInstrumentation
1413
private static readonly MethodInfo FinalizerMethodInfo = typeof(UserCodeInstrumentation).GetMethod(nameof(Finalizer), BindingFlags.Static | BindingFlags.NonPublic);
1514

1615
private readonly Harmony _harmony;
17-
private readonly string[] _namespaces;
18-
private readonly bool _includePrivateMethods;
16+
private readonly Configuration _configuration;
1917

20-
public UserCodeInstrumentation(Harmony harmony)
18+
public UserCodeInstrumentation(Harmony harmony, Configuration configuration = null)
2119
{
2220
_harmony = harmony;
23-
var namespacesStr = Environment.GetEnvironmentVariable("OTEL_DOTNET_AUTO_NAMESPACES");
24-
_namespaces = namespacesStr?.Split(',')
25-
.Select(x => x.Trim())
26-
.Where(x => !string.IsNullOrWhiteSpace(x))
27-
.ToArray()
28-
?? Array.Empty<string>();
29-
_includePrivateMethods = Environment.GetEnvironmentVariable("OTEL_DOTNET_AUTO_INCLUDE_PRIVATE_METHODS")
30-
?.Equals("true", StringComparison.InvariantCultureIgnoreCase) == true;
31-
32-
Logger.LogInfo($"Requested to auto-instrument {_namespaces.Length} namespaces:\n"+
33-
string.Join("\n", _namespaces));
21+
_configuration = configuration ?? ConfigurationProvider.GetConfiguration();
22+
23+
Logger.LogInfo("Configuration:\n" + _configuration.ToJson());
3424
}
3525

3626
public void Instrument(Assembly assembly)
3727
{
38-
var name = assembly.GetName().Name;
39-
if (ShouldInstrumentAssembly(name))
28+
MethodInfo[] methods;
29+
try
4030
{
41-
var relevantTypes = assembly.GetTypes().Where(ShouldInstrumentType).ToArray();
42-
var methods = relevantTypes
43-
.SelectMany(t => t
44-
.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
45-
.Where(m => ShouldInstrumentMethod(t,m)))
46-
.ToArray();
47-
foreach (var method in methods)
48-
{
49-
PatchMethod(method);
50-
}
31+
methods = MethodDiscovery.GetMethodsToPatch(assembly, _configuration);
32+
}
33+
catch (Exception e)
34+
{
35+
Logger.LogError($"Failed to discover methods to instrument in '{assembly.FullName}'", e);
36+
return;
37+
}
38+
39+
foreach (var method in methods)
40+
{
41+
PatchMethod(method);
5142
}
5243
}
5344

@@ -69,44 +60,9 @@ private void PatchMethod(MethodInfo originalMethodInfo)
6960
{
7061
Logger.LogError($"Failed to patch {methodFullName}", e);
7162
}
72-
73-
}
74-
75-
private bool ShouldInstrumentAssembly(string assemblyName)
76-
{
77-
return _namespaces.Any(ns => ns.StartsWith(assemblyName, StringComparison.OrdinalIgnoreCase) ||
78-
assemblyName.StartsWith(ns, StringComparison.OrdinalIgnoreCase));
79-
}
80-
81-
private bool ShouldInstrumentType(Type type)
82-
{
83-
return !typeof(Delegate).IsAssignableFrom(type) &&
84-
!type.IsGenericType &&
85-
_namespaces.Any(ns => type.FullName?.StartsWith(ns, StringComparison.OrdinalIgnoreCase) == true);
86-
}
87-
88-
private bool ShouldInstrumentMethod(Type type, MethodInfo methodInfo)
89-
{
90-
return methodInfo.DeclaringType == type &&
91-
!methodInfo.IsAbstract &&
92-
!methodInfo.IsSpecialName && // property accessors and operator overloading methods
93-
!methodInfo.IsGenericMethod &&
94-
methodInfo.Name != "GetHashCode" &&
95-
methodInfo.Name != "Equals" &&
96-
methodInfo.Name != "ToString" &&
97-
methodInfo.Name != "Deconstruct" &&
98-
methodInfo.Name != "MoveNext" &&
99-
methodInfo.Name != "SetStateMachine" &&
100-
methodInfo.Name != "PrintMembers" &&
101-
methodInfo.Name != "Dispose" &&
102-
methodInfo.Name != "BuildKey" &&
103-
methodInfo.Name != "IsEmpty" &&
104-
methodInfo.Name != "GetResultByIndex" &&
105-
methodInfo.Name != "<Clone>$" &&
106-
(methodInfo.IsPublic || _includePrivateMethods);
10763
}
10864

109-
private bool DoesAlreadyStartActivity(MethodInfo methodInfo)
65+
private static bool DoesAlreadyStartActivity(MethodInfo methodInfo)
11066
{
11167
var instructions = PatchProcessor.GetOriginalInstructions(methodInfo);
11268
foreach (var instruction in instructions)
@@ -142,6 +98,10 @@ private static void Finalizer(MethodBase __originalMethod, Activity __state, Exc
14298
{
14399
activity.RecordException(__exception);
144100
activity.SetStatus(ActivityStatusCode.Error);
101+
}
102+
else
103+
{
104+
activity.SetStatus(ActivityStatusCode.Ok);
145105
}
146106
activity.Dispose();
147107
Logger.LogDebug($"Closed Activity: {activity.Source.Name}.{activity.OperationName}");

src/OpenTelemetry.AutoInstrumentation.Digma/Instrumentation/VerticaInstrumentation.cs

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ public class VerticaInstrumentation
2020

2121
private readonly Harmony _harmony;
2222

23-
private readonly string[] TargetMethodNames =
23+
private readonly Func<MethodBase, bool>[] TargetMethodPredicates =
2424
{
25-
"ExecuteReader",
26-
"ExecuteScalar",
27-
"ExecuteNonQuery",
25+
m => m.Name == "ExecuteReader" && m.GetParameters().Any(p => p.ParameterType.Name == "CommandBehavior"),
26+
m => m.Name == "ExecuteScalar",
27+
m => m.Name == "ExecuteNonQuery",
2828
};
2929

3030
public VerticaInstrumentation(Harmony harmony)
@@ -36,13 +36,6 @@ public void Instrument(Assembly verticaDataAssembly)
3636
{
3737
try
3838
{
39-
var verticaCommandType = verticaDataAssembly.GetType("Vertica.Data.VerticaClient.VerticaCommand", throwOnError: false);
40-
if (verticaCommandType == null)
41-
{
42-
Logger.LogError("Vertica.Data.VerticaClient.VerticaCommand not found.");
43-
return;
44-
}
45-
4639
var sCommandType = verticaDataAssembly.GetType("Vertica.Data.Internal.ADO.Net.SCommand", throwOnError: false);
4740
if (sCommandType == null)
4841
{
@@ -78,9 +71,9 @@ public void Instrument(Assembly verticaDataAssembly)
7871
return;
7972
}
8073

81-
var methodInfos = verticaCommandType.GetMethods(BindingFlags.Public | BindingFlags.Instance)
82-
.Where(x => TargetMethodNames.Contains(x.Name))
83-
.Where(x => x.DeclaringType == verticaCommandType)
74+
var methodInfos = sCommandType.GetMethods(BindingFlags.Public | BindingFlags.Instance)
75+
.Where(x => TargetMethodPredicates.Any(p => p(x)))
76+
.Where(x => x.DeclaringType == sCommandType)
8477
.ToArray();
8578
foreach (var methodInfo in methodInfos)
8679
{

src/OpenTelemetry.AutoInstrumentation.Digma/OpenTelemetry.AutoInstrumentation.Digma.csproj

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
<Authors>Digma Team</Authors>
1111
<Company>Digma</Company>
1212
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
13-
13+
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
14+
1415
<!-- Version placeholder - will be overriden by github action before publishing
1516
(see ".github/workflows/composite/pack/action.yml" line 18)-->
1617
<Version>0.0.0</Version>
@@ -21,6 +22,7 @@
2122
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="9.0.0" PrivateAssets="all" />
2223
</ItemGroup>
2324

24-
25-
25+
<ItemGroup Condition="'$(TargetFramework)' == 'net47'">
26+
<PackageReference Include="System.Text.Json" Version="9.0.0" />
27+
</ItemGroup>
2628
</Project>

0 commit comments

Comments
 (0)