From a8c746f7cbb41a50ab400fb97c796e63f938a8b9 Mon Sep 17 00:00:00 2001 From: Phil Bastian Date: Thu, 12 Jun 2025 15:43:12 +0800 Subject: [PATCH 01/19] convert SC transports from NServiceBus.Logging to Microsoft.Extensions.Logging --- .../DeadLetterQueueCheck.cs | 17 +++++----- .../QueueLengthProvider.cs | 16 +++++----- .../QueueLengthProvider.cs | 19 ++++++----- .../QueueLengthProvider.cs | 15 +++++---- .../DeadLetterQueueCheck.cs | 32 ++++++++----------- .../TransportTestsConfiguration.cs | 3 +- .../PostgreSqlTransportCustomization.cs | 8 ++--- .../QueueLengthProvider.cs | 11 ++++--- .../QueueLengthProvider.cs | 12 ++++--- .../TransportTestsConfiguration.cs | 3 +- .../QueueLengthProvider.cs | 20 ++++++------ .../SQSTransportCustomization.cs | 10 +++--- .../TransportTestsConfiguration.cs | 3 +- .../QueueLengthProvider.cs | 11 ++++--- .../SqlServerTransportCustomization.cs | 8 ++--- .../ServiceControl.Transports.csproj | 1 + .../TransportManifest.cs | 11 ++++--- 17 files changed, 102 insertions(+), 98 deletions(-) diff --git a/src/ServiceControl.Transports.ASBS/DeadLetterQueueCheck.cs b/src/ServiceControl.Transports.ASBS/DeadLetterQueueCheck.cs index abcc4fd05c..e44be27ca8 100644 --- a/src/ServiceControl.Transports.ASBS/DeadLetterQueueCheck.cs +++ b/src/ServiceControl.Transports.ASBS/DeadLetterQueueCheck.cs @@ -4,14 +4,15 @@ using System.Threading; using System.Threading.Tasks; using Azure.Messaging.ServiceBus.Administration; + using Microsoft.Extensions.Logging; using NServiceBus.CustomChecks; - using NServiceBus.Logging; + using ServiceControl.Infrastructure; public class DeadLetterQueueCheck : CustomCheck { public DeadLetterQueueCheck(TransportSettings settings) : base(id: "Dead Letter Queue", category: "Transport", repeatAfter: TimeSpan.FromHours(1)) { - Logger.Debug("Azure Service Bus Dead Letter Queue custom check starting"); + Logger.LogDebug("Azure Service Bus Dead Letter Queue custom check starting"); connectionString = settings.ConnectionString; stagingQueue = $"{settings.EndpointName}.staging"; @@ -25,7 +26,7 @@ public override async Task PerformCheck(CancellationToken cancellat return CheckResult.Pass; } - Logger.Debug("Checking Dead Letter Queue length"); + Logger.LogDebug("Checking Dead Letter Queue length"); var managementClient = new ServiceBusAdministrationClient(connectionString); var queueRuntimeInfo = await managementClient.GetQueueRuntimePropertiesAsync(stagingQueue, cancellationToken); @@ -33,13 +34,11 @@ public override async Task PerformCheck(CancellationToken cancellat if (deadLetterMessageCount > 0) { - var result = $"{deadLetterMessageCount} messages in the Dead Letter Queue '{stagingQueue}'. This could indicate a problem with ServiceControl's retries. Please submit a support ticket to Particular if you would like help from our engineers to ensure no message loss while resolving these dead letter messages."; - - Logger.Warn(result); - return CheckResult.Failed(result); + Logger.LogWarning("{DeadLetterMessageCount} messages in the Dead Letter Queue '{StagingQueue}'. This could indicate a problem with ServiceControl's retries. Please submit a support ticket to Particular if you would like help from our engineers to ensure no message loss while resolving these dead letter messages.", deadLetterMessageCount, stagingQueue); + return CheckResult.Failed($"{deadLetterMessageCount} messages in the Dead Letter Queue '{stagingQueue}'. This could indicate a problem with ServiceControl's retries. Please submit a support ticket to Particular if you would like help from our engineers to ensure no message loss while resolving these dead letter messages."); } - Logger.Debug("No messages in Dead Letter Queue"); + Logger.LogDebug("No messages in Dead Letter Queue"); return CheckResult.Pass; } @@ -49,6 +48,6 @@ public override async Task PerformCheck(CancellationToken cancellat bool runCheck; - static readonly ILog Logger = LogManager.GetLogger(typeof(DeadLetterQueueCheck)); + static readonly ILogger Logger = LoggerUtil.CreateStaticLogger(typeof(DeadLetterQueueCheck)); } } \ No newline at end of file diff --git a/src/ServiceControl.Transports.ASBS/QueueLengthProvider.cs b/src/ServiceControl.Transports.ASBS/QueueLengthProvider.cs index 24021da49a..f6fb44e593 100644 --- a/src/ServiceControl.Transports.ASBS/QueueLengthProvider.cs +++ b/src/ServiceControl.Transports.ASBS/QueueLengthProvider.cs @@ -6,17 +6,18 @@ namespace ServiceControl.Transports.ASBS using System.Threading; using System.Threading.Tasks; using Azure.Messaging.ServiceBus.Administration; - using NServiceBus.Logging; + using Microsoft.Extensions.Logging; class QueueLengthProvider : AbstractQueueLengthProvider { - public QueueLengthProvider(TransportSettings settings, Action store) : base(settings, store) + public QueueLengthProvider(TransportSettings settings, Action store, ILogger logger) : base(settings, store) { var connectionSettings = ConnectionStringParser.Parse(ConnectionString); queryDelayInterval = connectionSettings.QueryDelayInterval ?? TimeSpan.FromMilliseconds(500); managementClient = connectionSettings.AuthenticationMethod.BuildManagementClient(); + this.logger = logger; } public override void TrackEndpointInputQueue(EndpointToQueueMapping queueToTrack) => @@ -32,14 +33,14 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) { try { - Logger.Debug("Waiting for next interval"); + logger.LogDebug("Waiting for next interval"); await Task.Delay(queryDelayInterval, stoppingToken); - Logger.DebugFormat("Querying management client."); + logger.LogDebug("Querying management client."); var queueRuntimeInfos = await GetQueueList(stoppingToken); - Logger.DebugFormat("Retrieved details of {0} queues", queueRuntimeInfos.Count); + logger.LogDebug("Retrieved details of {QueueCount} queues", queueRuntimeInfos.Count); UpdateAllQueueLengths(queueRuntimeInfos); } @@ -49,7 +50,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } catch (Exception e) { - Logger.Error("Error querying Azure Service Bus queue sizes.", e); + logger.LogError(e, "Error querying Azure Service Bus queue sizes."); } } } @@ -109,7 +110,6 @@ void UpdateQueueLength(KeyValuePair monitoredEndpoint, IReadOnly readonly ConcurrentDictionary endpointQueueMappings = new ConcurrentDictionary(); readonly ServiceBusAdministrationClient managementClient; readonly TimeSpan queryDelayInterval; - - static readonly ILog Logger = LogManager.GetLogger(); + readonly ILogger logger; } } \ No newline at end of file diff --git a/src/ServiceControl.Transports.ASQ/QueueLengthProvider.cs b/src/ServiceControl.Transports.ASQ/QueueLengthProvider.cs index 58a7b5783d..48ebcf6a6d 100644 --- a/src/ServiceControl.Transports.ASQ/QueueLengthProvider.cs +++ b/src/ServiceControl.Transports.ASQ/QueueLengthProvider.cs @@ -1,19 +1,22 @@ namespace ServiceControl.Transports.ASQ { using System; - using System.Linq; - using System.Threading.Tasks; using System.Collections.Concurrent; + using System.Linq; using System.Threading; - using NServiceBus.Logging; + using System.Threading.Tasks; using Azure.Storage.Queues; using Azure.Storage.Queues.Models; + using Microsoft.Extensions.Logging; class QueueLengthProvider : AbstractQueueLengthProvider { - public QueueLengthProvider(TransportSettings settings, Action store) + public QueueLengthProvider(TransportSettings settings, Action store, ILogger logger) : base(settings, store) - => connectionString = ConnectionString.RemoveCustomConnectionStringParts(out _); + { + connectionString = ConnectionString.RemoveCustomConnectionStringParts(out _); + this.logger = logger; + } public override void TrackEndpointInputQueue(EndpointToQueueMapping queueToTrack) { @@ -48,7 +51,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } catch (Exception e) { - Logger.Error("Error querying sql queue sizes.", e); + logger.LogError(e, "Error querying sql queue sizes."); } } } @@ -76,7 +79,7 @@ async Task FetchLength(QueueLengthValue queueLength, CancellationToken cancellat // simple "log once" approach to do not flood logs if (problematicQueuesNames.TryAdd(queueLength.QueueName, queueLength.QueueName)) { - Logger.Error($"Obtaining Azure Storage Queue count failed for '{queueLength.QueueName}'", ex); + logger.LogError(ex, "Obtaining Azure Storage Queue count failed for '{QueueName}'", queueLength.QueueName); } } } @@ -102,7 +105,7 @@ void UpdateQueueLengthStore() readonly ConcurrentDictionary queueLengths = new ConcurrentDictionary(); readonly ConcurrentDictionary problematicQueuesNames = new ConcurrentDictionary(); - static readonly ILog Logger = LogManager.GetLogger(); + readonly ILogger logger; static readonly TimeSpan QueryDelayInterval = TimeSpan.FromMilliseconds(200); class QueueLengthValue diff --git a/src/ServiceControl.Transports.Learning/QueueLengthProvider.cs b/src/ServiceControl.Transports.Learning/QueueLengthProvider.cs index 68959ada38..93bc2fcc41 100644 --- a/src/ServiceControl.Transports.Learning/QueueLengthProvider.cs +++ b/src/ServiceControl.Transports.Learning/QueueLengthProvider.cs @@ -7,14 +7,17 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; - using NServiceBus.Logging; + using Microsoft.Extensions.Logging; using ServiceControl.Transports.Learning; class QueueLengthProvider : AbstractQueueLengthProvider { - public QueueLengthProvider(TransportSettings settings, Action store) - : base(settings, store) => + public QueueLengthProvider(TransportSettings settings, Action store, ILogger logger) + : base(settings, store) + { rootFolder = LearningTransportCustomization.FindStoragePath(ConnectionString); + this.logger = logger; + } public override void TrackEndpointInputQueue(EndpointToQueueMapping queueToTrack) => endpointsHash.AddOrUpdate(queueToTrack, queueToTrack, (_, __) => queueToTrack); @@ -36,7 +39,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } catch (Exception ex) { - Log.Warn("Problem getting learning transport queue length", ex); + logger.LogWarning(ex, "Problem getting learning transport queue length"); } } } @@ -60,7 +63,7 @@ void UpdateStore(ILookup queueLengths) } else { - Log.Warn($"Queue Length data missing for queue {instance.InputQueue} (Endpoint {instance.EndpointName})"); + logger.LogWarning("Queue Length data missing for queue {InputQueue} (Endpoint {EndpointName})", instance.InputQueue, instance.EndpointName); } } } @@ -87,7 +90,7 @@ void UpdateStore(ILookup queueLengths) readonly ConcurrentDictionary endpointsHash = new ConcurrentDictionary(); static readonly TimeSpan QueryDelayInterval = TimeSpan.FromMilliseconds(200); - static readonly ILog Log = LogManager.GetLogger(); + readonly ILogger logger; } } \ No newline at end of file diff --git a/src/ServiceControl.Transports.Msmq/DeadLetterQueueCheck.cs b/src/ServiceControl.Transports.Msmq/DeadLetterQueueCheck.cs index 7f229048f6..61c5378d4d 100644 --- a/src/ServiceControl.Transports.Msmq/DeadLetterQueueCheck.cs +++ b/src/ServiceControl.Transports.Msmq/DeadLetterQueueCheck.cs @@ -5,13 +5,13 @@ using System.Diagnostics; using System.Threading; using System.Threading.Tasks; + using Microsoft.Extensions.Logging; using NServiceBus.CustomChecks; - using NServiceBus.Logging; using Transports; public class DeadLetterQueueCheck : CustomCheck { - public DeadLetterQueueCheck(TransportSettings settings) : + public DeadLetterQueueCheck(TransportSettings settings, ILogger logger) : base("Dead Letter Queue", "Transport", TimeSpan.FromHours(1)) { runCheck = settings.RunCustomChecks; @@ -20,7 +20,7 @@ public DeadLetterQueueCheck(TransportSettings settings) : return; } - Logger.Debug("MSMQ Dead Letter Queue custom check starting"); + logger.LogDebug("MSMQ Dead Letter Queue custom check starting"); categoryName = Read("Msmq/PerformanceCounterCategoryName", "MSMQ Queue"); counterName = Read("Msmq/PerformanceCounterName", "Messages in Queue"); @@ -32,8 +32,10 @@ public DeadLetterQueueCheck(TransportSettings settings) : } catch (InvalidOperationException ex) { - Logger.Error(CounterMightBeLocalized(categoryName, counterName, counterInstanceName), ex); + logger.LogError(ex, CounterMightBeLocalized("CategoryName", "CounterName", "CounterInstanceName"), categoryName, counterName, counterInstanceName); } + + this.logger = logger; } public override Task PerformCheck(CancellationToken cancellationToken = default) @@ -43,9 +45,8 @@ public override Task PerformCheck(CancellationToken cancellationTok return CheckResult.Pass; } - Logger.Debug("Checking Dead Letter Queue length"); + logger.LogDebug("Checking Dead Letter Queue length"); float currentValue; - string result; try { if (dlqPerformanceCounter == null) @@ -57,25 +58,18 @@ public override Task PerformCheck(CancellationToken cancellationTok } catch (InvalidOperationException ex) { - result = CounterMightBeLocalized(categoryName, counterName, counterInstanceName); - Logger.Warn(result, ex); - return CheckResult.Failed(result); + logger.LogWarning(ex, CounterMightBeLocalized("CategoryName", "CounterName", "CounterInstanceName"), categoryName, counterName, counterInstanceName); + return CheckResult.Failed(CounterMightBeLocalized(categoryName, counterName, counterInstanceName)); } if (currentValue <= 0) { - Logger.Debug("No messages in Dead Letter Queue"); + logger.LogDebug("No messages in Dead Letter Queue"); return CheckResult.Pass; } - result = MessagesInDeadLetterQueue(currentValue); - Logger.Warn(result); - return CheckResult.Failed(result); - } - - static string MessagesInDeadLetterQueue(float currentValue) - { - return $"{currentValue} messages in the Dead Letter Queue on {Environment.MachineName}. This could indicate a problem with ServiceControl's retries. Please submit a support ticket to Particular if you would like help from our engineers to ensure no message loss while resolving these dead letter messages."; + logger.LogWarning("{DeadLetterMessageCount} messages in the Dead Letter Queue on {MachineName}. This could indicate a problem with ServiceControl's retries. Please submit a support ticket to Particular if you would like help from our engineers to ensure no message loss while resolving these dead letter messages.", currentValue, Environment.MachineName); + return CheckResult.Failed($"{currentValue} messages in the Dead Letter Queue on {Environment.MachineName}. This could indicate a problem with ServiceControl's retries. Please submit a support ticket to Particular if you would like help from our engineers to ensure no message loss while resolving these dead letter messages."); } static string CounterMightBeLocalized(string categoryName, string counterName, string counterInstanceName) @@ -123,6 +117,6 @@ static bool TryRead(string root, string name, out string value) string counterInstanceName; bool runCheck; - static readonly ILog Logger = LogManager.GetLogger(typeof(DeadLetterQueueCheck)); + readonly ILogger logger; } } \ No newline at end of file diff --git a/src/ServiceControl.Transports.PostgreSql.Tests/TransportTestsConfiguration.cs b/src/ServiceControl.Transports.PostgreSql.Tests/TransportTestsConfiguration.cs index 819248c612..2d6c34b86c 100644 --- a/src/ServiceControl.Transports.PostgreSql.Tests/TransportTestsConfiguration.cs +++ b/src/ServiceControl.Transports.PostgreSql.Tests/TransportTestsConfiguration.cs @@ -2,6 +2,7 @@ { using System; using System.Threading.Tasks; + using ServiceControl.Infrastructure; using ServiceControl.Transports.PostgreSql; using Transports; @@ -13,7 +14,7 @@ partial class TransportTestsConfiguration public Task Configure() { - TransportCustomization = new PostgreSqlTransportCustomization(); + TransportCustomization = new PostgreSqlTransportCustomization(LoggerUtil.CreateStaticLogger()); ConnectionString = Environment.GetEnvironmentVariable(ConnectionStringKey); if (string.IsNullOrEmpty(ConnectionString)) diff --git a/src/ServiceControl.Transports.PostgreSql/PostgreSqlTransportCustomization.cs b/src/ServiceControl.Transports.PostgreSql/PostgreSqlTransportCustomization.cs index 934bee47a1..c5f878f21f 100644 --- a/src/ServiceControl.Transports.PostgreSql/PostgreSqlTransportCustomization.cs +++ b/src/ServiceControl.Transports.PostgreSql/PostgreSqlTransportCustomization.cs @@ -4,11 +4,11 @@ using System.Runtime.CompilerServices; using BrokerThroughput; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using NServiceBus; -using NServiceBus.Logging; using NServiceBus.Transport.PostgreSql; -public class PostgreSqlTransportCustomization : TransportCustomization +public class PostgreSqlTransportCustomization(ILogger logger) : TransportCustomization { protected override void CustomizeTransportForPrimaryEndpoint(EndpointConfiguration endpointConfiguration, PostgreSqlTransport transportDefinition, TransportSettings transportSettings) { @@ -66,7 +66,7 @@ protected override PostgreSqlTransport CreateTransport(TransportSettings transpo if (transportSettings.GetOrDefault("TransportSettings.EnableDtc")) { - Logger.Error("The EnableDtc setting is no longer supported natively within ServiceControl. If you require distributed transactions, you will have to use a Transport Adapter (https://docs.particular.net/servicecontrol/transport-adapter/)"); + logger.LogError("The EnableDtc setting is no longer supported natively within ServiceControl. If you require distributed transactions, you will have to use a Transport Adapter (https://docs.particular.net/servicecontrol/transport-adapter/)"); } DisableDelayedDelivery(transport) = true; @@ -93,6 +93,4 @@ protected override string ToTransportQualifiedQueueNameCore(string queueName) static extern ref bool DisableDelayedDelivery(PostgreSqlTransport transport); const string DefaultSubscriptionTableName = "SubscriptionRouting"; - - static readonly ILog Logger = LogManager.GetLogger(typeof(PostgreSqlTransportCustomization)); } \ No newline at end of file diff --git a/src/ServiceControl.Transports.PostgreSql/QueueLengthProvider.cs b/src/ServiceControl.Transports.PostgreSql/QueueLengthProvider.cs index d58b5c526c..79b128032d 100644 --- a/src/ServiceControl.Transports.PostgreSql/QueueLengthProvider.cs +++ b/src/ServiceControl.Transports.PostgreSql/QueueLengthProvider.cs @@ -6,17 +6,18 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Npgsql; -using NServiceBus.Logging; class QueueLengthProvider : AbstractQueueLengthProvider { - public QueueLengthProvider(TransportSettings settings, Action store) : base(settings, store) + public QueueLengthProvider(TransportSettings settings, Action store, ILogger logger) : base(settings, store) { connectionString = ConnectionString .RemoveCustomConnectionStringParts(out var customSchema, out _); defaultSchema = customSchema ?? "public"; + this.logger = logger; } public override void TrackEndpointInputQueue(EndpointToQueueMapping queueToTrack) { @@ -55,7 +56,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } catch (Exception e) { - Logger.Error("Error querying sql queue sizes.", e); + logger.LogError(e, "Error querying SQL queue sizes."); } } } @@ -113,7 +114,7 @@ async Task UpdateChunk(NpgsqlConnection connection, KeyValuePair(); + readonly ILogger logger; static readonly TimeSpan QueryDelayInterval = TimeSpan.FromMilliseconds(200); diff --git a/src/ServiceControl.Transports.RabbitMQ/QueueLengthProvider.cs b/src/ServiceControl.Transports.RabbitMQ/QueueLengthProvider.cs index 3c60019abe..311a408aa9 100644 --- a/src/ServiceControl.Transports.RabbitMQ/QueueLengthProvider.cs +++ b/src/ServiceControl.Transports.RabbitMQ/QueueLengthProvider.cs @@ -4,12 +4,12 @@ using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; - using NServiceBus.Logging; + using Microsoft.Extensions.Logging; using NServiceBus.Transport.RabbitMQ.ManagementApi; class QueueLengthProvider : AbstractQueueLengthProvider { - public QueueLengthProvider(TransportSettings settings, Action store, ITransportCustomization transportCustomization) : base(settings, store) + public QueueLengthProvider(TransportSettings settings, Action store, ITransportCustomization transportCustomization, ILogger logger) : base(settings, store) { if (transportCustomization is IManagementClientProvider provider) { @@ -19,6 +19,8 @@ public QueueLengthProvider(TransportSettings settings, Action @@ -50,7 +52,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } catch (Exception e) { - Logger.Error("Queue length query loop failure.", e); + logger.LogError(e, "Queue length query loop failure."); } } } @@ -91,7 +93,7 @@ async Task FetchQueueLengths(CancellationToken cancellationToken) } catch (Exception e) { - Logger.Warn($"Error querying queue length for {queueName}", e); + logger.LogWarning(e, "Error querying queue length for {QueueName}", queueName); } } } @@ -101,7 +103,7 @@ async Task FetchQueueLengths(CancellationToken cancellationToken) readonly ConcurrentDictionary endpointQueues = new(); readonly ConcurrentDictionary sizes = new(); - static readonly ILog Logger = LogManager.GetLogger(); + readonly ILogger logger; readonly Lazy managementClient; } diff --git a/src/ServiceControl.Transports.SQS.Tests/TransportTestsConfiguration.cs b/src/ServiceControl.Transports.SQS.Tests/TransportTestsConfiguration.cs index c64f5d1bca..c931ef2f58 100644 --- a/src/ServiceControl.Transports.SQS.Tests/TransportTestsConfiguration.cs +++ b/src/ServiceControl.Transports.SQS.Tests/TransportTestsConfiguration.cs @@ -2,6 +2,7 @@ { using System; using System.Threading.Tasks; + using ServiceControl.Infrastructure; using Transports; using Transports.SQS; @@ -13,7 +14,7 @@ partial class TransportTestsConfiguration public Task Configure() { - TransportCustomization = new SQSTransportCustomization(); + TransportCustomization = new SQSTransportCustomization(LoggerUtil.CreateStaticLogger()); ConnectionString = Environment.GetEnvironmentVariable(ConnectionStringKey); if (string.IsNullOrEmpty(ConnectionString)) diff --git a/src/ServiceControl.Transports.SQS/QueueLengthProvider.cs b/src/ServiceControl.Transports.SQS/QueueLengthProvider.cs index 431d5358ac..ea8901fca9 100644 --- a/src/ServiceControl.Transports.SQS/QueueLengthProvider.cs +++ b/src/ServiceControl.Transports.SQS/QueueLengthProvider.cs @@ -1,19 +1,19 @@ namespace ServiceControl.Transports.SQS { using System; - using System.Linq; - using System.Threading.Tasks; - using System.Data.Common; using System.Collections.Concurrent; using System.Collections.Generic; + using System.Data.Common; + using System.Linq; using System.Threading; + using System.Threading.Tasks; using Amazon.Runtime; using Amazon.SQS; - using NServiceBus.Logging; + using Microsoft.Extensions.Logging; class QueueLengthProvider : AbstractQueueLengthProvider { - public QueueLengthProvider(TransportSettings settings, Action store) : base(settings, store) + public QueueLengthProvider(TransportSettings settings, Action store, ILogger logger) : base(settings, store) { var builder = new DbConnectionStringBuilder { ConnectionString = ConnectionString }; if (builder.ContainsKey("AccessKeyId") || builder.ContainsKey("SecretAccessKey")) @@ -24,13 +24,15 @@ public QueueLengthProvider(TransportSettings settings, Action clientFactory = () => new AmazonSQSClient(); - static readonly ILog Logger = LogManager.GetLogger(); + readonly ILogger logger; } } diff --git a/src/ServiceControl.Transports.SQS/SQSTransportCustomization.cs b/src/ServiceControl.Transports.SQS/SQSTransportCustomization.cs index 8c17902b53..4ae8e6a82e 100644 --- a/src/ServiceControl.Transports.SQS/SQSTransportCustomization.cs +++ b/src/ServiceControl.Transports.SQS/SQSTransportCustomization.cs @@ -2,7 +2,6 @@ { using System; using System.Linq; - using System.Runtime.CompilerServices; using Amazon; using Amazon.Runtime; using Amazon.S3; @@ -10,11 +9,11 @@ using Amazon.SQS; using BrokerThroughput; using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Logging; using NServiceBus; using NServiceBus.Configuration.AdvancedExtensibility; - using NServiceBus.Logging; - public class SQSTransportCustomization : TransportCustomization + public class SQSTransportCustomization(ILogger logger) : TransportCustomization { protected override void CustomizeTransportForPrimaryEndpoint(EndpointConfiguration endpointConfiguration, SqsTransport transportDefinition, TransportSettings transportSettings) { @@ -65,7 +64,7 @@ protected override SqsTransport CreateTransport(TransportSettings transportSetti else { //See https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-creds.html#creds-assign - log.Info( + logger.LogInformation( "BasicAWSCredentials have not been supplied in the connection string. Attempting to use existing environment or IAM role credentials for SQS Client."); sqsClient = new AmazonSQSClient(); snsClient = new AmazonSimpleNotificationServiceClient(); @@ -101,7 +100,7 @@ protected override SqsTransport CreateTransport(TransportSettings transportSetti } else { - log.Info( + logger.LogInformation( "BasicAWSCredentials have not been supplied in the connection string. Attempting to use existing environment or IAM role credentials for S3 Client."); s3Client = new AmazonS3Client(); } @@ -121,6 +120,5 @@ static void PromoteEnvironmentVariableFromConnectionString(string value, string environmentVariableName) => Environment.SetEnvironmentVariable(environmentVariableName, value, EnvironmentVariableTarget.Process); - static readonly ILog log = LogManager.GetLogger(); } } \ No newline at end of file diff --git a/src/ServiceControl.Transports.SqlServer.Tests/TransportTestsConfiguration.cs b/src/ServiceControl.Transports.SqlServer.Tests/TransportTestsConfiguration.cs index 9cf82c6dde..abcad12793 100644 --- a/src/ServiceControl.Transports.SqlServer.Tests/TransportTestsConfiguration.cs +++ b/src/ServiceControl.Transports.SqlServer.Tests/TransportTestsConfiguration.cs @@ -2,6 +2,7 @@ { using System; using System.Threading.Tasks; + using ServiceControl.Infrastructure; using Transports; using Transports.SqlServer; @@ -13,7 +14,7 @@ partial class TransportTestsConfiguration public Task Configure() { - TransportCustomization = new SqlServerTransportCustomization(); + TransportCustomization = new SqlServerTransportCustomization(LoggerUtil.CreateStaticLogger()); ConnectionString = Environment.GetEnvironmentVariable(ConnectionStringKey); if (string.IsNullOrEmpty(ConnectionString)) diff --git a/src/ServiceControl.Transports.SqlServer/QueueLengthProvider.cs b/src/ServiceControl.Transports.SqlServer/QueueLengthProvider.cs index 196905a678..b9fe29a227 100644 --- a/src/ServiceControl.Transports.SqlServer/QueueLengthProvider.cs +++ b/src/ServiceControl.Transports.SqlServer/QueueLengthProvider.cs @@ -7,16 +7,17 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Data.SqlClient; - using NServiceBus.Logging; + using Microsoft.Extensions.Logging; class QueueLengthProvider : AbstractQueueLengthProvider { - public QueueLengthProvider(TransportSettings settings, Action store) : base(settings, store) + public QueueLengthProvider(TransportSettings settings, Action store, ILogger logger) : base(settings, store) { connectionString = ConnectionString .RemoveCustomConnectionStringParts(out var customSchema, out _); defaultSchema = customSchema ?? "dbo"; + this.logger = logger; } public override void TrackEndpointInputQueue(EndpointToQueueMapping queueToTrack) { @@ -53,7 +54,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } catch (Exception e) { - Logger.Error("Error querying sql queue sizes.", e); + logger.LogError(e, "Error querying sql queue sizes."); } } } @@ -111,7 +112,7 @@ async Task UpdateChunk(SqlConnection connection, KeyValuePair[] c if (queueLength == -1) { - Logger.Warn($"Table {chunkPair.Key} does not exist."); + logger.LogWarning("Table {TableName} does not exist.", chunkPair.Key); } else { @@ -128,7 +129,7 @@ async Task UpdateChunk(SqlConnection connection, KeyValuePair[] c readonly string connectionString; readonly string defaultSchema; - static readonly ILog Logger = LogManager.GetLogger(); + readonly ILogger logger; static readonly TimeSpan QueryDelayInterval = TimeSpan.FromMilliseconds(200); diff --git a/src/ServiceControl.Transports.SqlServer/SqlServerTransportCustomization.cs b/src/ServiceControl.Transports.SqlServer/SqlServerTransportCustomization.cs index 24d3dec0f3..a89ee8fecc 100644 --- a/src/ServiceControl.Transports.SqlServer/SqlServerTransportCustomization.cs +++ b/src/ServiceControl.Transports.SqlServer/SqlServerTransportCustomization.cs @@ -4,12 +4,12 @@ using System.Runtime.CompilerServices; using BrokerThroughput; using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Logging; using NServiceBus; using NServiceBus.Configuration.AdvancedExtensibility; - using NServiceBus.Logging; using NServiceBus.Transport.SqlServer; - public class SqlServerTransportCustomization : TransportCustomization + public class SqlServerTransportCustomization(ILogger logger) : TransportCustomization { protected override void CustomizeTransportForPrimaryEndpoint(EndpointConfiguration endpointConfiguration, SqlServerTransport transportDefinition, TransportSettings transportSettings) { @@ -73,7 +73,7 @@ protected override SqlServerTransport CreateTransport(TransportSettings transpor if (transportSettings.GetOrDefault("TransportSettings.EnableDtc")) { - Logger.Error("The EnableDtc setting is no longer supported natively within ServiceControl. If you require distributed transactions, you will have to use a Transport Adapter (https://docs.particular.net/servicecontrol/transport-adapter/)"); + logger.LogError("The EnableDtc setting is no longer supported natively within ServiceControl. If you require distributed transactions, you will have to use a Transport Adapter (https://docs.particular.net/servicecontrol/transport-adapter/)"); } DisableDelayedDelivery(transport) = true; @@ -87,7 +87,5 @@ protected override SqlServerTransport CreateTransport(TransportSettings transpor static extern ref bool DisableDelayedDelivery(SqlServerTransport transport); const string defaultSubscriptionTableName = "SubscriptionRouting"; - - static readonly ILog Logger = LogManager.GetLogger(typeof(SqlServerTransportCustomization)); } } \ No newline at end of file diff --git a/src/ServiceControl.Transports/ServiceControl.Transports.csproj b/src/ServiceControl.Transports/ServiceControl.Transports.csproj index 435491c160..263429e5d4 100644 --- a/src/ServiceControl.Transports/ServiceControl.Transports.csproj +++ b/src/ServiceControl.Transports/ServiceControl.Transports.csproj @@ -9,6 +9,7 @@ + diff --git a/src/ServiceControl.Transports/TransportManifest.cs b/src/ServiceControl.Transports/TransportManifest.cs index c37538867a..429d80db9b 100644 --- a/src/ServiceControl.Transports/TransportManifest.cs +++ b/src/ServiceControl.Transports/TransportManifest.cs @@ -5,7 +5,8 @@ using System.IO; using System.Linq; using System.Text.Json; - using NServiceBus.Logging; + using Microsoft.Extensions.Logging; + using ServiceControl.Infrastructure; public class TransportManifest { @@ -60,7 +61,7 @@ static TransportManifestLibrary() } catch (Exception ex) { - logger.Warn($"Failed to load transport manifests from {assemblyDirectory}", ex); + logger.LogWarning(ex, "Failed to load transport manifests from {AssemblyDirectory}", assemblyDirectory); } try @@ -79,10 +80,10 @@ static TransportManifestLibrary() } catch (Exception ex) { - logger.Warn($"Failed to load transport manifests from development locations", ex); + logger.LogWarning(ex, "Failed to load transport manifests from development locations"); } - TransportManifests.SelectMany(t => t.Definitions).ToList().ForEach(m => logger.Info($"Found transport manifest for {m.DisplayName}")); + TransportManifests.SelectMany(t => t.Definitions).ToList().ForEach(m => logger.LogInformation("Found transport manifest for {TransportManifestDisplayName}", m.DisplayName)); } static string GetAssemblyDirectory() @@ -105,7 +106,7 @@ public static TransportManifestDefinition Find(string transportType) return transportManifestDefinition; } - static readonly ILog logger = LogManager.GetLogger(typeof(TransportManifestLibrary)); + static readonly ILogger logger = LoggerUtil.CreateStaticLogger(typeof(TransportManifestLibrary)); } } From 42d01e7d03328ebdfa00a8ccc050bbd485a29147 Mon Sep 17 00:00:00 2001 From: Phil Bastian Date: Mon, 16 Jun 2025 07:20:35 +0800 Subject: [PATCH 02/19] remove signedassembly requirement so that infrastructure can be imported --- src/ServiceControl.Transports/ServiceControl.Transports.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ServiceControl.Transports/ServiceControl.Transports.csproj b/src/ServiceControl.Transports/ServiceControl.Transports.csproj index 263429e5d4..263249e023 100644 --- a/src/ServiceControl.Transports/ServiceControl.Transports.csproj +++ b/src/ServiceControl.Transports/ServiceControl.Transports.csproj @@ -2,7 +2,6 @@ net8.0 - true ..\NServiceBus.snk From 96ec1242a80d3658cc1c788440dcb398fae22f7d Mon Sep 17 00:00:00 2001 From: Phil Bastian Date: Mon, 16 Jun 2025 07:39:31 +0800 Subject: [PATCH 03/19] revert previous change and instead propogate signing back to servicecontrol.infrastructure --- .../ServiceControl.Configuration.csproj | 2 ++ .../ServiceControl.Infrastructure.csproj | 2 ++ src/ServiceControl.Transports/ServiceControl.Transports.csproj | 1 + 3 files changed, 5 insertions(+) diff --git a/src/ServiceControl.Configuration/ServiceControl.Configuration.csproj b/src/ServiceControl.Configuration/ServiceControl.Configuration.csproj index 2a585451b7..302ef7cbea 100644 --- a/src/ServiceControl.Configuration/ServiceControl.Configuration.csproj +++ b/src/ServiceControl.Configuration/ServiceControl.Configuration.csproj @@ -2,6 +2,8 @@ net8.0 + true + ..\NServiceBus.snk diff --git a/src/ServiceControl.Infrastructure/ServiceControl.Infrastructure.csproj b/src/ServiceControl.Infrastructure/ServiceControl.Infrastructure.csproj index 123180a850..a686479039 100644 --- a/src/ServiceControl.Infrastructure/ServiceControl.Infrastructure.csproj +++ b/src/ServiceControl.Infrastructure/ServiceControl.Infrastructure.csproj @@ -2,6 +2,8 @@ net8.0 + true + ..\NServiceBus.snk diff --git a/src/ServiceControl.Transports/ServiceControl.Transports.csproj b/src/ServiceControl.Transports/ServiceControl.Transports.csproj index 263249e023..263429e5d4 100644 --- a/src/ServiceControl.Transports/ServiceControl.Transports.csproj +++ b/src/ServiceControl.Transports/ServiceControl.Transports.csproj @@ -2,6 +2,7 @@ net8.0 + true ..\NServiceBus.snk From 5aaabe6af15a776920f5ec7ab0406ab28a7827b7 Mon Sep 17 00:00:00 2001 From: Phil Bastian Date: Mon, 16 Jun 2025 08:13:00 +0800 Subject: [PATCH 04/19] fix signature of customisation classes that are dynamically created --- .../PostgreSqlTransportCustomization.cs | 5 ++++- .../SQSTransportCustomization.cs | 5 ++++- .../SqlServerTransportCustomization.cs | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/ServiceControl.Transports.PostgreSql/PostgreSqlTransportCustomization.cs b/src/ServiceControl.Transports.PostgreSql/PostgreSqlTransportCustomization.cs index c5f878f21f..4523792f18 100644 --- a/src/ServiceControl.Transports.PostgreSql/PostgreSqlTransportCustomization.cs +++ b/src/ServiceControl.Transports.PostgreSql/PostgreSqlTransportCustomization.cs @@ -7,8 +7,9 @@ using Microsoft.Extensions.Logging; using NServiceBus; using NServiceBus.Transport.PostgreSql; +using ServiceControl.Infrastructure; -public class PostgreSqlTransportCustomization(ILogger logger) : TransportCustomization +public class PostgreSqlTransportCustomization() : TransportCustomization { protected override void CustomizeTransportForPrimaryEndpoint(EndpointConfiguration endpointConfiguration, PostgreSqlTransport transportDefinition, TransportSettings transportSettings) { @@ -93,4 +94,6 @@ protected override string ToTransportQualifiedQueueNameCore(string queueName) static extern ref bool DisableDelayedDelivery(PostgreSqlTransport transport); const string DefaultSubscriptionTableName = "SubscriptionRouting"; + + static readonly ILogger logger = LoggerUtil.CreateStaticLogger(); } \ No newline at end of file diff --git a/src/ServiceControl.Transports.SQS/SQSTransportCustomization.cs b/src/ServiceControl.Transports.SQS/SQSTransportCustomization.cs index 4ae8e6a82e..8aa9a0d530 100644 --- a/src/ServiceControl.Transports.SQS/SQSTransportCustomization.cs +++ b/src/ServiceControl.Transports.SQS/SQSTransportCustomization.cs @@ -12,8 +12,9 @@ using Microsoft.Extensions.Logging; using NServiceBus; using NServiceBus.Configuration.AdvancedExtensibility; + using ServiceControl.Infrastructure; - public class SQSTransportCustomization(ILogger logger) : TransportCustomization + public class SQSTransportCustomization() : TransportCustomization { protected override void CustomizeTransportForPrimaryEndpoint(EndpointConfiguration endpointConfiguration, SqsTransport transportDefinition, TransportSettings transportSettings) { @@ -120,5 +121,7 @@ static void PromoteEnvironmentVariableFromConnectionString(string value, string environmentVariableName) => Environment.SetEnvironmentVariable(environmentVariableName, value, EnvironmentVariableTarget.Process); + static readonly ILogger logger = LoggerUtil.CreateStaticLogger(); + } } \ No newline at end of file diff --git a/src/ServiceControl.Transports.SqlServer/SqlServerTransportCustomization.cs b/src/ServiceControl.Transports.SqlServer/SqlServerTransportCustomization.cs index a89ee8fecc..034a23dc9c 100644 --- a/src/ServiceControl.Transports.SqlServer/SqlServerTransportCustomization.cs +++ b/src/ServiceControl.Transports.SqlServer/SqlServerTransportCustomization.cs @@ -8,8 +8,9 @@ using NServiceBus; using NServiceBus.Configuration.AdvancedExtensibility; using NServiceBus.Transport.SqlServer; + using ServiceControl.Infrastructure; - public class SqlServerTransportCustomization(ILogger logger) : TransportCustomization + public class SqlServerTransportCustomization() : TransportCustomization { protected override void CustomizeTransportForPrimaryEndpoint(EndpointConfiguration endpointConfiguration, SqlServerTransport transportDefinition, TransportSettings transportSettings) { @@ -87,5 +88,7 @@ protected override SqlServerTransport CreateTransport(TransportSettings transpor static extern ref bool DisableDelayedDelivery(SqlServerTransport transport); const string defaultSubscriptionTableName = "SubscriptionRouting"; + + static readonly ILogger logger = LoggerUtil.CreateStaticLogger(); } } \ No newline at end of file From 67920ff323507faca9b36f50018aefded54ed10a Mon Sep 17 00:00:00 2001 From: Phil Bastian Date: Mon, 16 Jun 2025 08:49:17 +0800 Subject: [PATCH 05/19] add ilogger to test services and remove direct construction with logger --- .../TransportTestsConfiguration.cs | 3 +-- .../TransportTestsConfiguration.cs | 3 +-- .../TransportTestsConfiguration.cs | 3 +-- src/ServiceControl.Transports.Tests/TransportTestFixture.cs | 2 ++ 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/ServiceControl.Transports.PostgreSql.Tests/TransportTestsConfiguration.cs b/src/ServiceControl.Transports.PostgreSql.Tests/TransportTestsConfiguration.cs index 2d6c34b86c..819248c612 100644 --- a/src/ServiceControl.Transports.PostgreSql.Tests/TransportTestsConfiguration.cs +++ b/src/ServiceControl.Transports.PostgreSql.Tests/TransportTestsConfiguration.cs @@ -2,7 +2,6 @@ { using System; using System.Threading.Tasks; - using ServiceControl.Infrastructure; using ServiceControl.Transports.PostgreSql; using Transports; @@ -14,7 +13,7 @@ partial class TransportTestsConfiguration public Task Configure() { - TransportCustomization = new PostgreSqlTransportCustomization(LoggerUtil.CreateStaticLogger()); + TransportCustomization = new PostgreSqlTransportCustomization(); ConnectionString = Environment.GetEnvironmentVariable(ConnectionStringKey); if (string.IsNullOrEmpty(ConnectionString)) diff --git a/src/ServiceControl.Transports.SQS.Tests/TransportTestsConfiguration.cs b/src/ServiceControl.Transports.SQS.Tests/TransportTestsConfiguration.cs index c931ef2f58..c64f5d1bca 100644 --- a/src/ServiceControl.Transports.SQS.Tests/TransportTestsConfiguration.cs +++ b/src/ServiceControl.Transports.SQS.Tests/TransportTestsConfiguration.cs @@ -2,7 +2,6 @@ { using System; using System.Threading.Tasks; - using ServiceControl.Infrastructure; using Transports; using Transports.SQS; @@ -14,7 +13,7 @@ partial class TransportTestsConfiguration public Task Configure() { - TransportCustomization = new SQSTransportCustomization(LoggerUtil.CreateStaticLogger()); + TransportCustomization = new SQSTransportCustomization(); ConnectionString = Environment.GetEnvironmentVariable(ConnectionStringKey); if (string.IsNullOrEmpty(ConnectionString)) diff --git a/src/ServiceControl.Transports.SqlServer.Tests/TransportTestsConfiguration.cs b/src/ServiceControl.Transports.SqlServer.Tests/TransportTestsConfiguration.cs index abcad12793..9cf82c6dde 100644 --- a/src/ServiceControl.Transports.SqlServer.Tests/TransportTestsConfiguration.cs +++ b/src/ServiceControl.Transports.SqlServer.Tests/TransportTestsConfiguration.cs @@ -2,7 +2,6 @@ { using System; using System.Threading.Tasks; - using ServiceControl.Infrastructure; using Transports; using Transports.SqlServer; @@ -14,7 +13,7 @@ partial class TransportTestsConfiguration public Task Configure() { - TransportCustomization = new SqlServerTransportCustomization(LoggerUtil.CreateStaticLogger()); + TransportCustomization = new SqlServerTransportCustomization(); ConnectionString = Environment.GetEnvironmentVariable(ConnectionStringKey); if (string.IsNullOrEmpty(ConnectionString)) diff --git a/src/ServiceControl.Transports.Tests/TransportTestFixture.cs b/src/ServiceControl.Transports.Tests/TransportTestFixture.cs index 0f6d9027e1..cd8c301137 100644 --- a/src/ServiceControl.Transports.Tests/TransportTestFixture.cs +++ b/src/ServiceControl.Transports.Tests/TransportTestFixture.cs @@ -20,6 +20,7 @@ class TransportTestFixture [SetUp] public virtual async Task Setup() { + //TODO remove LogManager usage LogManager.UseFactory(new TestContextAppenderFactory()); configuration = new TransportTestsConfiguration(); testCancellationTokenSource = Debugger.IsAttached ? new CancellationTokenSource() : new CancellationTokenSource(TestTimeout); @@ -102,6 +103,7 @@ protected async Task StartQueueLengthProvider(string queueName configuration.TransportCustomization.CustomizeMonitoringEndpoint(new EndpointConfiguration("queueName"), transportSettings); serviceCollection.AddSingleton>((qlt, _) => onQueueLengthReported(qlt.First())); + serviceCollection.AddLogging(); var serviceProvider = serviceCollection.BuildServiceProvider(); queueLengthProvider = serviceProvider.GetRequiredService(); From b3c690a6c198c3976157c4b48351ce76e69157a4 Mon Sep 17 00:00:00 2001 From: Phil Bastian Date: Tue, 17 Jun 2025 07:48:10 +0800 Subject: [PATCH 06/19] get tests to use ilogger --- .../LoggerUtil.cs | 14 ++- .../FullEndpointTestFixture.cs | 2 + .../TestContextAppender.cs | 86 +++---------------- .../TestContextAppenderFactory.cs | 13 ++- .../TransportManifestLibraryTests.cs | 7 ++ .../TransportTestFixture.cs | 3 +- 6 files changed, 46 insertions(+), 79 deletions(-) diff --git a/src/ServiceControl.Infrastructure/LoggerUtil.cs b/src/ServiceControl.Infrastructure/LoggerUtil.cs index 039d0b4938..0757d17fa0 100644 --- a/src/ServiceControl.Infrastructure/LoggerUtil.cs +++ b/src/ServiceControl.Infrastructure/LoggerUtil.cs @@ -6,8 +6,18 @@ public static class LoggerUtil { + /// + /// used for tests + /// + public static ILoggerFactory LoggerFactory { private get; set; } + public static void BuildLogger(this ILoggingBuilder loggingBuilder, LogLevel level) { + if (LoggerFactory != null) + { + return; + } + //TODO: can we get these from settings too? loggingBuilder.AddNLog(); loggingBuilder.AddSeq(); @@ -16,13 +26,13 @@ public static void BuildLogger(this ILoggingBuilder loggingBuilder, LogLevel lev public static ILogger CreateStaticLogger(LogLevel level = LogLevel.Information) { - var factory = LoggerFactory.Create(configure => configure.BuildLogger(level)); + var factory = LoggerFactory ?? Microsoft.Extensions.Logging.LoggerFactory.Create(configure => configure.BuildLogger(level)); return factory.CreateLogger(); } public static ILogger CreateStaticLogger(Type type, LogLevel level = LogLevel.Information) { - var factory = LoggerFactory.Create(configure => configure.BuildLogger(level)); + var factory = LoggerFactory ?? Microsoft.Extensions.Logging.LoggerFactory.Create(configure => configure.BuildLogger(level)); return factory.CreateLogger(type); } } diff --git a/src/ServiceControl.Transports.Tests/FullEndpointTestFixture.cs b/src/ServiceControl.Transports.Tests/FullEndpointTestFixture.cs index 55f7f24383..f2c4d13313 100644 --- a/src/ServiceControl.Transports.Tests/FullEndpointTestFixture.cs +++ b/src/ServiceControl.Transports.Tests/FullEndpointTestFixture.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using NServiceBus.AcceptanceTesting.Customization; using NUnit.Framework; + using ServiceControl.Infrastructure; [TestFixture] class FullEndpointTestFixture @@ -11,6 +12,7 @@ class FullEndpointTestFixture [SetUp] public virtual async Task Setup() { + LoggerUtil.LoggerFactory = new TestContextAppenderFactory(); configuration = new TransportTestsConfiguration(); var queueSuffix = $"-{System.IO.Path.GetRandomFileName().Replace(".", string.Empty)}"; diff --git a/src/ServiceControl.Transports.Tests/TestContextAppender.cs b/src/ServiceControl.Transports.Tests/TestContextAppender.cs index 26a3a5645c..e3b9524253 100644 --- a/src/ServiceControl.Transports.Tests/TestContextAppender.cs +++ b/src/ServiceControl.Transports.Tests/TestContextAppender.cs @@ -1,87 +1,29 @@ namespace ServiceControl.Transport.Tests { using System; - using NServiceBus.Logging; + using Microsoft.Extensions.Logging; using NUnit.Framework; - class TestContextAppender : ILog + class TestContextAppender(string categoryName) : ILogger { - public bool IsDebugEnabled => false; - public bool IsInfoEnabled => false; - public bool IsWarnEnabled => true; - public bool IsErrorEnabled => true; - public bool IsFatalEnabled => true; - - public void Debug(string message) => Log(message, LogLevel.Debug); - - public void Debug(string message, Exception exception) - { - var fullMessage = $"{message} {exception}"; - Log(fullMessage, LogLevel.Debug); - } - - public void DebugFormat(string format, params object[] args) + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { - var fullMessage = string.Format(format, args); - Log(fullMessage, LogLevel.Debug); + if (IsEnabled(logLevel)) + { + TestContext.Out.WriteLine($"{categoryName}: {formatter(state, exception)}"); + } } + public bool IsEnabled(LogLevel logLevel) => logLevel >= LogLevel.Warning; - public void Info(string message) => Log(message, LogLevel.Info); + public IDisposable BeginScope(TState state) where TState : notnull => Disposable.Instance; - public void Info(string message, Exception exception) + class Disposable : IDisposable { - var fullMessage = $"{message} {exception}"; - Log(fullMessage, LogLevel.Info); - } + public static Disposable Instance = new(); - public void InfoFormat(string format, params object[] args) - { - var fullMessage = string.Format(format, args); - Log(fullMessage, LogLevel.Info); + public void Dispose() + { + } } - - public void Warn(string message) => Log(message, LogLevel.Warn); - - public void Warn(string message, Exception exception) - { - var fullMessage = $"{message} {exception}"; - Log(fullMessage, LogLevel.Warn); - } - - public void WarnFormat(string format, params object[] args) - { - var fullMessage = string.Format(format, args); - Log(fullMessage, LogLevel.Warn); - } - - public void Error(string message) => Log(message, LogLevel.Error); - - public void Error(string message, Exception exception) - { - var fullMessage = $"{message} {exception}"; - Log(fullMessage, LogLevel.Error); - } - - public void ErrorFormat(string format, params object[] args) - { - var fullMessage = string.Format(format, args); - Log(fullMessage, LogLevel.Error); - } - - public void Fatal(string message) => Log(message, LogLevel.Fatal); - - public void Fatal(string message, Exception exception) - { - var fullMessage = $"{message} {exception}"; - Log(fullMessage, LogLevel.Fatal); - } - - public void FatalFormat(string format, params object[] args) - { - var fullMessage = string.Format(format, args); - Log(fullMessage, LogLevel.Fatal); - } - - static void Log(string message, LogLevel _) => TestContext.Out.WriteLine(message); } } \ No newline at end of file diff --git a/src/ServiceControl.Transports.Tests/TestContextAppenderFactory.cs b/src/ServiceControl.Transports.Tests/TestContextAppenderFactory.cs index 75d8dbfcef..9c5b4da728 100644 --- a/src/ServiceControl.Transports.Tests/TestContextAppenderFactory.cs +++ b/src/ServiceControl.Transports.Tests/TestContextAppenderFactory.cs @@ -1,12 +1,17 @@ namespace ServiceControl.Transport.Tests { - using System; - using NServiceBus.Logging; + using Microsoft.Extensions.Logging; class TestContextAppenderFactory : ILoggerFactory { - public ILog GetLogger(Type type) => GetLogger(type.FullName); + public void AddProvider(ILoggerProvider provider) + { + } - public ILog GetLogger(string name) => new TestContextAppender(); + public ILogger CreateLogger(string categoryName) => new TestContextAppender(categoryName); + + public void Dispose() + { + } } } \ No newline at end of file diff --git a/src/ServiceControl.Transports.Tests/TransportManifestLibraryTests.cs b/src/ServiceControl.Transports.Tests/TransportManifestLibraryTests.cs index aa678c8b01..78a6aea1d7 100644 --- a/src/ServiceControl.Transports.Tests/TransportManifestLibraryTests.cs +++ b/src/ServiceControl.Transports.Tests/TransportManifestLibraryTests.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using NUnit.Framework; using Particular.Approvals; + using ServiceControl.Infrastructure; using ServiceControl.Transports; [TestFixture] @@ -16,6 +17,12 @@ public class TransportManifestLibraryTests const string transportType = "ServiceControl.Transports.ASBS.ASBSTransportCustomization, ServiceControl.Transports.ASBS"; const string transportAlias = "ServiceControl.Transports.AzureServiceBus.AzureServiceBusTransport, ServiceControl.Transports.AzureServiceBus"; + [SetUp] + public void SetUp() + { + LoggerUtil.LoggerFactory = new TestContextAppenderFactory(); + } + [Test] public void Should_find_transport_manifest_by_name() { diff --git a/src/ServiceControl.Transports.Tests/TransportTestFixture.cs b/src/ServiceControl.Transports.Tests/TransportTestFixture.cs index cd8c301137..4a769f63bb 100644 --- a/src/ServiceControl.Transports.Tests/TransportTestFixture.cs +++ b/src/ServiceControl.Transports.Tests/TransportTestFixture.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using NServiceBus; + using NServiceBus.Extensions.Logging; using NServiceBus.Logging; using NServiceBus.Transport; using NUnit.Framework; @@ -21,7 +22,7 @@ class TransportTestFixture public virtual async Task Setup() { //TODO remove LogManager usage - LogManager.UseFactory(new TestContextAppenderFactory()); + LogManager.UseFactory(new ExtensionsLoggerFactory(new TestContextAppenderFactory())); configuration = new TransportTestsConfiguration(); testCancellationTokenSource = Debugger.IsAttached ? new CancellationTokenSource() : new CancellationTokenSource(TestTimeout); registrations = []; From 66aaa0225579fce5193958868a94003f123b153a Mon Sep 17 00:00:00 2001 From: JasonTaylorDev Date: Tue, 10 Jun 2025 10:34:26 +1000 Subject: [PATCH 07/19] Switch to .NET logging --- .../CustomChecks/CheckDirtyMemory.cs | 14 ++++---- .../CustomChecks/CheckFreeDiskSpace.cs | 32 ++++++++--------- ...CheckMinimumStorageRequiredForIngestion.cs | 35 +++++++++---------- .../CustomChecks/CheckRavenDBIndexLag.cs | 20 ++++++----- .../RavenEmbeddedPersistenceLifecycle.cs | 8 ++--- .../RavenPersistenceConfiguration.cs | 22 +++++++----- .../LoggerUtil.cs | 27 ++++++++++++++ .../ServiceControlComponentRunner.cs | 3 +- .../Hosting/Commands/SetupCommand.cs | 8 ++--- .../MonitoredEndpointMessageTypeParser.cs | 7 ++-- .../Licensing/ActiveLicense.cs | 14 +++++--- .../Licensing/LicenseCheckHostedService.cs | 7 ++-- src/ServiceControl.Monitoring/Program.cs | 11 +++--- .../LegacyQueueLengthReportHandler.cs | 15 ++------ .../ServiceControl.Monitoring.csproj | 1 - 15 files changed, 118 insertions(+), 106 deletions(-) create mode 100644 src/ServiceControl.Infrastructure/LoggerUtil.cs diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckDirtyMemory.cs b/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckDirtyMemory.cs index 37731858d8..409b3d6df1 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckDirtyMemory.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckDirtyMemory.cs @@ -3,26 +3,24 @@ namespace ServiceControl.Audit.Persistence.RavenDB.CustomChecks; using System; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using NServiceBus.CustomChecks; -using NServiceBus.Logging; -class CheckDirtyMemory(MemoryInformationRetriever memoryInformationRetriever) : CustomCheck("RavenDB dirty memory", "ServiceControl.Audit Health", TimeSpan.FromMinutes(5)) +class CheckDirtyMemory(MemoryInformationRetriever memoryInformationRetriever, ILogger logger) : CustomCheck("RavenDB dirty memory", "ServiceControl.Audit Health", TimeSpan.FromMinutes(5)) { + public override async Task PerformCheck(CancellationToken cancellationToken = default) { var (isHighDirty, dirtyMemory) = await memoryInformationRetriever.GetMemoryInformation(cancellationToken); - Log.Debug($"RavenDB dirty memory value: {dirtyMemory}."); + logger.LogDebug("RavenDB dirty memory value: {DirtyMemory}.", dirtyMemory); if (isHighDirty) { - var message = $"There is a high level of RavenDB dirty memory ({dirtyMemory}). See https://docs.particular.net/servicecontrol/troubleshooting#ravendb-dirty-memory for guidance on how to mitigate the issue."; - Log.Warn(message); - return CheckResult.Failed(message); + logger.LogWarning("There is a high level of RavenDB dirty memory ({DirtyMemory}). See https://docs.particular.net/servicecontrol/troubleshooting#ravendb-dirty-memory for guidance on how to mitigate the issue.", dirtyMemory); + return CheckResult.Failed($"There is a high level of RavenDB dirty memory ({dirtyMemory}). See https://docs.particular.net/servicecontrol/troubleshooting#ravendb-dirty-memory for guidance on how to mitigate the issue."); } return CheckResult.Pass; } - - static readonly ILog Log = LogManager.GetLogger(); } \ No newline at end of file diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckFreeDiskSpace.cs b/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckFreeDiskSpace.cs index 18705af5b9..923b6208d6 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckFreeDiskSpace.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckFreeDiskSpace.cs @@ -5,17 +5,18 @@ using System.IO; using System.Threading; using System.Threading.Tasks; + using Microsoft.Extensions.Logging; using NServiceBus.CustomChecks; - using NServiceBus.Logging; using RavenDB; + using ServiceControl.Infrastructure; - class CheckFreeDiskSpace(DatabaseConfiguration databaseConfiguration) : CustomCheck("ServiceControl.Audit database", "Storage space", TimeSpan.FromMinutes(5)) + class CheckFreeDiskSpace(DatabaseConfiguration databaseConfiguration, ILogger logger) : CustomCheck("ServiceControl.Audit database", "Storage space", TimeSpan.FromMinutes(5)) { public override Task PerformCheck(CancellationToken cancellationToken = default) { - if (Logger.IsDebugEnabled) + if (logger.IsEnabled(LogLevel.Debug)) { - Logger.Debug($"Check ServiceControl data drive space remaining custom check starting. Threshold {percentageThreshold:P0}"); + logger.LogDebug("Check ServiceControl data drive space remaining custom check starting. Threshold {PercentageThreshold:P0}", percentageThreshold); } if (!databaseConfiguration.ServerConfiguration.UseEmbeddedServer) @@ -34,9 +35,9 @@ public override Task PerformCheck(CancellationToken cancellationTok var percentRemaining = (decimal)dataDriveInfo.AvailableFreeSpace / dataDriveInfo.TotalSize; - if (Logger.IsDebugEnabled) + if (logger.IsEnabled(LogLevel.Debug)) { - Logger.Debug($"Free space: {availableFreeSpace:N0}B | Total: {totalSpace:N0}B | Percent remaining {percentRemaining:P1}"); + logger.LogDebug("Free space: {AvailableFreeSpace:N0}B | Total: {TotalSpace:N0}B | Percent remaining {PercentRemaining:P1}", availableFreeSpace, totalSpace, percentRemaining); } return percentRemaining > percentageThreshold @@ -51,26 +52,22 @@ public static int Parse(IDictionary settings) thresholdValue = $"{DataSpaceRemainingThresholdDefault}"; } - string message; if (!int.TryParse(thresholdValue, out var threshold)) { - message = $"{RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey} must be an integer."; - Logger.Fatal(message); - throw new Exception(message); + Logger.LogCritical("{RavenPersistenceConfigurationDataSpaceRemainingThresholdKey} must be an integer.", RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey); + throw new Exception($"{RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey} must be an integer."); } if (threshold < 0) { - message = $"{RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey} is invalid, minimum value is 0."; - Logger.Fatal(message); - throw new Exception(message); + Logger.LogCritical("{RavenPersistenceConfigurationDataSpaceRemainingThresholdKey} is invalid, minimum value is 0.", RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey); + throw new Exception($"{RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey} is invalid, minimum value is 0."); } if (threshold > 100) { - message = $"{RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey} is invalid, maximum value is 100."; - Logger.Fatal(message); - throw new Exception(message); + Logger.LogCritical("{RavenPersistenceConfigurationDataSpaceRemainingThresholdKey} is invalid, maximum value is 100.", RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey); + throw new Exception($"{RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey} is invalid, maximum value is 100."); } return threshold; @@ -78,8 +75,7 @@ public static int Parse(IDictionary settings) readonly string dataPathRoot = Path.GetPathRoot(databaseConfiguration.ServerConfiguration.DbPath); readonly decimal percentageThreshold = databaseConfiguration.DataSpaceRemainingThreshold / 100m; - + static readonly ILogger Logger = LoggerUtil.CreateStaticLogger(); public const int DataSpaceRemainingThresholdDefault = 20; - static readonly ILog Logger = LogManager.GetLogger(typeof(CheckFreeDiskSpace)); } } \ No newline at end of file diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs b/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs index 6d2fccbc38..56153b3169 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs @@ -5,19 +5,20 @@ using System.IO; using System.Threading; using System.Threading.Tasks; + using Microsoft.Extensions.Logging; using NServiceBus.CustomChecks; - using NServiceBus.Logging; using RavenDB; + using ServiceControl.Infrastructure; - class CheckMinimumStorageRequiredForIngestion(MinimumRequiredStorageState stateHolder, DatabaseConfiguration databaseConfiguration) : CustomCheck("Audit Message Ingestion Process", "ServiceControl.Audit Health", TimeSpan.FromSeconds(5)) + class CheckMinimumStorageRequiredForIngestion(MinimumRequiredStorageState stateHolder, DatabaseConfiguration databaseConfiguration, ILogger logger) : CustomCheck("Audit Message Ingestion Process", "ServiceControl.Audit Health", TimeSpan.FromSeconds(5)) { public override Task PerformCheck(CancellationToken cancellationToken = default) { var percentageThreshold = databaseConfiguration.MinimumStorageLeftRequiredForIngestion / 100m; - if (Logger.IsDebugEnabled) + if (logger.IsEnabled(LogLevel.Debug)) { - Logger.Debug($"Check ServiceControl data drive space starting. Threshold {percentageThreshold:P0}"); + logger.LogDebug("Check ServiceControl data drive space starting. Threshold {PercentageThreshold:P0}", percentageThreshold); } // Should be checking UseEmbeddedServer but need to check DbPath instead for the ATT hack to work @@ -35,9 +36,9 @@ public override Task PerformCheck(CancellationToken cancellationTok var percentRemaining = (decimal)dataDriveInfo.AvailableFreeSpace / dataDriveInfo.TotalSize; - if (Logger.IsDebugEnabled) + if (logger.IsEnabled(LogLevel.Debug)) { - Logger.Debug($"Free space: {availableFreeSpace} | Total: {totalSpace} | Percent remaining {percentRemaining:P0}"); + logger.LogDebug("Free space: {AvailableFreeSpace} | Total: {TotalSpace} | Percent remaining {PercentRemaining:P0}", availableFreeSpace, totalSpace, percentRemaining); } if (percentRemaining > percentageThreshold) @@ -46,10 +47,9 @@ public override Task PerformCheck(CancellationToken cancellationTok return SuccessResult; } - var message = $"Audit message ingestion stopped! {percentRemaining:P0} disk space remaining on data drive '{dataDriveInfo.VolumeLabel} ({dataDriveInfo.RootDirectory})' on '{Environment.MachineName}'. This is less than {percentageThreshold}% - the minimal required space configured. The threshold can be set using the {RavenPersistenceConfiguration.MinimumStorageLeftRequiredForIngestionKey} configuration setting."; - Logger.Warn(message); + logger.LogWarning("Audit message ingestion stopped! {PercentRemaining:P0} disk space remaining on data drive '{DataDriveInfoVolumeLabel} ({DataDriveInfoRootDirectory})' on '{EnvironmentMachineName}'. This is less than {PercentageThreshold}% - the minimal required space configured. The threshold can be set using the {RavenPersistenceConfigurationMinimumStorageLeftRequiredForIngestionKey} configuration setting.", percentRemaining, dataDriveInfo.VolumeLabel, dataDriveInfo.RootDirectory, Environment.MachineName, percentageThreshold, RavenPersistenceConfiguration.MinimumStorageLeftRequiredForIngestionKey); stateHolder.CanIngestMore = false; - return CheckResult.Failed(message); + return CheckResult.Failed($"Audit message ingestion stopped! {percentRemaining:P0} disk space remaining on data drive '{dataDriveInfo.VolumeLabel} ({dataDriveInfo.RootDirectory})' on '{Environment.MachineName}'. This is less than {percentageThreshold}% - the minimal required space configured. The threshold can be set using the {RavenPersistenceConfiguration.MinimumStorageLeftRequiredForIngestionKey} configuration setting."); } public static int Parse(IDictionary settings) @@ -61,23 +61,20 @@ public static int Parse(IDictionary settings) if (!int.TryParse(thresholdValue, out var threshold)) { - var message = $"{RavenPersistenceConfiguration.MinimumStorageLeftRequiredForIngestionKey} must be an integer."; - Logger.Fatal(message); - throw new Exception(message); + Logger.LogCritical("{RavenPersistenceConfigurationMinimumStorageLeftRequiredForIngestionKey} must be an integer.", RavenPersistenceConfiguration.MinimumStorageLeftRequiredForIngestionKey); + throw new Exception($"{RavenPersistenceConfiguration.MinimumStorageLeftRequiredForIngestionKey} must be an integer."); } if (threshold < 0) { - var message = $"{RavenPersistenceConfiguration.MinimumStorageLeftRequiredForIngestionKey} is invalid, minimum value is 0."; - Logger.Fatal(message); - throw new Exception(message); + Logger.LogCritical("{RavenPersistenceConfigurationMinimumStorageLeftRequiredForIngestionKey} is invalid, minimum value is 0.", RavenPersistenceConfiguration.MinimumStorageLeftRequiredForIngestionKey); + throw new Exception($"{RavenPersistenceConfiguration.MinimumStorageLeftRequiredForIngestionKey} is invalid, minimum value is 0."); } if (threshold > 100) { - var message = $"{RavenPersistenceConfiguration.MinimumStorageLeftRequiredForIngestionKey} is invalid, maximum value is 100."; - Logger.Fatal(message); - throw new Exception(message); + Logger.LogCritical("{RavenPersistenceConfigurationMinimumStorageLeftRequiredForIngestionKey} is invalid, maximum value is 100.", RavenPersistenceConfiguration.MinimumStorageLeftRequiredForIngestionKey); + throw new Exception($"{RavenPersistenceConfiguration.MinimumStorageLeftRequiredForIngestionKey} is invalid, maximum value is 100."); } return threshold; @@ -85,6 +82,6 @@ public static int Parse(IDictionary settings) public const int MinimumStorageLeftRequiredForIngestionDefault = 5; static readonly Task SuccessResult = Task.FromResult(CheckResult.Pass); - static readonly ILog Logger = LogManager.GetLogger(typeof(CheckMinimumStorageRequiredForIngestion)); + static readonly ILogger Logger = LoggerUtil.CreateStaticLogger(); } } \ No newline at end of file diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckRavenDBIndexLag.cs b/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckRavenDBIndexLag.cs index 41f4347a71..193e384f2d 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckRavenDBIndexLag.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckRavenDBIndexLag.cs @@ -5,12 +5,13 @@ using System.Text; using System.Threading; using System.Threading.Tasks; + using Microsoft.Extensions.Logging; using NServiceBus.CustomChecks; - using NServiceBus.Logging; using Raven.Client.Documents.Operations; using ServiceControl.Audit.Persistence.RavenDB; + using ServiceControl.Infrastructure; - class CheckRavenDBIndexLag(IRavenDocumentStoreProvider documentStoreProvider) : CustomCheck("Audit Database Index Lag", "ServiceControl.Audit Health", TimeSpan.FromMinutes(5)) + class CheckRavenDBIndexLag(IRavenDocumentStoreProvider documentStoreProvider, ILogger logger) : CustomCheck("Audit Database Index Lag", "ServiceControl.Audit Health", TimeSpan.FromMinutes(5)) { public override async Task PerformCheck(CancellationToken cancellationToken = default) { @@ -20,7 +21,7 @@ public override async Task PerformCheck(CancellationToken cancellat CreateDiagnosticsLogEntry(statistics, indexes); - var indexCountWithTooMuchLag = CheckAndReportIndexesWithTooMuchIndexLag(indexes); + var indexCountWithTooMuchLag = CheckAndReportIndexesWithTooMuchIndexLag(indexes, logger); if (indexCountWithTooMuchLag > 0) { @@ -30,7 +31,7 @@ public override async Task PerformCheck(CancellationToken cancellat return CheckResult.Pass; } - static int CheckAndReportIndexesWithTooMuchIndexLag(IndexInformation[] indexes) + static int CheckAndReportIndexesWithTooMuchIndexLag(IndexInformation[] indexes, ILogger logger) { int indexCountWithTooMuchLag = 0; @@ -43,12 +44,12 @@ static int CheckAndReportIndexesWithTooMuchIndexLag(IndexInformation[] indexes) if (indexLag > IndexLagThresholdError) { indexCountWithTooMuchLag++; - Log.Error($"Index [{indexStats.Name}] IndexingLag {indexLag} is above error threshold ({IndexLagThresholdError}). Launch in maintenance mode to let indexes catch up."); + logger.LogError("Index [{IndexStatsName}] IndexingLag {IndexLag} is above error threshold ({IndexLagThresholdError}). Launch in maintenance mode to let indexes catch up.", indexStats.Name, indexLag, IndexLagThresholdError); } else if (indexLag > IndexLagThresholdWarning) { indexCountWithTooMuchLag++; - Log.Warn($"Index [{indexStats.Name}] IndexingLag {indexLag} is above warning threshold ({IndexLagThresholdWarning}). Launch in maintenance mode to let indexes catch up."); + logger.LogWarning("Index [{IndexStatsName}] IndexingLag {IndexLag} is above warning threshold ({IndexLagThresholdWarning}). Launch in maintenance mode to let indexes catch up.", indexStats.Name, indexLag, IndexLagThresholdWarning); } } } @@ -58,7 +59,7 @@ static int CheckAndReportIndexesWithTooMuchIndexLag(IndexInformation[] indexes) static void CreateDiagnosticsLogEntry(DatabaseStatistics statistics, IndexInformation[] indexes) { - if (!Log.IsDebugEnabled) + if (!Logger.IsEnabled(LogLevel.Debug)) { return; } @@ -72,11 +73,12 @@ static void CreateDiagnosticsLogEntry(DatabaseStatistics statistics, IndexInform { report.AppendLine($"- Index [{indexStats.Name,-44}] State: {indexStats.State}, Stale: {indexStats.IsStale,-5}, Priority: {indexStats.Priority,-6}, LastIndexingTime: {indexStats.LastIndexingTime:u}"); } - Log.Debug(report.ToString()); + + Logger.LogDebug(report.ToString()); } static readonly TimeSpan IndexLagThresholdWarning = TimeSpan.FromMinutes(1); static readonly TimeSpan IndexLagThresholdError = TimeSpan.FromMinutes(10); - static readonly ILog Log = LogManager.GetLogger(); + static readonly ILogger Logger = LoggerUtil.CreateStaticLogger(); } } \ No newline at end of file diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/RavenEmbeddedPersistenceLifecycle.cs b/src/ServiceControl.Audit.Persistence.RavenDB/RavenEmbeddedPersistenceLifecycle.cs index f7f1ecee16..80bf05b742 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/RavenEmbeddedPersistenceLifecycle.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/RavenEmbeddedPersistenceLifecycle.cs @@ -6,12 +6,12 @@ namespace ServiceControl.Audit.Persistence.RavenDB using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; - using NServiceBus.Logging; + using Microsoft.Extensions.Logging; using Raven.Client.Documents; using Raven.Client.Exceptions.Database; using ServiceControl.RavenDB; - sealed class RavenEmbeddedPersistenceLifecycle(DatabaseConfiguration databaseConfiguration, IHostApplicationLifetime lifetime) : IRavenPersistenceLifecycle, IRavenDocumentStoreProvider, IDisposable + sealed class RavenEmbeddedPersistenceLifecycle(DatabaseConfiguration databaseConfiguration, IHostApplicationLifetime lifetime, ILogger logger) : IRavenPersistenceLifecycle, IRavenDocumentStoreProvider, IDisposable { public async ValueTask GetDocumentStore(CancellationToken cancellationToken = default) { @@ -61,7 +61,7 @@ public async Task Initialize(CancellationToken cancellationToken = default) } catch (DatabaseLoadTimeoutException e) { - Log.Warn("Connecting to the embedded RavenDB database timed out. Retrying in 500ms...", e); + logger.LogWarning(e, "Connecting to the embedded RavenDB database timed out. Retrying in 500ms..."); await Task.Delay(500, cancellationToken); } } @@ -83,7 +83,5 @@ public void Dispose() IDocumentStore? documentStore; EmbeddedDatabase? database; readonly SemaphoreSlim initializeSemaphore = new(1, 1); - - static readonly ILog Log = LogManager.GetLogger(typeof(RavenEmbeddedPersistenceLifecycle)); } } diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/RavenPersistenceConfiguration.cs b/src/ServiceControl.Audit.Persistence.RavenDB/RavenPersistenceConfiguration.cs index b22003ceae..fc8aa2f281 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/RavenPersistenceConfiguration.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/RavenPersistenceConfiguration.cs @@ -5,7 +5,8 @@ using System.IO; using System.Reflection; using CustomChecks; - using NServiceBus.Logging; + using Microsoft.Extensions.Logging; + using ServiceControl.Infrastructure; public class RavenPersistenceConfiguration : IPersistenceConfiguration { @@ -141,15 +142,17 @@ static int GetExpirationProcessTimerInSeconds(PersistenceSettings settings) expirationProcessTimerInSeconds = int.Parse(expirationProcessTimerInSecondsString); } + var maxExpirationProcessTimerInSeconds = TimeSpan.FromHours(3).TotalSeconds; + if (expirationProcessTimerInSeconds < 0) { - Logger.Error($"ExpirationProcessTimerInSeconds cannot be negative. Defaulting to {ExpirationProcessTimerInSecondsDefault}"); + Logger.LogError("ExpirationProcessTimerInSeconds cannot be negative. Defaulting to {ExpirationProcessTimerInSecondsDefault}", ExpirationProcessTimerInSecondsDefault); return ExpirationProcessTimerInSecondsDefault; } - if (expirationProcessTimerInSeconds > TimeSpan.FromHours(3).TotalSeconds) + if (expirationProcessTimerInSeconds > maxExpirationProcessTimerInSeconds) { - Logger.Error($"ExpirationProcessTimerInSeconds cannot be larger than {TimeSpan.FromHours(3).TotalSeconds}. Defaulting to {ExpirationProcessTimerInSecondsDefault}"); + Logger.LogError("ExpirationProcessTimerInSeconds cannot be larger than {MaxExpirationProcessTimerInSeconds}. Defaulting to {ExpirationProcessTimerInSecondsDefault}", maxExpirationProcessTimerInSeconds, ExpirationProcessTimerInSecondsDefault); return ExpirationProcessTimerInSecondsDefault; } @@ -165,15 +168,17 @@ static int GetBulkInsertCommitTimeout(PersistenceSettings settings) bulkInsertCommitTimeoutInSeconds = int.Parse(bulkInsertCommitTimeoutString); } + var maxBulkInsertCommitTimeoutInSeconds = TimeSpan.FromHours(1).TotalSeconds; + if (bulkInsertCommitTimeoutInSeconds < 0) { - Logger.Error($"BulkInsertCommitTimeout cannot be negative. Defaulting to {BulkInsertCommitTimeoutInSecondsDefault}"); + Logger.LogError("BulkInsertCommitTimeout cannot be negative. Defaulting to {BulkInsertCommitTimeoutInSecondsDefault}", BulkInsertCommitTimeoutInSecondsDefault); return BulkInsertCommitTimeoutInSecondsDefault; } - if (bulkInsertCommitTimeoutInSeconds > TimeSpan.FromHours(1).TotalSeconds) + if (bulkInsertCommitTimeoutInSeconds > maxBulkInsertCommitTimeoutInSeconds) { - Logger.Error($"BulkInsertCommitTimeout cannot be larger than {TimeSpan.FromHours(1).TotalSeconds}. Defaulting to {BulkInsertCommitTimeoutInSecondsDefault}"); + Logger.LogError("BulkInsertCommitTimeout cannot be larger than {MaxBulkInsertCommitTimeoutInSeconds}. Defaulting to {BulkInsertCommitTimeoutInSecondsDefault}", maxBulkInsertCommitTimeoutInSeconds, BulkInsertCommitTimeoutInSecondsDefault); return BulkInsertCommitTimeoutInSecondsDefault; } @@ -193,9 +198,8 @@ static string GetLogPath(PersistenceSettings settings) return logPath; } - static readonly ILog Logger = LogManager.GetLogger(typeof(RavenPersistenceConfiguration)); - const int ExpirationProcessTimerInSecondsDefault = 600; const int BulkInsertCommitTimeoutInSecondsDefault = 60; + static readonly ILogger Logger = LoggerUtil.CreateStaticLogger(); } } diff --git a/src/ServiceControl.Infrastructure/LoggerUtil.cs b/src/ServiceControl.Infrastructure/LoggerUtil.cs new file mode 100644 index 0000000000..98aeb7d865 --- /dev/null +++ b/src/ServiceControl.Infrastructure/LoggerUtil.cs @@ -0,0 +1,27 @@ +namespace ServiceControl.Infrastructure +{ + using System; + using Microsoft.Extensions.Logging; + using NLog.Extensions.Logging; + + public static class LoggerUtil + { + public static void BuildLogger(this ILoggingBuilder loggingBuilder, LogLevel level) + { + loggingBuilder.AddNLog(); + loggingBuilder.SetMinimumLevel(level); + } + + public static ILogger CreateStaticLogger(LogLevel level = LogLevel.Information) + { + var factory = LoggerFactory.Create(configure => configure.BuildLogger(level)); + return factory.CreateLogger(); + } + + public static ILogger CreateStaticLogger(Type type, LogLevel level = LogLevel.Information) + { + var factory = LoggerFactory.Create(configure => configure.BuildLogger(level)); + return factory.CreateLogger(type); + } + } +} \ No newline at end of file diff --git a/src/ServiceControl.Monitoring.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs b/src/ServiceControl.Monitoring.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs index debc3ed629..212f1e03a1 100644 --- a/src/ServiceControl.Monitoring.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs +++ b/src/ServiceControl.Monitoring.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs @@ -18,6 +18,7 @@ namespace ServiceControl.Monitoring.AcceptanceTests.TestSupport using NServiceBus.AcceptanceTesting; using NServiceBus.AcceptanceTesting.Support; using NServiceBus.Logging; + using ServiceControl.Infrastructure; class ServiceControlComponentRunner( ITransportIntegration transportToUse, @@ -72,7 +73,7 @@ async Task InitializeServiceControl(ScenarioContext context) using (new DiagnosticTimer($"Creating infrastructure for {settings.InstanceName}")) { - var setupCommand = new SetupCommand(); + var setupCommand = new SetupCommand(LoggerUtil.CreateStaticLogger()); await setupCommand.Execute(new HostArguments([]), settings); } diff --git a/src/ServiceControl.Monitoring/Hosting/Commands/SetupCommand.cs b/src/ServiceControl.Monitoring/Hosting/Commands/SetupCommand.cs index 3bd7830438..d585434197 100644 --- a/src/ServiceControl.Monitoring/Hosting/Commands/SetupCommand.cs +++ b/src/ServiceControl.Monitoring/Hosting/Commands/SetupCommand.cs @@ -1,16 +1,16 @@ namespace ServiceControl.Monitoring { using System.Threading.Tasks; - using NServiceBus.Logging; + using Microsoft.Extensions.Logging; using Transports; - class SetupCommand : AbstractCommand + class SetupCommand(ILogger logger) : AbstractCommand { public override Task Execute(HostArguments args, Settings settings) { if (args.SkipQueueCreation) { - Logger.Info("Skipping queue creation"); + logger.LogInformation("Skipping queue creation"); return Task.CompletedTask; } @@ -19,7 +19,5 @@ public override Task Execute(HostArguments args, Settings settings) var transportCustomization = TransportFactory.Create(transportSettings); return transportCustomization.ProvisionQueues(transportSettings, []); } - - static readonly ILog Logger = LogManager.GetLogger(); } } \ No newline at end of file diff --git a/src/ServiceControl.Monitoring/Http/Diagrams/MonitoredEndpointMessageTypeParser.cs b/src/ServiceControl.Monitoring/Http/Diagrams/MonitoredEndpointMessageTypeParser.cs index 3faa1e81c7..f089104b4f 100644 --- a/src/ServiceControl.Monitoring/Http/Diagrams/MonitoredEndpointMessageTypeParser.cs +++ b/src/ServiceControl.Monitoring/Http/Diagrams/MonitoredEndpointMessageTypeParser.cs @@ -3,7 +3,8 @@ namespace ServiceControl.Monitoring.Http.Diagrams using System; using System.Linq; using System.Reflection; - using NServiceBus.Logging; + using Microsoft.Extensions.Logging; + using ServiceControl.Infrastructure; public static class MonitoredEndpointMessageTypeParser { @@ -40,7 +41,7 @@ public static MonitoredEndpointMessageType Parse(string typeName) } catch (Exception e) { - Logger.Warn($"Error parsing message type: {typeName}.", e); + LoggerUtil.CreateStaticLogger(typeof(MonitoredEndpointMessageTypeParser)).LogWarning(e, "Error parsing message type: {typeName}.", typeName); } } @@ -50,7 +51,5 @@ public static MonitoredEndpointMessageType Parse(string typeName) TypeName = typeName }; } - - static readonly ILog Logger = LogManager.GetLogger(typeof(MonitoredEndpointMessageTypeParser)); } } \ No newline at end of file diff --git a/src/ServiceControl.Monitoring/Licensing/ActiveLicense.cs b/src/ServiceControl.Monitoring/Licensing/ActiveLicense.cs index a3a001e0ad..f5ca3be8bc 100644 --- a/src/ServiceControl.Monitoring/Licensing/ActiveLicense.cs +++ b/src/ServiceControl.Monitoring/Licensing/ActiveLicense.cs @@ -1,11 +1,15 @@ namespace ServiceControl.Monitoring.Licensing { - using global::ServiceControl.LicenseManagement; - using NServiceBus.Logging; + using ServiceControl.LicenseManagement; + using Microsoft.Extensions.Logging; public class ActiveLicense { - public ActiveLicense() => Refresh(); + public ActiveLicense(ILogger logger) + { + this.logger = logger; + Refresh(); + } public bool IsValid { get; set; } @@ -13,7 +17,7 @@ public class ActiveLicense public void Refresh() { - Logger.Debug("Refreshing ActiveLicense"); + logger.LogDebug("Refreshing ActiveLicense"); var detectedLicense = LicenseManager.FindLicense(); @@ -22,6 +26,6 @@ public void Refresh() Details = detectedLicense.Details; } - static readonly ILog Logger = LogManager.GetLogger(typeof(ActiveLicense)); + readonly ILogger logger; } } \ No newline at end of file diff --git a/src/ServiceControl.Monitoring/Licensing/LicenseCheckHostedService.cs b/src/ServiceControl.Monitoring/Licensing/LicenseCheckHostedService.cs index a5b10f73b5..b29e47fd05 100644 --- a/src/ServiceControl.Monitoring/Licensing/LicenseCheckHostedService.cs +++ b/src/ServiceControl.Monitoring/Licensing/LicenseCheckHostedService.cs @@ -4,10 +4,10 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; - using NServiceBus.Logging; + using Microsoft.Extensions.Logging; using ServiceControl.Infrastructure.BackgroundTasks; - class LicenseCheckHostedService(ActiveLicense activeLicense, IAsyncTimer scheduler) : IHostedService + class LicenseCheckHostedService(ActiveLicense activeLicense, IAsyncTimer scheduler, ILogger logger) : IHostedService { public Task StartAsync(CancellationToken cancellationToken) { @@ -16,7 +16,7 @@ public Task StartAsync(CancellationToken cancellationToken) { activeLicense.Refresh(); return ScheduleNextExecutionTask; - }, due, due, ex => Logger.Error("Unhandled error while refreshing the license.", ex)); + }, due, due, ex => logger.LogError(ex, "Unhandled error while refreshing the license.")); return Task.CompletedTask; } @@ -24,7 +24,6 @@ public Task StartAsync(CancellationToken cancellationToken) TimerJob timer; - static readonly ILog Logger = LogManager.GetLogger(); static readonly Task ScheduleNextExecutionTask = Task.FromResult(TimerJobExecutionResult.ScheduleNextExecution); } } \ No newline at end of file diff --git a/src/ServiceControl.Monitoring/Program.cs b/src/ServiceControl.Monitoring/Program.cs index 8620eba3cc..fa34bf4978 100644 --- a/src/ServiceControl.Monitoring/Program.cs +++ b/src/ServiceControl.Monitoring/Program.cs @@ -1,13 +1,13 @@ using System; using System.Reflection; -using NServiceBus.Logging; +using Microsoft.Extensions.Logging; using ServiceControl.Configuration; using ServiceControl.Infrastructure; using ServiceControl.Monitoring; try { - AppDomain.CurrentDomain.UnhandledException += (s, e) => LogManager.GetLogger(typeof(Program)).Error("Unhandled exception was caught.", e.ExceptionObject as Exception); + AppDomain.CurrentDomain.UnhandledException += (s, e) => LoggerUtil.CreateStaticLogger().LogError(e.ExceptionObject as Exception, "Unhandled exception was caught."); // Hack: See https://github.com/Particular/ServiceControl/issues/4392 var exitCode = await IntegratedSetup.Run(); @@ -32,12 +32,11 @@ } catch (Exception ex) { - NLog.LogManager.GetCurrentClassLogger().Fatal(ex, "Unrecoverable error"); + LoggerUtil.CreateStaticLogger().LogCritical(ex, "Unrecoverable error"); throw; } finally { // The following log statement is meant to leave a trail in the logs to determine if the process was killed - NLog.LogManager.GetCurrentClassLogger().Info("Shutdown complete"); - NLog.LogManager.Shutdown(); -} \ No newline at end of file + LoggerUtil.CreateStaticLogger().LogInformation("Shutdown complete"); +} diff --git a/src/ServiceControl.Monitoring/QueueLength/LegacyQueueLengthReportHandler.cs b/src/ServiceControl.Monitoring/QueueLength/LegacyQueueLengthReportHandler.cs index 82a72e4a8b..93d905558d 100644 --- a/src/ServiceControl.Monitoring/QueueLength/LegacyQueueLengthReportHandler.cs +++ b/src/ServiceControl.Monitoring/QueueLength/LegacyQueueLengthReportHandler.cs @@ -5,33 +5,24 @@ using System.Threading; using System.Threading.Tasks; using Infrastructure; + using Microsoft.Extensions.Logging; using NServiceBus; - using NServiceBus.Logging; using NServiceBus.Metrics; - class LegacyQueueLengthReportHandler : IHandleMessages + class LegacyQueueLengthReportHandler(LegacyQueueLengthReportHandler.LegacyQueueLengthEndpoints legacyEndpoints, ILogger logger) : IHandleMessages { - public LegacyQueueLengthReportHandler(LegacyQueueLengthEndpoints legacyEndpoints) - { - this.legacyEndpoints = legacyEndpoints; - } - public Task Handle(MetricReport message, IMessageHandlerContext context) { var endpointInstanceId = EndpointInstanceId.From(context.MessageHeaders); if (legacyEndpoints.TryAdd(endpointInstanceId.InstanceId)) { - Logger.Warn($"Legacy queue length report received from {endpointInstanceId.InstanceName} instance of {endpointInstanceId.EndpointName}"); + logger.LogWarning("Legacy queue length report received from {EndpointInstanceIdInstanceName} instance of {EndpointInstanceIdEndpointName}", endpointInstanceId.InstanceName, endpointInstanceId.EndpointName); } return Task.CompletedTask; } - LegacyQueueLengthEndpoints legacyEndpoints; - - static readonly ILog Logger = LogManager.GetLogger(typeof(LegacyQueueLengthReportHandler)); - public class LegacyQueueLengthEndpoints { public bool TryAdd(string id) diff --git a/src/ServiceControl.Monitoring/ServiceControl.Monitoring.csproj b/src/ServiceControl.Monitoring/ServiceControl.Monitoring.csproj index 35246ea1b8..1a9b705469 100644 --- a/src/ServiceControl.Monitoring/ServiceControl.Monitoring.csproj +++ b/src/ServiceControl.Monitoring/ServiceControl.Monitoring.csproj @@ -24,7 +24,6 @@ - From e2196e6fbb2f591e82a276e2687acd6ca070022e Mon Sep 17 00:00:00 2001 From: JasonTaylorDev Date: Thu, 12 Jun 2025 17:08:39 +1000 Subject: [PATCH 08/19] Work in progress --- src/Directory.Packages.props | 4 +- .../ServiceControlComponentRunner.cs | 2 +- .../ServiceControlComponentRunner.cs | 3 +- .../CustomChecks/CheckFreeDiskSpace.cs | 10 ++-- .../RavenPersistenceConfiguration.cs | 2 +- .../HostApplicationBuilderExtensions.cs | 2 +- .../Infrastructure/WebApi/RootController.cs | 2 +- .../LoggerUtil.cs | 5 +- .../LoggingConfigurator.cs | 10 +++- .../LoggingSettings.cs | 55 +++++++++++-------- .../ServiceControl.Infrastructure.csproj | 2 + .../ServiceControlComponentRunner.cs | 3 +- .../HostApplicationBuilderExtensions.cs | 6 +- .../Hosting/Commands/SetupCommand.cs | 5 +- src/ServiceControl.Monitoring/Program.cs | 8 ++- .../ServiceControl.Monitoring.csproj | 1 + .../HostApplicationBuilderExtensions.cs | 2 +- .../Infrastructure/Api/ConfigurationApi.cs | 2 +- 18 files changed, 72 insertions(+), 52 deletions(-) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 60eb7072c9..dfd35a5f78 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -24,6 +24,7 @@ + @@ -63,6 +64,7 @@ + @@ -91,4 +93,4 @@ - + \ No newline at end of file diff --git a/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs b/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs index 19bbe6b57a..f1b3e58a01 100644 --- a/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs +++ b/src/ServiceControl.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs @@ -15,7 +15,7 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; - using NLog; + using Microsoft.Extensions.Logging; using NServiceBus; using NServiceBus.AcceptanceTesting; using NServiceBus.AcceptanceTesting.Support; diff --git a/src/ServiceControl.Audit.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs b/src/ServiceControl.Audit.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs index 0866b504db..ce71680748 100644 --- a/src/ServiceControl.Audit.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs +++ b/src/ServiceControl.Audit.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs @@ -17,6 +17,7 @@ namespace ServiceControl.Audit.AcceptanceTests.TestSupport using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.Hosting; + using Microsoft.Extensions.Logging; using NServiceBus; using NServiceBus.AcceptanceTesting; using NServiceBus.AcceptanceTesting.Support; @@ -43,7 +44,7 @@ async Task InitializeServiceControl(ScenarioContext context) var logPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); Directory.CreateDirectory(logPath); - var loggingSettings = new LoggingSettings(Settings.SettingsRootNamespace, defaultLevel: NLog.LogLevel.Debug, logPath: logPath); + var loggingSettings = new LoggingSettings(Settings.SettingsRootNamespace, defaultLevel: LogLevel.Debug, logPath: logPath); settings = new Settings(transportToUse.TypeName, persistenceToUse.PersistenceType, loggingSettings) { diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckFreeDiskSpace.cs b/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckFreeDiskSpace.cs index 923b6208d6..45efbac8e0 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckFreeDiskSpace.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckFreeDiskSpace.cs @@ -8,7 +8,6 @@ using Microsoft.Extensions.Logging; using NServiceBus.CustomChecks; using RavenDB; - using ServiceControl.Infrastructure; class CheckFreeDiskSpace(DatabaseConfiguration databaseConfiguration, ILogger logger) : CustomCheck("ServiceControl.Audit database", "Storage space", TimeSpan.FromMinutes(5)) { @@ -45,7 +44,7 @@ public override Task PerformCheck(CancellationToken cancellationTok : CheckResult.Failed($"{percentRemaining:P0} disk space remaining on data drive '{dataDriveInfo.VolumeLabel} ({dataDriveInfo.RootDirectory})' on '{Environment.MachineName}'."); } - public static int Parse(IDictionary settings) + public static int Parse(IDictionary settings, ILogger logger) { if (!settings.TryGetValue(RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey, out var thresholdValue)) { @@ -54,19 +53,19 @@ public static int Parse(IDictionary settings) if (!int.TryParse(thresholdValue, out var threshold)) { - Logger.LogCritical("{RavenPersistenceConfigurationDataSpaceRemainingThresholdKey} must be an integer.", RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey); + logger.LogCritical("{RavenPersistenceConfigurationDataSpaceRemainingThresholdKey} must be an integer.", RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey); throw new Exception($"{RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey} must be an integer."); } if (threshold < 0) { - Logger.LogCritical("{RavenPersistenceConfigurationDataSpaceRemainingThresholdKey} is invalid, minimum value is 0.", RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey); + logger.LogCritical("{RavenPersistenceConfigurationDataSpaceRemainingThresholdKey} is invalid, minimum value is 0.", RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey); throw new Exception($"{RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey} is invalid, minimum value is 0."); } if (threshold > 100) { - Logger.LogCritical("{RavenPersistenceConfigurationDataSpaceRemainingThresholdKey} is invalid, maximum value is 100.", RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey); + logger.LogCritical("{RavenPersistenceConfigurationDataSpaceRemainingThresholdKey} is invalid, maximum value is 100.", RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey); throw new Exception($"{RavenPersistenceConfiguration.DataSpaceRemainingThresholdKey} is invalid, maximum value is 100."); } @@ -75,7 +74,6 @@ public static int Parse(IDictionary settings) readonly string dataPathRoot = Path.GetPathRoot(databaseConfiguration.ServerConfiguration.DbPath); readonly decimal percentageThreshold = databaseConfiguration.DataSpaceRemainingThreshold / 100m; - static readonly ILogger Logger = LoggerUtil.CreateStaticLogger(); public const int DataSpaceRemainingThresholdDefault = 20; } } \ No newline at end of file diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/RavenPersistenceConfiguration.cs b/src/ServiceControl.Audit.Persistence.RavenDB/RavenPersistenceConfiguration.cs index fc8aa2f281..3611a3f646 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/RavenPersistenceConfiguration.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/RavenPersistenceConfiguration.cs @@ -114,7 +114,7 @@ internal static DatabaseConfiguration GetDatabaseConfiguration(PersistenceSettin serverConfiguration = new ServerConfiguration(dbPath, serverUrl, logPath, logsMode); } - var dataSpaceRemainingThreshold = CheckFreeDiskSpace.Parse(settings.PersisterSpecificSettings); + var dataSpaceRemainingThreshold = CheckFreeDiskSpace.Parse(settings.PersisterSpecificSettings, Logger); var minimumStorageLeftRequiredForIngestion = CheckMinimumStorageRequiredForIngestion.Parse(settings.PersisterSpecificSettings); var expirationProcessTimerInSeconds = GetExpirationProcessTimerInSeconds(settings); diff --git a/src/ServiceControl.Audit/HostApplicationBuilderExtensions.cs b/src/ServiceControl.Audit/HostApplicationBuilderExtensions.cs index ccacf5db1c..f1bbebb1d7 100644 --- a/src/ServiceControl.Audit/HostApplicationBuilderExtensions.cs +++ b/src/ServiceControl.Audit/HostApplicationBuilderExtensions.cs @@ -41,7 +41,7 @@ public static void AddServiceControlAudit(this IHostApplicationBuilder builder, builder.Logging.ClearProviders(); builder.Logging.AddNLog(); - builder.Logging.SetMinimumLevel(settings.LoggingSettings.ToHostLogLevel()); + builder.Logging.SetMinimumLevel(settings.LoggingSettings.LogLevel); var services = builder.Services; var transportSettings = settings.ToTransportSettings(); diff --git a/src/ServiceControl.Audit/Infrastructure/WebApi/RootController.cs b/src/ServiceControl.Audit/Infrastructure/WebApi/RootController.cs index 42c53825d3..159e8c6d49 100644 --- a/src/ServiceControl.Audit/Infrastructure/WebApi/RootController.cs +++ b/src/ServiceControl.Audit/Infrastructure/WebApi/RootController.cs @@ -51,7 +51,7 @@ public OkObjectResult Config() Logging = new { settings.LoggingSettings.LogPath, - LoggingLevel = settings.LoggingSettings.LogLevel.Name + LoggingLevel = settings.LoggingSettings.LogLevel } }, DataRetention = new diff --git a/src/ServiceControl.Infrastructure/LoggerUtil.cs b/src/ServiceControl.Infrastructure/LoggerUtil.cs index 98aeb7d865..3fac64d05a 100644 --- a/src/ServiceControl.Infrastructure/LoggerUtil.cs +++ b/src/ServiceControl.Infrastructure/LoggerUtil.cs @@ -9,13 +9,16 @@ public static class LoggerUtil public static void BuildLogger(this ILoggingBuilder loggingBuilder, LogLevel level) { loggingBuilder.AddNLog(); + loggingBuilder.AddSeq(); loggingBuilder.SetMinimumLevel(level); } public static ILogger CreateStaticLogger(LogLevel level = LogLevel.Information) { var factory = LoggerFactory.Create(configure => configure.BuildLogger(level)); - return factory.CreateLogger(); + var logger = factory.CreateLogger(); + logger.LogError("JTD: This is a test log message."); + return logger; } public static ILogger CreateStaticLogger(Type type, LogLevel level = LogLevel.Information) diff --git a/src/ServiceControl.Infrastructure/LoggingConfigurator.cs b/src/ServiceControl.Infrastructure/LoggingConfigurator.cs index f32c84918c..1e505963da 100644 --- a/src/ServiceControl.Infrastructure/LoggingConfigurator.cs +++ b/src/ServiceControl.Infrastructure/LoggingConfigurator.cs @@ -60,11 +60,15 @@ public static void ConfigureLogging(LoggingSettings loggingSettings) nlogConfig.LoggingRules.Add(aspNetCoreRule); nlogConfig.LoggingRules.Add(httpClientRule); - nlogConfig.LoggingRules.Add(new LoggingRule("*", loggingSettings.LogLevel, consoleTarget)); + // HACK: Fix LogLevel to Info for testing purposes only. + // nlogConfig.LoggingRules.Add(new LoggingRule("*", loggingSettings.LogLevel, consoleTarget)); + nlogConfig.LoggingRules.Add(new LoggingRule("*", LogLevel.Info, consoleTarget)); if (!AppEnvironment.RunningInContainer) { - nlogConfig.LoggingRules.Add(new LoggingRule("*", loggingSettings.LogLevel, fileTarget)); + // HACK: Fix LogLevel to Info for testing purposes only. + //nlogConfig.LoggingRules.Add(new LoggingRule("*", loggingSettings.LogLevel, fileTarget)); + nlogConfig.LoggingRules.Add(new LoggingRule("*", LogLevel.Info, fileTarget)); } NLog.LogManager.Configuration = nlogConfig; @@ -74,7 +78,7 @@ public static void ConfigureLogging(LoggingSettings loggingSettings) var logger = LogManager.GetLogger("LoggingConfiguration"); var logEventInfo = new LogEventInfo { TimeStamp = DateTime.UtcNow }; var loggingTo = AppEnvironment.RunningInContainer ? "console" : fileTarget.FileName.Render(logEventInfo); - logger.InfoFormat("Logging to {0} with LogLevel '{1}'", loggingTo, loggingSettings.LogLevel.Name); + logger.InfoFormat("Logging to {0} with LogLevel '{1}'", loggingTo, LogLevel.Info.Name); } const long megaByte = 1024 * 1024; diff --git a/src/ServiceControl.Infrastructure/LoggingSettings.cs b/src/ServiceControl.Infrastructure/LoggingSettings.cs index 55282759a3..dd9fc18008 100644 --- a/src/ServiceControl.Infrastructure/LoggingSettings.cs +++ b/src/ServiceControl.Infrastructure/LoggingSettings.cs @@ -1,21 +1,19 @@ namespace ServiceControl.Infrastructure; using System; +using System.Collections.Generic; using System.IO; -using NLog; -using NLog.Common; +using Microsoft.Extensions.Logging; using ServiceControl.Configuration; -public class LoggingSettings(SettingsRootNamespace rootNamespace, LogLevel defaultLevel = null, string logPath = null) +public class LoggingSettings(SettingsRootNamespace rootNamespace, LogLevel defaultLevel = LogLevel.Information, string logPath = null) { public LogLevel LogLevel { get; } = InitializeLogLevel(rootNamespace, defaultLevel); - public string LogPath { get; } = SettingsReader.Read(rootNamespace, "LogPath", Environment.ExpandEnvironmentVariables(logPath ?? DefaultLogLocation())); + public string LogPath { get; } = SettingsReader.Read(rootNamespace, logPathKey, Environment.ExpandEnvironmentVariables(logPath ?? DefaultLogLocation())); static LogLevel InitializeLogLevel(SettingsRootNamespace rootNamespace, LogLevel defaultLevel) { - defaultLevel ??= LogLevel.Info; - var levelText = SettingsReader.Read(rootNamespace, logLevelKey); if (string.IsNullOrWhiteSpace(levelText)) @@ -23,31 +21,40 @@ static LogLevel InitializeLogLevel(SettingsRootNamespace rootNamespace, LogLevel return defaultLevel; } - try - { - return LogLevel.FromString(levelText); - } - catch - { - InternalLogger.Warn($"Failed to parse {logLevelKey} setting. Defaulting to {defaultLevel.Name}."); - return defaultLevel; - } + return ParseLogLevel(levelText, defaultLevel); } // SC installer always populates LogPath in app.config on installation/change/upgrade so this will only be used when // debugging or if the entry is removed manually. In those circumstances default to the folder containing the exe static string DefaultLogLocation() => Path.Combine(AppContext.BaseDirectory, ".logs"); - public Microsoft.Extensions.Logging.LogLevel ToHostLogLevel() => LogLevel switch + // This is not a complete mapping of NLog levels, just the ones that are different. + static readonly Dictionary NLogAliases = + new(StringComparer.OrdinalIgnoreCase) + { + ["info"] = LogLevel.Information, + ["warn"] = LogLevel.Warning, + ["fatal"] = LogLevel.Critical, + ["off"] = LogLevel.None + }; + + static LogLevel ParseLogLevel(string value, LogLevel defaultLevel) { - _ when LogLevel == LogLevel.Trace => Microsoft.Extensions.Logging.LogLevel.Trace, - _ when LogLevel == LogLevel.Debug => Microsoft.Extensions.Logging.LogLevel.Debug, - _ when LogLevel == LogLevel.Info => Microsoft.Extensions.Logging.LogLevel.Information, - _ when LogLevel == LogLevel.Warn => Microsoft.Extensions.Logging.LogLevel.Warning, - _ when LogLevel == LogLevel.Error => Microsoft.Extensions.Logging.LogLevel.Error, - _ when LogLevel == LogLevel.Fatal => Microsoft.Extensions.Logging.LogLevel.Critical, - _ => Microsoft.Extensions.Logging.LogLevel.None - }; + if (Enum.TryParse(value, ignoreCase: true, out LogLevel parsedLevel)) + { + return parsedLevel; + } + + if (NLogAliases.TryGetValue(value.Trim(), out parsedLevel)) + { + return parsedLevel; + } + + LoggerUtil.CreateStaticLogger().LogWarning("Failed to parse {LogLevelKey} setting. Defaulting to {DefaultLevel}.", logLevelKey, defaultLevel); + + return defaultLevel; + } const string logLevelKey = "LogLevel"; + const string logPathKey = "LogPath"; } \ No newline at end of file diff --git a/src/ServiceControl.Infrastructure/ServiceControl.Infrastructure.csproj b/src/ServiceControl.Infrastructure/ServiceControl.Infrastructure.csproj index ea352f1711..37332ed734 100644 --- a/src/ServiceControl.Infrastructure/ServiceControl.Infrastructure.csproj +++ b/src/ServiceControl.Infrastructure/ServiceControl.Infrastructure.csproj @@ -9,9 +9,11 @@ + + \ No newline at end of file diff --git a/src/ServiceControl.Monitoring.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs b/src/ServiceControl.Monitoring.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs index 212f1e03a1..debc3ed629 100644 --- a/src/ServiceControl.Monitoring.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs +++ b/src/ServiceControl.Monitoring.AcceptanceTests/TestSupport/ServiceControlComponentRunner.cs @@ -18,7 +18,6 @@ namespace ServiceControl.Monitoring.AcceptanceTests.TestSupport using NServiceBus.AcceptanceTesting; using NServiceBus.AcceptanceTesting.Support; using NServiceBus.Logging; - using ServiceControl.Infrastructure; class ServiceControlComponentRunner( ITransportIntegration transportToUse, @@ -73,7 +72,7 @@ async Task InitializeServiceControl(ScenarioContext context) using (new DiagnosticTimer($"Creating infrastructure for {settings.InstanceName}")) { - var setupCommand = new SetupCommand(LoggerUtil.CreateStaticLogger()); + var setupCommand = new SetupCommand(); await setupCommand.Execute(new HostArguments([]), settings); } diff --git a/src/ServiceControl.Monitoring/HostApplicationBuilderExtensions.cs b/src/ServiceControl.Monitoring/HostApplicationBuilderExtensions.cs index 90adce523d..7bda9a78ab 100644 --- a/src/ServiceControl.Monitoring/HostApplicationBuilderExtensions.cs +++ b/src/ServiceControl.Monitoring/HostApplicationBuilderExtensions.cs @@ -22,6 +22,7 @@ namespace ServiceControl.Monitoring; using NServiceBus.Features; using NServiceBus.Transport; using QueueLength; +using ServiceControl.Infrastructure; using Timings; using Transports; @@ -31,9 +32,8 @@ public static void AddServiceControlMonitoring(this IHostApplicationBuilder host Func onCriticalError, Settings settings, EndpointConfiguration endpointConfiguration) { - hostBuilder.Logging.ClearProviders(); - hostBuilder.Logging.AddNLog(); - hostBuilder.Logging.SetMinimumLevel(settings.LoggingSettings.ToHostLogLevel()); + hostBuilder.Services.AddLogging(); + hostBuilder.Logging.BuildLogger(settings.LoggingSettings.LogLevel); var services = hostBuilder.Services; diff --git a/src/ServiceControl.Monitoring/Hosting/Commands/SetupCommand.cs b/src/ServiceControl.Monitoring/Hosting/Commands/SetupCommand.cs index d585434197..22a84b27f7 100644 --- a/src/ServiceControl.Monitoring/Hosting/Commands/SetupCommand.cs +++ b/src/ServiceControl.Monitoring/Hosting/Commands/SetupCommand.cs @@ -2,15 +2,16 @@ namespace ServiceControl.Monitoring { using System.Threading.Tasks; using Microsoft.Extensions.Logging; + using ServiceControl.Infrastructure; using Transports; - class SetupCommand(ILogger logger) : AbstractCommand + class SetupCommand : AbstractCommand { public override Task Execute(HostArguments args, Settings settings) { if (args.SkipQueueCreation) { - logger.LogInformation("Skipping queue creation"); + LoggerUtil.CreateStaticLogger().LogInformation("Skipping queue creation"); return Task.CompletedTask; } diff --git a/src/ServiceControl.Monitoring/Program.cs b/src/ServiceControl.Monitoring/Program.cs index fa34bf4978..5aaa6f8c35 100644 --- a/src/ServiceControl.Monitoring/Program.cs +++ b/src/ServiceControl.Monitoring/Program.cs @@ -5,9 +5,11 @@ using ServiceControl.Infrastructure; using ServiceControl.Monitoring; +var logger = LoggerUtil.CreateStaticLogger(); + try { - AppDomain.CurrentDomain.UnhandledException += (s, e) => LoggerUtil.CreateStaticLogger().LogError(e.ExceptionObject as Exception, "Unhandled exception was caught."); + AppDomain.CurrentDomain.UnhandledException += (s, e) => logger.LogError(e.ExceptionObject as Exception, "Unhandled exception was caught."); // Hack: See https://github.com/Particular/ServiceControl/issues/4392 var exitCode = await IntegratedSetup.Run(); @@ -32,11 +34,11 @@ } catch (Exception ex) { - LoggerUtil.CreateStaticLogger().LogCritical(ex, "Unrecoverable error"); + logger.LogCritical(ex, "Unrecoverable error"); throw; } finally { // The following log statement is meant to leave a trail in the logs to determine if the process was killed - LoggerUtil.CreateStaticLogger().LogInformation("Shutdown complete"); + logger.LogInformation("Shutdown complete"); } diff --git a/src/ServiceControl.Monitoring/ServiceControl.Monitoring.csproj b/src/ServiceControl.Monitoring/ServiceControl.Monitoring.csproj index 1a9b705469..9fbcf67af7 100644 --- a/src/ServiceControl.Monitoring/ServiceControl.Monitoring.csproj +++ b/src/ServiceControl.Monitoring/ServiceControl.Monitoring.csproj @@ -25,6 +25,7 @@ + diff --git a/src/ServiceControl/HostApplicationBuilderExtensions.cs b/src/ServiceControl/HostApplicationBuilderExtensions.cs index 50d0733fb9..76140a95ea 100644 --- a/src/ServiceControl/HostApplicationBuilderExtensions.cs +++ b/src/ServiceControl/HostApplicationBuilderExtensions.cs @@ -46,7 +46,7 @@ public static void AddServiceControl(this IHostApplicationBuilder hostBuilder, S logging.ClearProviders(); //HINT: configuration used by NLog comes from LoggingConfigurator.cs logging.AddNLog(); - logging.SetMinimumLevel(settings.LoggingSettings.ToHostLogLevel()); + logging.SetMinimumLevel(settings.LoggingSettings.LogLevel); var services = hostBuilder.Services; var transportSettings = settings.ToTransportSettings(); diff --git a/src/ServiceControl/Infrastructure/Api/ConfigurationApi.cs b/src/ServiceControl/Infrastructure/Api/ConfigurationApi.cs index fa06e80bc9..f8af272eb4 100644 --- a/src/ServiceControl/Infrastructure/Api/ConfigurationApi.cs +++ b/src/ServiceControl/Infrastructure/Api/ConfigurationApi.cs @@ -60,7 +60,7 @@ public Task GetConfig(CancellationToken cancellationToken) Logging = new { settings.LoggingSettings.LogPath, - LoggingLevel = settings.LoggingSettings.LogLevel.Name + LoggingLevel = settings.LoggingSettings.LogLevel } }, DataRetention = new From e9f287e1af3e39087ca5332410898b6d714de941 Mon Sep 17 00:00:00 2001 From: JasonTaylorDev Date: Fri, 13 Jun 2025 11:45:22 +1000 Subject: [PATCH 09/19] Remove test code --- src/ServiceControl.Infrastructure/LoggerUtil.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ServiceControl.Infrastructure/LoggerUtil.cs b/src/ServiceControl.Infrastructure/LoggerUtil.cs index 3fac64d05a..623775e202 100644 --- a/src/ServiceControl.Infrastructure/LoggerUtil.cs +++ b/src/ServiceControl.Infrastructure/LoggerUtil.cs @@ -16,9 +16,7 @@ public static void BuildLogger(this ILoggingBuilder loggingBuilder, LogLevel lev public static ILogger CreateStaticLogger(LogLevel level = LogLevel.Information) { var factory = LoggerFactory.Create(configure => configure.BuildLogger(level)); - var logger = factory.CreateLogger(); - logger.LogError("JTD: This is a test log message."); - return logger; + return factory.CreateLogger(); } public static ILogger CreateStaticLogger(Type type, LogLevel level = LogLevel.Information) From 9b97c92d59daf0bc156f269bdc8ee47249e8552e Mon Sep 17 00:00:00 2001 From: JasonTaylorDev Date: Tue, 17 Jun 2025 10:06:13 +1000 Subject: [PATCH 10/19] Improve logging format for storage space details --- .../CustomChecks/CheckMinimumStorageRequiredForIngestion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs b/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs index 56153b3169..033a99fbbe 100644 --- a/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs +++ b/src/ServiceControl.Audit.Persistence.RavenDB/CustomChecks/CheckMinimumStorageRequiredForIngestion.cs @@ -38,7 +38,7 @@ public override Task PerformCheck(CancellationToken cancellationTok if (logger.IsEnabled(LogLevel.Debug)) { - logger.LogDebug("Free space: {AvailableFreeSpace} | Total: {TotalSpace} | Percent remaining {PercentRemaining:P0}", availableFreeSpace, totalSpace, percentRemaining); + logger.LogDebug("Free space: {AvailableFreeSpace:N0}B | Total: {TotalSpace:N0}B | Percent remaining {PercentRemaining:P0}", availableFreeSpace, totalSpace, percentRemaining); } if (percentRemaining > percentageThreshold) From 005968d492cc02852c676a1c41e146c52fe676bb Mon Sep 17 00:00:00 2001 From: JasonTaylorDev Date: Tue, 17 Jun 2025 10:10:46 +1000 Subject: [PATCH 11/19] Properly shutdown NLog in Program.cs --- src/ServiceControl.Monitoring/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ServiceControl.Monitoring/Program.cs b/src/ServiceControl.Monitoring/Program.cs index 5aaa6f8c35..7a618a381f 100644 --- a/src/ServiceControl.Monitoring/Program.cs +++ b/src/ServiceControl.Monitoring/Program.cs @@ -41,4 +41,5 @@ { // The following log statement is meant to leave a trail in the logs to determine if the process was killed logger.LogInformation("Shutdown complete"); + NLog.LogManager.Shutdown(); } From 49ba86c1c1af137403b646ebd559811a8bfabea5 Mon Sep 17 00:00:00 2001 From: JasonTaylorDev Date: Tue, 17 Jun 2025 10:52:10 +1000 Subject: [PATCH 12/19] Remove Seq logging and prepare for .NET logging migration --- src/ServiceControl.Infrastructure/LoggerUtil.cs | 1 - .../LoggingConfigurator.cs | 11 +++++++---- .../ServiceControl.Infrastructure.csproj | 1 - .../ServiceControl.Monitoring.csproj | 1 - 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ServiceControl.Infrastructure/LoggerUtil.cs b/src/ServiceControl.Infrastructure/LoggerUtil.cs index 623775e202..98aeb7d865 100644 --- a/src/ServiceControl.Infrastructure/LoggerUtil.cs +++ b/src/ServiceControl.Infrastructure/LoggerUtil.cs @@ -9,7 +9,6 @@ public static class LoggerUtil public static void BuildLogger(this ILoggingBuilder loggingBuilder, LogLevel level) { loggingBuilder.AddNLog(); - loggingBuilder.AddSeq(); loggingBuilder.SetMinimumLevel(level); } diff --git a/src/ServiceControl.Infrastructure/LoggingConfigurator.cs b/src/ServiceControl.Infrastructure/LoggingConfigurator.cs index 1e505963da..5ca1c06c9b 100644 --- a/src/ServiceControl.Infrastructure/LoggingConfigurator.cs +++ b/src/ServiceControl.Infrastructure/LoggingConfigurator.cs @@ -12,6 +12,7 @@ namespace ServiceControl.Infrastructure using LogManager = NServiceBus.Logging.LogManager; + // TODO: Migrate from NLog to .NET logging public static class LoggingConfigurator { public static void ConfigureLogging(LoggingSettings loggingSettings) @@ -60,14 +61,16 @@ public static void ConfigureLogging(LoggingSettings loggingSettings) nlogConfig.LoggingRules.Add(aspNetCoreRule); nlogConfig.LoggingRules.Add(httpClientRule); - // HACK: Fix LogLevel to Info for testing purposes only. - // nlogConfig.LoggingRules.Add(new LoggingRule("*", loggingSettings.LogLevel, consoleTarget)); + // HACK: Fixed LogLevel to Info for testing purposes only. + // Migrate to .NET logging and change back to loggingSettings.LogLevel. + // nlogConfig.LoggingRules.Add(new LoggingRule("*", loggingSettings.LogLevel, consoleTarget)); nlogConfig.LoggingRules.Add(new LoggingRule("*", LogLevel.Info, consoleTarget)); if (!AppEnvironment.RunningInContainer) { - // HACK: Fix LogLevel to Info for testing purposes only. - //nlogConfig.LoggingRules.Add(new LoggingRule("*", loggingSettings.LogLevel, fileTarget)); + // HACK: Fixed LogLevel to Info for testing purposes only. + // Migrate to .NET logging and change back to loggingSettings.LogLevel. + // nlogConfig.LoggingRules.Add(new LoggingRule("*", loggingSettings.LogLevel, fileTarget)); nlogConfig.LoggingRules.Add(new LoggingRule("*", LogLevel.Info, fileTarget)); } diff --git a/src/ServiceControl.Infrastructure/ServiceControl.Infrastructure.csproj b/src/ServiceControl.Infrastructure/ServiceControl.Infrastructure.csproj index 37332ed734..29f38a7373 100644 --- a/src/ServiceControl.Infrastructure/ServiceControl.Infrastructure.csproj +++ b/src/ServiceControl.Infrastructure/ServiceControl.Infrastructure.csproj @@ -13,7 +13,6 @@ - \ No newline at end of file diff --git a/src/ServiceControl.Monitoring/ServiceControl.Monitoring.csproj b/src/ServiceControl.Monitoring/ServiceControl.Monitoring.csproj index 9fbcf67af7..1a9b705469 100644 --- a/src/ServiceControl.Monitoring/ServiceControl.Monitoring.csproj +++ b/src/ServiceControl.Monitoring/ServiceControl.Monitoring.csproj @@ -25,7 +25,6 @@ - From 938c963fe4cdf413c91586365141edf193fd7db4 Mon Sep 17 00:00:00 2001 From: JasonTaylorDev Date: Tue, 17 Jun 2025 12:05:17 +1000 Subject: [PATCH 13/19] Update LogLevel format --- .../APIApprovals.PlatformSampleSettings.approved.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ServiceControl.Audit.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt b/src/ServiceControl.Audit.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt index f718f461aa..7113c06339 100644 --- a/src/ServiceControl.Audit.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt +++ b/src/ServiceControl.Audit.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt @@ -1,9 +1,6 @@ { "LoggingSettings": { - "LogLevel": { - "Name": "Info", - "Ordinal": 2 - }, + "LogLevel": "Information", "LogPath": "C:\\Logs" }, "MessageFilter": null, From fb7f77e2b255ca037398c6259dbced9678638096 Mon Sep 17 00:00:00 2001 From: JasonTaylorDev Date: Tue, 17 Jun 2025 12:22:18 +1000 Subject: [PATCH 14/19] Update LogLevel format in logging settings --- .../SettingsTests.PlatformSampleSettings.approved.txt | 5 +---- .../APIApprovals.PlatformSampleSettings.approved.txt | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/ServiceControl.Monitoring.UnitTests/ApprovalFiles/SettingsTests.PlatformSampleSettings.approved.txt b/src/ServiceControl.Monitoring.UnitTests/ApprovalFiles/SettingsTests.PlatformSampleSettings.approved.txt index 5d32f42fbb..070234384e 100644 --- a/src/ServiceControl.Monitoring.UnitTests/ApprovalFiles/SettingsTests.PlatformSampleSettings.approved.txt +++ b/src/ServiceControl.Monitoring.UnitTests/ApprovalFiles/SettingsTests.PlatformSampleSettings.approved.txt @@ -1,9 +1,6 @@ { "LoggingSettings": { - "LogLevel": { - "Name": "Info", - "Ordinal": 2 - }, + "LogLevel": "Information", "LogPath": "C:\\Logs" }, "InstanceName": "Particular.Monitoring", diff --git a/src/ServiceControl.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt b/src/ServiceControl.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt index a23480af26..246f3e5678 100644 --- a/src/ServiceControl.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt +++ b/src/ServiceControl.UnitTests/ApprovalFiles/APIApprovals.PlatformSampleSettings.approved.txt @@ -1,9 +1,6 @@ { "LoggingSettings": { - "LogLevel": { - "Name": "Info", - "Ordinal": 2 - }, + "LogLevel": "Information", "LogPath": "C:\\Logs" }, "NotificationsFilter": null, From 07d560604369a142746471b3834f1ee34f239771 Mon Sep 17 00:00:00 2001 From: Phil Bastian Date: Tue, 17 Jun 2025 15:38:53 +0800 Subject: [PATCH 15/19] enable adding test logging provider as part of loggerutils and create framework for settings driven logger selection --- .../LoggerUtil.cs | 35 +++++++++++++------ .../ServiceControl.Infrastructure.csproj | 1 + .../TestLogger}/TestContextAppender.cs | 2 +- .../TestLogger}/TestContextAppenderFactory.cs | 4 +-- .../TestLogger/TestContextProvider.cs | 14 ++++++++ .../FullEndpointTestFixture.cs | 2 +- .../ServiceControl.Transports.Tests.csproj | 1 + .../TransportManifestLibraryTests.cs | 2 +- .../TransportTestFixture.cs | 1 + 9 files changed, 46 insertions(+), 16 deletions(-) rename src/{ServiceControl.Transports.Tests => ServiceControl.Infrastructure/TestLogger}/TestContextAppender.cs (94%) rename src/{ServiceControl.Transports.Tests => ServiceControl.Infrastructure/TestLogger}/TestContextAppenderFactory.cs (71%) create mode 100644 src/ServiceControl.Infrastructure/TestLogger/TestContextProvider.cs diff --git a/src/ServiceControl.Infrastructure/LoggerUtil.cs b/src/ServiceControl.Infrastructure/LoggerUtil.cs index 0757d17fa0..34fc16beb1 100644 --- a/src/ServiceControl.Infrastructure/LoggerUtil.cs +++ b/src/ServiceControl.Infrastructure/LoggerUtil.cs @@ -1,38 +1,51 @@ namespace ServiceControl.Infrastructure { using System; + using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using NLog.Extensions.Logging; + using ServiceControl.Infrastructure.TestLogger; + + [Flags] + public enum Loggers + { + None = 0, + Test = 1 << 0, + NLog = 1 << 1, + Seq = 1 << 2, + } public static class LoggerUtil { - /// - /// used for tests - /// - public static ILoggerFactory LoggerFactory { private get; set; } + public static Loggers ActiveLoggers { private get; set; } = Loggers.None; public static void BuildLogger(this ILoggingBuilder loggingBuilder, LogLevel level) { - if (LoggerFactory != null) + if ((Loggers.Test & ActiveLoggers) == Loggers.Test) + { + loggingBuilder.Services.AddSingleton(new TestContextProvider()); + } + if ((Loggers.NLog & ActiveLoggers) == Loggers.NLog) + { + loggingBuilder.AddNLog(); + } + if ((Loggers.Seq & ActiveLoggers) == Loggers.Seq) { - return; + loggingBuilder.AddSeq(); } - //TODO: can we get these from settings too? - loggingBuilder.AddNLog(); - loggingBuilder.AddSeq(); loggingBuilder.SetMinimumLevel(level); } public static ILogger CreateStaticLogger(LogLevel level = LogLevel.Information) { - var factory = LoggerFactory ?? Microsoft.Extensions.Logging.LoggerFactory.Create(configure => configure.BuildLogger(level)); + var factory = LoggerFactory.Create(configure => configure.BuildLogger(level)); return factory.CreateLogger(); } public static ILogger CreateStaticLogger(Type type, LogLevel level = LogLevel.Information) { - var factory = LoggerFactory ?? Microsoft.Extensions.Logging.LoggerFactory.Create(configure => configure.BuildLogger(level)); + var factory = LoggerFactory.Create(configure => configure.BuildLogger(level)); return factory.CreateLogger(type); } } diff --git a/src/ServiceControl.Infrastructure/ServiceControl.Infrastructure.csproj b/src/ServiceControl.Infrastructure/ServiceControl.Infrastructure.csproj index 467cca8759..b32d80b1fa 100644 --- a/src/ServiceControl.Infrastructure/ServiceControl.Infrastructure.csproj +++ b/src/ServiceControl.Infrastructure/ServiceControl.Infrastructure.csproj @@ -15,6 +15,7 @@ + diff --git a/src/ServiceControl.Transports.Tests/TestContextAppender.cs b/src/ServiceControl.Infrastructure/TestLogger/TestContextAppender.cs similarity index 94% rename from src/ServiceControl.Transports.Tests/TestContextAppender.cs rename to src/ServiceControl.Infrastructure/TestLogger/TestContextAppender.cs index e3b9524253..c353a3b60a 100644 --- a/src/ServiceControl.Transports.Tests/TestContextAppender.cs +++ b/src/ServiceControl.Infrastructure/TestLogger/TestContextAppender.cs @@ -1,4 +1,4 @@ -namespace ServiceControl.Transport.Tests +namespace ServiceControl.Infrastructure.TestLogger { using System; using Microsoft.Extensions.Logging; diff --git a/src/ServiceControl.Transports.Tests/TestContextAppenderFactory.cs b/src/ServiceControl.Infrastructure/TestLogger/TestContextAppenderFactory.cs similarity index 71% rename from src/ServiceControl.Transports.Tests/TestContextAppenderFactory.cs rename to src/ServiceControl.Infrastructure/TestLogger/TestContextAppenderFactory.cs index 9c5b4da728..98e1d683fb 100644 --- a/src/ServiceControl.Transports.Tests/TestContextAppenderFactory.cs +++ b/src/ServiceControl.Infrastructure/TestLogger/TestContextAppenderFactory.cs @@ -1,8 +1,8 @@ -namespace ServiceControl.Transport.Tests +namespace ServiceControl.Infrastructure.TestLogger { using Microsoft.Extensions.Logging; - class TestContextAppenderFactory : ILoggerFactory + public class TestContextAppenderFactory : ILoggerFactory { public void AddProvider(ILoggerProvider provider) { diff --git a/src/ServiceControl.Infrastructure/TestLogger/TestContextProvider.cs b/src/ServiceControl.Infrastructure/TestLogger/TestContextProvider.cs new file mode 100644 index 0000000000..e879c814a3 --- /dev/null +++ b/src/ServiceControl.Infrastructure/TestLogger/TestContextProvider.cs @@ -0,0 +1,14 @@ +namespace ServiceControl.Infrastructure.TestLogger +{ + using Microsoft.Extensions.Logging; + + public class TestContextProvider : ILoggerProvider + { + public ILogger CreateLogger(string categoryName) => new TestContextAppender(categoryName); + + public void Dispose() + { + + } + } +} diff --git a/src/ServiceControl.Transports.Tests/FullEndpointTestFixture.cs b/src/ServiceControl.Transports.Tests/FullEndpointTestFixture.cs index f2c4d13313..c1034d05f2 100644 --- a/src/ServiceControl.Transports.Tests/FullEndpointTestFixture.cs +++ b/src/ServiceControl.Transports.Tests/FullEndpointTestFixture.cs @@ -12,7 +12,7 @@ class FullEndpointTestFixture [SetUp] public virtual async Task Setup() { - LoggerUtil.LoggerFactory = new TestContextAppenderFactory(); + LoggerUtil.ActiveLoggers = Loggers.Test; configuration = new TransportTestsConfiguration(); var queueSuffix = $"-{System.IO.Path.GetRandomFileName().Replace(".", string.Empty)}"; diff --git a/src/ServiceControl.Transports.Tests/ServiceControl.Transports.Tests.csproj b/src/ServiceControl.Transports.Tests/ServiceControl.Transports.Tests.csproj index 8c7d0e9e78..aedb05ec31 100644 --- a/src/ServiceControl.Transports.Tests/ServiceControl.Transports.Tests.csproj +++ b/src/ServiceControl.Transports.Tests/ServiceControl.Transports.Tests.csproj @@ -5,6 +5,7 @@ + diff --git a/src/ServiceControl.Transports.Tests/TransportManifestLibraryTests.cs b/src/ServiceControl.Transports.Tests/TransportManifestLibraryTests.cs index 78a6aea1d7..6345f0e618 100644 --- a/src/ServiceControl.Transports.Tests/TransportManifestLibraryTests.cs +++ b/src/ServiceControl.Transports.Tests/TransportManifestLibraryTests.cs @@ -20,7 +20,7 @@ public class TransportManifestLibraryTests [SetUp] public void SetUp() { - LoggerUtil.LoggerFactory = new TestContextAppenderFactory(); + LoggerUtil.ActiveLoggers = Loggers.Test; } [Test] diff --git a/src/ServiceControl.Transports.Tests/TransportTestFixture.cs b/src/ServiceControl.Transports.Tests/TransportTestFixture.cs index 4a769f63bb..ce312b76f6 100644 --- a/src/ServiceControl.Transports.Tests/TransportTestFixture.cs +++ b/src/ServiceControl.Transports.Tests/TransportTestFixture.cs @@ -13,6 +13,7 @@ using NServiceBus.Logging; using NServiceBus.Transport; using NUnit.Framework; + using ServiceControl.Infrastructure.TestLogger; using Transports; [TestFixture] From a843701c7fd7851c85bcce61d633291d6605d752 Mon Sep 17 00:00:00 2001 From: Phil Bastian Date: Tue, 17 Jun 2025 15:56:17 +0800 Subject: [PATCH 16/19] add ability to select logging provider from config --- src/ServiceControl.Audit/App.config | 3 +++ .../LoggingSettings.cs | 26 ++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/ServiceControl.Audit/App.config b/src/ServiceControl.Audit/App.config index 00a70ad0a2..e8e4a84c26 100644 --- a/src/ServiceControl.Audit/App.config +++ b/src/ServiceControl.Audit/App.config @@ -21,6 +21,9 @@ These settings are only here so that we can debug ServiceControl while developin + + + diff --git a/src/ServiceControl.Infrastructure/LoggingSettings.cs b/src/ServiceControl.Infrastructure/LoggingSettings.cs index dd9fc18008..a005b64425 100644 --- a/src/ServiceControl.Infrastructure/LoggingSettings.cs +++ b/src/ServiceControl.Infrastructure/LoggingSettings.cs @@ -3,14 +3,33 @@ namespace ServiceControl.Infrastructure; using System; using System.Collections.Generic; using System.IO; +using System.Linq; using Microsoft.Extensions.Logging; using ServiceControl.Configuration; -public class LoggingSettings(SettingsRootNamespace rootNamespace, LogLevel defaultLevel = LogLevel.Information, string logPath = null) +public class LoggingSettings { - public LogLevel LogLevel { get; } = InitializeLogLevel(rootNamespace, defaultLevel); + public LoggingSettings(SettingsRootNamespace rootNamespace, LogLevel defaultLevel = LogLevel.Information, string logPath = null) + { + LogLevel = InitializeLogLevel(rootNamespace, defaultLevel); + LogPath = SettingsReader.Read(rootNamespace, logPathKey, Environment.ExpandEnvironmentVariables(logPath ?? DefaultLogLocation())); + + var loggingProviders = SettingsReader.Read(rootNamespace, loggingProvidersKey).Split(","); + var activeLoggers = Loggers.None; + if (loggingProviders.Contains("NLog")) + { + activeLoggers |= Loggers.NLog; + } + if (loggingProviders.Contains("Seq")) + { + activeLoggers |= Loggers.Seq; + } + LoggerUtil.ActiveLoggers = activeLoggers; + } + + public LogLevel LogLevel { get; } - public string LogPath { get; } = SettingsReader.Read(rootNamespace, logPathKey, Environment.ExpandEnvironmentVariables(logPath ?? DefaultLogLocation())); + public string LogPath { get; } static LogLevel InitializeLogLevel(SettingsRootNamespace rootNamespace, LogLevel defaultLevel) { @@ -57,4 +76,5 @@ static LogLevel ParseLogLevel(string value, LogLevel defaultLevel) const string logLevelKey = "LogLevel"; const string logPathKey = "LogPath"; + const string loggingProvidersKey = "LoggingProviders"; } \ No newline at end of file From 3e598862045512ad7d614f847d94508185752826 Mon Sep 17 00:00:00 2001 From: Phil Bastian Date: Wed, 18 Jun 2025 07:04:03 +0800 Subject: [PATCH 17/19] handle setting not existing --- src/ServiceControl.Infrastructure/LoggingSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ServiceControl.Infrastructure/LoggingSettings.cs b/src/ServiceControl.Infrastructure/LoggingSettings.cs index a005b64425..1a80ccb875 100644 --- a/src/ServiceControl.Infrastructure/LoggingSettings.cs +++ b/src/ServiceControl.Infrastructure/LoggingSettings.cs @@ -14,7 +14,7 @@ public LoggingSettings(SettingsRootNamespace rootNamespace, LogLevel defaultLeve LogLevel = InitializeLogLevel(rootNamespace, defaultLevel); LogPath = SettingsReader.Read(rootNamespace, logPathKey, Environment.ExpandEnvironmentVariables(logPath ?? DefaultLogLocation())); - var loggingProviders = SettingsReader.Read(rootNamespace, loggingProvidersKey).Split(","); + var loggingProviders = (SettingsReader.Read(rootNamespace, loggingProvidersKey) ?? "").Split(","); var activeLoggers = Loggers.None; if (loggingProviders.Contains("NLog")) { From ab580f69fc4288ec013855dbda990417967a88ed Mon Sep 17 00:00:00 2001 From: Phil Bastian Date: Wed, 18 Jun 2025 07:53:24 +0800 Subject: [PATCH 18/19] change logmanager logger factory to the standard one now used by the rest of SC --- src/ServiceControl.Infrastructure/LoggingConfigurator.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ServiceControl.Infrastructure/LoggingConfigurator.cs b/src/ServiceControl.Infrastructure/LoggingConfigurator.cs index 5ca1c06c9b..4845965394 100644 --- a/src/ServiceControl.Infrastructure/LoggingConfigurator.cs +++ b/src/ServiceControl.Infrastructure/LoggingConfigurator.cs @@ -2,15 +2,15 @@ namespace ServiceControl.Infrastructure { using System; using System.IO; + using Microsoft.Extensions.Logging; using NLog; using NLog.Config; - using NLog.Extensions.Logging; using NLog.Layouts; using NLog.Targets; using NServiceBus.Extensions.Logging; using ServiceControl.Configuration; - using LogManager = NServiceBus.Logging.LogManager; + using LogLevel = NLog.LogLevel; // TODO: Migrate from NLog to .NET logging public static class LoggingConfigurator @@ -76,7 +76,7 @@ public static void ConfigureLogging(LoggingSettings loggingSettings) NLog.LogManager.Configuration = nlogConfig; - LogManager.UseFactory(new ExtensionsLoggerFactory(new NLogLoggerFactory())); + LogManager.UseFactory(new ExtensionsLoggerFactory(LoggerFactory.Create(configure => configure.BuildLogger(loggingSettings.LogLevel)))); var logger = LogManager.GetLogger("LoggingConfiguration"); var logEventInfo = new LogEventInfo { TimeStamp = DateTime.UtcNow }; From 3e169708ad450c3c6a768bf433a372cf3e8303ef Mon Sep 17 00:00:00 2001 From: Phil Bastian Date: Wed, 18 Jun 2025 13:03:14 +0800 Subject: [PATCH 19/19] ensure logger for transport tests --- src/ServiceControl.Transports.Tests/TransportTestFixture.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ServiceControl.Transports.Tests/TransportTestFixture.cs b/src/ServiceControl.Transports.Tests/TransportTestFixture.cs index ce312b76f6..ac1501232b 100644 --- a/src/ServiceControl.Transports.Tests/TransportTestFixture.cs +++ b/src/ServiceControl.Transports.Tests/TransportTestFixture.cs @@ -13,6 +13,7 @@ using NServiceBus.Logging; using NServiceBus.Transport; using NUnit.Framework; + using ServiceControl.Infrastructure; using ServiceControl.Infrastructure.TestLogger; using Transports; @@ -24,6 +25,7 @@ public virtual async Task Setup() { //TODO remove LogManager usage LogManager.UseFactory(new ExtensionsLoggerFactory(new TestContextAppenderFactory())); + LoggerUtil.ActiveLoggers = Loggers.Test; configuration = new TransportTestsConfiguration(); testCancellationTokenSource = Debugger.IsAttached ? new CancellationTokenSource() : new CancellationTokenSource(TestTimeout); registrations = [];