diff --git a/Autometrics Samples.sln b/Autometrics Samples.sln deleted file mode 100644 index 83c7d88..0000000 --- a/Autometrics Samples.sln +++ /dev/null @@ -1,37 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.5.33627.172 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Autometrics.Samples.ConsoleApp", "examples\Autometrics.Samples.ConsoleApp\Autometrics.Samples.ConsoleApp.csproj", "{7905F97A-78BA-4B43-9E31-AA52E1E2338E}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Autometrics.Samples.Library", "examples\Autometrics.Samples.Library\Autometrics.Samples.Library.csproj", "{F6692CF6-2DD5-4F09-BD97-5CF125AF2DC6}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Autometrics.Samples.WebApp", "examples\Autometrics.Samples.WebApp\Autometrics.Samples.WebApp.csproj", "{05CD2671-F249-40A7-A775-294018BF673B}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7905F97A-78BA-4B43-9E31-AA52E1E2338E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7905F97A-78BA-4B43-9E31-AA52E1E2338E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7905F97A-78BA-4B43-9E31-AA52E1E2338E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7905F97A-78BA-4B43-9E31-AA52E1E2338E}.Release|Any CPU.Build.0 = Release|Any CPU - {F6692CF6-2DD5-4F09-BD97-5CF125AF2DC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F6692CF6-2DD5-4F09-BD97-5CF125AF2DC6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F6692CF6-2DD5-4F09-BD97-5CF125AF2DC6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F6692CF6-2DD5-4F09-BD97-5CF125AF2DC6}.Release|Any CPU.Build.0 = Release|Any CPU - {05CD2671-F249-40A7-A775-294018BF673B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {05CD2671-F249-40A7-A775-294018BF673B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {05CD2671-F249-40A7-A775-294018BF673B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {05CD2671-F249-40A7-A775-294018BF673B}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {7B5D2BC5-3197-4A76-A94E-468600181D0C} - EndGlobalSection -EndGlobal diff --git a/Autometrics-CS.sln b/Autometrics-CS.sln index 052462d..703ba0d 100644 --- a/Autometrics-CS.sln +++ b/Autometrics-CS.sln @@ -15,24 +15,57 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Autometrics.Instrumentation.Tests", "src\Autometrics.Instrumentation.Tests\Autometrics.Instrumentation.Tests.csproj", "{93EAE891-CAB6-47D4-80A8-B151A423FDA6}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{12A476A7-0753-4A7E-BFA9-6D2701B34E89}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Autometrics.Samples.ConsoleApp", "examples\Autometrics.Samples.ConsoleApp\Autometrics.Samples.ConsoleApp.csproj", "{488AB5DF-36E4-4992-B9A7-8C2E6D188CDC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Autometrics.Samples.Library", "examples\Autometrics.Samples.Library\Autometrics.Samples.Library.csproj", "{8EDA190B-A429-4BB9-949F-4507870F3275}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Autometrics.Samples.WebApp", "examples\Autometrics.Samples.WebApp\Autometrics.Samples.WebApp.csproj", "{99E9E740-B530-4B7F-9598-C044E669F56E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Local Release|Any CPU = Local Release|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A220D3AC-D7FC-4CCA-BC38-F209691851C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A220D3AC-D7FC-4CCA-BC38-F209691851C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A220D3AC-D7FC-4CCA-BC38-F209691851C2}.Local Release|Any CPU.ActiveCfg = Local Release|Any CPU + {A220D3AC-D7FC-4CCA-BC38-F209691851C2}.Local Release|Any CPU.Build.0 = Local Release|Any CPU {A220D3AC-D7FC-4CCA-BC38-F209691851C2}.Release|Any CPU.ActiveCfg = Release|Any CPU {A220D3AC-D7FC-4CCA-BC38-F209691851C2}.Release|Any CPU.Build.0 = Release|Any CPU {93EAE891-CAB6-47D4-80A8-B151A423FDA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {93EAE891-CAB6-47D4-80A8-B151A423FDA6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93EAE891-CAB6-47D4-80A8-B151A423FDA6}.Local Release|Any CPU.ActiveCfg = Local Release|Any CPU + {93EAE891-CAB6-47D4-80A8-B151A423FDA6}.Local Release|Any CPU.Build.0 = Local Release|Any CPU {93EAE891-CAB6-47D4-80A8-B151A423FDA6}.Release|Any CPU.ActiveCfg = Release|Any CPU {93EAE891-CAB6-47D4-80A8-B151A423FDA6}.Release|Any CPU.Build.0 = Release|Any CPU + {488AB5DF-36E4-4992-B9A7-8C2E6D188CDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {488AB5DF-36E4-4992-B9A7-8C2E6D188CDC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {488AB5DF-36E4-4992-B9A7-8C2E6D188CDC}.Local Release|Any CPU.ActiveCfg = Local Release|Any CPU + {488AB5DF-36E4-4992-B9A7-8C2E6D188CDC}.Local Release|Any CPU.Build.0 = Local Release|Any CPU + {488AB5DF-36E4-4992-B9A7-8C2E6D188CDC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8EDA190B-A429-4BB9-949F-4507870F3275}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8EDA190B-A429-4BB9-949F-4507870F3275}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8EDA190B-A429-4BB9-949F-4507870F3275}.Local Release|Any CPU.ActiveCfg = Local Release|Any CPU + {8EDA190B-A429-4BB9-949F-4507870F3275}.Local Release|Any CPU.Build.0 = Local Release|Any CPU + {8EDA190B-A429-4BB9-949F-4507870F3275}.Release|Any CPU.ActiveCfg = Release|Any CPU + {99E9E740-B530-4B7F-9598-C044E669F56E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {99E9E740-B530-4B7F-9598-C044E669F56E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {99E9E740-B530-4B7F-9598-C044E669F56E}.Local Release|Any CPU.ActiveCfg = Local Release|Any CPU + {99E9E740-B530-4B7F-9598-C044E669F56E}.Local Release|Any CPU.Build.0 = Local Release|Any CPU + {99E9E740-B530-4B7F-9598-C044E669F56E}.Release|Any CPU.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {488AB5DF-36E4-4992-B9A7-8C2E6D188CDC} = {12A476A7-0753-4A7E-BFA9-6D2701B34E89} + {8EDA190B-A429-4BB9-949F-4507870F3275} = {12A476A7-0753-4A7E-BFA9-6D2701B34E89} + {99E9E740-B530-4B7F-9598-C044E669F56E} = {12A476A7-0753-4A7E-BFA9-6D2701B34E89} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {422C978E-1479-4996-B5D7-B494E97E3B45} EndGlobalSection diff --git a/examples/Autometrics.Samples.ConsoleApp/Autometrics.Samples.ConsoleApp.csproj b/examples/Autometrics.Samples.ConsoleApp/Autometrics.Samples.ConsoleApp.csproj index 6a0af5f..b23c946 100644 --- a/examples/Autometrics.Samples.ConsoleApp/Autometrics.Samples.ConsoleApp.csproj +++ b/examples/Autometrics.Samples.ConsoleApp/Autometrics.Samples.ConsoleApp.csproj @@ -8,6 +8,7 @@ 1.0.2.1 + Debug;Release;Local Release @@ -18,7 +19,7 @@ - + diff --git a/examples/Autometrics.Samples.ConsoleApp/ConsoleMetrics.cs b/examples/Autometrics.Samples.ConsoleApp/ConsoleMetrics.cs index f5226a3..ce639a7 100644 --- a/examples/Autometrics.Samples.ConsoleApp/ConsoleMetrics.cs +++ b/examples/Autometrics.Samples.ConsoleApp/ConsoleMetrics.cs @@ -11,6 +11,9 @@ public static void GenerateActivity() // Create a meter provider with the console exporter connected to the Autometrics.Instrumentation meter using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter("Autometrics.Instrumentation") + .AddView( + instrumentName: "function.calls.duration", + new ExplicitBucketHistogramConfiguration { Boundaries = new double[] { 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10 } }) .AddConsoleExporter() .Build(); diff --git a/examples/Autometrics.Samples.ConsoleApp/OtelCollector.cs b/examples/Autometrics.Samples.ConsoleApp/OtelCollector.cs index 7ccebcc..2eee1a5 100644 --- a/examples/Autometrics.Samples.ConsoleApp/OtelCollector.cs +++ b/examples/Autometrics.Samples.ConsoleApp/OtelCollector.cs @@ -11,6 +11,9 @@ public static void GenerateActivity() // Create a meter provider with the Otlp exporter connected to the Autometrics.Instrumentation using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter("Autometrics.Instrumentation") + .AddView( + instrumentName: "function.calls.duration", + new ExplicitBucketHistogramConfiguration { Boundaries = new double[] { 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10 } }) .AddOtlpExporter() .Build(); diff --git a/examples/Autometrics.Samples.ConsoleApp/Program.cs b/examples/Autometrics.Samples.ConsoleApp/Program.cs index b737dca..6afea6f 100644 --- a/examples/Autometrics.Samples.ConsoleApp/Program.cs +++ b/examples/Autometrics.Samples.ConsoleApp/Program.cs @@ -15,6 +15,9 @@ private static void Main(string[] args) Console.WriteLine("6. Exit"); Console.Write("Enter the option number: "); + //set the environment variable for the service name, yuo can also use OTEL_SERVICE_NAME + System.Environment.SetEnvironmentVariable("AUTOMETRICS_SERVICE_NAME", "Console Simulator!"); + if (int.TryParse(Console.ReadLine(), out int option)) { switch (option) diff --git a/examples/Autometrics.Samples.ConsoleApp/ScrapableMetrics.cs b/examples/Autometrics.Samples.ConsoleApp/ScrapableMetrics.cs index bc8da49..4fe7cb4 100644 --- a/examples/Autometrics.Samples.ConsoleApp/ScrapableMetrics.cs +++ b/examples/Autometrics.Samples.ConsoleApp/ScrapableMetrics.cs @@ -11,10 +11,13 @@ public static void GenerateActivity() // Create a meter provider with the console exporter connected to the Autometrics.Instrumentation meter using var meterProvider = Sdk.CreateMeterProviderBuilder() .AddMeter("Autometrics.Instrumentation") + .AddView( + instrumentName: "function.calls.duration", + new ExplicitBucketHistogramConfiguration { Boundaries = new double[] { 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10 } }) .AddPrometheusHttpListener( options => { - options.UriPrefixes = new string[] { "http://localhost:9090/" }; + options.UriPrefixes = new string[] { "http://localhost:9091/" }; }) .Build(); diff --git a/examples/Autometrics.Samples.Library/Autometrics.Samples.Library.csproj b/examples/Autometrics.Samples.Library/Autometrics.Samples.Library.csproj index 652385b..9561a46 100644 --- a/examples/Autometrics.Samples.Library/Autometrics.Samples.Library.csproj +++ b/examples/Autometrics.Samples.Library/Autometrics.Samples.Library.csproj @@ -4,10 +4,11 @@ net6.0 enable enable + Debug;Release;Local Release - + diff --git a/examples/Autometrics.Samples.WebApp/Autometrics.Samples.WebApp.csproj b/examples/Autometrics.Samples.WebApp/Autometrics.Samples.WebApp.csproj index c1ee348..31a7f73 100644 --- a/examples/Autometrics.Samples.WebApp/Autometrics.Samples.WebApp.csproj +++ b/examples/Autometrics.Samples.WebApp/Autometrics.Samples.WebApp.csproj @@ -4,6 +4,7 @@ net6.0 enable enable + Debug;Release;Local Release @@ -12,7 +13,8 @@ - + + diff --git a/examples/Autometrics.Samples.WebApp/Program.cs b/examples/Autometrics.Samples.WebApp/Program.cs index 2efa887..4e408f1 100644 --- a/examples/Autometrics.Samples.WebApp/Program.cs +++ b/examples/Autometrics.Samples.WebApp/Program.cs @@ -11,6 +11,10 @@ // Additional configuration or other exporters can be added here builder.AddOtlpExporter(); builder.AddMeter("Autometrics.Instrumentation"); + builder.AddView( + instrumentName: "function.calls.duration", + new ExplicitBucketHistogramConfiguration { Boundaries = new double[] { 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10 } } + ); }); var app = builder.Build(); diff --git a/src/Autometrics.Instrumentation.Tests/Autometrics.Instrumentation.Tests.csproj b/src/Autometrics.Instrumentation.Tests/Autometrics.Instrumentation.Tests.csproj index 362c3da..0126c47 100644 --- a/src/Autometrics.Instrumentation.Tests/Autometrics.Instrumentation.Tests.csproj +++ b/src/Autometrics.Instrumentation.Tests/Autometrics.Instrumentation.Tests.csproj @@ -7,6 +7,7 @@ false true + Debug;Release;Local Release diff --git a/src/Autometrics.Instrumentation.Tests/Utils/MetricReading.cs b/src/Autometrics.Instrumentation.Tests/Utils/MetricReading.cs index 159b6e8..7803432 100644 --- a/src/Autometrics.Instrumentation.Tests/Utils/MetricReading.cs +++ b/src/Autometrics.Instrumentation.Tests/Utils/MetricReading.cs @@ -31,7 +31,7 @@ public MetricReading(string metricName, T reading, KeyValuePair case "result": Result = tag.Value?.ToString(); break; - case "caller": + case "caller.function": Caller = tag.Value?.ToString(); break; case "objectiveName": diff --git a/src/Autometrics.Instrumentation/Aspects/AutometricsAspect.cs b/src/Autometrics.Instrumentation/Aspects/AutometricsAspect.cs index 0ca6352..a2313f9 100644 --- a/src/Autometrics.Instrumentation/Aspects/AutometricsAspect.cs +++ b/src/Autometrics.Instrumentation/Aspects/AutometricsAspect.cs @@ -1,11 +1,8 @@ using AspectInjector.Broker; using Autometrics.Instrumentation.Attributes; using Autometrics.Instrumentation.SLO; -using System; -using System.Collections.Generic; using System.Diagnostics; using System.Reflection; -using System.Text; using System.Text.RegularExpressions; namespace Autometrics.Instrumentation.Aspects @@ -18,10 +15,6 @@ namespace Autometrics.Instrumentation.Aspects [Aspect(Scope.Global)] public class AutometricsAspect { - // Aspect Injection renames methods, if we get a renamed one it will look like this: __a$_around_SaferMethod_100663303_o - // We need a regex to identify these methods - private static readonly string renamedMethodRegex = @"^__a\$_around_(?.*)_\d{5,10}_o$"; - /// /// This is the method that will be injected into the target method, it will wrap the target method in a try/catch block and record the duration of the method call /// If any exceptions are thrown, they will be rethrown after the duration is recorded and a result of error tagged on the metric @@ -40,18 +33,21 @@ public object HandleMethod( [Argument(Source.Triggers)] Attribute[] triggers) { Stopwatch stopwatch = new Stopwatch(); - stopwatch.Start(); bool success = false; // We'll start with our objective as null, but if the trigger has one we'll use it Objective? slo = null; + string? serviceName = null; if (triggers.Length > 0 && triggers[0] is AutometricsAttribute attribute) { slo = attribute.SLO; + serviceName = attribute.GetServiceName(); } try { + // start the stopwatch, call the method, stop the stopwatch to record the duration + stopwatch.Start(); object result = method(arguments); success = true; return result; @@ -64,50 +60,9 @@ public object HandleMethod( finally { stopwatch.Stop(); - MetricCounters.RecordFunctionCall(stopwatch.Elapsed.TotalSeconds, methodName, success, metadata.DeclaringType.FullName, GetCallingMethodName(metadata), slo); + CallingMethod caller = new CallingMethod(metadata); + MetricCounters.RecordFunctionCall(stopwatch.Elapsed.TotalSeconds, methodName, success, metadata.DeclaringType.FullName, caller, serviceName, slo); } } - - /// - /// This resolves the name of the calling method, and if the calling method was renamed by AspectInjector, it will attempt to return the original name via regex - /// - /// The metadata from AspectInjector - /// - private static string? GetCallingMethodName(MethodBase method) - { - var stackTrace = new StackTrace(); - var stackFrames = stackTrace.GetFrames(); - MethodBase callingMethod = null; - - if (stackFrames == null) - { - return null; - } - - for (int i = 0; i < stackFrames.Length; i++) - { - if (stackFrames[i].GetMethod() == method) - { - // The calling method is the one before the current method in the stack - callingMethod = i + 1 < stackFrames.Length ? stackFrames[i + 1].GetMethod() : null; - } - } - - // If the calling method isn't null, match it to our regex to see if it's a renamed method, then return the original name - if (callingMethod != null) - { - var match = Regex.Match(callingMethod.Name, renamedMethodRegex, RegexOptions.Compiled); - if (match.Success) - { - return match.Groups["originalMethodName"].Value; - } - else - { - return callingMethod.Name; - } - } - - return null; - } } -} +} \ No newline at end of file diff --git a/src/Autometrics.Instrumentation/Attributes/AutometricsAttribute.cs b/src/Autometrics.Instrumentation/Attributes/AutometricsAttribute.cs index c21d97d..9f0af99 100644 --- a/src/Autometrics.Instrumentation/Attributes/AutometricsAttribute.cs +++ b/src/Autometrics.Instrumentation/Attributes/AutometricsAttribute.cs @@ -17,20 +17,38 @@ public class AutometricsAttribute : Attribute /// Service-Level Objectives (SLOs) Spec /// public Objective? SLO { get; } + public string EntryAssemblyName { get; private set; } public AutometricsAttribute() { SLO = null; + EntryAssemblyName = GetAssemblyName(); } public AutometricsAttribute(string objectiveName, ObjectivePercentile objectivePercentile, ObjectiveLatency objectiveLatencyThreshold, ObjectiveType objectiveType = ObjectiveType.SuccessAndLatency) { SLO = new Objective(objectiveName, objectivePercentile, objectiveLatencyThreshold, objectiveType); + EntryAssemblyName = GetAssemblyName(); } public AutometricsAttribute(string objectiveName, ObjectivePercentile objectivePercentile) { SLO = new Objective(objectiveName, objectivePercentile); + EntryAssemblyName = GetAssemblyName(); + } + + private string GetAssemblyName() + { + return Assembly.GetEntryAssembly()?.GetName().Name ?? "Unknown"; + } + + /// + /// Gets the ServiceName, starting at the Attribule level, then the Environment Variable, then the EntryAssemblyName + /// + /// + public string GetServiceName() + { + return Environment.GetEnvironmentVariable("AUTOMETRICS_SERVICE_NAME") ?? Environment.GetEnvironmentVariable("OTEL_SERVICE_NAME") ?? EntryAssemblyName; } } diff --git a/src/Autometrics.Instrumentation/Attributes/CallingMethod.cs b/src/Autometrics.Instrumentation/Attributes/CallingMethod.cs new file mode 100644 index 0000000..9874515 --- /dev/null +++ b/src/Autometrics.Instrumentation/Attributes/CallingMethod.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; + +namespace Autometrics.Instrumentation.Attributes +{ + internal class CallingMethod + { + // Aspect Injection renames methods, if we get a renamed one it will look like this: __a$_around_SaferMethod_100663303_o + // We need a regex to identify these methods + private static readonly Regex _methodRegex = new Regex(@"^__a\$_around_(?.*)_\d{5,10}_o$", RegexOptions.Compiled); + + public CallingMethod(MethodBase method) + { + try + { + SetCallingMethod(method); + } + catch + { + HasMethodData = false; + } + } + + public string? MethodModule { get; set; } + public string? MethodName { get; set; } + public bool HasMethodData { get; internal set; } = false; + + /// + /// This resolves the name of the calling method, and if the calling method was renamed by AspectInjector, it will attempt to return the original name via regex + /// + /// The metadata from AspectInjector + /// + private void SetCallingMethod(MethodBase method) + { + var stackTrace = new StackTrace(); + var stackFrames = stackTrace.GetFrames(); + MethodBase callingMethod = null; + + if (stackFrames == null) + { + MethodModule = null; + MethodName = null; + return; + } + + int length = stackFrames.Length; + for (int i = 0; i < length; i++) + { + if (stackFrames[i].GetMethod() == method) + { + // The calling method is the one before the current method in the stack + callingMethod = i + 1 < length ? stackFrames[i + 1].GetMethod() : null; + } + } + + // If the calling method isn't null, match it to our regex to see if it's a renamed method, then return the original name + if (callingMethod != null) + { + HasMethodData = true; + var match = _methodRegex.Match(callingMethod.Name); + if (match.Success) + { + MethodName = match.Groups["originalMethodName"].Value; + } + else + { + MethodName = callingMethod.Name; + } + + MethodModule = callingMethod.Module.Name; + } + } + } +} diff --git a/src/Autometrics.Instrumentation/Autometrics.Instrumentation.csproj b/src/Autometrics.Instrumentation/Autometrics.Instrumentation.csproj index d786c6f..78e32ca 100644 --- a/src/Autometrics.Instrumentation/Autometrics.Instrumentation.csproj +++ b/src/Autometrics.Instrumentation/Autometrics.Instrumentation.csproj @@ -5,23 +5,23 @@ 10.0 enable enable - 0.0.1.0 True Autometrics, P2P-Nathan https://github.com/autometrics-dev Autometrics Instrumentation - 0.1.0-alpha1 + 0.2.0-beta A .NET instrumentation of the Autometrics (https://github.com/autometrics-dev) observability micro-framework. It makes it quick and easy to instrument your code to collect standardized metrics, including function call counts, durations, and build information. - README.md - This is the initial Alpha release of Autometrics for .NET + Autometrics_README.md + This is the initial Beta release of Autometrics for .NET LICENSE.txt True snupkg autometrics_logo.png + Debug;Release;Local Release - + @@ -38,7 +38,7 @@ True \ - + True \ diff --git a/src/Autometrics.Instrumentation/README.md b/src/Autometrics.Instrumentation/Autometrics_README.md similarity index 100% rename from src/Autometrics.Instrumentation/README.md rename to src/Autometrics.Instrumentation/Autometrics_README.md diff --git a/src/Autometrics.Instrumentation/MetricCounters.cs b/src/Autometrics.Instrumentation/MetricCounters.cs index f3ef199..62583db 100644 --- a/src/Autometrics.Instrumentation/MetricCounters.cs +++ b/src/Autometrics.Instrumentation/MetricCounters.cs @@ -1,4 +1,5 @@ -using Autometrics.Instrumentation.SLO; +using Autometrics.Instrumentation.Attributes; +using Autometrics.Instrumentation.SLO; using System.Diagnostics.Metrics; using System.Reflection; @@ -66,23 +67,24 @@ static MetricCounters() /// The name of the function tracked /// A string value of "ok" or "error" based on the outcome /// optional caller data - internal static void RecordFunctionCall(double duration, string functionName, bool success, string declaringType, string? caller, Objective? slo) + internal static void RecordFunctionCall(double duration, string functionName, bool success, string declaringType, CallingMethod caller, string? serviceName, Objective? slo) { List> callTags = new List> { new KeyValuePair("function", functionName), new KeyValuePair("module", declaringType), - new KeyValuePair("result", success ? "ok" : "error") + new KeyValuePair("result", success ? "ok" : "error"), + new KeyValuePair("service.name", serviceName) }; - if (caller != null) + if (caller.HasMethodData) { - callTags.Add(new KeyValuePair("caller", caller)); + callTags.Add(new KeyValuePair("caller.function", caller.MethodName)); + callTags.Add(new KeyValuePair("caller.module", caller.MethodModule)); } if (slo != null) { - functionCallCount.Add(1, slo.GetCallCountTags(callTags)); functionCallDuration.Record(duration, slo.GetCallDurationTags(callTags)); } @@ -103,6 +105,13 @@ internal static void RecordFunctionCall(double duration, string functionName, bo { Dictionary buildTags = new Dictionary(); + // if our environmental variables are not null, use that as the service name + string? serviceName = Environment.GetEnvironmentVariable("AUTOMETRICS_SERVICE_NAME") ?? Environment.GetEnvironmentVariable("OTEL_SERVICE_NAME"); + if (serviceName != null) + { + buildTags.Add("service.name", serviceName); + } + AssemblyName? assemblyName = assembly?.GetName(); AssemblyInformationalVersionAttribute assemblyInformationalVersionAttribute = assembly.GetCustomAttribute(); if (assemblyName != null) diff --git a/src/Autometrics.Instrumentation/SLO/Objective.cs b/src/Autometrics.Instrumentation/SLO/Objective.cs index 6e5dfa1..7fafc7d 100644 --- a/src/Autometrics.Instrumentation/SLO/Objective.cs +++ b/src/Autometrics.Instrumentation/SLO/Objective.cs @@ -47,12 +47,16 @@ public Objective(string objectiveName, ObjectivePercentile objectivePercentile, /// public KeyValuePair[] GetCallCountTags(List> callTags) { + List> countTags = new List>(); + if (ObjectiveType == ObjectiveType.SuccessRate || ObjectiveType == ObjectiveType.SuccessAndLatency) { - callTags.Add(new KeyValuePair("objective.name", ObjectiveName)); - callTags.Add(new KeyValuePair("objective.percentile", ((double)ObjectivePercentile / 10))); + countTags.Add(new KeyValuePair("objective.name", ObjectiveName)); + countTags.Add(new KeyValuePair("objective.percentile", ((double)ObjectivePercentile / 10))); } - return callTags.ToArray(); + + countTags.AddRange(callTags); + return countTags.ToArray(); } /// @@ -62,13 +66,17 @@ public Objective(string objectiveName, ObjectivePercentile objectivePercentile, /// public KeyValuePair[] GetCallDurationTags(List> callTags) { + List> durationTags = new List>(); + if (ObjectiveType == ObjectiveType.LatencyThreshold || ObjectiveType == ObjectiveType.SuccessAndLatency) { - callTags.Add(new KeyValuePair("objective.name", ObjectiveName)); - callTags.Add(new KeyValuePair("objective.percentile", (double)ObjectivePercentile / 10)); - callTags.Add(new KeyValuePair("objective.latency_threshold", (double)ObjectiveLatencyThreshold / 1000)); + durationTags.Add(new KeyValuePair("objective.name", ObjectiveName)); + durationTags.Add(new KeyValuePair("objective.percentile", (double)ObjectivePercentile / 10)); + durationTags.Add(new KeyValuePair("objective.latency_threshold", (double)ObjectiveLatencyThreshold / 1000)); } - return callTags.ToArray(); + + durationTags.AddRange(callTags); + return durationTags.ToArray(); } } } \ No newline at end of file