From 8f33017bf69bef166a43e123f8a5e0ed4184b212 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 30 Oct 2024 15:21:45 +1300 Subject: [PATCH 1/4] Removed Metrics (deprecated) --- Sentry-CI-Build-macOS.slnf | 5 +- Sentry.sln | 9 +- .../Sentry.Samples.Console.Metrics/Program.cs | 141 ---- .../Sentry.Samples.Console.Metrics.csproj | 45 -- .../Program.cs | 25 +- src/Sentry/DisabledMetricAggregator.cs | 76 --- src/Sentry/Extensibility/DisabledHub.cs | 5 - src/Sentry/Extensibility/HubAdapter.cs | 7 - src/Sentry/IHub.cs | 5 - src/Sentry/IMetricAggregator.cs | 137 ---- src/Sentry/IMetricHub.cs | 24 - .../SystemDiagnosticsMetricsIntegration.cs | 38 -- src/Sentry/Internal/Hub.cs | 18 +- .../SystemDiagnosticsMetricsListener.cs | 90 --- src/Sentry/MetricAggregator.cs | 537 --------------- src/Sentry/Platforms/Android/SentrySdk.cs | 1 - src/Sentry/SentryOptions.cs | 19 - src/Sentry/SentrySdk.cs | 7 - src/Sentry/Timing.cs | 88 --- .../BindableSentryAspNetCoreOptionsTests.cs | 4 - ...indableSentryAzureFunctionsOptionsTests.cs | 4 - .../SentryLoggingOptionsTests.cs | 4 - .../BindableSentryMauiOptionsTests.cs | 4 - ...piApprovalTests.Run.DotNet6_0.verified.txt | 24 - ...piApprovalTests.Run.DotNet7_0.verified.txt | 24 - ...piApprovalTests.Run.DotNet8_0.verified.txt | 24 - .../ApiApprovalTests.Run.Net4_8.verified.txt | 24 - .../BindableSentryOptionsTests.cs | 4 - ...ystemDiagnosticsMetricsIntegrationTests.cs | 79 --- .../SystemDiagnosticsMetricsListenerTests.cs | 300 -------- test/Sentry.Tests/MetricAggregatorTests.cs | 641 ------------------ ...y_registered.DotNet8_0.DotNet.verified.txt | 10 - test/Sentry.Tests/SentryOptionsTests.cs | 11 - test/Sentry.Tests/TimingTests.cs | 143 ---- 34 files changed, 5 insertions(+), 2572 deletions(-) delete mode 100644 samples/Sentry.Samples.Console.Metrics/Program.cs delete mode 100644 samples/Sentry.Samples.Console.Metrics/Sentry.Samples.Console.Metrics.csproj delete mode 100644 src/Sentry/DisabledMetricAggregator.cs delete mode 100644 src/Sentry/IMetricAggregator.cs delete mode 100644 src/Sentry/IMetricHub.cs delete mode 100644 src/Sentry/Integrations/SystemDiagnosticsMetricsIntegration.cs delete mode 100644 src/Sentry/Internal/SystemDiagnosticsMetricsListener.cs delete mode 100644 src/Sentry/MetricAggregator.cs delete mode 100644 src/Sentry/Timing.cs delete mode 100644 test/Sentry.Tests/Integrations/SystemDiagnosticsMetricsIntegrationTests.cs delete mode 100644 test/Sentry.Tests/Internals/SystemDiagnosticsMetricsListenerTests.cs delete mode 100644 test/Sentry.Tests/MetricAggregatorTests.cs delete mode 100644 test/Sentry.Tests/TimingTests.cs diff --git a/Sentry-CI-Build-macOS.slnf b/Sentry-CI-Build-macOS.slnf index 9bb9d4847d..c96b6d7382 100644 --- a/Sentry-CI-Build-macOS.slnf +++ b/Sentry-CI-Build-macOS.slnf @@ -16,7 +16,6 @@ "samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj", "samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj", "samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj", - "samples\\Sentry.Samples.Console.Metrics\\Sentry.Samples.Console.Metrics.csproj", "samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj", "samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj", "samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj", @@ -36,10 +35,10 @@ "samples\\Sentry.Samples.OpenTelemetry.Console\\Sentry.Samples.OpenTelemetry.Console.csproj", "samples\\Sentry.Samples.Serilog\\Sentry.Samples.Serilog.csproj", "src\\Sentry.Android.AssemblyReader\\Sentry.Android.AssemblyReader.csproj", - "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.AspNetCore.Blazor.WebAssembly\\Sentry.AspNetCore.Blazor.WebAssembly.csproj", "src\\Sentry.AspNetCore.Grpc\\Sentry.AspNetCore.Grpc.csproj", "src\\Sentry.AspNetCore\\Sentry.AspNetCore.csproj", + "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.Azure.Functions.Worker\\Sentry.Azure.Functions.Worker.csproj", "src\\Sentry.Bindings.Android\\Sentry.Bindings.Android.csproj", "src\\Sentry.Bindings.Cocoa\\Sentry.Bindings.Cocoa.csproj", @@ -80,4 +79,4 @@ "test\\SingleFileTestApp\\SingleFileTestApp.csproj" ] } -} +} \ No newline at end of file diff --git a/Sentry.sln b/Sentry.sln index 4ebd081d31..82631572fc 100644 --- a/Sentry.sln +++ b/Sentry.sln @@ -155,8 +155,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastSerialization", "module EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Console.Native", "samples\Sentry.Samples.Console.Native\Sentry.Samples.Console.Native.csproj", "{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Console.Metrics", "samples\Sentry.Samples.Console.Metrics\Sentry.Samples.Console.Metrics.csproj", "{BD2D08FC-8675-4157-A73C-D75F6A3856D3}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.MacOS", "samples\Sentry.Samples.MacOS\Sentry.Samples.MacOS.csproj", "{5B100CC0-1A78-407E-A5A5-94BC06D67461}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Hangfire", "samples\Sentry.Samples.Hangfire\Sentry.Samples.Hangfire.csproj", "{407C477D-69C0-4B02-8A68-EE6B5A81C696}" @@ -165,7 +163,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Hangfire", "src\Sent EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Hangfire.Tests", "test\Sentry.Hangfire.Tests\Sentry.Hangfire.Tests.csproj", "{46E40BE8-1AB0-4846-B0A2-A40AD0272C64}" EndProject -Project("{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.AspNetCore.WebAPI.Profiling", "samples\Sentry.Samples.AspNetCore.WebAPI.Profiling\Sentry.Samples.AspNetCore.WebAPI.Profiling.csproj", "{A5B26C14-7313-4EDC-91E3-287F9374AB75}" +Project("{00000000-0000-0000-0000-000000000000}") = "Sentry.Samples.AspNetCore.WebAPI.Profiling", "samples\Sentry.Samples.AspNetCore.WebAPI.Profiling\Sentry.Samples.AspNetCore.WebAPI.Profiling.csproj", "{A5B26C14-7313-4EDC-91E3-287F9374AB75}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{233D34AB-970E-4913-AA1E-172E833FB5B2}" ProjectSection(SolutionItems) = preProject @@ -471,10 +469,6 @@ Global {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|Any CPU.Build.0 = Debug|Any CPU {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.Build.0 = Release|Any CPU - {BD2D08FC-8675-4157-A73C-D75F6A3856D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BD2D08FC-8675-4157-A73C-D75F6A3856D3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BD2D08FC-8675-4157-A73C-D75F6A3856D3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BD2D08FC-8675-4157-A73C-D75F6A3856D3}.Release|Any CPU.Build.0 = Release|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|Any CPU.Build.0 = Debug|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -575,7 +569,6 @@ Global {67269916-C417-4CEE-BD7D-CA66C3830AEE} = {A3CCA27E-4DF8-479D-833C-CAA0950715AA} {8032310D-3C06-442C-A318-F365BCC4C804} = {A3CCA27E-4DF8-479D-833C-CAA0950715AA} {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2} = {21B42F60-5802-404E-90F0-AEBCC56760C0} - {BD2D08FC-8675-4157-A73C-D75F6A3856D3} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {5B100CC0-1A78-407E-A5A5-94BC06D67461} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {407C477D-69C0-4B02-8A68-EE6B5A81C696} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {EADF25F5-8D02-4747-AB54-5F2BAA648471} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} diff --git a/samples/Sentry.Samples.Console.Metrics/Program.cs b/samples/Sentry.Samples.Console.Metrics/Program.cs deleted file mode 100644 index e506fc36d1..0000000000 --- a/samples/Sentry.Samples.Console.Metrics/Program.cs +++ /dev/null @@ -1,141 +0,0 @@ -#pragma warning disable CS0618 // Obsolete Warning -using System.Diagnostics.Metrics; -using System.Text.RegularExpressions; - -namespace Sentry.Samples.Console.Metrics; - -internal static class Program -{ - private static readonly Random Roll = Random.Shared; - - // Sentry also supports capturing System.Diagnostics.Metrics - private static readonly Meter HatsMeter = new("HatCo.HatStore", "1.0.0"); - private static readonly Counter HatsSold = HatsMeter.CreateCounter( - name: "hats-sold", - unit: "Hats", - description: "The number of hats sold in our store"); - - private static async Task Main() - { - // Enable the SDK - using (SentrySdk.Init(options => - { - // You can set here in code, or you can set it in the SENTRY_DSN environment variable. - // See https://docs.sentry.io/product/sentry-basics/dsn-explainer/ - options.Dsn = "https://eb18e953812b41c3aeb042e666fd3b5c@o447951.ingest.sentry.io/5428537"; - - options.Debug = true; - options.StackTraceMode = StackTraceMode.Enhanced; - options.SampleRate = 1.0f; // Not recommended in production - may adversely impact quota - options.TracesSampleRate = 1.0f; // Not recommended in production - may adversely impact quota - // Initialize some (non null) ExperimentalMetricsOptions to enable Sentry Metrics, - options.ExperimentalMetrics = new ExperimentalMetricsOptions - { - EnableCodeLocations = true, // Set this to false if you don't want to track code locations - CaptureSystemDiagnosticsInstruments = [ - // Capture System.Diagnostics.Metrics matching the name "HatCo.HatStore", which is the name - // of the custom HatsMeter defined above - "hats-sold" - ], - // Capture all built in metrics (this is the default - you can override this to capture some or - // none of these if you prefer) - CaptureSystemDiagnosticsMeters = BuiltInSystemDiagnosticsMeters.All - }; - })) - { - System.Console.WriteLine("Measure, Yeah, Measure!"); - - Action[] actions = [PlaySetBingo, CreateRevenueGauge, MeasureShrimp, SellHats]; - do - { - // Run a random action - var idx = Roll.Next(0, actions.Length); - actions[idx](); - - // Make an API call - await CallSampleApiAsync(); - - // Optional: Delay to prevent tight looping - var sleepTime = Roll.Next(1, 5); - System.Console.WriteLine($"Sleeping for {sleepTime} second(s)."); - System.Console.WriteLine("Press any key to stop..."); - Thread.Sleep(TimeSpan.FromSeconds(sleepTime)); - } - while (!System.Console.KeyAvailable); - System.Console.WriteLine("Measure up"); - } - } - - private static void PlaySetBingo() - { - const int attempts = 10; - var solution = new[] { 3, 5, 7, 11, 13, 17 }; - - // StartTimer creates a distribution that is designed to measure the amount of time it takes to run code - // blocks. By default it will use a unit of Seconds - we're configuring it to use milliseconds here though. - // The return value is an IDisposable and the timer will stop when the timer is disposed of. - using (SentrySdk.Metrics.StartTimer("bingo", MeasurementUnit.Duration.Millisecond)) - { - for (var i = 0; i < attempts; i++) - { - var guess = Roll.Next(1, 100); - // This demonstrates the use of a set metric. - SentrySdk.Metrics.Gauge("guesses", guess); - - // And this is a counter - SentrySdk.Metrics.Increment(solution.Contains(guess) ? "correct_answers" : "incorrect_answers"); - } - } - } - - private static void CreateRevenueGauge() - { - const int sampleCount = 100; - using (SentrySdk.Metrics.StartTimer(nameof(CreateRevenueGauge), MeasurementUnit.Duration.Millisecond)) - { - for (var i = 0; i < sampleCount; i++) - { - var movement = Roll.NextDouble() * 30 - Roll.NextDouble() * 10; - // This demonstrates measuring something in your app using a gauge... we're also using a custom - // measurement unit here (which is optional - by default the unit will be "None") - SentrySdk.Metrics.Gauge("revenue", movement, MeasurementUnit.Custom("$")); - } - } - } - - private static void MeasureShrimp() - { - const int sampleCount = 30; - using (SentrySdk.Metrics.StartTimer(nameof(MeasureShrimp), MeasurementUnit.Duration.Millisecond)) - { - for (var i = 0; i < sampleCount; i++) - { - var sizeOfShrimp = 15 + Roll.NextDouble() * 30; - // This is an example of emitting a distribution metric - SentrySdk.Metrics.Distribution("shrimp.size", sizeOfShrimp, MeasurementUnit.Custom("cm")); - } - } - } - - private static void SellHats() - { - // Here we're emitting the metric using System.Diagnostics.Metrics instead of SentrySdk.Metrics. - // We won't see accurate code locations for these, so Sentry.Metrics are preferable but support - // for System.Diagnostics.Metrics means Sentry can collect a bunch built in metrics without you - // having to instrument anything... see case 4 below - HatsSold.Add(Roll.Next(0, 1000)); - } - - private static async Task CallSampleApiAsync() - { - // Here we demonstrate collecting some built in metrics for HTTP requests... this works because - // we've configured ExperimentalMetricsOptions.CaptureInstruments to match "http.client.*" - // - // See https://learn.microsoft.com/en-us/dotnet/core/diagnostics/built-in-metrics-system-net#systemnethttp - var httpClient = new HttpClient(); - var url = "https://api.sampleapis.com/coffee/hot"; - var result = await httpClient.GetAsync(url); - System.Console.WriteLine($"GET {url} {result.StatusCode}"); - } -} -#pragma warning restore CS0618 diff --git a/samples/Sentry.Samples.Console.Metrics/Sentry.Samples.Console.Metrics.csproj b/samples/Sentry.Samples.Console.Metrics/Sentry.Samples.Console.Metrics.csproj deleted file mode 100644 index 93cb6b917b..0000000000 --- a/samples/Sentry.Samples.Console.Metrics/Sentry.Samples.Console.Metrics.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - Exe - net8.0 - - - - - - sentry-sdks - sentry-dotnet - - - true - true - - - true - - - - - - - - - - - diff --git a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs index e7bcb3be57..3cb706852c 100644 --- a/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs +++ b/samples/Sentry.Samples.OpenTelemetry.AspNetCore/Program.cs @@ -1,6 +1,5 @@ using System.Diagnostics; using Microsoft.AspNetCore.Authentication; -using OpenTelemetry.Metrics; using OpenTelemetry.Resources; using OpenTelemetry.Trace; using Sentry.OpenTelemetry; @@ -23,18 +22,7 @@ .AddHttpClientInstrumentation() // Finally we configure OpenTelemetry to send traces to Sentry .AddSentry() - ) - // This block configures OpenTelemetry metrics that we care about... later we'll configure Sentry to capture these - .WithMetrics(metrics => - { - metrics - .AddRuntimeInstrumentation() // <-- Requires the OpenTelemetry.Instrumentation.Runtime package - // Collect some of the built-in ASP.NET Core metrics - .AddMeter( - "Microsoft.AspNetCore.Hosting", - "Microsoft.AspNetCore.Server.Kestrel", - "System.Net.Http"); - }); + ); builder.WebHost.UseSentry(options => { @@ -46,17 +34,6 @@ options.SendDefaultPii = true; options.TracesSampleRate = 1.0; options.UseOpenTelemetry(); // <-- Configure Sentry to use OpenTelemetry trace information - // This shows experimental support for capturing OpenTelemetry metrics with Sentry - options.ExperimentalMetrics = new ExperimentalMetricsOptions() - { - // Here we're telling Sentry to capture all built-in metrics. This includes all the metrics we configured - // OpenTelemetry to emit when we called `builder.Services.AddOpenTelemetry()` above: - // - "OpenTelemetry.Instrumentation.Runtime" - // - "Microsoft.AspNetCore.Hosting", - // - "Microsoft.AspNetCore.Server.Kestrel", - // - "System.Net.Http" - CaptureSystemDiagnosticsMeters = BuiltInSystemDiagnosticsMeters.All - }; }); builder.Services diff --git a/src/Sentry/DisabledMetricAggregator.cs b/src/Sentry/DisabledMetricAggregator.cs deleted file mode 100644 index f07306dbeb..0000000000 --- a/src/Sentry/DisabledMetricAggregator.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace Sentry; - -internal class DisabledMetricAggregator : IMetricAggregator -{ - public void Increment(string key, double value = 1.0, MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, int stackLevel = 1) - { - // No Op - } - - public void Gauge(string key, double value = 1.0, MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, int stackLevel = 1) - { - // No Op - } - - public void Distribution(string key, double value = 1.0, MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, int stackLevel = 1) - { - // No Op - } - - public void Set(string key, int value, MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, int stackLevel = 1) - { - // No Op - } - - public void Set(string key, string value, MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, int stackLevel = 1) - { - // No Op - } - - public void Timing(string key, double value, MeasurementUnit.Duration unit = MeasurementUnit.Duration.Second, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, int stackLevel = 1) - { - // No Op - } - - public IDisposable StartTimer(string key, MeasurementUnit.Duration unit = MeasurementUnit.Duration.Second, - IDictionary? tags = null, - int stackLevel = 1) - { - // No Op - return NoOpDisposable.Instance; - } - - public Task FlushAsync(bool force = true, CancellationToken cancellationToken = default) - { - // No Op - return Task.CompletedTask; - } - - public void Dispose() - { - // No Op - } -} - -internal class NoOpDisposable : IDisposable -{ - private static readonly Lazy LazyInstance = new(); - internal static NoOpDisposable Instance => LazyInstance.Value; - - public void Dispose() - { - // No Op - } -} diff --git a/src/Sentry/Extensibility/DisabledHub.cs b/src/Sentry/Extensibility/DisabledHub.cs index d8f302068a..3ed44e3a09 100644 --- a/src/Sentry/Extensibility/DisabledHub.cs +++ b/src/Sentry/Extensibility/DisabledHub.cs @@ -195,11 +195,6 @@ public SentryId CaptureCheckIn( /// public Task FlushAsync(TimeSpan timeout) => Task.CompletedTask; - /// - /// Disabled Metrics Aggregator (all methods are no-op). - /// - public IMetricAggregator Metrics { get; } = new DisabledMetricAggregator(); - /// /// No-Op. /// diff --git a/src/Sentry/Extensibility/HubAdapter.cs b/src/Sentry/Extensibility/HubAdapter.cs index fbb2dbab7b..5a4f01b62e 100644 --- a/src/Sentry/Extensibility/HubAdapter.cs +++ b/src/Sentry/Extensibility/HubAdapter.cs @@ -287,13 +287,6 @@ public SentryId CaptureCheckIn( public Task FlushAsync(TimeSpan timeout) => SentrySdk.FlushAsync(timeout); - /// - [Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major release. " + - "Sentry will reject all metrics sent after October 7, 2024." + - "Learn more: https://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-to-Metrics")] - public IMetricAggregator Metrics - => SentrySdk.Metrics; - /// /// Forwards the call to /// diff --git a/src/Sentry/IHub.cs b/src/Sentry/IHub.cs index de2cb8be12..5b8d923f7b 100644 --- a/src/Sentry/IHub.cs +++ b/src/Sentry/IHub.cs @@ -17,11 +17,6 @@ public interface IHub : ISentryClient, ISentryScopeManager /// SentryId LastEventId { get; } - /// - /// - /// - IMetricAggregator Metrics { get; } - /// /// Starts a transaction. /// diff --git a/src/Sentry/IMetricAggregator.cs b/src/Sentry/IMetricAggregator.cs deleted file mode 100644 index 2f52ae4eeb..0000000000 --- a/src/Sentry/IMetricAggregator.cs +++ /dev/null @@ -1,137 +0,0 @@ -namespace Sentry; - -/// -/// Exposes EXPERIMENTAL capability to emit metrics. This API is subject to change without major version bumps so use -/// with caution. We advise disabling in production at the moment. -/// -public interface IMetricAggregator : IDisposable -{ - /// - /// Emits a Counter metric - /// - /// A unique key identifying the metric - /// The value to be added - /// An optional - /// Optional Tags to associate with the metric - /// - /// The time when the metric was emitted. Defaults to the time at which the metric is emitted, if no value is provided. - /// - /// Optional number of stacks levels to ignore when determining the code location - void Increment(string key, - double value = 1.0, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1); - - /// - /// Emits a Gauge metric - /// - /// A unique key identifying the metric - /// The value to be added - /// An optional - /// Optional Tags to associate with the metric - /// - /// The time when the metric was emitted. Defaults to the time at which the metric is emitted, if no value is provided. - /// - /// Optional number of stacks levels to ignore when determining the code location - void Gauge(string key, - double value = 1.0, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1); - - /// - /// Emits a Distribution metric - /// - /// A unique key identifying the metric - /// The value to be added - /// An optional - /// Optional Tags to associate with the metric - /// - /// The time when the metric was emitted. Defaults to the time at which the metric is emitted, if no value is provided. - /// - /// Optional number of stacks levels to ignore when determining the code location - void Distribution(string key, - double value = 1.0, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1); - - /// - /// Emits a Set metric - /// - /// A unique key identifying the metric - /// The value to be added - /// An optional - /// Optional Tags to associate with the metric - /// - /// The time when the metric was emitted. Defaults to the time at which the metric is emitted, if no value is provided. - /// - /// Optional number of stacks levels to ignore when determining the code location - void Set(string key, - int value, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1); - - /// - /// Emits a Set metric a - /// - /// A unique key identifying the metric - /// The value to be added - /// An optional - /// Optional Tags to associate with the metric - /// - /// The time when the metric was emitted. Defaults to the time at which the metric is emitted, if no value is provided. - /// - /// Optional number of stacks levels to ignore when determining the code location - void Set(string key, - string value, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1); - - /// - /// Emits a distribution with the time it takes to run a given code block. - /// - /// A unique key identifying the metric - /// The value to be added - /// - /// An optional . Defaults to - /// - /// Optional Tags to associate with the metric - /// The time when the metric was emitted - /// Optional number of stacks levels to ignore when determining the code location - void Timing(string key, - double value, - MeasurementUnit.Duration unit = MeasurementUnit.Duration.Second, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1); - - /// - /// Measures the time it takes to run a given code block and emits this as a metric. - /// - /// - /// using (SentrySdk.Metrics.StartTimer("my-operation")) - /// { - /// ... - /// } - /// - IDisposable StartTimer(string key, MeasurementUnit.Duration unit = MeasurementUnit.Duration.Second, - IDictionary? tags = null, int stackLevel = 1); - - /// - /// Flushes any flushable metrics and/or code locations. - /// If is true then the cutoff is ignored and all metrics are flushed. - /// - /// Forces all buckets to be flushed, ignoring the cutoff - /// A - /// False if a shutdown is requested during flush, true otherwise - Task FlushAsync(bool force = true, CancellationToken cancellationToken = default); -} diff --git a/src/Sentry/IMetricHub.cs b/src/Sentry/IMetricHub.cs deleted file mode 100644 index 99f9d7d778..0000000000 --- a/src/Sentry/IMetricHub.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Sentry.Protocol.Metrics; - -namespace Sentry; - -internal interface IMetricHub -{ - /// - /// Captures one or more metrics to be sent to Sentry. - /// - void CaptureMetrics(IEnumerable metrics); - - /// - /// Captures one or more to be sent to Sentry. - /// - void CaptureCodeLocations(CodeLocations codeLocations); - - /// - /// Starts a child span for the current transaction or, if there is no active transaction, starts a new transaction. - /// - ISpan StartSpan(string operation, string description); - - /// - ISpan? GetSpan(); -} diff --git a/src/Sentry/Integrations/SystemDiagnosticsMetricsIntegration.cs b/src/Sentry/Integrations/SystemDiagnosticsMetricsIntegration.cs deleted file mode 100644 index efa1af2755..0000000000 --- a/src/Sentry/Integrations/SystemDiagnosticsMetricsIntegration.cs +++ /dev/null @@ -1,38 +0,0 @@ -#if NET8_0_OR_GREATER -using Sentry.Extensibility; -using Sentry.Internal; - -namespace Sentry.Integrations; - -internal class SystemDiagnosticsMetricsIntegration : ISdkIntegration -{ - private readonly Action _initializeListener; - internal const string NoListenersAreConfiguredMessage = "System.Diagnostics.Metrics Integration is disabled because no listeners are configured."; - - public SystemDiagnosticsMetricsIntegration() - { - _initializeListener = SystemDiagnosticsMetricsListener.InitializeDefaultListener; - } - - /// - /// Overload for testing purposes - /// - internal SystemDiagnosticsMetricsIntegration(Action initializeListener) - { - _initializeListener = initializeListener; - } - - public void Register(IHub hub, SentryOptions options) - { - var captureInstruments = options.ExperimentalMetrics?.CaptureSystemDiagnosticsInstruments; - var captureMeters = options.ExperimentalMetrics?.CaptureSystemDiagnosticsMeters; - if (captureInstruments is not { Count: > 0 } && captureMeters is not { Count: > 0 }) - { - options.LogInfo(NoListenersAreConfiguredMessage); - return; - } - - _initializeListener(options.ExperimentalMetrics!); - } -} -#endif diff --git a/src/Sentry/Internal/Hub.cs b/src/Sentry/Internal/Hub.cs index cba3580fa9..accec331cc 100644 --- a/src/Sentry/Internal/Hub.cs +++ b/src/Sentry/Internal/Hub.cs @@ -5,7 +5,7 @@ namespace Sentry.Internal; -internal class Hub : IHub, IMetricHub, IDisposable +internal class Hub : IHub, IDisposable { private readonly object _sessionPauseLock = new(); @@ -21,9 +21,6 @@ internal class Hub : IHub, IMetricHub, IDisposable internal IInternalScopeManager ScopeManager { get; } - /// - public IMetricAggregator Metrics { get; } - private int _isEnabled = 1; public bool IsEnabled => _isEnabled == 1; @@ -63,16 +60,6 @@ internal Hub( PushScope(); } - if (options.ExperimentalMetrics is not null) - { - options.LogDebug("Registering integration: Metrics"); - Metrics = new MetricAggregator(options, this); - } - else - { - Metrics = new DisabledMetricAggregator(); - } - foreach (var integration in options.Integrations) { options.LogDebug("Registering integration: '{0}'.", integration.GetType().Name); @@ -536,7 +523,6 @@ public void CaptureTransaction(SentryTransaction transaction, Scope? scope, Sent } } - /// public void CaptureMetrics(IEnumerable metrics) { if (!IsEnabled) @@ -558,7 +544,6 @@ public void CaptureMetrics(IEnumerable metrics) } } - /// public void CaptureCodeLocations(CodeLocations codeLocations) { if (!IsEnabled) @@ -577,7 +562,6 @@ public void CaptureCodeLocations(CodeLocations codeLocations) } } - /// public ISpan StartSpan(string operation, string description) { ITransactionTracer? currentTransaction = null; diff --git a/src/Sentry/Internal/SystemDiagnosticsMetricsListener.cs b/src/Sentry/Internal/SystemDiagnosticsMetricsListener.cs deleted file mode 100644 index 3e9e15b280..0000000000 --- a/src/Sentry/Internal/SystemDiagnosticsMetricsListener.cs +++ /dev/null @@ -1,90 +0,0 @@ -#if NET8_0_OR_GREATER -using System.Diagnostics.Metrics; - -namespace Sentry.Internal; - -internal class SystemDiagnosticsMetricsListener : IDisposable -{ - private readonly Lazy _metricsAggregator; - private IMetricAggregator MetricsAggregator => _metricsAggregator.Value; - private static SystemDiagnosticsMetricsListener? DefaultListener; - - internal readonly MeterListener _sentryListener = new(); - -#pragma warning disable CS0618 // Obsolete Warning - private SystemDiagnosticsMetricsListener(ExperimentalMetricsOptions metricsOptions) - : this(metricsOptions, () => SentrySdk.Metrics) -#pragma warning restore CS0618 - { - } - - - /// - /// Overload for testing purposes - allows us to supply a mock IMetricAggregator - /// - internal SystemDiagnosticsMetricsListener(ExperimentalMetricsOptions metricsOptions, Func metricsAggregatorResolver) - { - _metricsAggregator = new Lazy(metricsAggregatorResolver); - _sentryListener.InstrumentPublished = (instrument, listener) => - { - if (metricsOptions.CaptureSystemDiagnosticsMeters.MatchesSubstringOrRegex(instrument.Meter.Name) - || metricsOptions.CaptureSystemDiagnosticsInstruments.MatchesSubstringOrRegex(instrument.Name)) - { - listener.EnableMeasurementEvents(instrument); - } - }; - _sentryListener.SetMeasurementEventCallback(RecordMeasurement); - _sentryListener.SetMeasurementEventCallback(RecordMeasurement); - _sentryListener.SetMeasurementEventCallback(RecordMeasurement); - _sentryListener.SetMeasurementEventCallback(RecordMeasurement); - _sentryListener.SetMeasurementEventCallback(RecordMeasurement); - _sentryListener.SetMeasurementEventCallback(RecordMeasurement); - _sentryListener.SetMeasurementEventCallback(RecordMeasurement); - _sentryListener.Start(); - } - - internal static void InitializeDefaultListener(ExperimentalMetricsOptions metricsOptions) - { - var oldListener = Interlocked.Exchange( - ref DefaultListener, - new SystemDiagnosticsMetricsListener(metricsOptions) - ); - oldListener?.Dispose(); - } - - internal void RecordMeasurement( - Instrument instrument, - T measurement, - ReadOnlySpan> tags, - object? _) - where T : struct, IConvertible - { - var unit = MeasurementUnit.Parse(instrument.Unit); - var tagDict = tags.ToImmutableArray().ToImmutableDictionary( - kvp => kvp.Key, - kvp => kvp.Value?.ToString() ?? string.Empty - ); - var doubleMeasurement = Convert.ToDouble(measurement); - switch (instrument) - { - case Counter: - case UpDownCounter: - case ObservableCounter: - case ObservableUpDownCounter: - MetricsAggregator.Increment(instrument.Name, doubleMeasurement, unit, tagDict); - break; - case Histogram: - MetricsAggregator.Distribution(instrument.Name, doubleMeasurement, unit, tagDict); - break; - case ObservableGauge: - MetricsAggregator.Gauge(instrument.Name, doubleMeasurement, unit, tagDict); - break; - } - } - - public void Dispose() - { - _sentryListener.Dispose(); - } -} -#endif diff --git a/src/Sentry/MetricAggregator.cs b/src/Sentry/MetricAggregator.cs deleted file mode 100644 index cb1ce2089f..0000000000 --- a/src/Sentry/MetricAggregator.cs +++ /dev/null @@ -1,537 +0,0 @@ -using Sentry.Extensibility; -using Sentry.Force.Crc32; -using Sentry.Internal; -using Sentry.Internal.Extensions; -using Sentry.Protocol.Metrics; - -namespace Sentry; - -internal class MetricAggregator : IMetricAggregator -{ - internal const string DisposingMessage = "Disposing MetricAggregator."; - internal const string AlreadyDisposedMessage = "Already disposed MetricAggregator."; - internal const string CancelledMessage = "Stopping the Metric Aggregator due to a cancellation."; - internal const string ShutdownScheduledMessage = "Shutdown scheduled. Stopping by: {0}."; - internal const string ShutdownImmediatelyMessage = "Exiting immediately due to 0 shutdown timeout."; - internal const string FlushShutdownMessage = "Shutdown token triggered. Exiting metric aggregator."; - - private readonly SentryOptions _options; - private readonly IMetricHub _metricHub; - - private readonly SemaphoreSlim _codeLocationLock = new(1, 1); - private readonly ReaderWriterLockSlim _bucketsLock = new ReaderWriterLockSlim(); - - private readonly CancellationTokenSource _shutdownSource; - private volatile bool _disposed; - - // The key for this dictionary is the Timestamp for the bucket, rounded down to the nearest RollupInSeconds... so it - // aggregates all of the metrics data for a particular time period. The Value is a dictionary for the metrics, - // each of which has a key that uniquely identifies it within the time period - internal Dictionary> Buckets => _buckets.Value; - - private readonly Lazy>> _buckets - = new(() => new Dictionary>()); - - internal long _lastClearedStaleLocations = DateTimeOffset.UtcNow.GetDayBucketKey(); - internal readonly ConcurrentDictionary> _seenLocations = new(); - internal Dictionary> _pendingLocations = new(); - - internal readonly Task _loopTask; - - internal MetricAggregator(SentryOptions options, IMetricHub metricHub, - CancellationTokenSource? shutdownSource = null, bool disableLoopTask = false) - { - _options = options; - _metricHub = metricHub; - _shutdownSource = shutdownSource ?? new CancellationTokenSource(); - - if (disableLoopTask) - { - // We can stop the loop from running during testing - _options.LogDebug("LoopTask disabled."); - _loopTask = Task.CompletedTask; - } - else - { - options.LogDebug("Starting MetricsAggregator."); - _loopTask = Task.Run(RunLoopAsync); - } - } - - /// - public void Increment(string key, - double value = 1.0, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1) => Emit(MetricType.Counter, key, value, unit, tags, timestamp, stackLevel + 1); - - /// - public void Gauge(string key, - double value = 1.0, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1) => Emit(MetricType.Gauge, key, value, unit, tags, timestamp, stackLevel + 1); - - /// - public void Distribution(string key, - double value = 1.0, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1) => Emit(MetricType.Distribution, key, value, unit, tags, timestamp, stackLevel + 1); - - /// - public void Set(string key, - int value, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1) => Emit(MetricType.Set, key, value, unit, tags, timestamp, stackLevel + 1); - - /// - public void Set(string key, - string value, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1) - { - // Compute the CRC32 hash of the value as byte array and cast it to a 32-bit signed integer - // Mask the lower 32 bits to ensure the result fits within the 32-bit integer range - var hash = (int)(Crc32Algorithm.Compute(Encoding.UTF8.GetBytes(value)) & 0xFFFFFFFF); - - Emit(MetricType.Set, key, hash, unit, tags, timestamp, stackLevel + 1); - } - - /// - public virtual void Timing(string key, - double value, - MeasurementUnit.Duration unit = MeasurementUnit.Duration.Second, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1) => Emit(MetricType.Distribution, key, value, unit, tags, timestamp, stackLevel + 1); - - /// - public IDisposable StartTimer(string key, MeasurementUnit.Duration unit = MeasurementUnit.Duration.Second, - IDictionary? tags = null, int stackLevel = 1) - => new Timing(this, _metricHub, _options, key, unit, tags, stackLevel + 1); - - private void Emit( - MetricType type, - string key, - double value = 1.0, - MeasurementUnit? unit = null, - IDictionary? tags = null, - DateTimeOffset? timestamp = null, - int stackLevel = 1 - ) - { - timestamp ??= DateTimeOffset.UtcNow; - unit ??= MeasurementUnit.None; - - var updatedTags = tags != null ? new Dictionary(tags) : new Dictionary(); - updatedTags.AddIfNotNullOrEmpty("release", _options.Release); - updatedTags.AddIfNotNullOrEmpty("environment", _options.Environment); - var span = _metricHub.GetSpan(); - if (span?.GetTransaction() is { } transaction) - { - updatedTags.AddIfNotNullOrEmpty("transaction", transaction.TransactionName); - } - - Func addValuesFactory = type switch - { - MetricType.Counter => _ => new CounterMetric(key, value, unit.Value, updatedTags, timestamp), - MetricType.Gauge => _ => new GaugeMetric(key, value, unit.Value, updatedTags, timestamp), - MetricType.Distribution => _ => new DistributionMetric(key, value, unit.Value, updatedTags, timestamp), - MetricType.Set => _ => new SetMetric(key, (int)value, unit.Value, updatedTags, timestamp), - _ => throw new ArgumentOutOfRangeException(nameof(type), type, "Unknown MetricType") - }; - - var timeBucket = GetOrAddTimeBucket(timestamp.Value.GetTimeBucketKey()); - - timeBucket.AddOrUpdate( - MetricHelper.GetMetricBucketKey(type, key, unit.Value, updatedTags), - addValuesFactory, - (_, metric) => - { - // This prevents multiple threads from trying to mutate the metric at the same time. The only other - // operations performed against metrics are adding one to the bucket (guaranteed to be atomic due to - // the use of a ConcurrentDictionary for the timeBucket) and removing buckets entirely. - // - // With a very small flushShift (e.g. 0.0) it might be possible for a metric to be emitted to a bucket - // that was removed after a flush, in which case that metric.Add(value) would never make it to Sentry. - // We've never seen this happen in unit testing (where we always set the flushShift to 0.0) so this - // remains only a theoretical possibility of data loss (not confirmed). If this becomes a real problem - // and we need to guarantee delivery of every metric.Add, we'll need to build a more complex mechanism - // to coordinate flushing with emission. - lock (metric) - { - metric.Add(value); - } - return metric; - }); - - if (_options.ExperimentalMetrics is { EnableCodeLocations: true }) - { - RecordCodeLocation(type, key, unit.Value, stackLevel + 1, timestamp.Value); - } - - switch (span) - { - case TransactionTracer transactionTracer: - transactionTracer.MetricsSummary.Add(type, key, value, unit, tags); - break; - case SpanTracer spanTracer: - spanTracer.MetricsSummary.Add(type, key, value, unit, tags); - break; - } - } - - private ConcurrentDictionary GetOrAddTimeBucket(long bucketKey) - { - _bucketsLock.EnterUpgradeableReadLock(); - try - { - if (Buckets.TryGetValue(bucketKey, out var existingBucket)) - { - return existingBucket; - } - - _bucketsLock.EnterWriteLock(); - try - { - // Check again in case another thread added the bucket while we were waiting for the write lock - if (Buckets.TryGetValue(bucketKey, out existingBucket)) - { - return existingBucket; - } - - var timeBucket = new ConcurrentDictionary(); - Buckets[bucketKey] = timeBucket; - return timeBucket; - } - finally - { - _bucketsLock.ExitWriteLock(); - } - } - finally - { - _bucketsLock.ExitUpgradeableReadLock(); - } - } - - internal virtual void RecordCodeLocation( - MetricType type, - string key, - MeasurementUnit unit, - int stackLevel, - DateTimeOffset timestamp - ) - { - var startOfDay = timestamp.GetDayBucketKey(); - var metaKey = new MetricResourceIdentifier(type, key, unit); - var seenToday = _seenLocations.GetOrAdd(startOfDay, _ => []); - - _codeLocationLock.Wait(); - try - { - // Group metadata by day to make flushing more efficient. - if (!seenToday.Add(metaKey)) - { - // If we've seen the location, we don't want to create a stack trace etc. again. It could be a different - // location with the same metaKey but the alternative would be to generate the stack trace every time a - // metric is recorded, which would impact performance too much. - return; - } - - if (GetCodeLocation(stackLevel + 1) is not { } location) - { - return; - } - - if (!_pendingLocations.TryGetValue(startOfDay, out var todaysLocations)) - { - todaysLocations = new Dictionary(); - _pendingLocations[startOfDay] = todaysLocations; - } - - todaysLocations[metaKey] = location; - } - finally - { - _codeLocationLock.Release(); - } - } - - internal SentryStackFrame? GetCodeLocation(int stackLevel) - { - var stackTrace = new StackTrace(true); - var frames = DebugStackTrace.Create(_options, stackTrace, false).Frames; - return (frames.Count >= stackLevel) - ? frames[^(stackLevel + 1)] - : null; - } - - private async Task RunLoopAsync() - { - _options.LogDebug("MetricsAggregator Started."); - - using var shutdownTimeout = new CancellationTokenSource(); - var shutdownRequested = false; - - try - { - while (!shutdownTimeout.IsCancellationRequested) - { - // If the cancellation was signaled, run until the end of the queue or shutdownTimeout - try - { - await Task.Delay(_options.ShutdownTimeout, _shutdownSource.Token).ConfigureAwait(false); - } - // Cancellation requested and no timeout allowed, so exit even if there are more items - catch (OperationCanceledException) when (_options.ShutdownTimeout == TimeSpan.Zero) - { - _options.LogDebug(ShutdownImmediatelyMessage); - - await shutdownTimeout.CancelAsync().ConfigureAwait(false); - - return; - } - // Cancellation requested, scheduled shutdown - catch (OperationCanceledException) - { - _options.LogDebug(ShutdownScheduledMessage, _options.ShutdownTimeout); - - shutdownTimeout.CancelAfterSafe(_options.ShutdownTimeout); - - shutdownRequested = true; - } - - await FlushAsync(shutdownRequested, shutdownTimeout.Token).ConfigureAwait(false); - - if (shutdownRequested) - { - return; - } - } - } - catch (Exception e) - { - _options.LogFatal(e, "Exception in the Metric Aggregator."); - throw; - } - } - - private readonly SemaphoreSlim _flushLock = new(1, 1); - - /// - public async Task FlushAsync(bool force = true, CancellationToken cancellationToken = default) - { - try - { - await _flushLock.WaitAsync(cancellationToken).ConfigureAwait(false); - - foreach (var key in GetFlushableBuckets(force)) - { - cancellationToken.ThrowIfCancellationRequested(); - - _options.LogDebug("Flushing metrics for bucket {0}", key); - ConcurrentDictionary? bucket; - _bucketsLock.EnterWriteLock(); - try - { - if (!Buckets.ContainsKey(key)) - { - continue; - } - bucket = Buckets[key]; - Buckets.Remove(key); - } - finally - { - _bucketsLock.ExitWriteLock(); - } - - _metricHub.CaptureMetrics(bucket.Values); - _options.LogDebug("Metric flushed for bucket {0}", key); - } - - foreach (var (timestamp, locations) in FlushableLocations()) - { - cancellationToken.ThrowIfCancellationRequested(); - - _options.LogDebug("Flushing code locations: ", timestamp); - var codeLocations = new CodeLocations(timestamp, locations); - _metricHub.CaptureCodeLocations(codeLocations); - _options.LogDebug("Code locations flushed: ", timestamp); - } - - ClearStaleLocations(); - } - catch (OperationCanceledException) - { - _options.LogInfo(FlushShutdownMessage); - } - catch (Exception exception) - { - _options.LogError(exception, "Error processing metrics."); - } - finally - { - // If the shutdown token was cancelled before we start this method, we can get here - // without the _flushLock.CurrentCount (i.e. available threads) having been decremented - if (_flushLock.CurrentCount < 1) - { - _flushLock.Release(); - } - } - } - - /// - /// Returns the keys for any buckets that are ready to be flushed (i.e. are for periods before the cutoff) - /// - /// Forces all buckets to be flushed, ignoring the cutoff - /// - /// An enumerable containing the keys for any buckets that are ready to be flushed - /// - internal IEnumerable GetFlushableBuckets(bool force = false) - { - if (!_buckets.IsValueCreated) - { - yield break; - } - - long[] keys; - _bucketsLock.EnterReadLock(); - try - { - keys = Buckets.Keys.ToArray(); - } - finally - { - _bucketsLock.ExitReadLock(); - } - if (force) - { - // Return all the buckets in this case - foreach (var key in keys) - { - yield return key; - } - } - else - { - var cutoff = MetricHelper.GetCutoff(); - foreach (var key in keys) - { - var bucketTime = DateTimeOffset.FromUnixTimeSeconds(key); - if (bucketTime < cutoff) - { - yield return key; - } - } - } - } - - private Dictionary> FlushableLocations() - { - _codeLocationLock.Wait(); - try - { - var result = _pendingLocations; - _pendingLocations = new Dictionary>(); - return result; - } - finally - { - _codeLocationLock.Release(); - } - } - - /// - /// Clear out stale seen locations once a day - /// - internal void ClearStaleLocations(DateTimeOffset? testNow = null) - { - var now = testNow ?? DateTimeOffset.UtcNow; - var today = now.GetDayBucketKey(); - if (_lastClearedStaleLocations == today) - { - return; - } - // Allow 60 seconds for all code locations to be sent at the transition from one day to the next - const int staleGraceInMinutes = 1; - if (now.Minute < staleGraceInMinutes) - { - return; - } - - foreach (var dailyValues in _seenLocations.Keys.ToArray()) - { - if (dailyValues < today) - { - _seenLocations.TryRemove(dailyValues, out _); - } - } - _lastClearedStaleLocations = today; - } - - /// - public async ValueTask DisposeAsync() - { - _options.LogDebug(DisposingMessage); - - if (_disposed) - { - _options.LogDebug(AlreadyDisposedMessage); - return; - } - - _disposed = true; - - try - { - // Request the LoopTask stop. - await _shutdownSource.CancelAsync().ConfigureAwait(false); - - // Now wait for the Loop to stop. - // NOTE: While non-intuitive, do not pass a timeout or cancellation token here. We are waiting for - // the _continuation_ of the method, not its _execution_. If we stop waiting prematurely, this may cause - // unexpected behavior in client applications. - await _loopTask.ConfigureAwait(false); - } - catch (OperationCanceledException) - { - _options.LogDebug(CancelledMessage); - } - catch (Exception exception) - { - _options.LogError(exception, "Async Disposing the Metric Aggregator threw an exception."); - } - finally - { - _flushLock.Dispose(); - _shutdownSource.Dispose(); - _loopTask.Dispose(); - } - } - - public void Dispose() - { - try - { - DisposeAsync().AsTask().GetAwaiter().GetResult(); - } - catch (OperationCanceledException) - { - // Ignore - } - catch (Exception exception) - { - _options.LogError(exception, "Disposing the Metric Aggregator threw an exception."); - } - } -} diff --git a/src/Sentry/Platforms/Android/SentrySdk.cs b/src/Sentry/Platforms/Android/SentrySdk.cs index d2454fc396..6b5d2ba42d 100644 --- a/src/Sentry/Platforms/Android/SentrySdk.cs +++ b/src/Sentry/Platforms/Android/SentrySdk.cs @@ -89,7 +89,6 @@ private static void InitSentryAndroidSdk(SentryOptions options) // These options we have behind feature flags if (options is { IsPerformanceMonitoringEnabled: true, Native.EnableTracing: true }) { - o.EnableTracing = null; o.TracesSampleRate = (JavaDouble?)options.TracesSampleRate; if (options.TracesSampler is { } tracesSampler) diff --git a/src/Sentry/SentryOptions.cs b/src/Sentry/SentryOptions.cs index 66ebac2c7c..37e9a04c4b 100644 --- a/src/Sentry/SentryOptions.cs +++ b/src/Sentry/SentryOptions.cs @@ -199,13 +199,6 @@ internal IEnumerable Integrations } #endif -#if NET8_0_OR_GREATER - if ((_defaultIntegrations & DefaultIntegrations.SystemDiagnosticsMetricsIntegration) != 0) - { - yield return new SystemDiagnosticsMetricsIntegration(); - } -#endif - foreach (var integration in _integrations) { yield return integration; @@ -1098,18 +1091,6 @@ public bool JsonPreserveReferences [EditorBrowsable(EditorBrowsableState.Never)] public Func? AssemblyReader { get; set; } - /// - /// - /// Settings for the EXPERIMENTAL metrics feature. This feature is preview only and subject to change without a - /// major version bump. Currently it's recommended for noodling only - DON'T USE IN PRODUCTION! - /// - /// - /// By default the ExperimentalMetrics Options is null, which means the feature is disabled. If you want to enable - /// Experimental metrics, you must set this property to a non-null value. - /// - /// - public ExperimentalMetricsOptions? ExperimentalMetrics { get; set; } - /// /// The Spotlight URL. Defaults to http://localhost:8969/stream /// diff --git a/src/Sentry/SentrySdk.cs b/src/Sentry/SentrySdk.cs index bf32c20d6c..4fb044be15 100644 --- a/src/Sentry/SentrySdk.cs +++ b/src/Sentry/SentrySdk.cs @@ -667,13 +667,6 @@ public static TransactionContext ContinueTrace( string? operation = null) => CurrentHub.ContinueTrace(traceHeader, baggageHeader, name, operation); - /// - [Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major release. " + - "Sentry will reject all metrics sent after October 7, 2024." + - "Learn more: https://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-to-Metrics")] - public static IMetricAggregator Metrics - => CurrentHub.Metrics; - /// [DebuggerStepThrough] public static void StartSession() diff --git a/src/Sentry/Timing.cs b/src/Sentry/Timing.cs deleted file mode 100644 index 2c6a0d6517..0000000000 --- a/src/Sentry/Timing.cs +++ /dev/null @@ -1,88 +0,0 @@ -using Sentry.Extensibility; -using Sentry.Internal; -using Sentry.Protocol.Metrics; - -namespace Sentry; - -/// -/// Measures the time it takes to run a given code block and emits this as a metric. -/// -/// -/// using (var timing = new Timing("my-operation")) -/// { -/// ... -/// } -/// -internal class Timing : IDisposable -{ - internal const string OperationName = "metric.timing"; - public const string MetricsOrigin = "auto.metrics"; - - private readonly SentryOptions _options; - private readonly MetricAggregator _metricAggregator; - private readonly string _key; - private readonly MeasurementUnit.Duration _unit; - private readonly IDictionary? _tags; - internal readonly Stopwatch _stopwatch = new(); - private readonly ISpan _span; - internal readonly DateTime _startTime = DateTime.UtcNow; - - /// - /// Creates a new instance. - /// - internal Timing(MetricAggregator metricAggregator, IMetricHub metricHub, SentryOptions options, - string key, MeasurementUnit.Duration unit, IDictionary? tags, int stackLevel) - { - _options = options; - _metricAggregator = metricAggregator; - _key = key; - _unit = unit; - _tags = tags; - _stopwatch.Start(); - - _span = metricHub.StartSpan(OperationName, key); - _span.SetOrigin(MetricsOrigin); - if (tags is not null) - { - _span.SetTags(tags); - } - - // Report code locations here for better accuracy - _metricAggregator.RecordCodeLocation(MetricType.Distribution, key, unit, stackLevel + 1, _startTime); - } - - /// - public void Dispose() - { - _stopwatch.Stop(); - DisposeInternal(_stopwatch.Elapsed); - } - - internal void DisposeInternal(TimeSpan elapsed) - { - try - { - var value = _unit switch - { - MeasurementUnit.Duration.Week => elapsed.TotalDays / 7, - MeasurementUnit.Duration.Day => elapsed.TotalDays, - MeasurementUnit.Duration.Hour => elapsed.TotalHours, - MeasurementUnit.Duration.Minute => elapsed.TotalMinutes, - MeasurementUnit.Duration.Second => elapsed.TotalSeconds, - MeasurementUnit.Duration.Millisecond => elapsed.TotalMilliseconds, - MeasurementUnit.Duration.Microsecond => elapsed.TotalMilliseconds * 1000, - MeasurementUnit.Duration.Nanosecond => elapsed.TotalMilliseconds * 1000000, - _ => throw new ArgumentOutOfRangeException(nameof(_unit), _unit, null) - }; - _metricAggregator.Timing(_key, value, _unit, _tags, _startTime); - } - catch (Exception e) - { - _options.LogError(e, "Error capturing timing '{0}'", _key); - } - finally - { - _span?.Finish(); - } - } -} diff --git a/test/Sentry.AspNetCore.Tests/BindableSentryAspNetCoreOptionsTests.cs b/test/Sentry.AspNetCore.Tests/BindableSentryAspNetCoreOptionsTests.cs index 8bf4b185d9..c7c18cc0e5 100644 --- a/test/Sentry.AspNetCore.Tests/BindableSentryAspNetCoreOptionsTests.cs +++ b/test/Sentry.AspNetCore.Tests/BindableSentryAspNetCoreOptionsTests.cs @@ -5,10 +5,6 @@ namespace Sentry.AspNetCore.Tests; public class BindableSentryAspNetCoreOptionsTests : BindableTests { - public BindableSentryAspNetCoreOptionsTests() : base(nameof(SentryAspNetCoreOptions.ExperimentalMetrics)) - { - } - [Fact] public void BindableProperties_MatchOptionsProperties() { diff --git a/test/Sentry.Azure.Functions.Worker.Tests/BindableSentryAzureFunctionsOptionsTests.cs b/test/Sentry.Azure.Functions.Worker.Tests/BindableSentryAzureFunctionsOptionsTests.cs index 3e196cee11..2f061b45e2 100644 --- a/test/Sentry.Azure.Functions.Worker.Tests/BindableSentryAzureFunctionsOptionsTests.cs +++ b/test/Sentry.Azure.Functions.Worker.Tests/BindableSentryAzureFunctionsOptionsTests.cs @@ -5,10 +5,6 @@ namespace Sentry.Azure.Functions.Worker.Tests; public class BindableSentryAzureFunctionsOptionsTests : BindableTests { - public BindableSentryAzureFunctionsOptionsTests() : base(nameof(SentryAzureFunctionsOptions.ExperimentalMetrics)) - { - } - [Fact] public void BindableProperties_MatchOptionsProperties() { diff --git a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsTests.cs b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsTests.cs index 26ba0fb740..772473b267 100644 --- a/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsTests.cs +++ b/test/Sentry.Extensions.Logging.Tests/SentryLoggingOptionsTests.cs @@ -5,10 +5,6 @@ namespace Sentry.Extensions.Logging.Tests; public class SentryLoggingOptionsTests : BindableTests { - public SentryLoggingOptionsTests() : base(nameof(SentryLoggingOptions.ExperimentalMetrics)) - { - } - [Fact] public void BindableProperties_MatchOptionsProperties() { diff --git a/test/Sentry.Maui.Tests/BindableSentryMauiOptionsTests.cs b/test/Sentry.Maui.Tests/BindableSentryMauiOptionsTests.cs index 1a5b885b33..5da7c9ba30 100644 --- a/test/Sentry.Maui.Tests/BindableSentryMauiOptionsTests.cs +++ b/test/Sentry.Maui.Tests/BindableSentryMauiOptionsTests.cs @@ -5,10 +5,6 @@ namespace Sentry.Maui.Tests; public class BindableSentryMauiOptionsTests : BindableTests { - public BindableSentryMauiOptionsTests() : base(nameof(SentryMauiOptions.ExperimentalMetrics)) - { - } - [Fact] public void BindableProperties_MatchOptionsProperties() { diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt index 3f6d8bb6de..225135ec55 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet6_0.verified.txt @@ -193,7 +193,6 @@ namespace Sentry public interface IHub : Sentry.ISentryClient, Sentry.ISentryScopeManager { Sentry.SentryId LastEventId { get; } - Sentry.IMetricAggregator Metrics { get; } void BindException(System.Exception exception, Sentry.ISpan span); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope); @@ -208,17 +207,6 @@ namespace Sentry void StartSession(); Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext); } - public interface IMetricAggregator : System.IDisposable - { - void Distribution(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - System.Threading.Tasks.Task FlushAsync(bool force = true, System.Threading.CancellationToken cancellationToken = default); - void Gauge(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Increment(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Set(string key, int value, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Set(string key, string value, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - System.IDisposable StartTimer(string key, Sentry.MeasurementUnit.Duration unit = 3, System.Collections.Generic.IDictionary? tags = null, int stackLevel = 1); - void Timing(string key, double value, Sentry.MeasurementUnit.Duration unit = 3, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - } public interface IScopeObserver { void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); @@ -670,7 +658,6 @@ namespace Sentry public bool EnableScopeSync { get; set; } public bool EnableSpotlight { get; set; } public string? Environment { get; set; } - public Sentry.ExperimentalMetricsOptions? ExperimentalMetrics { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } public System.Collections.Generic.IList FailedRequestTargets { get; set; } public System.TimeSpan FlushTimeout { get; set; } @@ -782,11 +769,6 @@ namespace Sentry { public static bool IsEnabled { get; } public static Sentry.SentryId LastEventId { get; } - [System.Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major " + - "release. Sentry will reject all metrics sent after October 7, 2024.Learn more: h" + - "ttps://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-" + - "to-Metrics")] - public static Sentry.IMetricAggregator Metrics { get; } public static void AddBreadcrumb(Sentry.Breadcrumb breadcrumb, Sentry.SentryHint? hint = null) { } public static void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public static void AddBreadcrumb(Sentry.Infrastructure.ISystemClock? clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } @@ -1283,7 +1265,6 @@ namespace Sentry.Extensibility public static readonly Sentry.Extensibility.DisabledHub Instance; public bool IsEnabled { get; } public Sentry.SentryId LastEventId { get; } - public Sentry.IMetricAggregator Metrics { get; } public void BindClient(Sentry.ISentryClient client) { } public void BindException(System.Exception exception, Sentry.ISpan span) { } public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } @@ -1323,11 +1304,6 @@ namespace Sentry.Extensibility public static readonly Sentry.Extensibility.HubAdapter Instance; public bool IsEnabled { get; } public Sentry.SentryId LastEventId { get; } - [System.Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major " + - "release. Sentry will reject all metrics sent after October 7, 2024.Learn more: h" + - "ttps://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-" + - "to-Metrics")] - public Sentry.IMetricAggregator Metrics { get; } public void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public void AddBreadcrumb(Sentry.Infrastructure.ISystemClock clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public void BindClient(Sentry.ISentryClient client) { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt index 3f6d8bb6de..225135ec55 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet7_0.verified.txt @@ -193,7 +193,6 @@ namespace Sentry public interface IHub : Sentry.ISentryClient, Sentry.ISentryScopeManager { Sentry.SentryId LastEventId { get; } - Sentry.IMetricAggregator Metrics { get; } void BindException(System.Exception exception, Sentry.ISpan span); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope); @@ -208,17 +207,6 @@ namespace Sentry void StartSession(); Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext); } - public interface IMetricAggregator : System.IDisposable - { - void Distribution(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - System.Threading.Tasks.Task FlushAsync(bool force = true, System.Threading.CancellationToken cancellationToken = default); - void Gauge(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Increment(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Set(string key, int value, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Set(string key, string value, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - System.IDisposable StartTimer(string key, Sentry.MeasurementUnit.Duration unit = 3, System.Collections.Generic.IDictionary? tags = null, int stackLevel = 1); - void Timing(string key, double value, Sentry.MeasurementUnit.Duration unit = 3, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - } public interface IScopeObserver { void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); @@ -670,7 +658,6 @@ namespace Sentry public bool EnableScopeSync { get; set; } public bool EnableSpotlight { get; set; } public string? Environment { get; set; } - public Sentry.ExperimentalMetricsOptions? ExperimentalMetrics { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } public System.Collections.Generic.IList FailedRequestTargets { get; set; } public System.TimeSpan FlushTimeout { get; set; } @@ -782,11 +769,6 @@ namespace Sentry { public static bool IsEnabled { get; } public static Sentry.SentryId LastEventId { get; } - [System.Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major " + - "release. Sentry will reject all metrics sent after October 7, 2024.Learn more: h" + - "ttps://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-" + - "to-Metrics")] - public static Sentry.IMetricAggregator Metrics { get; } public static void AddBreadcrumb(Sentry.Breadcrumb breadcrumb, Sentry.SentryHint? hint = null) { } public static void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public static void AddBreadcrumb(Sentry.Infrastructure.ISystemClock? clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } @@ -1283,7 +1265,6 @@ namespace Sentry.Extensibility public static readonly Sentry.Extensibility.DisabledHub Instance; public bool IsEnabled { get; } public Sentry.SentryId LastEventId { get; } - public Sentry.IMetricAggregator Metrics { get; } public void BindClient(Sentry.ISentryClient client) { } public void BindException(System.Exception exception, Sentry.ISpan span) { } public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } @@ -1323,11 +1304,6 @@ namespace Sentry.Extensibility public static readonly Sentry.Extensibility.HubAdapter Instance; public bool IsEnabled { get; } public Sentry.SentryId LastEventId { get; } - [System.Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major " + - "release. Sentry will reject all metrics sent after October 7, 2024.Learn more: h" + - "ttps://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-" + - "to-Metrics")] - public Sentry.IMetricAggregator Metrics { get; } public void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public void AddBreadcrumb(Sentry.Infrastructure.ISystemClock clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public void BindClient(Sentry.ISentryClient client) { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt index 67d3cb89b7..5d13ca7595 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.DotNet8_0.verified.txt @@ -194,7 +194,6 @@ namespace Sentry public interface IHub : Sentry.ISentryClient, Sentry.ISentryScopeManager { Sentry.SentryId LastEventId { get; } - Sentry.IMetricAggregator Metrics { get; } void BindException(System.Exception exception, Sentry.ISpan span); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope); @@ -209,17 +208,6 @@ namespace Sentry void StartSession(); Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext); } - public interface IMetricAggregator : System.IDisposable - { - void Distribution(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - System.Threading.Tasks.Task FlushAsync(bool force = true, System.Threading.CancellationToken cancellationToken = default); - void Gauge(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Increment(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Set(string key, int value, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Set(string key, string value, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - System.IDisposable StartTimer(string key, Sentry.MeasurementUnit.Duration unit = 3, System.Collections.Generic.IDictionary? tags = null, int stackLevel = 1); - void Timing(string key, double value, Sentry.MeasurementUnit.Duration unit = 3, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - } public interface IScopeObserver { void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); @@ -671,7 +659,6 @@ namespace Sentry public bool EnableScopeSync { get; set; } public bool EnableSpotlight { get; set; } public string? Environment { get; set; } - public Sentry.ExperimentalMetricsOptions? ExperimentalMetrics { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } public System.Collections.Generic.IList FailedRequestTargets { get; set; } public System.TimeSpan FlushTimeout { get; set; } @@ -784,11 +771,6 @@ namespace Sentry { public static bool IsEnabled { get; } public static Sentry.SentryId LastEventId { get; } - [System.Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major " + - "release. Sentry will reject all metrics sent after October 7, 2024.Learn more: h" + - "ttps://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-" + - "to-Metrics")] - public static Sentry.IMetricAggregator Metrics { get; } public static void AddBreadcrumb(Sentry.Breadcrumb breadcrumb, Sentry.SentryHint? hint = null) { } public static void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public static void AddBreadcrumb(Sentry.Infrastructure.ISystemClock? clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } @@ -1285,7 +1267,6 @@ namespace Sentry.Extensibility public static readonly Sentry.Extensibility.DisabledHub Instance; public bool IsEnabled { get; } public Sentry.SentryId LastEventId { get; } - public Sentry.IMetricAggregator Metrics { get; } public void BindClient(Sentry.ISentryClient client) { } public void BindException(System.Exception exception, Sentry.ISpan span) { } public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } @@ -1325,11 +1306,6 @@ namespace Sentry.Extensibility public static readonly Sentry.Extensibility.HubAdapter Instance; public bool IsEnabled { get; } public Sentry.SentryId LastEventId { get; } - [System.Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major " + - "release. Sentry will reject all metrics sent after October 7, 2024.Learn more: h" + - "ttps://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-" + - "to-Metrics")] - public Sentry.IMetricAggregator Metrics { get; } public void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public void AddBreadcrumb(Sentry.Infrastructure.ISystemClock clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public void BindClient(Sentry.ISentryClient client) { } diff --git a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt index 9364b82ee2..5c6899448c 100644 --- a/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt +++ b/test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt @@ -192,7 +192,6 @@ namespace Sentry public interface IHub : Sentry.ISentryClient, Sentry.ISentryScopeManager { Sentry.SentryId LastEventId { get; } - Sentry.IMetricAggregator Metrics { get; } void BindException(System.Exception exception, Sentry.ISpan span); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, System.Action configureScope); Sentry.SentryId CaptureEvent(Sentry.SentryEvent evt, Sentry.SentryHint? hint, System.Action configureScope); @@ -207,17 +206,6 @@ namespace Sentry void StartSession(); Sentry.ITransactionTracer StartTransaction(Sentry.ITransactionContext context, System.Collections.Generic.IReadOnlyDictionary customSamplingContext); } - public interface IMetricAggregator : System.IDisposable - { - void Distribution(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - System.Threading.Tasks.Task FlushAsync(bool force = true, System.Threading.CancellationToken cancellationToken = default); - void Gauge(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Increment(string key, double value = 1, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Set(string key, int value, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - void Set(string key, string value, Sentry.MeasurementUnit? unit = default, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - System.IDisposable StartTimer(string key, Sentry.MeasurementUnit.Duration unit = 3, System.Collections.Generic.IDictionary? tags = null, int stackLevel = 1); - void Timing(string key, double value, Sentry.MeasurementUnit.Duration unit = 3, System.Collections.Generic.IDictionary? tags = null, System.DateTimeOffset? timestamp = default, int stackLevel = 1); - } public interface IScopeObserver { void AddBreadcrumb(Sentry.Breadcrumb breadcrumb); @@ -668,7 +656,6 @@ namespace Sentry public bool EnableScopeSync { get; set; } public bool EnableSpotlight { get; set; } public string? Environment { get; set; } - public Sentry.ExperimentalMetricsOptions? ExperimentalMetrics { get; set; } public System.Collections.Generic.IList FailedRequestStatusCodes { get; set; } public System.Collections.Generic.IList FailedRequestTargets { get; set; } public System.TimeSpan FlushTimeout { get; set; } @@ -779,11 +766,6 @@ namespace Sentry { public static bool IsEnabled { get; } public static Sentry.SentryId LastEventId { get; } - [System.Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major " + - "release. Sentry will reject all metrics sent after October 7, 2024.Learn more: h" + - "ttps://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-" + - "to-Metrics")] - public static Sentry.IMetricAggregator Metrics { get; } public static void AddBreadcrumb(Sentry.Breadcrumb breadcrumb, Sentry.SentryHint? hint = null) { } public static void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public static void AddBreadcrumb(Sentry.Infrastructure.ISystemClock? clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } @@ -1280,7 +1262,6 @@ namespace Sentry.Extensibility public static readonly Sentry.Extensibility.DisabledHub Instance; public bool IsEnabled { get; } public Sentry.SentryId LastEventId { get; } - public Sentry.IMetricAggregator Metrics { get; } public void BindClient(Sentry.ISentryClient client) { } public void BindException(System.Exception exception, Sentry.ISpan span) { } public Sentry.SentryId CaptureCheckIn(string monitorSlug, Sentry.CheckInStatus status, Sentry.SentryId? sentryId = default, System.TimeSpan? duration = default, Sentry.Scope? scope = null, System.Action? configureMonitorOptions = null) { } @@ -1320,11 +1301,6 @@ namespace Sentry.Extensibility public static readonly Sentry.Extensibility.HubAdapter Instance; public bool IsEnabled { get; } public Sentry.SentryId LastEventId { get; } - [System.Obsolete("The SentrySdk.Metrics module is deprecated and will be removed in the next major " + - "release. Sentry will reject all metrics sent after October 7, 2024.Learn more: h" + - "ttps://sentry.zendesk.com/hc/en-us/articles/26369339769883-Upcoming-API-Changes-" + - "to-Metrics")] - public Sentry.IMetricAggregator Metrics { get; } public void AddBreadcrumb(string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public void AddBreadcrumb(Sentry.Infrastructure.ISystemClock clock, string message, string? category = null, string? type = null, System.Collections.Generic.IDictionary? data = null, Sentry.BreadcrumbLevel level = 0) { } public void BindClient(Sentry.ISentryClient client) { } diff --git a/test/Sentry.Tests/BindableSentryOptionsTests.cs b/test/Sentry.Tests/BindableSentryOptionsTests.cs index 977f8570d8..50f39e954a 100644 --- a/test/Sentry.Tests/BindableSentryOptionsTests.cs +++ b/test/Sentry.Tests/BindableSentryOptionsTests.cs @@ -5,10 +5,6 @@ namespace Sentry.Tests; public class BindableSentryOptionsTests : BindableTests { - public BindableSentryOptionsTests() : base(nameof(SentryOptions.ExperimentalMetrics)) - { - } - [Fact] public void BindableProperties_MatchOptionsProperties() { diff --git a/test/Sentry.Tests/Integrations/SystemDiagnosticsMetricsIntegrationTests.cs b/test/Sentry.Tests/Integrations/SystemDiagnosticsMetricsIntegrationTests.cs deleted file mode 100644 index 431afe8a31..0000000000 --- a/test/Sentry.Tests/Integrations/SystemDiagnosticsMetricsIntegrationTests.cs +++ /dev/null @@ -1,79 +0,0 @@ -#if NET8_0_OR_GREATER -namespace Sentry.Tests.Integrations; - -public class SystemDiagnosticsMetricsIntegrationTests -{ - [Fact] - public void Register_NoListenersConfigured_LogsDisabledMessage() - { - // Arrange - var logger = Substitute.For(); - logger.IsEnabled(Arg.Any()).Returns(true); - var options = new SentryOptions - { - Debug = true, - DiagnosticLogger = logger, - ExperimentalMetrics = new ExperimentalMetricsOptions() - { - CaptureSystemDiagnosticsInstruments = [], - CaptureSystemDiagnosticsMeters = [] - } - }; - var integration = new SystemDiagnosticsMetricsIntegration(); - - // Act - integration.Register(null!, options); - - // Assert - logger.Received(1).Log(SentryLevel.Info, SystemDiagnosticsMetricsIntegration.NoListenersAreConfiguredMessage, null); - } - - [Fact] - public void Register_CaptureSystemDiagnosticsInstruments_Succeeds() - { - // Arrange - var logger = Substitute.For(); - var options = new SentryOptions - { - DiagnosticLogger = logger, - ExperimentalMetrics = new ExperimentalMetricsOptions() - { - CaptureSystemDiagnosticsInstruments = [".*"], - CaptureSystemDiagnosticsMeters = [] - } - }; - var initializeDefaultListener = Substitute.For>(); - var integration = new SystemDiagnosticsMetricsIntegration(initializeDefaultListener); - - // Act - integration.Register(null!, options); - - // Assert - initializeDefaultListener.Received(1)(options.ExperimentalMetrics); - } - - [Fact] - public void Register_CaptureSystemDiagnosticsMeters_Succeeds() - { - // Arrange - var logger = Substitute.For(); - var options = new SentryOptions - { - DiagnosticLogger = logger, - ExperimentalMetrics = new ExperimentalMetricsOptions() - { - CaptureSystemDiagnosticsInstruments = [], - CaptureSystemDiagnosticsMeters = [".*"] - } - }; - var initializeDefaultListener = Substitute.For>(); - var integration = new SystemDiagnosticsMetricsIntegration(initializeDefaultListener); - - // Act - integration.Register(null!, options); - - // Assert - initializeDefaultListener.Received(1)(options.ExperimentalMetrics); - } -} -#endif diff --git a/test/Sentry.Tests/Internals/SystemDiagnosticsMetricsListenerTests.cs b/test/Sentry.Tests/Internals/SystemDiagnosticsMetricsListenerTests.cs deleted file mode 100644 index 7934326ec0..0000000000 --- a/test/Sentry.Tests/Internals/SystemDiagnosticsMetricsListenerTests.cs +++ /dev/null @@ -1,300 +0,0 @@ -#if NET8_0_OR_GREATER -using System.Diagnostics.Metrics; - -namespace Sentry.Tests.Internals; - -public class SystemDiagnosticsMetricsListenerTests -{ - private class Fixture - { - public readonly IMetricAggregator MockAggregator = Substitute.For(); - public readonly ExperimentalMetricsOptions MetricsOptions = new(); - - public SystemDiagnosticsMetricsListener GetSut() - { - return new SystemDiagnosticsMetricsListener(MetricsOptions, () => MockAggregator); - } - } - - private readonly Fixture _fixture = new(); - - private static Meter GetMeter() => new Meter(UniqueName(), "1.0.0"); - private static string UniqueName() => Guid.NewGuid().ToString(); - - [Fact] - public void RecordMeasurement_CounterInstrument_CallsIncrement() - { - // Arrange - var testMeter = GetMeter(); - var instrument = testMeter.CreateCounter(UniqueName(), "unit"); - const int measurement = 2; - ReadOnlySpan> tags = [ - new KeyValuePair("tag1", "value1"), - new KeyValuePair("tag2", 2), - ]; - var expectedTags = tags.ToImmutableArray().ToImmutableDictionary( - kvp => kvp.Key, - kvp => kvp.Value?.ToString() ?? string.Empty - ); - - // Act - var sut = _fixture.GetSut(); - sut.RecordMeasurement(instrument, measurement, tags, null); - - // Assert - _fixture.MockAggregator.Received().Increment( - instrument.Name, - measurement, - MeasurementUnit.Custom(instrument.Unit!), - Arg.Is>(arg => - expectedTags.All(tag => arg.ContainsKey(tag.Key) && arg[tag.Key] == tag.Value) - ) - ); - } - - [Fact] - public void RecordMeasurement_UpDownCounterInstrument_CallsIncrement() - { - // Arrange - var testMeter = GetMeter(); - var instrument = testMeter.CreateUpDownCounter(UniqueName(), "unit"); - const int measurement = -2; - ReadOnlySpan> tags = [ - new KeyValuePair("tag1", "value1"), - new KeyValuePair("tag2", 2), - ]; - var expectedTags = tags.ToImmutableArray().ToImmutableDictionary( - kvp => kvp.Key, - kvp => kvp.Value?.ToString() ?? string.Empty - ); - - // Act - var sut = _fixture.GetSut(); - sut.RecordMeasurement(instrument, measurement, tags, null); - - // Assert - _fixture.MockAggregator.Received().Increment( - instrument.Name, - measurement, - MeasurementUnit.Custom(instrument.Unit!), - Arg.Is>(arg => - expectedTags.All(tag => arg.ContainsKey(tag.Key) && arg[tag.Key] == tag.Value) - ) - ); - } - - [Fact] - public void RecordMeasurement_HistogramInstrument_CallsDistribution() - { - // Arrange - var testMeter = GetMeter(); - var instrument = testMeter.CreateHistogram(UniqueName(), "unit"); - const int measurement = 2; - ReadOnlySpan> tags = [ - new KeyValuePair("tag1", "value1"), - new KeyValuePair("tag2", 2), - ]; - var expectedTags = tags.ToImmutableArray().ToImmutableDictionary( - kvp => kvp.Key, - kvp => kvp.Value?.ToString() ?? string.Empty - ); - - // Act - var sut = _fixture.GetSut(); - sut.RecordMeasurement(instrument, measurement, tags, null); - - // Assert - _fixture.MockAggregator.Received().Distribution( - instrument.Name, - measurement, - MeasurementUnit.Custom(instrument.Unit!), - Arg.Is>(arg => - expectedTags.All(tag => arg.ContainsKey(tag.Key) && arg[tag.Key] == tag.Value) - ) - ); - } - - [Fact] - public void RecordMeasurement_ObservableGaugeInstrument_CallsGauge() - { - // Arrange - var testMeter = GetMeter(); - var instrument = testMeter.CreateObservableGauge(UniqueName(), () => new[] { new Measurement(2) }, "unit"); - const int measurement = 2; - ReadOnlySpan> tags = [ - new KeyValuePair("tag1", "value1"), - new KeyValuePair("tag2", 2), - ]; - var expectedTags = tags.ToImmutableArray().ToImmutableDictionary( - kvp => kvp.Key, - kvp => kvp.Value?.ToString() ?? string.Empty - ); - - // Act - var sut = _fixture.GetSut(); - sut.RecordMeasurement(instrument, measurement, tags, null); - - // Assert - _fixture.MockAggregator.Received().Gauge( - instrument.Name, - measurement, - MeasurementUnit.Custom(instrument.Unit!), - Arg.Is>(arg => - expectedTags.All(tag => arg.ContainsKey(tag.Key) && arg[tag.Key] == tag.Value) - ) - ); - } - - [Fact] - public void SystemDiagnosticsMetricsListener_Counter_AggregatesCorrectly() - { - // Arrange - var testMeter = GetMeter(); - var instrument = testMeter.CreateCounter(UniqueName(), "unit"); - _fixture.MetricsOptions.CaptureSystemDiagnosticsInstruments.Add(instrument.Name); - var total = 0d; - _fixture.MockAggregator.Increment( - instrument.Name, - Arg.Do(x => total += x), - Arg.Any(), - Arg.Any>()); - - // Act - var sut = _fixture.GetSut(); - instrument.Add(2); - instrument.Add(3); - - // Assert - _fixture.MockAggregator.Received(2).Increment( - instrument.Name, - Arg.Any(), - Arg.Any(), - Arg.Any>() - ); - total.Should().Be(5); - } - - [Fact] - public void SystemDiagnosticsMetricsListener_ObservableCounter_AggregatesCorrectly() - { - // Arrange - var testMeter = GetMeter(); - List> observedValues = [new Measurement(2), new Measurement(3)]; - var instrument = testMeter.CreateObservableCounter(UniqueName(), - () => observedValues); - _fixture.MetricsOptions.CaptureSystemDiagnosticsInstruments.Add(instrument.Name); - var total = 0d; - _fixture.MockAggregator.Increment( - instrument.Name, - Arg.Do(x => total += x), - Arg.Any(), - Arg.Any>()); - - // Act - var sut = _fixture.GetSut(); - sut._sentryListener.RecordObservableInstruments(); - - // Assert - _fixture.MockAggregator.Received(2).Increment( - instrument.Name, - Arg.Any(), - Arg.Any(), - Arg.Any>() - ); - total.Should().Be(5); - } - - [Fact] - public void SystemDiagnosticsMetricsListener_ObservableUpDownCounter_AggregatesCorrectly() - { - // Arrange - var testMeter = GetMeter(); - List> observedValues = [new Measurement(12), new Measurement(-5)]; - var instrument = testMeter.CreateObservableUpDownCounter(UniqueName(), - () => observedValues); - _fixture.MetricsOptions.CaptureSystemDiagnosticsInstruments.Add(instrument.Name); - var total = 0d; - _fixture.MockAggregator.Increment( - instrument.Name, - Arg.Do(x => total += x), - Arg.Any(), - Arg.Any>()); - - // Act - var sut = _fixture.GetSut(); - sut._sentryListener.RecordObservableInstruments(); - - // Assert - _fixture.MockAggregator.Received(2).Increment( - instrument.Name, - Arg.Any(), - Arg.Any(), - Arg.Any>() - ); - total.Should().Be(7); - } - - [Fact] - public void SystemDiagnosticsMetricsListener_OnlyListensToMatchingInstruments() - { - // Arrange - var testMeter = GetMeter(); - var match = testMeter.CreateCounter(UniqueName()); - var noMatch = testMeter.CreateCounter(UniqueName()); - _fixture.MetricsOptions.CaptureSystemDiagnosticsInstruments.Add(match.Name); - var total = 0d; - _fixture.MockAggregator.Increment( - Arg.Any(), - Arg.Do(x => total += x), - Arg.Any(), - Arg.Any>()); - - // Act - var sut = _fixture.GetSut(); - match.Add(5); - noMatch.Add(3); - - // Assert - _fixture.MockAggregator.Received(1).Increment( - Arg.Any(), - Arg.Any(), - Arg.Any(), - Arg.Any>() - ); - total.Should().Be(5); - } - - [Fact] - public void SystemDiagnosticsMetricsListener_OnlyListensToMatchingMeters() - { - // Arrange - var matchingMeter = GetMeter(); - var matching1 = matchingMeter.CreateCounter(UniqueName()); - var matching2 = matchingMeter.CreateCounter(UniqueName()); - var nonMatchingMeter = GetMeter(); - var nonMatching1 = nonMatchingMeter.CreateCounter(UniqueName()); - _fixture.MetricsOptions.CaptureSystemDiagnosticsMeters.Add(matchingMeter.Name); - var total = 0d; - _fixture.MockAggregator.Increment( - Arg.Any(), - Arg.Do(x => total += x), - Arg.Any(), - Arg.Any>()); - - // Act - var sut = _fixture.GetSut(); - matching1.Add(3); - matching2.Add(5); - nonMatching1.Add(7); - - // Assert - _fixture.MockAggregator.Received(2).Increment( - Arg.Any(), - Arg.Any(), - Arg.Any(), - Arg.Any>() - ); - total.Should().Be(8); - } -} -#endif diff --git a/test/Sentry.Tests/MetricAggregatorTests.cs b/test/Sentry.Tests/MetricAggregatorTests.cs deleted file mode 100644 index 638dfa1e1e..0000000000 --- a/test/Sentry.Tests/MetricAggregatorTests.cs +++ /dev/null @@ -1,641 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using FluentAssertions; -using NSubstitute; -using Sentry.Extensibility; -using Sentry.Protocol.Metrics; -using Xunit; - -namespace Sentry.Tests; - -public class MetricAggregatorTests -{ - private class Fixture - { - public readonly IDiagnosticLogger Logger; - public readonly SentryOptions Options; - - public readonly IMetricHub MetricHub; - public bool DisableFlushLoop; - public readonly CancellationTokenSource CancellationTokenSource; - - public Fixture() - { - Logger = Substitute.For(); - Options = new SentryOptions - { - Debug = true, - DiagnosticLogger = Logger - }; - MetricHub = Substitute.For(); - DisableFlushLoop = true; - CancellationTokenSource = new CancellationTokenSource(); - } - - public MetricAggregator GetSut() => new(Options, MetricHub, CancellationTokenSource, DisableFlushLoop); - } - - // private readonly Fixture _fixture = new(); - private readonly Fixture _fixture = new(); - - [Fact] - public void Increment_AggregatesMetrics() - { - // Arrange - var tx = Substitute.For(); - _fixture.MetricHub.GetSpan().Returns(tx); - - var metricType = MetricType.Counter; - var key = "counter_key"; - var unit = MeasurementUnit.None; - var tags = new Dictionary { ["tag1"] = "value1" }; - var sut = _fixture.GetSut(); - - // Act - DateTimeOffset firstTime = new(1970, 1, 1, 0, 0, 31, 0, TimeSpan.Zero); - sut.Increment(key, 3, unit, tags, firstTime); - - DateTimeOffset secondTime = new(1970, 1, 1, 0, 0, 38, 0, TimeSpan.Zero); - sut.Increment(key, 5, unit, tags, secondTime); - - DateTimeOffset thirdTime = new(1970, 1, 1, 0, 0, 40, 0, TimeSpan.Zero); - sut.Increment(key, 13, unit, tags, thirdTime); - - // Assert - var bucket1 = sut.Buckets[firstTime.GetTimeBucketKey()]; - var data1 = (CounterMetric)bucket1[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data1.Value.Should().Be(8); // First two emits are in the same bucket - - var bucket2 = sut.Buckets[thirdTime.GetTimeBucketKey()]; - var data2 = (CounterMetric)bucket2[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data2.Value.Should().Be(13); // First two emits are in the same bucket - } - - [Fact] - public void Gauge_AggregatesMetrics() - { - // Arrange - var tx = Substitute.For(); - _fixture.MetricHub.GetSpan().Returns(tx); - - var metricType = MetricType.Gauge; - var key = "gauge_key"; - var unit = MeasurementUnit.None; - var tags = new Dictionary { ["tag1"] = "value1" }; - var sut = _fixture.GetSut(); - - // Act - DateTimeOffset time1 = new(1970, 1, 1, 0, 0, 31, 0, TimeSpan.Zero); - sut.Gauge(key, 3, unit, tags, time1); - - DateTimeOffset time2 = new(1970, 1, 1, 0, 0, 38, 0, TimeSpan.Zero); - sut.Gauge(key, 5, unit, tags, time2); - - DateTimeOffset time3 = new(1970, 1, 1, 0, 0, 40, 0, TimeSpan.Zero); - sut.Gauge(key, 13, unit, tags, time3); - - // Assert - var bucket1 = sut.Buckets[time1.GetTimeBucketKey()]; - var data1 = (GaugeMetric)bucket1[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data1.Value.Should().Be(5); - data1.First.Should().Be(3); - data1.Min.Should().Be(3); - data1.Max.Should().Be(5); - data1.Sum.Should().Be(8); - data1.Count.Should().Be(2); - - var bucket2 = sut.Buckets[time3.GetTimeBucketKey()]; - var data2 = (GaugeMetric)bucket2[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data2.Value.Should().Be(13); - data2.First.Should().Be(13); - data2.Min.Should().Be(13); - data2.Max.Should().Be(13); - data2.Sum.Should().Be(13); - data2.Count.Should().Be(1); - } - - [Fact] - public void Distribution_AggregatesMetrics() - { - // Arrange - var tx = Substitute.For(); - _fixture.MetricHub.GetSpan().Returns(tx); - - var metricType = MetricType.Distribution; - var key = "distribution_key"; - var unit = MeasurementUnit.None; - var tags = new Dictionary { ["tag1"] = "value1" }; - var sut = _fixture.GetSut(); - - // Act - DateTimeOffset time1 = new(1970, 1, 1, 0, 0, 31, 0, TimeSpan.Zero); - sut.Distribution(key, 3, unit, tags, time1); - - DateTimeOffset time2 = new(1970, 1, 1, 0, 0, 38, 0, TimeSpan.Zero); - sut.Distribution(key, 5, unit, tags, time2); - - DateTimeOffset time3 = new(1970, 1, 1, 0, 0, 40, 0, TimeSpan.Zero); - sut.Distribution(key, 13, unit, tags, time3); - - // Assert - var bucket1 = sut.Buckets[time1.GetTimeBucketKey()]; - var data1 = (DistributionMetric)bucket1[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data1.Value.Should().BeEquivalentTo(new[] { 3, 5 }); - - var bucket2 = sut.Buckets[time3.GetTimeBucketKey()]; - var data2 = (DistributionMetric)bucket2[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data2.Value.Should().BeEquivalentTo(new[] { 13 }); - } - - [Fact] - public void Set_Int_AggregatesMetrics() - { - // Arrange - var tx = Substitute.For(); - _fixture.MetricHub.GetSpan().Returns(tx); - - var metricType = MetricType.Set; - var key = "set_key"; - var unit = MeasurementUnit.None; - var tags = new Dictionary { ["tag1"] = "value1" }; - var sut = _fixture.GetSut(); - - // Act - DateTimeOffset time1 = new(1970, 1, 1, 0, 0, 31, 0, TimeSpan.Zero); - sut.Set(key, 3, unit, tags, time1); - - DateTimeOffset time2 = new(1970, 1, 1, 0, 0, 38, 0, TimeSpan.Zero); - sut.Set(key, 5, unit, tags, time2); - - DateTimeOffset time3 = new(1970, 1, 1, 0, 0, 40, 0, TimeSpan.Zero); - sut.Set(key, 13, unit, tags, time3); - - DateTimeOffset time4 = new(1970, 1, 1, 0, 0, 42, 0, TimeSpan.Zero); - sut.Set(key, 13, unit, tags, time4); - - // Assert - var bucket1 = sut.Buckets[time1.GetTimeBucketKey()]; - var data1 = (SetMetric)bucket1[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data1.Value.Should().BeEquivalentTo(new[] { 3, 5 }); - - var bucket2 = sut.Buckets[time3.GetTimeBucketKey()]; - var data2 = (SetMetric)bucket2[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data2.Value.Should().BeEquivalentTo(new[] { 13 }); - } - - [Fact] - public void Set_String_AggregatesMetrics() - { - // Arrange - var tx = Substitute.For(); - _fixture.MetricHub.GetSpan().Returns(tx); - - var metricType = MetricType.Set; - var key = "set_key"; - var unit = MeasurementUnit.None; - var tags = new Dictionary { ["tag1"] = "value1" }; - var sut = _fixture.GetSut(); - - // Act - DateTimeOffset time1 = new(1970, 1, 1, 0, 0, 31, 0, TimeSpan.Zero); - sut.Set(key, "test_1", unit, tags, time1); - - DateTimeOffset time2 = new(1970, 1, 1, 0, 0, 38, 0, TimeSpan.Zero); - sut.Set(key, "test_2", unit, tags, time2); - - DateTimeOffset time3 = new(1970, 1, 1, 0, 0, 40, 0, TimeSpan.Zero); - sut.Set(key, "test_3", unit, tags, time3); - - DateTimeOffset time4 = new(1970, 1, 1, 0, 0, 42, 0, TimeSpan.Zero); - sut.Set(key, "test_3", unit, tags, time4); - - // Assert - var bucket1 = sut.Buckets[time1.GetTimeBucketKey()]; - var data1 = (SetMetric)bucket1[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data1.Value.Should().HaveCount(2); - - var bucket2 = sut.Buckets[time3.GetTimeBucketKey()]; - var data2 = (SetMetric)bucket2[MetricHelper.GetMetricBucketKey(metricType, key, unit, tags)]; - data2.Value.Should().HaveCount(1); - } - - [Fact] - public async Task GetFlushableBuckets_IsThreadsafe() - { - // Arrange - var tx = Substitute.For(); - _fixture.MetricHub.GetSpan().Returns(tx); - - const int numThreads = 100; - const int numThreadIterations = 1000; - var sent = 0; - MetricHelper.FlushShift = 0.0; - _fixture.DisableFlushLoop = false; - _fixture.MetricHub.CaptureMetrics(Arg.Do>(metrics => - { - foreach (var metric in metrics) - { - Interlocked.Add(ref sent, (int)((CounterMetric)metric).Value); - } - } - )); - var sut = _fixture.GetSut(); - - // Act... spawn some threads that add loads of metrics - var resetEvent = new ManualResetEvent(false); - var toProcess = numThreads; - for (var i = 0; i < numThreads; i++) - { - new Thread(delegate () - { - for (var i = 0; i < numThreadIterations; i++) - { - sut.Increment("counter"); - } - // If we're the last thread, signal - if (Interlocked.Decrement(ref toProcess) == 0) - { - resetEvent.Set(); - } - }).Start(); - } - - // Wait for workers. - resetEvent.WaitOne(); - await sut.FlushAsync(); - - // Assert - sent.Should().Be(numThreads * numThreadIterations); - } - - [Fact] - public void TestGetCodeLocation() - { - // Arrange - _fixture.Options.StackTraceMode = StackTraceMode.Enhanced; - var sut = _fixture.GetSut(); - - // Act - var result = sut.GetCodeLocation(1); - - // Assert - result.Should().NotBeNull(); - result!.Function.Should().Be($"void {nameof(MetricAggregatorTests)}.{nameof(TestGetCodeLocation)}()"); - } - - [Fact] - public void RecordCodeLocation_AddsMetricToSeenAndPendingLocations() - { - // Arrange - var type = MetricType.Counter; - var key = "counter_key"; - var unit = MeasurementUnit.None; - var stackLevel = 1; - var timestamp = DateTimeOffset.Now; - var sut = _fixture.GetSut(); - - // Act - sut.RecordCodeLocation(type, key, unit, stackLevel, timestamp); - - // Assert - var startOfDay = timestamp.GetDayBucketKey(); - sut._seenLocations.Keys.Should().Contain(startOfDay); - - var metaKey = new MetricResourceIdentifier(type, key, unit); - sut._seenLocations[startOfDay].Should().Contain(metaKey); - - sut._pendingLocations.Keys.Should().Contain(startOfDay); - sut._pendingLocations[startOfDay].Should().NotBeNull(); - sut._pendingLocations[startOfDay].Keys.Should().Contain(metaKey); - sut._pendingLocations[startOfDay][metaKey].Should().NotBeNull(); - sut._pendingLocations[startOfDay][metaKey].Function.Should().Be( - $"void {nameof(MetricAggregatorTests)}.{nameof(RecordCodeLocation_AddsMetricToSeenAndPendingLocations)}()" - ); - } - - [Fact] - public void RecordCodeLocation_RecordsLocationOnlyOnce() - { - // Arrange - var type = MetricType.Counter; - var key = "counter_key"; - var unit = MeasurementUnit.None; - var stackLevel = 1; - var timestamp = DateTimeOffset.Now; - var sut = _fixture.GetSut(); - - // Act - sut.RecordCodeLocation(type, key, unit, stackLevel, timestamp); - sut.RecordCodeLocation(type, key, unit, stackLevel, timestamp); - - // Assert - sut._pendingLocations.SelectMany(x => x.Value).Count().Should().Be(1); - } - - [Fact] - public void RecordCodeLocation_BadStackLevel_AddsToSeenButNotPending() - { - // Arrange - var type = MetricType.Counter; - var key = "counter_key"; - var unit = MeasurementUnit.None; - var stackLevel = short.MaxValue; - var timestamp = DateTimeOffset.Now; - var sut = _fixture.GetSut(); - - // Act - sut.RecordCodeLocation(type, key, unit, stackLevel, timestamp); - - // Assert - var startOfDay = timestamp.GetDayBucketKey(); - sut._seenLocations.Keys.Should().Contain(startOfDay); - - var metaKey = new MetricResourceIdentifier(type, key, unit); - sut._seenLocations[startOfDay].Should().Contain(metaKey); - - sut._pendingLocations.SelectMany(x => x.Value).Should().BeEmpty(); - } - - [Fact] - public void Dispose_OnlyExecutesOnce() - { - // Arrange - _fixture.Logger.IsEnabled(Arg.Any()).Returns(true); - var sut = _fixture.GetSut(); - - // Act - sut.Dispose(); - sut.Dispose(); - sut.Dispose(); - - // Assert - _fixture.Logger.Received(2).Log(SentryLevel.Debug, MetricAggregator.AlreadyDisposedMessage, null); - } - - [Fact] - public void Dispose_StopsLoopTask() - { - // Arrange - _fixture.Logger.IsEnabled(Arg.Any()).Returns(true); - _fixture.DisableFlushLoop = false; - _fixture.Options.ShutdownTimeout = TimeSpan.Zero; - var sut = _fixture.GetSut(); - - // Act - sut.Dispose(); - - // Assert - _fixture.Logger.Received(1).Log(SentryLevel.Debug, MetricAggregator.DisposingMessage, null); - sut._loopTask.Status.Should().BeOneOf(TaskStatus.RanToCompletion, TaskStatus.Faulted); - } - - [Fact] - public async Task Dispose_SwallowsException() - { - // Arrange - _fixture.CancellationTokenSource.Dispose(); - _fixture.DisableFlushLoop = false; - var sut = _fixture.GetSut(); - - // We expect an exception here, because we disposed the cancellation token source - await Assert.ThrowsAsync(() => sut._loopTask); - - // Act - await sut.DisposeAsync(); - - // Assert - sut._loopTask.Status.Should().Be(TaskStatus.Faulted); - } - - [Fact] - public async Task Cancel_NonZeroTimeout_SchedulesShutdown() - { - // Arrange - _fixture.Logger.IsEnabled(Arg.Any()).Returns(true); - _fixture.DisableFlushLoop = false; - _fixture.Options.ShutdownTimeout = TimeSpan.FromSeconds(1); - var sut = _fixture.GetSut(); - - // Act - await _fixture.CancellationTokenSource.CancelAsync(); -#pragma warning disable xUnit1031 - sut._loopTask.Wait(10000); -#pragma warning restore xUnit1031 - - // Assert - _fixture.Logger.Received(1).Log(SentryLevel.Debug, MetricAggregator.ShutdownScheduledMessage, null, Arg.Any()); - } - - [Fact] - public async Task Cancel_ZeroTimeout_ShutdownImmediately() - { - // Arrange - _fixture.Logger.IsEnabled(Arg.Any()).Returns(true); - _fixture.DisableFlushLoop = false; - _fixture.Options.ShutdownTimeout = TimeSpan.Zero; - var sut = _fixture.GetSut(); - - // Act - await _fixture.CancellationTokenSource.CancelAsync(); -#pragma warning disable xUnit1031 - sut._loopTask.Wait(10000); -#pragma warning restore xUnit1031 - - // Assert - _fixture.Logger.Received(1).Log(SentryLevel.Debug, MetricAggregator.ShutdownImmediatelyMessage, null); - } - - [Fact] - public void Emit_ActiveSpan_AppliesSpanTags() - { - // Arrange - _fixture.Options.Release = "test_release"; - _fixture.Options.Environment = "test_env"; - - var tx = Substitute.For(); - tx.TransactionName = "test_name"; - - _fixture.DisableFlushLoop = false; - _fixture.MetricHub.GetSpan().Returns(tx); - var sut = _fixture.GetSut(); - - // Act - sut.Increment("test_key"); - - // Assert - var bucket = sut.Buckets.SingleOrDefault().Value; - var metric = bucket.SingleOrDefault().Value; - metric.Should().BeOfType(); - var counter = (metric as CounterMetric)!; - counter.Key.Should().Be("test_key"); - counter.Tags["release"].Should().Be("test_release"); - counter.Tags["environment"].Should().Be("test_env"); - counter.Tags["transaction"].Should().Be("test_name"); - } - - [Fact] - public void Emit_ActiveSpan_SpanAggregates() - { - // Arrange - var hub = Substitute.For(); - var tx = new TransactionTracer(hub, "test_name", "test_op"); - tx.Release = "test_release"; - tx.Environment = "test_env"; - - var span = new SpanTracer(hub, tx, null, SentryId.Create(), "test_op"); - - _fixture.DisableFlushLoop = false; - _fixture.MetricHub.GetSpan().Returns(span); - var sut = _fixture.GetSut(); - - // Act - sut.Increment("test_key", 3); - sut.Increment("test_key", 5); - - // Assert - tx.MetricsSummary.Measurements.Should().BeEmpty(); - - var bucketKey = MetricHelper.GetMetricBucketKey(MetricType.Counter, "test_key", MeasurementUnit.None, null); - span.MetricsSummary.Measurements.Should().ContainKey(bucketKey); - var metric = span.MetricsSummary.Measurements[bucketKey]; - metric.Min.Should().Be(3); - metric.Max.Should().Be(5); - metric.Sum.Should().Be(8); - metric.Count.Should().Be(2); - } - - [Fact] - public void Emit_ActiveSpan_TransactionAggregates() - { - // Arrange - var hub = Substitute.For(); - var tx = new TransactionTracer(hub, "test_name", "test_op"); - tx.Release = "test_release"; - tx.Environment = "test_env"; - - _fixture.DisableFlushLoop = false; - _fixture.MetricHub.GetSpan().Returns(tx); - var sut = _fixture.GetSut(); - - // Act - sut.Increment("test_key", 3); - sut.Increment("test_key", 5); - - // Assert - var bucketKey = MetricHelper.GetMetricBucketKey(MetricType.Counter, "test_key", MeasurementUnit.None, null); - tx.MetricsSummary.Measurements.Should().ContainKey(bucketKey); - var metric = tx.MetricsSummary.Measurements[bucketKey]; - metric.Min.Should().Be(3); - metric.Max.Should().Be(5); - metric.Sum.Should().Be(8); - metric.Count.Should().Be(2); - } - - [Fact] - public async Task FlushAsync_FlushesPendingLocations() - { - // Arrange - var type = MetricType.Counter; - var key = "counter_key"; - var unit = MeasurementUnit.None; - var stackLevel = 1; - var timestamp = DateTimeOffset.Now.Subtract(TimeSpan.FromSeconds(20)); - var sut = _fixture.GetSut(); - sut.RecordCodeLocation(type, key, unit, stackLevel, timestamp); - - // Act - await sut.FlushAsync(); - - // Assert - _fixture.MetricHub.Received(1).CaptureCodeLocations(Arg.Any()); - } - - [Fact] - public async Task FlushAsync_Cancel_Exists() - { - // Arrange - _fixture.DisableFlushLoop = false; - _fixture.Logger.IsEnabled(Arg.Any()).Returns(true); - var cancellationTokenSource = new CancellationTokenSource(); - await cancellationTokenSource.CancelAsync(); - var sut = _fixture.GetSut(); - - // Act - await sut.FlushAsync(true, cancellationTokenSource.Token); - - // Assert - _fixture.Logger.Received(1).Log(SentryLevel.Info, MetricAggregator.FlushShutdownMessage, null); - } - - [Fact] - public void ClearStaleLocations_SameDay_NoClear() - { - // Arrange - var time = new DateTimeOffset(2000, 1, 1, 12, 0, 0, TimeSpan.Zero); - - var sut = _fixture.GetSut(); - sut._lastClearedStaleLocations = time.GetDayBucketKey(); - - var type = MetricType.Counter; - var key = "counter_key"; - var unit = MeasurementUnit.None; - var stackLevel = 1; - sut.RecordCodeLocation(type, key, unit, stackLevel, time.Subtract(TimeSpan.FromDays(1))); - - // Act - sut.ClearStaleLocations(time); - - // Assert - // (You need some way to check that "_seenLocations" are not modified. This is stubbed in as "SeenLocations") - sut._seenLocations.Should().NotBeEmpty(); - } - - [Fact] - public void ClearStaleLocations_GraceTime_NoClear() - { - // Arrange - var time = new DateTimeOffset(2000, 1, 1, 0, 0, 30, TimeSpan.Zero); - - var sut = _fixture.GetSut(); - sut._lastClearedStaleLocations = time.GetDayBucketKey() - 1; - - var type = MetricType.Counter; - var key = "counter_key"; - var unit = MeasurementUnit.None; - var stackLevel = 1; - sut.RecordCodeLocation(type, key, unit, stackLevel, time.Subtract(TimeSpan.FromDays(1))); - - // Act - sut.ClearStaleLocations(time); - - // Assert - // (You need some way to check that "_seenLocations" are not modified. This is stubbed in as "SeenLocations") - sut._seenLocations.Should().NotBeEmpty(); - } - - [Fact] - public void ClearStaleLocations_AfterGraceTime_Clear() - { - // Arrange - var time = new DateTimeOffset(2000, 1, 1, 0, 1, 30, TimeSpan.Zero); - - var sut = _fixture.GetSut(); - sut._lastClearedStaleLocations = time.GetDayBucketKey() - 1; - - var type = MetricType.Counter; - var key = "counter_key"; - var unit = MeasurementUnit.None; - var stackLevel = 1; - sut.RecordCodeLocation(type, key, unit, stackLevel, time.Subtract(TimeSpan.FromDays(1))); - - // Act - sut.ClearStaleLocations(time); - - // Assert - // (You need some way to check that "_seenLocations" are not modified. This is stubbed in as "SeenLocations") - sut._seenLocations.Should().BeEmpty(); - } -} diff --git a/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet8_0.DotNet.verified.txt b/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet8_0.DotNet.verified.txt index 9e3f2681b5..dfbc55fc32 100644 --- a/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet8_0.DotNet.verified.txt +++ b/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet8_0.DotNet.verified.txt @@ -34,15 +34,5 @@ Args: [ SentryDiagnosticListenerIntegration ] - }, - { - Message: Registering integration: '{0}'., - Args: [ - SystemDiagnosticsMetricsIntegration - ] - }, - { - Level: info, - Message: System.Diagnostics.Metrics Integration is disabled because no listeners are configured. } ] \ No newline at end of file diff --git a/test/Sentry.Tests/SentryOptionsTests.cs b/test/Sentry.Tests/SentryOptionsTests.cs index d9e8194039..cd20a5c7d3 100644 --- a/test/Sentry.Tests/SentryOptionsTests.cs +++ b/test/Sentry.Tests/SentryOptionsTests.cs @@ -303,17 +303,6 @@ public void DisableTaskUnobservedTaskExceptionCapture_UnobservedTaskExceptionInt p => p is UnobservedTaskExceptionIntegration); } -#if NET8_0_OR_GREATER - [Fact] - public void DisableSystemDiagnosticsMetricsIntegration_RemovesSystemDiagnosticsMetricsIntegration() - { - var sut = new SentryOptions(); - sut.DisableSystemDiagnosticsMetricsIntegration(); - Assert.DoesNotContain(sut.Integrations, - p => p.GetType() == typeof(SystemDiagnosticsMetricsIntegration)); - } -#endif - [Fact] public void AddIntegration_StoredInOptions() { diff --git a/test/Sentry.Tests/TimingTests.cs b/test/Sentry.Tests/TimingTests.cs deleted file mode 100644 index 941b0d41c3..0000000000 --- a/test/Sentry.Tests/TimingTests.cs +++ /dev/null @@ -1,143 +0,0 @@ -using Sentry.Protocol.Metrics; - -namespace Sentry.Tests; - -public class TimingTests -{ - private class Fixture - { - public readonly IHub Hub; - public IMetricHub MetricHub { get; } - public SentryOptions Options { get; } - public IDiagnosticLogger Logger { get; } - public MetricAggregator MetricAggregator { get; } - - public string Key { get; set; } = "key"; - - public MeasurementUnit.Duration Unit { get; set; } = MeasurementUnit.Duration.Second; - public Dictionary Tags { get; set; } = new(); - - public Fixture() - { - Hub = Substitute.For(); - MetricHub = Substitute.For(); - Logger = Substitute.For(); - Logger.IsEnabled(Arg.Any()).Returns(true); - Options = new() - { - Debug = true, - DiagnosticLogger = Logger - }; - MetricAggregator = Substitute.For(Options, MetricHub, null, true); - } - - public Timing GetSut() => new(MetricAggregator, MetricHub, Options, Key, Unit, Tags, 1); - } - private readonly Fixture _fixture = new(); - - [Fact] - public void Constructor_CreatesSpan() - { - // Arrange - _fixture.Tags = new Dictionary { { "tag1", "value1" } }; - - var span = new TransactionTracer(_fixture.Hub, Timing.OperationName, _fixture.Key); - _fixture.MetricHub.StartSpan(Timing.OperationName, _fixture.Key).Returns(span); - - // Act - _ = _fixture.GetSut(); - - // Assert - _fixture.MetricHub.Received(1).StartSpan(Timing.OperationName, _fixture.Key); - span.Tags.Should().BeEquivalentTo(_fixture.Tags); - span.Origin.Should().Be(Timing.MetricsOrigin); - } - - [Fact] - public void Constructor_RecordsCodeLocation() - { - // Act - var timing = _fixture.GetSut(); - - // Assert - _fixture.MetricAggregator.Received(1).RecordCodeLocation(MetricType.Distribution, _fixture.Key, MeasurementUnit.Duration.Second, 2, timing._startTime); - } - - [Fact] - public void Constructor_StartsStopwatch() - { - // Act - var timing = _fixture.GetSut(); - - // Assert - timing._stopwatch.IsRunning.Should().BeTrue(); - } - - [Fact] - public void Dispose_StopsStopwatch() - { - // Arrange - var timing = _fixture.GetSut(); - - // Act - var stopwatch = timing._stopwatch; - timing.Dispose(); - - // Assert - stopwatch.IsRunning.Should().BeFalse(); - } - - [Theory] - [InlineData(MeasurementUnit.Duration.Week, 7)] - [InlineData(MeasurementUnit.Duration.Day, 1)] - [InlineData(MeasurementUnit.Duration.Hour, 1 / 24.0)] - [InlineData(MeasurementUnit.Duration.Minute, 1 / (24.0 * 60))] - [InlineData(MeasurementUnit.Duration.Second, 1 / (24.0 * 60 * 60))] - [InlineData(MeasurementUnit.Duration.Millisecond, 1 / (24.0 * 60 * 60 * 1000))] - [InlineData(MeasurementUnit.Duration.Microsecond, 1 / (24.0 * 60 * 60 * 1000000))] - [InlineData(MeasurementUnit.Duration.Nanosecond, 1 / (24.0 * 60 * 60 * 1000000000))] - public void DisposeInternal_ValidUnits_RecordsTiming(MeasurementUnit.Duration unit, double expectedRatio) - { - // Arrange - _fixture.Unit = unit; - var timing = _fixture.GetSut(); - var elapsed = TimeSpan.FromDays(1); // 1 day - - // Act - timing.DisposeInternal(elapsed); - - // Assert - _fixture.MetricAggregator.Received(1).Timing( - Arg.Any(), - elapsed.TotalDays / expectedRatio, // Expected value - unit, - Arg.Any>(), - Arg.Any()); - } - - [Fact] - public void Dispose_InvalidUnit_LogsError() - { - // Arrange - _fixture.Unit = (MeasurementUnit.Duration)int.MaxValue; - var timing = _fixture.GetSut(); - - // Act - timing.Dispose(); - - // Assert - _fixture.MetricAggregator.Received(0).Timing( - Arg.Any(), - Arg.Any(), - Arg.Any(), - Arg.Any>(), - Arg.Any() - ); - _fixture.Logger.Received(1).Log( - SentryLevel.Error, - "Error capturing timing '{0}'", - Arg.Any(), - _fixture.Key - ); - } -} From 936752611cecfba3be0f50e1c6df76be6af9d0a0 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 30 Oct 2024 15:31:59 +1300 Subject: [PATCH 2/4] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3f642415a..47abaca587 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - `SentryOptions.EnableTracing` has been removed. Instead, tracing should be enabled or disabled by setting the `SentryOptions.TracesSampleRate` or by using `SentryOptions.TracesSampler` to configure a sampling function ([#3569](https://github.com/getsentry/sentry-dotnet/pull/3569)) - The `FailedRequestTargets`, `TagFilters` and `TracePropagationTargets` options have all been changed from `SubstringOrRegexPattern` to `IList` ([#3566](https://github.com/getsentry/sentry-dotnet/pull/3566)) - `Scope.Transaction` is now always stored as an `AsyncLocal` also in [Global Mode](https://docs.sentry.io/platforms/dotnet/configuration/options/#is-global-mode-enabled), to prevent auto-instrumented spans from the UI ending up parented to transactions from a background task (or vice versa). ([#3596](https://github.com/getsentry/sentry-dotnet/pull/3596)) +- Sentry's Experimental Metrics feature has been deprecated and removed from the SDK. ([#3718](https://github.com/getsentry/sentry-dotnet/pull/3718)) ## Unreleased ## Unreleased From 1cee0a99e34ab8a26dad00fe79c397076a30ae12 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 30 Oct 2024 16:14:45 +1300 Subject: [PATCH 3/4] Update solution filters --- .generated.NoMobile.sln | 10 ++-------- Sentry-CI-Build-Linux.slnf | 1 - Sentry-CI-Build-Windows.slnf | 1 - Sentry-CI-Build-macOS.slnf | 4 ++-- SentryCore.slnf | 1 - SentryNoMobile.slnf | 1 - scripts/generate-solution-filters-config.yaml | 1 - 7 files changed, 4 insertions(+), 15 deletions(-) diff --git a/.generated.NoMobile.sln b/.generated.NoMobile.sln index 02c720c31a..82631572fc 100644 --- a/.generated.NoMobile.sln +++ b/.generated.NoMobile.sln @@ -155,8 +155,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FastSerialization", "module EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Console.Native", "samples\Sentry.Samples.Console.Native\Sentry.Samples.Console.Native.csproj", "{FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Console.Metrics", "samples\Sentry.Samples.Console.Metrics\Sentry.Samples.Console.Metrics.csproj", "{BD2D08FC-8675-4157-A73C-D75F6A3856D3}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.MacOS", "samples\Sentry.Samples.MacOS\Sentry.Samples.MacOS.csproj", "{5B100CC0-1A78-407E-A5A5-94BC06D67461}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.Hangfire", "samples\Sentry.Samples.Hangfire\Sentry.Samples.Hangfire.csproj", "{407C477D-69C0-4B02-8A68-EE6B5A81C696}" @@ -165,7 +163,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Hangfire", "src\Sent EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Hangfire.Tests", "test\Sentry.Hangfire.Tests\Sentry.Hangfire.Tests.csproj", "{46E40BE8-1AB0-4846-B0A2-A40AD0272C64}" EndProject -Project("{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sentry.Samples.AspNetCore.WebAPI.Profiling", "samples\Sentry.Samples.AspNetCore.WebAPI.Profiling\Sentry.Samples.AspNetCore.WebAPI.Profiling.csproj", "{A5B26C14-7313-4EDC-91E3-287F9374AB75}" +Project("{00000000-0000-0000-0000-000000000000}") = "Sentry.Samples.AspNetCore.WebAPI.Profiling", "samples\Sentry.Samples.AspNetCore.WebAPI.Profiling\Sentry.Samples.AspNetCore.WebAPI.Profiling.csproj", "{A5B26C14-7313-4EDC-91E3-287F9374AB75}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "root", "root", "{233D34AB-970E-4913-AA1E-172E833FB5B2}" ProjectSection(SolutionItems) = preProject @@ -276,6 +274,7 @@ Global {4E0DC405-C372-4396-A5DF-F6AA108DA01C}.Release|Any CPU.Build.0 = Release|Any CPU {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B175EC8-6B64-4345-A158-091CB8876077}.Debug|Any CPU.Deploy.0 = Debug|Any CPU {9B175EC8-6B64-4345-A158-091CB8876077}.Release|Any CPU.ActiveCfg = Release|Any CPU {9B175EC8-6B64-4345-A158-091CB8876077}.Release|Any CPU.Build.0 = Release|Any CPU {EE0DC846-52F3-46AF-BC0D-DEF81150CEC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU @@ -470,10 +469,6 @@ Global {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Debug|Any CPU.Build.0 = Debug|Any CPU {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.ActiveCfg = Release|Any CPU {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2}.Release|Any CPU.Build.0 = Release|Any CPU - {BD2D08FC-8675-4157-A73C-D75F6A3856D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {BD2D08FC-8675-4157-A73C-D75F6A3856D3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {BD2D08FC-8675-4157-A73C-D75F6A3856D3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {BD2D08FC-8675-4157-A73C-D75F6A3856D3}.Release|Any CPU.Build.0 = Release|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Debug|Any CPU.Build.0 = Debug|Any CPU {5B100CC0-1A78-407E-A5A5-94BC06D67461}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -574,7 +569,6 @@ Global {67269916-C417-4CEE-BD7D-CA66C3830AEE} = {A3CCA27E-4DF8-479D-833C-CAA0950715AA} {8032310D-3C06-442C-A318-F365BCC4C804} = {A3CCA27E-4DF8-479D-833C-CAA0950715AA} {FC8AEABA-1A40-4891-9EBA-4B6A1F7244B2} = {21B42F60-5802-404E-90F0-AEBCC56760C0} - {BD2D08FC-8675-4157-A73C-D75F6A3856D3} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {5B100CC0-1A78-407E-A5A5-94BC06D67461} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {407C477D-69C0-4B02-8A68-EE6B5A81C696} = {21B42F60-5802-404E-90F0-AEBCC56760C0} {EADF25F5-8D02-4747-AB54-5F2BAA648471} = {230B9384-90FD-4551-A5DE-1A5C197F25B6} diff --git a/Sentry-CI-Build-Linux.slnf b/Sentry-CI-Build-Linux.slnf index fb30347797..d772922ad2 100644 --- a/Sentry-CI-Build-Linux.slnf +++ b/Sentry-CI-Build-Linux.slnf @@ -15,7 +15,6 @@ "samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj", "samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj", "samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj", - "samples\\Sentry.Samples.Console.Metrics\\Sentry.Samples.Console.Metrics.csproj", "samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj", "samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj", "samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj", diff --git a/Sentry-CI-Build-Windows.slnf b/Sentry-CI-Build-Windows.slnf index 489fde2fc7..061bba0a20 100644 --- a/Sentry-CI-Build-Windows.slnf +++ b/Sentry-CI-Build-Windows.slnf @@ -16,7 +16,6 @@ "samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj", "samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj", "samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj", - "samples\\Sentry.Samples.Console.Metrics\\Sentry.Samples.Console.Metrics.csproj", "samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj", "samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj", "samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj", diff --git a/Sentry-CI-Build-macOS.slnf b/Sentry-CI-Build-macOS.slnf index c96b6d7382..0ed945bc3b 100644 --- a/Sentry-CI-Build-macOS.slnf +++ b/Sentry-CI-Build-macOS.slnf @@ -35,10 +35,10 @@ "samples\\Sentry.Samples.OpenTelemetry.Console\\Sentry.Samples.OpenTelemetry.Console.csproj", "samples\\Sentry.Samples.Serilog\\Sentry.Samples.Serilog.csproj", "src\\Sentry.Android.AssemblyReader\\Sentry.Android.AssemblyReader.csproj", + "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.AspNetCore.Blazor.WebAssembly\\Sentry.AspNetCore.Blazor.WebAssembly.csproj", "src\\Sentry.AspNetCore.Grpc\\Sentry.AspNetCore.Grpc.csproj", "src\\Sentry.AspNetCore\\Sentry.AspNetCore.csproj", - "src\\Sentry.AspNet\\Sentry.AspNet.csproj", "src\\Sentry.Azure.Functions.Worker\\Sentry.Azure.Functions.Worker.csproj", "src\\Sentry.Bindings.Android\\Sentry.Bindings.Android.csproj", "src\\Sentry.Bindings.Cocoa\\Sentry.Bindings.Cocoa.csproj", @@ -79,4 +79,4 @@ "test\\SingleFileTestApp\\SingleFileTestApp.csproj" ] } -} \ No newline at end of file +} diff --git a/SentryCore.slnf b/SentryCore.slnf index b56bac9e01..d2eb48df03 100644 --- a/SentryCore.slnf +++ b/SentryCore.slnf @@ -4,7 +4,6 @@ "projects": [ "benchmarks\\Sentry.Benchmarks\\Sentry.Benchmarks.csproj", "samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj", - "samples\\Sentry.Samples.Console.Metrics\\Sentry.Samples.Console.Metrics.csproj", "samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj", "src\\Sentry.Profiling\\Sentry.Profiling.csproj", "src\\Sentry\\Sentry.csproj", diff --git a/SentryNoMobile.slnf b/SentryNoMobile.slnf index 4603d550c3..7c5571e30a 100644 --- a/SentryNoMobile.slnf +++ b/SentryNoMobile.slnf @@ -14,7 +14,6 @@ "samples\\Sentry.Samples.Azure.Functions.Worker\\Sentry.Samples.Azure.Functions.Worker.csproj", "samples\\Sentry.Samples.Console.Basic\\Sentry.Samples.Console.Basic.csproj", "samples\\Sentry.Samples.Console.Customized\\Sentry.Samples.Console.Customized.csproj", - "samples\\Sentry.Samples.Console.Metrics\\Sentry.Samples.Console.Metrics.csproj", "samples\\Sentry.Samples.Console.Native\\Sentry.Samples.Console.Native.csproj", "samples\\Sentry.Samples.Console.Profiling\\Sentry.Samples.Console.Profiling.csproj", "samples\\Sentry.Samples.EntityFramework\\Sentry.Samples.EntityFramework.csproj", diff --git a/scripts/generate-solution-filters-config.yaml b/scripts/generate-solution-filters-config.yaml index adc0d61fd6..1483f02fd1 100644 --- a/scripts/generate-solution-filters-config.yaml +++ b/scripts/generate-solution-filters-config.yaml @@ -119,7 +119,6 @@ filterConfigs: patterns: - "**/Sentry.Benchmarks.csproj" - "**/Sentry.Samples.Console.Basic.csproj" - - "**/Sentry.Samples.Console.Metrics.csproj" - "**/Sentry.Samples.Console.Profiling.csproj" - "**/Sentry.Profiling.csproj" - "**/Sentry.csproj" From b762f81ad26c6c835467943eaa3ef7924a810a43 Mon Sep 17 00:00:00 2001 From: James Crosswell Date: Wed, 30 Oct 2024 19:25:24 +1300 Subject: [PATCH 4/4] Update SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet8_0.Windows.DotNet.verified.txt --- ...ly_registered.DotNet8_0.Windows.DotNet.verified.txt | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet8_0.Windows.DotNet.verified.txt b/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet8_0.Windows.DotNet.verified.txt index e14cfaf075..10eaeaf749 100644 --- a/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet8_0.Windows.DotNet.verified.txt +++ b/test/Sentry.Tests/SentryOptionsTests.Integrations_default_ones_are_properly_registered.DotNet8_0.Windows.DotNet.verified.txt @@ -40,15 +40,5 @@ Args: [ WinUIUnhandledExceptionIntegration ] - }, - { - Message: Registering integration: '{0}'., - Args: [ - SystemDiagnosticsMetricsIntegration - ] - }, - { - Level: info, - Message: System.Diagnostics.Metrics Integration is disabled because no listeners are configured. } ] \ No newline at end of file