diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2e14cf85..05d6fc61 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,6 @@ jobs: uses: actions/setup-dotnet@v4.0.0 with: dotnet-version: 8.0.x - dotnet-quality: 'preview' - name: Build run: dotnet build src --configuration Release - name: Upload packages diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4b845ba1..60a0b61c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,6 @@ jobs: uses: actions/setup-dotnet@v4.0.0 with: dotnet-version: 8.0.x - dotnet-quality: 'preview' - name: Build run: dotnet build src --configuration Release - name: Sign NuGet packages diff --git a/src/NServiceBus.Metrics.AcceptanceTests/DeterministicGuid.cs b/src/NServiceBus.Metrics.AcceptanceTests/DeterministicGuid.cs deleted file mode 100644 index 83e42712..00000000 --- a/src/NServiceBus.Metrics.AcceptanceTests/DeterministicGuid.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace NServiceBus.Utils -{ - using System; - using System.Security.Cryptography; - using System.Text; - - static class DeterministicGuid - { - public static Guid Create(params object[] data) - { - // use MD5 hash to get a 16-byte hash of the string - using (var provider = MD5.Create()) - { - var inputBytes = Encoding.Default.GetBytes(string.Concat(data)); - var hashBytes = provider.ComputeHash(inputBytes); - // generate a guid from the hash: - return new Guid(hashBytes); - } - } - } -} \ No newline at end of file diff --git a/src/NServiceBus.Metrics.AcceptanceTests/EndpointTemplates/DefaultServer.cs b/src/NServiceBus.Metrics.AcceptanceTests/EndpointTemplates/DefaultServer.cs index bfaf9393..5da5fae0 100644 --- a/src/NServiceBus.Metrics.AcceptanceTests/EndpointTemplates/DefaultServer.cs +++ b/src/NServiceBus.Metrics.AcceptanceTests/EndpointTemplates/DefaultServer.cs @@ -1,7 +1,6 @@ namespace NServiceBus.AcceptanceTests.EndpointTemplates { using System; - using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using AcceptanceTesting.Customization; @@ -9,30 +8,16 @@ public class DefaultServer : IEndpointSetupTemplate { - public DefaultServer() - { - typesToInclude = []; - } - - public DefaultServer(List typesToInclude) - { - this.typesToInclude = typesToInclude; - } - public async Task GetConfiguration(RunDescriptor runDescriptor, EndpointCustomizationConfiguration endpointConfiguration, Func configurationBuilderCustomization) { - var types = endpointConfiguration.GetTypesScopedByTestClass(); - - typesToInclude.AddRange(types); - var configuration = new EndpointConfiguration(endpointConfiguration.EndpointName); - configuration.TypesToIncludeInScan(typesToInclude); configuration.EnableInstallers(); configuration.UsePersistence(); var storageDir = Path.Combine(NServiceBusAcceptanceTest.StorageRootDir, NUnit.Framework.TestContext.CurrentContext.Test.ID); configuration.UseTransport(new LearningTransport { StorageDirectory = storageDir }); + configuration.UseSerialization(); var recoverability = configuration.Recoverability(); recoverability.Delayed(delayed => delayed.NumberOfRetries(0)); @@ -42,9 +27,10 @@ public async Task GetConfiguration(RunDescriptor runDescr await configurationBuilderCustomization(configuration); + // scan types at the end so that all types used by the configuration have been loaded into the AppDomain + configuration.ScanTypesForTest(endpointConfiguration); + return configuration; } - - List typesToInclude; } } \ No newline at end of file diff --git a/src/NServiceBus.Metrics.AcceptanceTests/EndpointTemplates/EndpointCustomizationConfigurationExtensions.cs b/src/NServiceBus.Metrics.AcceptanceTests/EndpointTemplates/EndpointCustomizationConfigurationExtensions.cs deleted file mode 100644 index 9b0b73cb..00000000 --- a/src/NServiceBus.Metrics.AcceptanceTests/EndpointTemplates/EndpointCustomizationConfigurationExtensions.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace NServiceBus.AcceptanceTests.EndpointTemplates -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using AcceptanceTesting.Support; - using Hosting.Helpers; - - public static class EndpointCustomizationConfigurationExtensions - { - public static IEnumerable GetTypesScopedByTestClass(this EndpointCustomizationConfiguration endpointConfiguration) - { - var assemblies = new AssemblyScanner().GetScannableAssemblies(); - - var types = assemblies.Assemblies - //exclude all test types by default - .Where(a => - { - var references = a.GetReferencedAssemblies(); - - return references.All(an => an.Name != "nunit.framework"); - }) - .SelectMany(a => a.GetTypes()); - - types = types.Union(GetNestedTypeRecursive(endpointConfiguration.BuilderType.DeclaringType, endpointConfiguration.BuilderType)); - - types = types.Union(endpointConfiguration.TypesToInclude); - - return types.Where(t => !endpointConfiguration.TypesToExclude.Contains(t)).ToList(); - } - - static IEnumerable GetNestedTypeRecursive(Type rootType, Type builderType) - { - if (rootType == null) - { - throw new InvalidOperationException("Make sure you nest the endpoint infrastructure inside the TestFixture as nested classes"); - } - - yield return rootType; - - if (typeof(IEndpointConfigurationFactory).IsAssignableFrom(rootType) && rootType != builderType) - { - yield break; - } - - foreach (var nestedType in rootType.GetNestedTypes(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).SelectMany(t => GetNestedTypeRecursive(t, builderType))) - { - yield return nestedType; - } - } - } -} \ No newline at end of file diff --git a/src/NServiceBus.Metrics.AcceptanceTests/NServiceBus.Metrics.AcceptanceTests.csproj b/src/NServiceBus.Metrics.AcceptanceTests/NServiceBus.Metrics.AcceptanceTests.csproj index f729d397..45376986 100644 --- a/src/NServiceBus.Metrics.AcceptanceTests/NServiceBus.Metrics.AcceptanceTests.csproj +++ b/src/NServiceBus.Metrics.AcceptanceTests/NServiceBus.Metrics.AcceptanceTests.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/NServiceBus.Metrics.AcceptanceTests/PayloadAssert.cs b/src/NServiceBus.Metrics.AcceptanceTests/PayloadAssert.cs deleted file mode 100644 index 39c2e3ef..00000000 --- a/src/NServiceBus.Metrics.AcceptanceTests/PayloadAssert.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Generic; -using System.Text.RegularExpressions; -using NUnit.Framework; - -public static class PayloadAssert -{ - public static void ContainsMeters(string payload, List meters) - { - var matches = Regex.Matches(payload, @"""Name"":""(.+?(?=""))"); - var foundMeters = new List(); - foreach (Match match in matches) - { - foundMeters.Add(match.Groups[1].Value); - } - - CollectionAssert.AreEquivalent(foundMeters, meters); - } -} \ No newline at end of file diff --git a/src/NServiceBus.Metrics.AcceptanceTests/ScenarioDescriptors/TypeScanner.cs b/src/NServiceBus.Metrics.AcceptanceTests/ScenarioDescriptors/TypeScanner.cs deleted file mode 100644 index aeafe587..00000000 --- a/src/NServiceBus.Metrics.AcceptanceTests/ScenarioDescriptors/TypeScanner.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace NServiceBus.AcceptanceTests.ScenarioDescriptors -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Reflection; - using Hosting.Helpers; - - public class TypeScanner - { - static IEnumerable AvailableAssemblies - { - get - { - if (assemblies == null) - { - var result = new AssemblyScanner().GetScannableAssemblies(); - - assemblies = result.Assemblies.Where(a => - { - var references = a.GetReferencedAssemblies(); - - return references.All(an => an.Name != "nunit.framework"); - }).ToList(); - } - - return assemblies; - } - } - - public static IEnumerable GetAllTypesAssignableTo() - { - return AvailableAssemblies.SelectMany(a => a.GetTypes()) - .Where(t => typeof(T).IsAssignableFrom(t) && t != typeof(T)) - .ToList(); - } - - static List assemblies; - } -} \ No newline at end of file diff --git a/src/NServiceBus.Metrics.AcceptanceTests/Tests/SubscriptionBehaviorExtensions.cs b/src/NServiceBus.Metrics.AcceptanceTests/Tests/SubscriptionBehaviorExtensions.cs deleted file mode 100644 index 30b7a81a..00000000 --- a/src/NServiceBus.Metrics.AcceptanceTests/Tests/SubscriptionBehaviorExtensions.cs +++ /dev/null @@ -1,70 +0,0 @@ -namespace NServiceBus.Metrics.AcceptanceTests -{ - using System; - using System.Linq; - using System.Threading.Tasks; - using AcceptanceTesting; - using Pipeline; - using Microsoft.Extensions.DependencyInjection; - using Transport; - - static class SubscriptionBehaviorExtensions - { - public class SubscriptionEventArgs - { - /// - /// The address of the subscriber. - /// - public string SubscriberReturnAddress { get; set; } - - /// - /// The type of message the client subscribed to. - /// - public string MessageType { get; set; } - } - - public static void OnEndpointSubscribed(this EndpointConfiguration b, Action action) where TContext : ScenarioContext - { - b.Pipeline.Register(builder => - { - var context = builder.GetRequiredService(); - return new SubscriptionBehavior(action, context); - }, "Provides notifications when endpoints subscribe"); - } - - class SubscriptionBehavior : IBehavior where TContext : ScenarioContext - { - Action action; - TContext scenarioContext; - - public SubscriptionBehavior(Action action, TContext scenarioContext) - { - this.action = action; - this.scenarioContext = scenarioContext; - } - - public async Task Invoke(ITransportReceiveContext context, Func next) - { - await next(context).ConfigureAwait(false); - var subscriptionMessageType = GetSubscriptionMessageTypeFrom(context.Message); - if (subscriptionMessageType != null) - { - if (!context.Message.Headers.TryGetValue(Headers.SubscriberTransportAddress, out var returnAddress)) - { - context.Message.Headers.TryGetValue(Headers.ReplyToAddress, out returnAddress); - } - action(new SubscriptionEventArgs - { - MessageType = subscriptionMessageType, - SubscriberReturnAddress = returnAddress - }, scenarioContext); - } - } - - static string GetSubscriptionMessageTypeFrom(IncomingMessage msg) - { - return (from header in msg.Headers where header.Key == Headers.SubscriptionMessageType select header.Value).FirstOrDefault(); - } - } - } -} \ No newline at end of file diff --git a/src/NServiceBus.Metrics.AcceptanceTests/Tests/TagExtensions.cs b/src/NServiceBus.Metrics.AcceptanceTests/Tests/TagExtensions.cs deleted file mode 100644 index 7ae1a7ca..00000000 --- a/src/NServiceBus.Metrics.AcceptanceTests/Tests/TagExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; - -static class TagExtensions -{ - public static string GetTagValue(this IEnumerable tags, string key) - { - if (!TryGetTagValue(tags, key, out var result)) - { - throw new Exception($"Tag {key} not found."); - } - return result; - } - - public static bool TryGetTagValue(this IEnumerable tags, string key, out string value) - { - value = tags - .Where(t => t.StartsWith($"{key}:", StringComparison.OrdinalIgnoreCase)) - .Select(x => x.Substring(key.Length + 1)) - .FirstOrDefault(); - - return value != null; - } -} diff --git a/src/NServiceBus.Metrics.AcceptanceTests/Tests/When_having_metrics_handlers_registered.cs b/src/NServiceBus.Metrics.AcceptanceTests/Tests/When_having_metrics_handlers_registered.cs index 8ef5bbe8..feb26db7 100644 --- a/src/NServiceBus.Metrics.AcceptanceTests/Tests/When_having_metrics_handlers_registered.cs +++ b/src/NServiceBus.Metrics.AcceptanceTests/Tests/When_having_metrics_handlers_registered.cs @@ -26,7 +26,7 @@ public When_having_metrics_handlers_registered() { var probesNames = typeof(IProbe).Assembly.GetTypes() .Where(t => t.GetCustomAttribute() != null) - .Select(t => t.GetCustomAttribute().Name) + .Select(t => t.GetCustomAttribute()!.Name) .ToArray(); errorProbes = @@ -67,8 +67,7 @@ public async Task Should_call_fail_probes_on_success() var context = await Scenario.Define() .WithEndpoint(b => b.When(s => s.SendLocal(new MyMessage())).DoNotFailOnErrorMessages()) .Done(c => c.Values.Count >= errorProbes.Count) - .Run() - .ConfigureAwait(false); + .Run(); foreach (var kvp in context.Values) { @@ -86,8 +85,7 @@ class Context : ScenarioContext class ConsumingReporter : EndpointConfigurationBuilder { - public ConsumingReporter() - { + public ConsumingReporter() => EndpointSetup((c, r) => { var context = (Context)r.ScenarioContext; @@ -95,21 +93,16 @@ public ConsumingReporter() c.EnableMetrics().RegisterObservers( ctx => { RegisterWritesToTestContext(ctx, context); }); }); - } public class MyHandler : IHandleMessages { - public Task Handle(MyMessage message, IMessageHandlerContext context) - { - return Task.Delay(100); - } + public Task Handle(MyMessage message, IMessageHandlerContext context) => Task.Delay(100); } } class ThrowingReporter : EndpointConfigurationBuilder { - public ThrowingReporter() - { + public ThrowingReporter() => EndpointSetup((c, r) => { c.Recoverability().Immediate(immediate => immediate.NumberOfRetries(1)); @@ -118,17 +111,12 @@ public ThrowingReporter() c.EnableMetrics().RegisterObservers( ctx => { RegisterWritesToTestContext(ctx, context); }); }); - } public class MyHandler : IHandleMessages { public Task Handle(MyMessage message, IMessageHandlerContext context) - { - var tcs = new TaskCompletionSource(); - tcs.SetException(new Exception()); - return tcs.Task; - } + => Task.FromException(new Exception()); } } diff --git a/src/NServiceBus.Metrics.AcceptanceTests/Tests/When_using_metrics_with_sendonly_endpoint.cs b/src/NServiceBus.Metrics.AcceptanceTests/Tests/When_using_metrics_with_sendonly_endpoint.cs new file mode 100644 index 00000000..013df7ea --- /dev/null +++ b/src/NServiceBus.Metrics.AcceptanceTests/Tests/When_using_metrics_with_sendonly_endpoint.cs @@ -0,0 +1,38 @@ +using System; +using NServiceBus; +using NServiceBus.AcceptanceTesting; +using NServiceBus.AcceptanceTests; +using NServiceBus.AcceptanceTests.EndpointTemplates; +using NUnit.Framework; + +public class When_using_metrics_with_sendonly_endpoint : NServiceBusAcceptanceTest +{ + [Test] + public void Should_fail() + { + var exception = Assert.CatchAsync(async () => + { + await Scenario.Define() + .WithEndpoint() + .Done(c => c.EndpointsStarted) + .Run(); + }); + + Assert.That(exception!.Message, Does.Contain("Metrics are not supported on send only endpoints")); + } + + class Context : ScenarioContext + { + } + + class SendOnlyEndpoint : EndpointConfigurationBuilder + { + public SendOnlyEndpoint() => + EndpointSetup((c, _) => + { + c.SendOnly(); + + c.EnableMetrics(); + }); + } +} \ No newline at end of file diff --git a/src/NServiceBus.Metrics.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt b/src/NServiceBus.Metrics.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt index 7754c6ab..e2486889 100644 --- a/src/NServiceBus.Metrics.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt +++ b/src/NServiceBus.Metrics.Tests/ApprovalFiles/APIApprovals.Approve.approved.txt @@ -2,7 +2,7 @@ [assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"NServiceBus.Metrics.Tests, PublicKey=00240000048000009400000006020000002400005253413100040000010001007f16e21368ff041183fab592d9e8ed37e7be355e93323147a1d29983d6e591b04282e4da0c9e18bd901e112c0033925eb7d7872c2f1706655891c5c9d57297994f707d16ee9a8f40d978f064ee1ffc73c0db3f4712691b23bf596f75130f4ec978cf78757ec034625a5f27e6bb50c618931ea49f6f628fd74271c32959efb1c5")] namespace NServiceBus { - public struct DurationEvent + public readonly struct DurationEvent { public readonly System.TimeSpan Duration; public readonly string MessageType; @@ -37,7 +37,7 @@ namespace NServiceBus public System.Collections.Generic.IReadOnlyCollection Durations { get; } public System.Collections.Generic.IReadOnlyCollection Signals { get; } } - public struct SignalEvent + public readonly struct SignalEvent { public readonly string MessageType; public SignalEvent(string messageType) { } diff --git a/src/NServiceBus.Metrics.Tests/NServiceBus.Metrics.Tests.csproj b/src/NServiceBus.Metrics.Tests/NServiceBus.Metrics.Tests.csproj index 211d2c71..7be2a464 100644 --- a/src/NServiceBus.Metrics.Tests/NServiceBus.Metrics.Tests.csproj +++ b/src/NServiceBus.Metrics.Tests/NServiceBus.Metrics.Tests.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/NServiceBus.Metrics/Extensions.cs b/src/NServiceBus.Metrics/Extensions.cs index 01a52712..3550482e 100644 --- a/src/NServiceBus.Metrics/Extensions.cs +++ b/src/NServiceBus.Metrics/Extensions.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using NServiceBus; -using NServiceBus.Features; +using NServiceBus.Settings; static class Extensions { @@ -30,9 +30,7 @@ public static bool TryGetDeliverAt(this ReceivePipelineCompleted completed, out } public static bool TryGetMessageType(this ReceivePipelineCompleted completed, out string processedMessageType) - { - return completed.ProcessedMessage.Headers.TryGetMessageType(out processedMessageType); - } + => completed.ProcessedMessage.Headers.TryGetMessageType(out processedMessageType); internal static bool TryGetMessageType(this IReadOnlyDictionary headers, out string processedMessageType) { @@ -45,9 +43,9 @@ internal static bool TryGetMessageType(this IReadOnlyDictionary return false; } - public static void ThrowIfSendonly(this FeatureConfigurationContext context) + public static void ThrowIfSendOnly(this IReadOnlySettings settings) { - var isSendOnly = context.Settings.GetOrDefault("Endpoint.SendOnly"); + var isSendOnly = settings.GetOrDefault("Endpoint.SendOnly"); if (isSendOnly) { throw new Exception("Metrics are not supported on send only endpoints."); diff --git a/src/NServiceBus.Metrics/Guard.cs b/src/NServiceBus.Metrics/Guard.cs deleted file mode 100644 index 093f2017..00000000 --- a/src/NServiceBus.Metrics/Guard.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; - -static class Guard -{ - public static void AgainstNull(string argumentName, object value) - { - if (value == null) - { - throw new ArgumentNullException(argumentName); - } - } - - public static void AgainstNullAndEmpty(string argumentName, string value) - { - if (string.IsNullOrWhiteSpace(value)) - { - throw new ArgumentNullException(argumentName); - } - } - - public static void AgainstNegativeAndZero(string argumentName, TimeSpan value) - { - if (value <= TimeSpan.Zero) - { - throw new ArgumentOutOfRangeException(argumentName); - } - } - - public static void AgainstNegativeAndZero(string argumentName, TimeSpan? optionalValue) - { - if (optionalValue.HasValue && optionalValue.Value <= TimeSpan.Zero) - { - throw new ArgumentOutOfRangeException(argumentName); - } - } -} \ No newline at end of file diff --git a/src/NServiceBus.Metrics/IDurationProbe.cs b/src/NServiceBus.Metrics/IDurationProbe.cs index 959705df..a60696ed 100644 --- a/src/NServiceBus.Metrics/IDurationProbe.cs +++ b/src/NServiceBus.Metrics/IDurationProbe.cs @@ -16,25 +16,17 @@ public interface IDurationProbe : IProbe /// /// Provides data for a single recorded duration. /// - public struct DurationEvent + public readonly struct DurationEvent(TimeSpan duration, string messageType) { - /// - /// Creates the duration event. - /// - public DurationEvent(TimeSpan duration, string messageType) - { - Duration = duration; - MessageType = messageType; - } /// /// The duration value. /// - public readonly TimeSpan Duration; + public readonly TimeSpan Duration = duration; /// /// The message type, the duration was recorded for, if any. /// - public readonly string MessageType; + public readonly string MessageType = messageType; } } \ No newline at end of file diff --git a/src/NServiceBus.Metrics/ISignalProbe.cs b/src/NServiceBus.Metrics/ISignalProbe.cs index 2b085f2b..ff847043 100644 --- a/src/NServiceBus.Metrics/ISignalProbe.cs +++ b/src/NServiceBus.Metrics/ISignalProbe.cs @@ -14,19 +14,11 @@ public interface ISignalProbe : IProbe /// /// Provides data for a single occurrence of a signal. /// - public struct SignalEvent + public readonly struct SignalEvent(string messageType) { - /// - /// Creates the signal event. - /// - public SignalEvent(string messageType) - { - MessageType = messageType; - } - /// /// The message type, the duration was recorded for, if any. /// - public readonly string MessageType; + public readonly string MessageType = messageType; } } \ No newline at end of file diff --git a/src/NServiceBus.Metrics/MetricsConfigurationExtensions.cs b/src/NServiceBus.Metrics/MetricsConfigurationExtensions.cs index 718dd668..161ddd13 100644 --- a/src/NServiceBus.Metrics/MetricsConfigurationExtensions.cs +++ b/src/NServiceBus.Metrics/MetricsConfigurationExtensions.cs @@ -1,5 +1,6 @@ namespace NServiceBus { + using System; using Configuration.AdvancedExtensibility; using Features; using Metrics; @@ -7,7 +8,7 @@ /// /// Extends Endpoint Configuration to provide Metric options /// - public static partial class MetricsConfigurationExtensions + public static class MetricsConfigurationExtensions { /// /// Enables the Metrics feature. @@ -16,11 +17,12 @@ public static partial class MetricsConfigurationExtensions /// An object containing configuration options for the Metrics feature. public static MetricsOptions EnableMetrics(this EndpointConfiguration endpointConfiguration) { - Guard.AgainstNull(nameof(endpointConfiguration), endpointConfiguration); + ArgumentNullException.ThrowIfNull(endpointConfiguration); var settings = endpointConfiguration.GetSettings(); var options = settings.GetOrCreate(); - settings.Set(typeof(MetricsFeature).FullName, FeatureState.Enabled); + + settings.EnableFeatureByDefault(); endpointConfiguration.Recoverability().Immediate(c => c.OnMessageBeingRetried((m, ct) => options.Immediate(m, ct))); endpointConfiguration.Recoverability().Delayed(c => c.OnMessageBeingRetried((m, ct) => options.Delayed(m, ct))); diff --git a/src/NServiceBus.Metrics/MetricsFeature.cs b/src/NServiceBus.Metrics/MetricsFeature.cs index 313d03ed..1306b1bf 100644 --- a/src/NServiceBus.Metrics/MetricsFeature.cs +++ b/src/NServiceBus.Metrics/MetricsFeature.cs @@ -15,9 +15,9 @@ public class MetricsFeature : Feature /// protected override void Setup(FeatureConfigurationContext context) { - context.ThrowIfSendonly(); - var settings = context.Settings; + settings.ThrowIfSendOnly(); + var options = settings.Get(); var probeContext = BuildProbes(context, options); @@ -29,8 +29,7 @@ static ProbeContext BuildProbes(FeatureConfigurationContext context, MetricsOpti { var durationBuilders = new DurationProbeBuilder[] { - new CriticalTimeProbeBuilder(context), - new ProcessingTimeProbeBuilder(context) + new CriticalTimeProbeBuilder(context), new ProcessingTimeProbeBuilder(context) }; var performanceDiagnosticsBehavior = new ReceivePerformanceDiagnosticsBehavior(); @@ -43,10 +42,10 @@ static ProbeContext BuildProbes(FeatureConfigurationContext context, MetricsOpti var signalBuilders = new SignalProbeBuilder[] { - new MessagePulledFromQueueProbeBuilder(performanceDiagnosticsBehavior), - new MessageProcessingFailureProbeBuilder(performanceDiagnosticsBehavior), - new MessageProcessingSuccessProbeBuilder(performanceDiagnosticsBehavior), - new RetriesProbeBuilder(options) + new MessagePulledFromQueueProbeBuilder(performanceDiagnosticsBehavior), + new MessageProcessingFailureProbeBuilder(performanceDiagnosticsBehavior), + new MessageProcessingSuccessProbeBuilder(performanceDiagnosticsBehavior), + new RetriesProbeBuilder(options) }; return new ProbeContext( @@ -55,21 +54,12 @@ static ProbeContext BuildProbes(FeatureConfigurationContext context, MetricsOpti ); } - class SetupRegisteredObservers : FeatureStartupTask + class SetupRegisteredObservers(MetricsOptions options, ProbeContext probeContext) : FeatureStartupTask { - readonly MetricsOptions options; - readonly ProbeContext probeContext; - - public SetupRegisteredObservers(MetricsOptions options, ProbeContext probeContext) - { - this.options = options; - this.probeContext = probeContext; - } - protected override Task OnStart(IMessageSession session, CancellationToken cancellationToken = default) { options.SetUpObservers(probeContext); - return Task.FromResult(0); + return Task.CompletedTask; } protected override Task OnStop(IMessageSession session, CancellationToken cancellationToken = default) => Task.FromResult(0); diff --git a/src/NServiceBus.Metrics/MetricsOptions.cs b/src/NServiceBus.Metrics/MetricsOptions.cs index 2b0fd62e..8378af37 100644 --- a/src/NServiceBus.Metrics/MetricsOptions.cs +++ b/src/NServiceBus.Metrics/MetricsOptions.cs @@ -10,8 +10,8 @@ /// public class MetricsOptions { - internal Func Immediate { get; set; } = (m, _) => Task.CompletedTask; - internal Func Delayed { get; set; } = (m, _) => Task.CompletedTask; + internal Func Immediate { get; set; } = (_, _) => Task.CompletedTask; + internal Func Delayed { get; set; } = (_, _) => Task.CompletedTask; /// /// Enables registering observers to available probes. @@ -19,16 +19,13 @@ public class MetricsOptions /// Action that registers observers to probes public void RegisterObservers(Action register) { - Guard.AgainstNull(nameof(register), register); + ArgumentNullException.ThrowIfNull(register); registerObservers += register; } - internal void SetUpObservers(ProbeContext probeContext) - { - registerObservers(probeContext); - } + internal void SetUpObservers(ProbeContext probeContext) => registerObservers(probeContext); - Action registerObservers = c => { }; + Action registerObservers = _ => { }; } } \ No newline at end of file diff --git a/src/NServiceBus.Metrics/NServiceBus.Metrics.csproj b/src/NServiceBus.Metrics/NServiceBus.Metrics.csproj index 2cbf258b..4d232482 100644 --- a/src/NServiceBus.Metrics/NServiceBus.Metrics.csproj +++ b/src/NServiceBus.Metrics/NServiceBus.Metrics.csproj @@ -3,7 +3,7 @@ net8.0 true - $(SolutionDir)NServiceBus.snk + ..\NServiceBus.snk @@ -13,7 +13,7 @@ - + diff --git a/src/NServiceBus.Metrics/ProbeBuilders/CriticalTimeProbeBuilder.cs b/src/NServiceBus.Metrics/ProbeBuilders/CriticalTimeProbeBuilder.cs index 3c914fbf..b07a5024 100644 --- a/src/NServiceBus.Metrics/ProbeBuilders/CriticalTimeProbeBuilder.cs +++ b/src/NServiceBus.Metrics/ProbeBuilders/CriticalTimeProbeBuilder.cs @@ -1,20 +1,12 @@ using System; +using System.Threading.Tasks; using NServiceBus; using NServiceBus.Features; -using NServiceBus.Metrics; [ProbeProperties(CriticalTime, "The time it took from sending to processing the message.")] -class CriticalTimeProbeBuilder : DurationProbeBuilder +class CriticalTimeProbeBuilder(FeatureConfigurationContext context) : DurationProbeBuilder { - public const string CriticalTime = "Critical Time"; - - public CriticalTimeProbeBuilder(FeatureConfigurationContext context) - { - this.context = context; - } - - protected override void WireUp(DurationProbe probe) - { + protected override void WireUp(DurationProbe probe) => context.Pipeline.OnReceivePipelineCompleted((e, _) => { if (e.TryGetDeliverAt(out DateTimeOffset startTime) || e.TryGetTimeSent(out startTime)) @@ -26,9 +18,8 @@ protected override void WireUp(DurationProbe probe) probe.Record(ref @event); } - return TaskExtensions.Completed; + return Task.CompletedTask; }); - } - readonly FeatureConfigurationContext context; + public const string CriticalTime = "Critical Time"; } diff --git a/src/NServiceBus.Metrics/ProbeBuilders/MessageProcessingFailureProbeBuilder.cs b/src/NServiceBus.Metrics/ProbeBuilders/MessageProcessingFailureProbeBuilder.cs index 03fec39d..9660d6d7 100644 --- a/src/NServiceBus.Metrics/ProbeBuilders/MessageProcessingFailureProbeBuilder.cs +++ b/src/NServiceBus.Metrics/ProbeBuilders/MessageProcessingFailureProbeBuilder.cs @@ -1,15 +1,5 @@ [ProbeProperties("# of msgs failures / sec", "The current number of failed processed messages by the transport per second.")] -class MessageProcessingFailureProbeBuilder : SignalProbeBuilder +class MessageProcessingFailureProbeBuilder(ReceivePerformanceDiagnosticsBehavior behavior) : SignalProbeBuilder { - public MessageProcessingFailureProbeBuilder(ReceivePerformanceDiagnosticsBehavior behavior) - { - this.behavior = behavior; - } - - protected override void WireUp(SignalProbe probe) - { - behavior.ProcessingFailure = probe; - } - - readonly ReceivePerformanceDiagnosticsBehavior behavior; + protected override void WireUp(SignalProbe probe) => behavior.ProcessingFailure = probe; } \ No newline at end of file diff --git a/src/NServiceBus.Metrics/ProbeBuilders/MessageProcessingSuccessProbeBuilder.cs b/src/NServiceBus.Metrics/ProbeBuilders/MessageProcessingSuccessProbeBuilder.cs index 14970029..c4ebe0c8 100644 --- a/src/NServiceBus.Metrics/ProbeBuilders/MessageProcessingSuccessProbeBuilder.cs +++ b/src/NServiceBus.Metrics/ProbeBuilders/MessageProcessingSuccessProbeBuilder.cs @@ -1,15 +1,5 @@ [ProbeProperties("# of msgs successfully processed / sec", "The current number of messages processed successfully by the transport per second.")] -class MessageProcessingSuccessProbeBuilder : SignalProbeBuilder +class MessageProcessingSuccessProbeBuilder(ReceivePerformanceDiagnosticsBehavior behavior) : SignalProbeBuilder { - public MessageProcessingSuccessProbeBuilder(ReceivePerformanceDiagnosticsBehavior behavior) - { - this.behavior = behavior; - } - - protected override void WireUp(SignalProbe probe) - { - behavior.ProcessingSuccess = probe; - } - - readonly ReceivePerformanceDiagnosticsBehavior behavior; + protected override void WireUp(SignalProbe probe) => behavior.ProcessingSuccess = probe; } \ No newline at end of file diff --git a/src/NServiceBus.Metrics/ProbeBuilders/MessagePulledFromQueueProbeBuilder.cs b/src/NServiceBus.Metrics/ProbeBuilders/MessagePulledFromQueueProbeBuilder.cs index 5507d69b..8ead41a7 100644 --- a/src/NServiceBus.Metrics/ProbeBuilders/MessagePulledFromQueueProbeBuilder.cs +++ b/src/NServiceBus.Metrics/ProbeBuilders/MessagePulledFromQueueProbeBuilder.cs @@ -1,15 +1,5 @@ [ProbeProperties("# of msgs pulled from the input queue /sec", "The current number of messages pulled from the input queue by the transport per second.")] -class MessagePulledFromQueueProbeBuilder : SignalProbeBuilder +class MessagePulledFromQueueProbeBuilder(ReceivePerformanceDiagnosticsBehavior behavior) : SignalProbeBuilder { - public MessagePulledFromQueueProbeBuilder(ReceivePerformanceDiagnosticsBehavior behavior) - { - this.behavior = behavior; - } - - protected override void WireUp(SignalProbe probe) - { - behavior.MessagePulledFromQueue = probe; - } - - readonly ReceivePerformanceDiagnosticsBehavior behavior; + protected override void WireUp(SignalProbe probe) => behavior.MessagePulledFromQueue = probe; } \ No newline at end of file diff --git a/src/NServiceBus.Metrics/ProbeBuilders/ProbePropertiesAttribute.cs b/src/NServiceBus.Metrics/ProbeBuilders/ProbePropertiesAttribute.cs index 506aae83..51b2a27f 100644 --- a/src/NServiceBus.Metrics/ProbeBuilders/ProbePropertiesAttribute.cs +++ b/src/NServiceBus.Metrics/ProbeBuilders/ProbePropertiesAttribute.cs @@ -1,14 +1,8 @@ using System; [AttributeUsage(AttributeTargets.Class)] -sealed class ProbePropertiesAttribute : Attribute +sealed class ProbePropertiesAttribute(string name, string description) : Attribute { - public ProbePropertiesAttribute(string name, string description) - { - Name = name; - Description = description; - } - - public readonly string Name; - public readonly string Description; + public readonly string Name = name; + public readonly string Description = description; } \ No newline at end of file diff --git a/src/NServiceBus.Metrics/ProbeBuilders/ProcessingTimeProbeBuilder.cs b/src/NServiceBus.Metrics/ProbeBuilders/ProcessingTimeProbeBuilder.cs index 8ec2e0be..b7c068da 100644 --- a/src/NServiceBus.Metrics/ProbeBuilders/ProcessingTimeProbeBuilder.cs +++ b/src/NServiceBus.Metrics/ProbeBuilders/ProcessingTimeProbeBuilder.cs @@ -1,17 +1,11 @@ +using System.Threading.Tasks; using NServiceBus; using NServiceBus.Features; -using NServiceBus.Metrics; [ProbeProperties(ProcessingTime, "The time it took to successfully process a message.")] -class ProcessingTimeProbeBuilder : DurationProbeBuilder +class ProcessingTimeProbeBuilder(FeatureConfigurationContext context) : DurationProbeBuilder { - public ProcessingTimeProbeBuilder(FeatureConfigurationContext context) - { - this.context = context; - } - - protected override void WireUp(DurationProbe probe) - { + protected override void WireUp(DurationProbe probe) => context.Pipeline.OnReceivePipelineCompleted((e, _) => { e.TryGetMessageType(out var messageTypeProcessed); @@ -22,11 +16,8 @@ protected override void WireUp(DurationProbe probe) probe.Record(ref @event); - return TaskExtensions.Completed; + return Task.CompletedTask; }); - } - - FeatureConfigurationContext context; public const string ProcessingTime = "Processing Time"; } \ No newline at end of file diff --git a/src/NServiceBus.Metrics/ProbeBuilders/RetriesProbeBuilder.cs b/src/NServiceBus.Metrics/ProbeBuilders/RetriesProbeBuilder.cs index 19a58d21..ae24326e 100644 --- a/src/NServiceBus.Metrics/ProbeBuilders/RetriesProbeBuilder.cs +++ b/src/NServiceBus.Metrics/ProbeBuilders/RetriesProbeBuilder.cs @@ -5,13 +5,8 @@ using System.Threading.Tasks; [ProbeProperties(Retries, "A message has been scheduled for retry (FLR or SLR)")] - class RetriesProbeBuilder : SignalProbeBuilder + class RetriesProbeBuilder(MetricsOptions options) : SignalProbeBuilder { - public RetriesProbeBuilder(MetricsOptions options) - { - this.options = options; - } - protected override void WireUp(SignalProbe probe) { options.Immediate = (retry, token) => Signal(retry.Headers, probe, token); @@ -27,7 +22,6 @@ static Task Signal(Dictionary messageHeaders, SignalProbe probe, return Task.CompletedTask; } - MetricsOptions options; public const string Retries = "Retries"; } } \ No newline at end of file diff --git a/src/NServiceBus.Metrics/ProbeContext.cs b/src/NServiceBus.Metrics/ProbeContext.cs index bf241ab0..808318da 100644 --- a/src/NServiceBus.Metrics/ProbeContext.cs +++ b/src/NServiceBus.Metrics/ProbeContext.cs @@ -5,25 +5,16 @@ /// /// Stores available probes /// - public class ProbeContext + public class ProbeContext(IReadOnlyCollection durations, IReadOnlyCollection signals) { - /// - /// Creates . - /// - public ProbeContext(IReadOnlyCollection durations, IReadOnlyCollection signals) - { - Durations = durations; - Signals = signals; - } - /// /// Duration type probes. /// - public IReadOnlyCollection Durations { get; } + public IReadOnlyCollection Durations { get; } = durations; /// /// Signal type probes. /// - public IReadOnlyCollection Signals { get; } + public IReadOnlyCollection Signals { get; } = signals; } } \ No newline at end of file diff --git a/src/NServiceBus.Metrics/Probes/DurationProbe.cs b/src/NServiceBus.Metrics/Probes/DurationProbe.cs index 6fc2dbfd..f34d679c 100644 --- a/src/NServiceBus.Metrics/Probes/DurationProbe.cs +++ b/src/NServiceBus.Metrics/Probes/DurationProbe.cs @@ -1,12 +1,8 @@ using System; using NServiceBus; -class DurationProbe : Probe, IDurationProbe +class DurationProbe(string name, string description) : Probe(name, description), IDurationProbe { - public DurationProbe(string name, string description) : base(name, description) - { - } - public void Register(OnEvent observer) { lock (Lock) @@ -15,18 +11,12 @@ public void Register(OnEvent observer) } } - public void Register(Action observer) - { - Register((ref DurationEvent e) => observer(e.Duration)); - } + public void Register(Action observer) => Register((ref DurationEvent e) => observer(e.Duration)); - internal void Record(ref DurationEvent e) - { - observers(ref e); - } + internal void Record(ref DurationEvent e) => observers(ref e); volatile OnEvent observers = Empty; - readonly object Lock = new object(); + readonly object Lock = new(); static void Empty(ref DurationEvent e) { } } \ No newline at end of file diff --git a/src/NServiceBus.Metrics/Probes/Probe.cs b/src/NServiceBus.Metrics/Probes/Probe.cs index d09c92d6..8134d3ef 100644 --- a/src/NServiceBus.Metrics/Probes/Probe.cs +++ b/src/NServiceBus.Metrics/Probes/Probe.cs @@ -1,12 +1,6 @@ -abstract class Probe +abstract class Probe(string name, string description) { - protected Probe(string name, string description) - { - Name = name; - Description = description; - } + public string Name { get; } = name; - public string Name { get; } - - public string Description { get; } + public string Description { get; } = description; } \ No newline at end of file diff --git a/src/NServiceBus.Metrics/Probes/SignalProbe.cs b/src/NServiceBus.Metrics/Probes/SignalProbe.cs index e084b59e..a047afd3 100644 --- a/src/NServiceBus.Metrics/Probes/SignalProbe.cs +++ b/src/NServiceBus.Metrics/Probes/SignalProbe.cs @@ -1,12 +1,8 @@ using System; using NServiceBus; -class SignalProbe : Probe, ISignalProbe +class SignalProbe(string name, string description) : Probe(name, description), ISignalProbe { - public SignalProbe(string name, string description) : base(name, description) - { - } - public void Register(OnEvent observer) { lock (Lock) @@ -15,18 +11,12 @@ public void Register(OnEvent observer) } } - public void Register(Action observer) - { - Register((ref SignalEvent e) => observer()); - } + public void Register(Action observer) => Register((ref SignalEvent e) => observer()); - internal void Signal(ref SignalEvent e) - { - observers(ref e); - } + internal void Signal(ref SignalEvent e) => observers(ref e); volatile OnEvent observers = Empty; - readonly object Lock = new object(); + readonly object Lock = new(); static void Empty(ref SignalEvent e) { } } \ No newline at end of file diff --git a/src/NServiceBus.Metrics/TaskExtensions.cs b/src/NServiceBus.Metrics/TaskExtensions.cs deleted file mode 100644 index b2bffe9a..00000000 --- a/src/NServiceBus.Metrics/TaskExtensions.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace NServiceBus.Metrics -{ - using System.Threading.Tasks; - - static class TaskExtensions - { - public static readonly Task Completed = Task.FromResult(0); - } -} \ No newline at end of file