diff --git a/src/Paramore.Brighter.Archive.Azure/AzureBlobArchiveProvider.cs b/src/Paramore.Brighter.Archive.Azure/AzureBlobArchiveProvider.cs index 9e08e74590..f66894e59d 100644 --- a/src/Paramore.Brighter.Archive.Azure/AzureBlobArchiveProvider.cs +++ b/src/Paramore.Brighter.Archive.Azure/AzureBlobArchiveProvider.cs @@ -2,14 +2,14 @@ using Azure.Storage.Blobs; using Azure.Storage.Blobs.Models; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Paramore.Brighter.Storage.Azure; -public class AzureBlobArchiveProvider(AzureBlobArchiveProviderOptions options) : IAmAnArchiveProvider +public class AzureBlobArchiveProvider(AzureBlobArchiveProviderOptions options, ILoggerFactory? loggerFactory = null) : IAmAnArchiveProvider { private readonly BlobContainerClient _containerClient = new BlobContainerClient(options.BlobContainerUri, options.TokenCredential); - private readonly ILogger _logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); /// /// Send a Message to the archive provider diff --git a/src/Paramore.Brighter.BoxProvisioning.MsSql/MsSqlBoxMigrationRunner.cs b/src/Paramore.Brighter.BoxProvisioning.MsSql/MsSqlBoxMigrationRunner.cs index dae702e170..aa8615d3b9 100644 --- a/src/Paramore.Brighter.BoxProvisioning.MsSql/MsSqlBoxMigrationRunner.cs +++ b/src/Paramore.Brighter.BoxProvisioning.MsSql/MsSqlBoxMigrationRunner.cs @@ -28,7 +28,7 @@ THE SOFTWARE. */ using System.Threading.Tasks; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Observability; namespace Paramore.Brighter.BoxProvisioning.MsSql; @@ -65,9 +65,10 @@ public MsSqlBoxMigrationRunner( ILogger? logger = null, TimeSpan? lockTimeout = null, IAmABrighterTracer? tracer = null, - MigrationHistoryScope scope = MigrationHistoryScope.Global) + MigrationHistoryScope scope = MigrationHistoryScope.Global, + ILoggerFactory? loggerFactory = null) : base(detectionHelper, catalog, configuration, lockTimeout ?? TimeSpan.FromSeconds(30), - logger ?? ApplicationLogging.CreateLogger(), + logger ?? (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(), tracer, scope) { _advisoryLock = advisoryLock ?? new MsSqlAdvisoryLock(); @@ -86,8 +87,9 @@ public MsSqlBoxMigrationRunner( IMsSqlAdvisoryLock? advisoryLock = null, ILogger? logger = null, IAmABrighterTracer? tracer = null, - MigrationHistoryScope scope = MigrationHistoryScope.Global) - : this(new MsSqlBoxDetectionHelper(), catalog, configuration, advisoryLock, logger, lockTimeout, tracer, scope) + MigrationHistoryScope scope = MigrationHistoryScope.Global, + ILoggerFactory? loggerFactory = null) + : this(new MsSqlBoxDetectionHelper(), catalog, configuration, advisoryLock, logger, lockTimeout, tracer, scope, loggerFactory) { } diff --git a/src/Paramore.Brighter.BoxProvisioning.MySql/MySqlBoxMigrationRunner.cs b/src/Paramore.Brighter.BoxProvisioning.MySql/MySqlBoxMigrationRunner.cs index bcf12e5999..c75767c5c6 100644 --- a/src/Paramore.Brighter.BoxProvisioning.MySql/MySqlBoxMigrationRunner.cs +++ b/src/Paramore.Brighter.BoxProvisioning.MySql/MySqlBoxMigrationRunner.cs @@ -26,8 +26,8 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using MySqlConnector; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; namespace Paramore.Brighter.BoxProvisioning.MySql; @@ -65,9 +65,10 @@ public MySqlBoxMigrationRunner( ILogger? logger = null, TimeSpan? lockTimeout = null, IAmABrighterTracer? tracer = null, - MigrationHistoryScope scope = MigrationHistoryScope.Global) + MigrationHistoryScope scope = MigrationHistoryScope.Global, + ILoggerFactory? loggerFactory = null) : base(detectionHelper, catalog, configuration, lockTimeout ?? TimeSpan.FromSeconds(30), - logger ?? ApplicationLogging.CreateLogger(), + logger ?? (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(), tracer, scope) { _advisoryLock = advisoryLock ?? new MySqlAdvisoryLock(); @@ -86,8 +87,9 @@ public MySqlBoxMigrationRunner( IMySqlAdvisoryLock? advisoryLock = null, ILogger? logger = null, IAmABrighterTracer? tracer = null, - MigrationHistoryScope scope = MigrationHistoryScope.Global) - : this(new MySqlBoxDetectionHelper(), catalog, configuration, advisoryLock, logger, lockTimeout, tracer, scope) + MigrationHistoryScope scope = MigrationHistoryScope.Global, + ILoggerFactory? loggerFactory = null) + : this(new MySqlBoxDetectionHelper(), catalog, configuration, advisoryLock, logger, lockTimeout, tracer, scope, loggerFactory) { } diff --git a/src/Paramore.Brighter.BoxProvisioning.PostgreSql/PostgreSqlBoxDetectionHelper.cs b/src/Paramore.Brighter.BoxProvisioning.PostgreSql/PostgreSqlBoxDetectionHelper.cs index fb824d08a5..59f4255380 100644 --- a/src/Paramore.Brighter.BoxProvisioning.PostgreSql/PostgreSqlBoxDetectionHelper.cs +++ b/src/Paramore.Brighter.BoxProvisioning.PostgreSql/PostgreSqlBoxDetectionHelper.cs @@ -28,7 +28,6 @@ THE SOFTWARE. */ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Npgsql; -using Paramore.Brighter.Logging; using Paramore.Brighter.PostgreSql; namespace Paramore.Brighter.BoxProvisioning.PostgreSql; @@ -71,15 +70,16 @@ private static string QuotedHistorySchema(string? historySchema) } /// - /// Initialises the detection helper with an optional logger. When unspecified, falls back - /// to . Existing callers that use the - /// parameterless form continue to work — the logger is currently only consumed for the + /// Initialises the detection helper with an optional logger. When unspecified, the logger is + /// sourced from the supplied (or + /// when that is also null). Existing callers that use + /// the parameterless form continue to work — the logger is currently only consumed for the /// rare UndefinedTable-swallow Debug emission in /// ; the helper remains a safe DI singleton. /// - public PostgreSqlBoxDetectionHelper(ILogger? logger = null) + public PostgreSqlBoxDetectionHelper(ILogger? logger = null, ILoggerFactory? loggerFactory = null) { - _logger = logger ?? ApplicationLogging.CreateLogger(); + _logger = logger ?? (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } /// diff --git a/src/Paramore.Brighter.BoxProvisioning.PostgreSql/PostgreSqlBoxMigrationRunner.cs b/src/Paramore.Brighter.BoxProvisioning.PostgreSql/PostgreSqlBoxMigrationRunner.cs index 2a2fa11d28..a9dacb1d6c 100644 --- a/src/Paramore.Brighter.BoxProvisioning.PostgreSql/PostgreSqlBoxMigrationRunner.cs +++ b/src/Paramore.Brighter.BoxProvisioning.PostgreSql/PostgreSqlBoxMigrationRunner.cs @@ -27,8 +27,8 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Npgsql; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; using Paramore.Brighter.PostgreSql; @@ -65,9 +65,10 @@ public PostgreSqlBoxMigrationRunner( ILogger? logger = null, TimeSpan? lockTimeout = null, IAmABrighterTracer? tracer = null, - MigrationHistoryScope scope = MigrationHistoryScope.Global) + MigrationHistoryScope scope = MigrationHistoryScope.Global, + ILoggerFactory? loggerFactory = null) : base(detectionHelper, catalog, configuration, lockTimeout ?? TimeSpan.FromSeconds(30), - logger ?? ApplicationLogging.CreateLogger(), + logger ?? (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(), tracer, scope) { _advisoryLock = advisoryLock ?? new PostgreSqlAdvisoryLock(); @@ -86,8 +87,9 @@ public PostgreSqlBoxMigrationRunner( IPostgreSqlAdvisoryLock? advisoryLock = null, ILogger? logger = null, IAmABrighterTracer? tracer = null, - MigrationHistoryScope scope = MigrationHistoryScope.Global) - : this(new PostgreSqlBoxDetectionHelper(), catalog, configuration, advisoryLock, logger, lockTimeout, tracer, scope) + MigrationHistoryScope scope = MigrationHistoryScope.Global, + ILoggerFactory? loggerFactory = null) + : this(new PostgreSqlBoxDetectionHelper(), catalog, configuration, advisoryLock, logger, lockTimeout, tracer, scope, loggerFactory) { } diff --git a/src/Paramore.Brighter.BoxProvisioning.Spanner/SpannerBoxMigrationRunner.cs b/src/Paramore.Brighter.BoxProvisioning.Spanner/SpannerBoxMigrationRunner.cs index af1ad2f420..dfebd7a05c 100644 --- a/src/Paramore.Brighter.BoxProvisioning.Spanner/SpannerBoxMigrationRunner.cs +++ b/src/Paramore.Brighter.BoxProvisioning.Spanner/SpannerBoxMigrationRunner.cs @@ -31,7 +31,6 @@ THE SOFTWARE. */ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Inbox.Spanner; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; using Paramore.Brighter.Outbox.Spanner; @@ -86,12 +85,13 @@ public SpannerBoxMigrationRunner( IAmABoxMigrationDetectionHelper detectionHelper, IAmARelationalDatabaseConfiguration configuration, IAmABrighterTracer? tracer = null, - ILogger? logger = null) + ILogger? logger = null, + ILoggerFactory? loggerFactory = null) { _detectionHelper = detectionHelper; _configuration = configuration; _tracer = tracer; - _logger = logger ?? ApplicationLogging.CreateLogger(); + _logger = logger ?? (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } /// @@ -102,8 +102,9 @@ public SpannerBoxMigrationRunner( public SpannerBoxMigrationRunner( IAmARelationalDatabaseConfiguration configuration, IAmABrighterTracer? tracer = null, - ILogger? logger = null) - : this(new SpannerBoxDetectionHelper(), configuration, tracer, logger) + ILogger? logger = null, + ILoggerFactory? loggerFactory = null) + : this(new SpannerBoxDetectionHelper(), configuration, tracer, logger, loggerFactory) { } diff --git a/src/Paramore.Brighter.BoxProvisioning.Sqlite/SqliteBoxMigrationRunner.cs b/src/Paramore.Brighter.BoxProvisioning.Sqlite/SqliteBoxMigrationRunner.cs index b8f1650531..a5b18b2cb8 100644 --- a/src/Paramore.Brighter.BoxProvisioning.Sqlite/SqliteBoxMigrationRunner.cs +++ b/src/Paramore.Brighter.BoxProvisioning.Sqlite/SqliteBoxMigrationRunner.cs @@ -27,7 +27,7 @@ THE SOFTWARE. */ using System.Threading.Tasks; using Microsoft.Data.Sqlite; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Observability; namespace Paramore.Brighter.BoxProvisioning.Sqlite; @@ -96,9 +96,10 @@ public SqliteBoxMigrationRunner( TimeSpan? lockTimeout = null, bool enableWalMode = true, IAmABrighterTracer? tracer = null, - MigrationHistoryScope scope = MigrationHistoryScope.Global) + MigrationHistoryScope scope = MigrationHistoryScope.Global, + ILoggerFactory? loggerFactory = null) : base(detectionHelper, catalog, configuration, lockTimeout ?? TimeSpan.FromSeconds(30), - logger ?? ApplicationLogging.CreateLogger(), + logger ?? (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(), tracer, scope) { _enableWalMode = enableWalMode; @@ -117,8 +118,9 @@ public SqliteBoxMigrationRunner( TimeSpan lockTimeout, bool enableWalMode = true, IAmABrighterTracer? tracer = null, - MigrationHistoryScope scope = MigrationHistoryScope.Global) - : this(new SqliteBoxDetectionHelper(), catalog, configuration, logger: null, lockTimeout: lockTimeout, enableWalMode: enableWalMode, tracer: tracer, scope: scope) + MigrationHistoryScope scope = MigrationHistoryScope.Global, + ILoggerFactory? loggerFactory = null) + : this(new SqliteBoxDetectionHelper(), catalog, configuration, logger: null, lockTimeout: lockTimeout, enableWalMode: enableWalMode, tracer: tracer, scope: scope, loggerFactory: loggerFactory) { } diff --git a/src/Paramore.Brighter.BoxProvisioning/SqlBoxProvisioner.cs b/src/Paramore.Brighter.BoxProvisioning/SqlBoxProvisioner.cs index 7783d2fedd..26efe43285 100644 --- a/src/Paramore.Brighter.BoxProvisioning/SqlBoxProvisioner.cs +++ b/src/Paramore.Brighter.BoxProvisioning/SqlBoxProvisioner.cs @@ -26,7 +26,7 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Paramore.Brighter.BoxProvisioning; @@ -52,10 +52,8 @@ public abstract class SqlBoxProvisioner where TConnection : DbConnection where TTransaction : DbTransaction { - // Static logger keeps the ctor surface unchanged across the 8 concrete provisioners; only - // exercised by the pre-lock-hint failure swallow below (Spec 0029 T-PERM). - private static readonly ILogger s_logger = - ApplicationLogging.CreateLogger>(); + // Logger is only exercised by the pre-lock-hint failure swallow below (Spec 0029 T-PERM). + private readonly ILogger _logger; private readonly IAmAVersionDetectingMigrationHelper _detectionHelper; private readonly IAmABoxMigrationCatalog _catalog; @@ -73,7 +71,8 @@ protected SqlBoxProvisioner( IAmABoxPayloadModeValidator payloadValidator, IAmARelationalDatabaseConfiguration configuration, IAmABoxMigrationRunner migrationRunner, - BoxType boxType) + BoxType boxType, + ILoggerFactory? loggerFactory = null) { _detectionHelper = detectionHelper; _catalog = catalog; @@ -81,6 +80,8 @@ protected SqlBoxProvisioner( _configuration = configuration; _migrationRunner = migrationRunner; BoxType = boxType; + _logger = (loggerFactory ?? NullLoggerFactory.Instance) + .CreateLogger>(); } /// @@ -173,7 +174,7 @@ private async Task DetectTableStateAsync( } catch (DbException ex) { - s_logger.LogDebug(ex, + _logger.LogDebug(ex, "Pre-lock historyExists hint for '{Schema}.{Table}' unavailable; deferring to the runner's under-lock authoritative detection.", EffectiveSchemaName, BoxTableName); historyExists = false; @@ -202,7 +203,7 @@ private async Task DetectTableStateAsync( } catch (DbException ex) { - s_logger.LogDebug(ex, + _logger.LogDebug(ex, "Pre-lock maxVersion hint for '{Schema}.{Table}' unavailable; deferring to the runner's under-lock authoritative detection.", EffectiveSchemaName, BoxTableName); maxVersion = 0; diff --git a/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionExtensions.cs b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionExtensions.cs index 1968852448..9895bcbd84 100644 --- a/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Paramore.Brighter.Extensions.DependencyInjection/ServiceCollectionExtensions.cs @@ -30,9 +30,9 @@ THE SOFTWARE. */ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using Paramore.Brighter.FeatureSwitch; -using Paramore.Brighter.Logging; using System.Text.Json; using Paramore.Brighter.CircuitBreaker; using Paramore.Brighter.JsonConverters; @@ -680,11 +680,9 @@ private static INeedInstrumentation AddEventBus( private static IAmACommandProcessor BuildCommandProcessor(IServiceProvider provider) { - var loggerFactory = provider.GetService(); - //if not supplied, use the default logger factory, which has no providers - if (loggerFactory != null) - ApplicationLogging.LoggerFactory = loggerFactory; - + //Resolve the container's logger factory and flow it through the builder as an instance, + //rather than copying it into a process-wide static (which would be disposed with the container). + var loggerFactory = provider.GetService() ?? NullLoggerFactory.Instance; var options = provider.GetRequiredService(); var subscriberRegistry = provider.GetRequiredService(); @@ -712,6 +710,7 @@ private static IAmACommandProcessor BuildCommandProcessor(IServiceProvider provi .ConfigureInstrumentation(provider.GetService(), options.InstrumentationOptions) .RequestContextFactory(provider.GetRequiredService()) .RequestSchedulerFactory(provider.GetRequiredService()) + .ConfigureLogging(loggerFactory) .Build(); var eventBusConfiguration = provider.GetService(); @@ -751,7 +750,8 @@ private static IAmACommandProcessor BuildCommandProcessor(IServiceProvider provi busConfiguration.MaxOutStandingCheckInterval, busConfiguration.OutBoxBag, TimeProvider.System, - busConfiguration.InstrumentationOptions); + busConfiguration.InstrumentationOptions, + serviceProvider.GetService() ?? NullLoggerFactory.Instance); } /// diff --git a/src/Paramore.Brighter.Inbox.MsSql/MsSqlInbox.cs b/src/Paramore.Brighter.Inbox.MsSql/MsSqlInbox.cs index ba36d9163d..16465eb514 100644 --- a/src/Paramore.Brighter.Inbox.MsSql/MsSqlInbox.cs +++ b/src/Paramore.Brighter.Inbox.MsSql/MsSqlInbox.cs @@ -26,7 +26,8 @@ THE SOFTWARE. */ using System; using System.Data; using Microsoft.Data.SqlClient; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.MsSql; using Paramore.Brighter.Observability; @@ -45,9 +46,10 @@ public class MsSqlInbox : RelationalDatabaseInbox /// /// The configuration. /// The Connection Provider. - public MsSqlInbox(IAmARelationalDatabaseConfiguration configuration, IAmARelationalDbConnectionProvider connectionProvider) + /// The logger to use; defaults to a null logger when not supplied + public MsSqlInbox(IAmARelationalDatabaseConfiguration configuration, IAmARelationalDbConnectionProvider connectionProvider, ILogger? logger = null) : base(DbSystem.MsSql, configuration, connectionProvider, - new MsSqlQueries(), ApplicationLogging.CreateLogger()) + new MsSqlQueries(), logger ?? NullLogger.Instance) { } @@ -55,8 +57,9 @@ public MsSqlInbox(IAmARelationalDatabaseConfiguration configuration, IAmARelatio /// Initializes a new instance of the class. /// /// The configuration. - public MsSqlInbox(IAmARelationalDatabaseConfiguration configuration) : this(configuration, - new MsSqlConnectionProvider(configuration)) + /// The logger to use; defaults to a null logger when not supplied + public MsSqlInbox(IAmARelationalDatabaseConfiguration configuration, ILogger? logger = null) : this(configuration, + new MsSqlConnectionProvider(configuration), logger) { } diff --git a/src/Paramore.Brighter.Inbox.MySql/MySqlInbox.cs b/src/Paramore.Brighter.Inbox.MySql/MySqlInbox.cs index 4fda383eb8..10701cda11 100644 --- a/src/Paramore.Brighter.Inbox.MySql/MySqlInbox.cs +++ b/src/Paramore.Brighter.Inbox.MySql/MySqlInbox.cs @@ -25,8 +25,9 @@ THE SOFTWARE. */ using System; using System.Data; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using MySqlConnector; -using Paramore.Brighter.Logging; using Paramore.Brighter.MySql; using Paramore.Brighter.Observability; @@ -44,9 +45,10 @@ public class MySqlInbox : RelationalDatabaseInbox /// /// The configuration. /// The Connection Provider. - public MySqlInbox(IAmARelationalDatabaseConfiguration configuration, IAmARelationalDbConnectionProvider connectionProvider) - : base(DbSystem.MySql, configuration, connectionProvider, - new MySqlQueries(), ApplicationLogging.CreateLogger()) + /// The logger to use; defaults to a null logger when not supplied + public MySqlInbox(IAmARelationalDatabaseConfiguration configuration, IAmARelationalDbConnectionProvider connectionProvider, ILogger? logger = null) + : base(DbSystem.MySql, configuration, connectionProvider, + new MySqlQueries(), logger ?? NullLogger.Instance) { } @@ -54,8 +56,9 @@ public MySqlInbox(IAmARelationalDatabaseConfiguration configuration, IAmARelatio /// Initializes a new instance of the class. /// /// The configuration. - public MySqlInbox(IAmARelationalDatabaseConfiguration configuration) : this(configuration, - new MySqlConnectionProvider(configuration)) + /// The logger to use; defaults to a null logger when not supplied + public MySqlInbox(IAmARelationalDatabaseConfiguration configuration, ILogger? logger = null) : this(configuration, + new MySqlConnectionProvider(configuration), logger) { } diff --git a/src/Paramore.Brighter.Inbox.Postgres/PostgreSqlInbox.cs b/src/Paramore.Brighter.Inbox.Postgres/PostgreSqlInbox.cs index 3c01e7e163..d4fa79c3e7 100644 --- a/src/Paramore.Brighter.Inbox.Postgres/PostgreSqlInbox.cs +++ b/src/Paramore.Brighter.Inbox.Postgres/PostgreSqlInbox.cs @@ -26,9 +26,10 @@ THE SOFTWARE. */ using System; using System.Data; using System.Linq; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Npgsql; using NpgsqlTypes; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; using Paramore.Brighter.PostgreSql; @@ -36,14 +37,14 @@ namespace Paramore.Brighter.Inbox.Postgres; public class PostgreSqlInbox : RelationalDatabaseInbox { - public PostgreSqlInbox(IAmARelationalDatabaseConfiguration configuration, IAmARelationalDbConnectionProvider connectionProvider) - : base(DbSystem.Postgresql, configuration, connectionProvider, - new PostgreSqlQueries(), ApplicationLogging.CreateLogger()) + public PostgreSqlInbox(IAmARelationalDatabaseConfiguration configuration, IAmARelationalDbConnectionProvider connectionProvider, ILogger? logger = null) + : base(DbSystem.Postgresql, configuration, connectionProvider, + new PostgreSqlQueries(), logger ?? NullLogger.Instance) { } - public PostgreSqlInbox(IAmARelationalDatabaseConfiguration configuration) - : this(configuration, new PostgreSqlConnectionProvider(configuration)) + public PostgreSqlInbox(IAmARelationalDatabaseConfiguration configuration, ILogger? logger = null) + : this(configuration, new PostgreSqlConnectionProvider(configuration), logger) { } diff --git a/src/Paramore.Brighter.Inbox.Spanner/SpannerInboxAsync.cs b/src/Paramore.Brighter.Inbox.Spanner/SpannerInboxAsync.cs index cc41221b54..ee1f2f5819 100644 --- a/src/Paramore.Brighter.Inbox.Spanner/SpannerInboxAsync.cs +++ b/src/Paramore.Brighter.Inbox.Spanner/SpannerInboxAsync.cs @@ -3,7 +3,8 @@ using System.Data.Common; using Google.Cloud.Spanner.Data; using Grpc.Core; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Observability; using Paramore.Brighter.Spanner; @@ -29,14 +30,15 @@ namespace Paramore.Brighter.Inbox.Spanner; /// public class SpannerInboxAsync( IAmARelationalDatabaseConfiguration configuration, - IAmARelationalDbConnectionProvider connectionProvider) + IAmARelationalDbConnectionProvider connectionProvider, + ILogger? logger = null) : RelationalDatabaseInbox(DbSystem.Spanner, configuration, connectionProvider, - new SpannerSqlQueries(), ApplicationLogging.CreateLogger()) + new SpannerSqlQueries(), logger ?? NullLogger.Instance) { - public SpannerInboxAsync(IAmARelationalDatabaseConfiguration configuration) - : this(configuration, new SpannerConnectionProvider(configuration)) + public SpannerInboxAsync(IAmARelationalDatabaseConfiguration configuration, ILogger? logger = null) + : this(configuration, new SpannerConnectionProvider(configuration), logger) { - + } /// diff --git a/src/Paramore.Brighter.Inbox.Sqlite/SqliteInbox.cs b/src/Paramore.Brighter.Inbox.Sqlite/SqliteInbox.cs index 28b177925f..ef24b39a7c 100644 --- a/src/Paramore.Brighter.Inbox.Sqlite/SqliteInbox.cs +++ b/src/Paramore.Brighter.Inbox.Sqlite/SqliteInbox.cs @@ -26,7 +26,8 @@ THE SOFTWARE. */ using System; using System.Data; using Microsoft.Data.Sqlite; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Observability; using Paramore.Brighter.Sqlite; @@ -45,9 +46,10 @@ public class SqliteInbox : RelationalDatabaseInbox /// /// The connection provider for the database. /// The configuration for the database. - public SqliteInbox(IAmARelationalDatabaseConfiguration configuration, IAmARelationalDbConnectionProvider connectionProvider) - : base(DbSystem.Sqlite, configuration, connectionProvider, - new SqliteQueries(), ApplicationLogging.CreateLogger()) + /// The logger to use; defaults to a null logger when not supplied + public SqliteInbox(IAmARelationalDatabaseConfiguration configuration, IAmARelationalDbConnectionProvider connectionProvider, ILogger? logger = null) + : base(DbSystem.Sqlite, configuration, connectionProvider, + new SqliteQueries(), logger ?? NullLogger.Instance) { } @@ -55,8 +57,9 @@ public SqliteInbox(IAmARelationalDatabaseConfiguration configuration, IAmARelati /// Initializes a new instance of the class. /// /// The configuration for the database. - public SqliteInbox(IAmARelationalDatabaseConfiguration configuration) - : this(configuration, new SqliteConnectionProvider(configuration)) + /// The logger to use; defaults to a null logger when not supplied + public SqliteInbox(IAmARelationalDatabaseConfiguration configuration, ILogger? logger = null) + : this(configuration, new SqliteConnectionProvider(configuration), logger) { } diff --git a/src/Paramore.Brighter.Locking.Azure/AzureBlobLockingProvider.cs b/src/Paramore.Brighter.Locking.Azure/AzureBlobLockingProvider.cs index 30031251f4..60f68a24e9 100644 --- a/src/Paramore.Brighter.Locking.Azure/AzureBlobLockingProvider.cs +++ b/src/Paramore.Brighter.Locking.Azure/AzureBlobLockingProvider.cs @@ -27,7 +27,7 @@ THE SOFTWARE. */ using Azure.Storage.Blobs; using Azure.Storage.Blobs.Specialized; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Paramore.Brighter.Locking.Azure; @@ -35,12 +35,13 @@ namespace Paramore.Brighter.Locking.Azure; /// The Azure Blob provider for distributed locks /// /// -public class AzureBlobLockingProvider(AzureBlobLockingProviderOptions options) : IDistributedLock +/// +public class AzureBlobLockingProvider(AzureBlobLockingProviderOptions options, ILoggerFactory? loggerFactory = null) : IDistributedLock { private readonly BlobContainerClient _containerClient = new BlobContainerClient(options.BlobContainerUri, options.TokenCredential); - private readonly ILogger _logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); /// /// Attempt to obtain a lock on a resource diff --git a/src/Paramore.Brighter.Locking.DynamoDB.V4/DynamoDbLockingProvider.cs b/src/Paramore.Brighter.Locking.DynamoDB.V4/DynamoDbLockingProvider.cs index 4ba0e47da8..fdbd116de3 100644 --- a/src/Paramore.Brighter.Locking.DynamoDB.V4/DynamoDbLockingProvider.cs +++ b/src/Paramore.Brighter.Locking.DynamoDB.V4/DynamoDbLockingProvider.cs @@ -24,7 +24,7 @@ THE SOFTWARE. */ using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.Model; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Paramore.Brighter.Locking.DynamoDB.V4; @@ -34,18 +34,19 @@ public partial class DynamoDbLockingProvider : IDistributedLock private readonly DynamoDbLockingProviderOptions _options; private readonly TimeProvider _timeProvider; - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; - public DynamoDbLockingProvider(IAmazonDynamoDB dynamoDb, DynamoDbLockingProviderOptions options) - :this(dynamoDb, options, TimeProvider.System) + public DynamoDbLockingProvider(IAmazonDynamoDB dynamoDb, DynamoDbLockingProviderOptions options, ILoggerFactory? loggerFactory = null) + :this(dynamoDb, options, TimeProvider.System, loggerFactory) { } - public DynamoDbLockingProvider(IAmazonDynamoDB dynamoDb, DynamoDbLockingProviderOptions options, TimeProvider timeProvider) + public DynamoDbLockingProvider(IAmazonDynamoDB dynamoDb, DynamoDbLockingProviderOptions options, TimeProvider timeProvider, ILoggerFactory? loggerFactory = null) { _dynamoDb = dynamoDb; _options = options; _timeProvider = timeProvider; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } /// @@ -64,11 +65,11 @@ public DynamoDbLockingProvider(IAmazonDynamoDB dynamoDb, DynamoDbLockingProvider } catch (ConditionalCheckFailedException) { - Log.UnableToObtainLockForResource(s_logger, resource); + Log.UnableToObtainLockForResource(_logger, resource); return null; } - Log.ObtainedLockForResource(s_logger, lockId, resource); + Log.ObtainedLockForResource(_logger, lockId, resource); return lockId; } @@ -89,7 +90,7 @@ public async Task ReleaseLockAsync(string resource, string lockId, CancellationT } catch (ConditionalCheckFailedException) { - Log.UnableToReleaseLockForResource(s_logger, lockId, resource); + Log.UnableToReleaseLockForResource(_logger, lockId, resource); } } } diff --git a/src/Paramore.Brighter.Locking.DynamoDB/DynamoDbLockingProvider.cs b/src/Paramore.Brighter.Locking.DynamoDB/DynamoDbLockingProvider.cs index 4ee1080d4c..d95e41f294 100644 --- a/src/Paramore.Brighter.Locking.DynamoDB/DynamoDbLockingProvider.cs +++ b/src/Paramore.Brighter.Locking.DynamoDB/DynamoDbLockingProvider.cs @@ -23,7 +23,7 @@ THE SOFTWARE. */ using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.Model; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Paramore.Brighter.Locking.DynamoDb { @@ -33,18 +33,19 @@ public partial class DynamoDbLockingProvider : IDistributedLock private readonly DynamoDbLockingProviderOptions _options; private readonly TimeProvider _timeProvider; - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; - public DynamoDbLockingProvider(IAmazonDynamoDB dynamoDb, DynamoDbLockingProviderOptions options) - :this(dynamoDb, options, TimeProvider.System) + public DynamoDbLockingProvider(IAmazonDynamoDB dynamoDb, DynamoDbLockingProviderOptions options, ILoggerFactory? loggerFactory = null) + :this(dynamoDb, options, TimeProvider.System, loggerFactory) { } - public DynamoDbLockingProvider(IAmazonDynamoDB dynamoDb, DynamoDbLockingProviderOptions options, TimeProvider timeProvider) + public DynamoDbLockingProvider(IAmazonDynamoDB dynamoDb, DynamoDbLockingProviderOptions options, TimeProvider timeProvider, ILoggerFactory? loggerFactory = null) { _dynamoDb = dynamoDb; _options = options; _timeProvider = timeProvider; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } /// @@ -63,11 +64,11 @@ public DynamoDbLockingProvider(IAmazonDynamoDB dynamoDb, DynamoDbLockingProvider } catch (ConditionalCheckFailedException) { - Log.UnableToObtainLockForResource(s_logger, resource); + Log.UnableToObtainLockForResource(_logger, resource); return null; } - Log.ObtainedLockForResource(s_logger, lockId, resource); + Log.ObtainedLockForResource(_logger, lockId, resource); return lockId; } @@ -88,7 +89,7 @@ public async Task ReleaseLockAsync(string resource, string lockId, CancellationT } catch (ConditionalCheckFailedException) { - Log.UnableToReleaseLockForResource(s_logger, lockId, resource); + Log.UnableToReleaseLockForResource(_logger, lockId, resource); } } } diff --git a/src/Paramore.Brighter.Locking.MsSql/MsSqlLockingProvider.cs b/src/Paramore.Brighter.Locking.MsSql/MsSqlLockingProvider.cs index 175ca9f14f..66af736697 100644 --- a/src/Paramore.Brighter.Locking.MsSql/MsSqlLockingProvider.cs +++ b/src/Paramore.Brighter.Locking.MsSql/MsSqlLockingProvider.cs @@ -28,7 +28,7 @@ THE SOFTWARE. */ using System.Data.Common; using Microsoft.Data.SqlClient; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.MsSql; namespace Paramore.Brighter.Locking.MsSql; @@ -37,12 +37,13 @@ namespace Paramore.Brighter.Locking.MsSql; /// The Microsoft Sql Server Locking Provider /// /// The Sql Server connection Provider -public class MsSqlLockingProvider(MsSqlConnectionProvider connectionProvider) +/// The factory used to create the logger for this provider +public class MsSqlLockingProvider(MsSqlConnectionProvider connectionProvider, ILoggerFactory? loggerFactory = null) : IDistributedLock, IAsyncDisposable, IDisposable { private readonly ConcurrentDictionary _connections = new(); - private readonly ILogger _logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); /// /// Attempt to obtain a lock on a resource diff --git a/src/Paramore.Brighter.Locking.MySql/MySqlLockingProvider.cs b/src/Paramore.Brighter.Locking.MySql/MySqlLockingProvider.cs index ad9a902327..97f18afc5a 100644 --- a/src/Paramore.Brighter.Locking.MySql/MySqlLockingProvider.cs +++ b/src/Paramore.Brighter.Locking.MySql/MySqlLockingProvider.cs @@ -7,8 +7,8 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using MySqlConnector; -using Paramore.Brighter.Logging; using Paramore.Brighter.MySql; namespace Paramore.Brighter.Locking.MySql; @@ -17,9 +17,10 @@ namespace Paramore.Brighter.Locking.MySql; /// The MySQL Locking Provider /// /// The MySQL connection Provider. -public class MySqlLockingProvider(MySqlConnectionProvider connectionProvider) : IDistributedLock, IAsyncDisposable +/// The factory used to create the logger for this provider. +public class MySqlLockingProvider(MySqlConnectionProvider connectionProvider, ILoggerFactory? loggerFactory = null) : IDistributedLock, IAsyncDisposable { - private readonly ILogger _logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); private readonly ConcurrentDictionary _connections = new(); /// diff --git a/src/Paramore.Brighter.Mediator/InMemoryJobChannel.cs b/src/Paramore.Brighter.Mediator/InMemoryJobChannel.cs index 4b3314ab2b..f784c13f48 100644 --- a/src/Paramore.Brighter.Mediator/InMemoryJobChannel.cs +++ b/src/Paramore.Brighter.Mediator/InMemoryJobChannel.cs @@ -28,7 +28,7 @@ THE SOFTWARE. */ using System.Threading.Channels; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Paramore.Brighter.Mediator; @@ -55,17 +55,20 @@ public enum FullChannelStrategy public class InMemoryJobChannel : IAmAJobChannel { private readonly Channel> _channel; - - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger>(); + + private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The maximum number of jobs the channel can hold. /// The strategy to use when the channel is full. + /// The factory used to create the logger for this channel. /// Thrown when the bounded capacity is less than or equal to 0. - public InMemoryJobChannel(int boundedCapacity = 100, FullChannelStrategy fullChannelStrategy = FullChannelStrategy.Wait) + public InMemoryJobChannel(int boundedCapacity = 100, FullChannelStrategy fullChannelStrategy = FullChannelStrategy.Wait, ILoggerFactory? loggerFactory = null) { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger>(); + if (boundedCapacity <= 0) throw new System.ArgumentOutOfRangeException(nameof(boundedCapacity), "Bounded capacity must be greater than 0"); diff --git a/src/Paramore.Brighter.Mediator/InMemoryStateStoreAsync.cs b/src/Paramore.Brighter.Mediator/InMemoryStateStoreAsync.cs index 91ea05ae1e..5cab512582 100644 --- a/src/Paramore.Brighter.Mediator/InMemoryStateStoreAsync.cs +++ b/src/Paramore.Brighter.Mediator/InMemoryStateStoreAsync.cs @@ -29,7 +29,7 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Paramore.Brighter.Mediator; @@ -41,15 +41,16 @@ public class InMemoryStateStoreAsync : IAmAStateStoreAsync private readonly ConcurrentDictionary _jobs = new(); private readonly TimeProvider _timeProvider; private DateTimeOffset _sinceTime; - - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + + private readonly ILogger _logger; /// /// Represents an in-memory store for jobs. /// - public InMemoryStateStoreAsync(TimeProvider? timeProvider = null) + public InMemoryStateStoreAsync(TimeProvider? timeProvider = null, ILoggerFactory? loggerFactory = null) { _timeProvider = timeProvider ?? TimeProvider.System; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } /// @@ -111,7 +112,7 @@ public InMemoryStateStoreAsync(TimeProvider? timeProvider = null) } catch (Exception e) { - s_logger.LogError($"Error saving job {job.Id} to in-memory store: {e.Message}"); + _logger.LogError($"Error saving job {job.Id} to in-memory store: {e.Message}"); return Task.FromException(e); } } diff --git a/src/Paramore.Brighter.Mediator/Runner.cs b/src/Paramore.Brighter.Mediator/Runner.cs index c5e9a41df3..3c1a491ba9 100644 --- a/src/Paramore.Brighter.Mediator/Runner.cs +++ b/src/Paramore.Brighter.Mediator/Runner.cs @@ -27,7 +27,7 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Paramore.Brighter.Mediator; @@ -43,7 +43,7 @@ public class Runner private readonly Scheduler _scheduler; private readonly string _runnerName = Uuid.New().ToString("N"); - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger>(); + private readonly ILogger _logger; /// /// Initializes a new instance of the class. @@ -52,12 +52,14 @@ public class Runner /// The job store to save job states. /// The command processor to handle commands. /// The scheduler which allows us to queue work that should be deferred - public Runner(IAmAJobChannel channel, IAmAStateStoreAsync stateStore, IAmACommandProcessor commandProcessor, Scheduler scheduler) + /// The factory used to create the logger for this runner. + public Runner(IAmAJobChannel channel, IAmAStateStoreAsync stateStore, IAmACommandProcessor commandProcessor, Scheduler scheduler, ILoggerFactory? loggerFactory = null) { _channel = channel; _stateStore = stateStore; _commandProcessor = commandProcessor; _scheduler = scheduler; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger>(); } /// @@ -67,7 +69,7 @@ public Runner(IAmAJobChannel channel, IAmAStateStoreAsync stateStore, IAm /// A task that completes when the job processing loop exits (via cancellation or channel closure). public async Task RunAsync(CancellationToken cancellationToken = default(CancellationToken)) { - s_logger.LogInformation("Starting runner {RunnerName}", _runnerName); + _logger.LogInformation("Starting runner {RunnerName}", _runnerName); try { @@ -75,7 +77,7 @@ public Runner(IAmAJobChannel channel, IAmAStateStoreAsync stateStore, IAm } finally { - s_logger.LogInformation("Finished runner {RunnerName}", _runnerName); + _logger.LogInformation("Finished runner {RunnerName}", _runnerName); } } @@ -84,7 +86,7 @@ public Runner(IAmAJobChannel channel, IAmAStateStoreAsync stateStore, IAm if (job is null) return; - s_logger.LogInformation("Executing job {JobId} on runner {RunnerName}", job.Id, _runnerName); + _logger.LogInformation("Executing job {JobId} on runner {RunnerName}", job.Id, _runnerName); job.State = JobState.Running; await _stateStore.SaveJobAsync(job, cancellationToken); @@ -92,7 +94,7 @@ public Runner(IAmAJobChannel channel, IAmAStateStoreAsync stateStore, IAm var step = job.CurrentStep(); while (step is not null) { - s_logger.LogInformation("Step is {StepName} with state {StepStste}", step.Name, step.State); + _logger.LogInformation("Step is {StepName} with state {StepStste}", step.Name, step.State); if (step.State == StepState.Queued) { await step.ExecuteAsync(_stateStore, _commandProcessor, _scheduler, cancellationToken); @@ -104,7 +106,7 @@ public Runner(IAmAJobChannel channel, IAmAStateStoreAsync stateStore, IAm //assume execute has advanced he step, if you your step loops endlessly it has not advanced the step!! step = job.CurrentStep(); - s_logger.LogInformation( + _logger.LogInformation( "Next step is {StepName} with state {StepState}", step is not null ? step.Name : "flow ends", step is not null ? step.State : StepState.Done); @@ -113,7 +115,7 @@ public Runner(IAmAJobChannel channel, IAmAStateStoreAsync stateStore, IAm if (job.State != JobState.Waiting) job.State = JobState.Done; - s_logger.LogInformation("Finished executing job {JobId} on {RunnerName}", job.Id, _runnerName); + _logger.LogInformation("Finished executing job {JobId} on {RunnerName}", job.Id, _runnerName); } private async Task ProcessJobs(CancellationToken cancellationToken = default(CancellationToken)) @@ -126,14 +128,14 @@ public Runner(IAmAJobChannel channel, IAmAStateStoreAsync stateStore, IAm if (_channel.IsClosed()) break; - s_logger.LogInformation("Looking for jobs on {RunnerName}", _runnerName); + _logger.LogInformation("Looking for jobs on {RunnerName}", _runnerName); var job = await _channel.DequeueJobAsync(cancellationToken); if (job is null) continue; - s_logger.LogInformation("Executing job {JobId} on {RunnerName}", job.Id, _runnerName); + _logger.LogInformation("Executing job {JobId} on {RunnerName}", job.Id, _runnerName); await Execute(job, cancellationToken); - s_logger.LogInformation("Finished job {JobId} on {RunnerName}", job.Id, _runnerName); + _logger.LogInformation("Finished job {JobId} on {RunnerName}", job.Id, _runnerName); } } } diff --git a/src/Paramore.Brighter.Mediator/Steps.cs b/src/Paramore.Brighter.Mediator/Steps.cs index cdbc00fd95..de6bb9d4ba 100644 --- a/src/Paramore.Brighter.Mediator/Steps.cs +++ b/src/Paramore.Brighter.Mediator/Steps.cs @@ -27,8 +27,8 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter; -using Paramore.Brighter.Logging; namespace Paramore.Brighter.Mediator; @@ -52,13 +52,14 @@ public abstract class Step( string name, Sequential? next, IStepTask? stepTask = null, - Action? onCompletion = null) + Action? onCompletion = null, + ILoggerFactory? loggerFactory = null) { /// Which job is being executed by the step. protected Job? Job ; /// The logger for the step. - protected static readonly ILogger s_logger = ApplicationLogging.CreateLogger>(); + protected readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger>(); /// The name of the step, used for tracing execution public string Name { get; init; } = name; @@ -116,9 +117,10 @@ public class ExclusiveChoice( ISpecification predicate, Action? onCompletion, Sequential? nextTrue, - Sequential? nextFalse + Sequential? nextFalse, + ILoggerFactory? loggerFactory = null ) - : Step(name, null, null, onCompletion) + : Step(name, null, null, onCompletion, loggerFactory) { /// /// The work of the step is done here. Note that this is an abstract method, so it must be implemented by the derived class. @@ -158,8 +160,9 @@ public override async Task ExecuteAsync( public class ParallelSplit( string name, - Func>>? onMap) - : Step(name, null) + Func>>? onMap, + ILoggerFactory? loggerFactory = null) + : Step(name, null, loggerFactory: loggerFactory) { /// /// The work of the step is done here. Note that this is an abstract method, so it must be implemented by the derived class. @@ -223,14 +226,15 @@ public override async Task ExecuteAsync( /// The next step in the sequence, following a faulted execution of the step /// The data that the step operates over public class Sequential( - string name, - IStepTask stepTask, - Action? onCompletion, - Sequential? next, - Action? onFaulted = null, - Sequential? faultNext = null -) - : Step(name, next, stepTask, onCompletion) + string name, + IStepTask stepTask, + Action? onCompletion, + Sequential? next, + Action? onFaulted = null, + Sequential? faultNext = null, + ILoggerFactory? loggerFactory = null +) + : Step(name, next, stepTask, onCompletion, loggerFactory) { /// /// The work of the step is done here. Note that this is an abstract method, so it must be implemented by the derived class. @@ -254,7 +258,7 @@ public override async Task ExecuteAsync( if (StepTask is null) { - s_logger.LogWarning("No task to execute for {Name}", Name); + _logger.LogWarning("No task to execute for {Name}", Name); State = StepState.Done; await stateStore.SaveJobAsync(Job, cancellationToken); return; @@ -303,9 +307,10 @@ public class Wait : Step /// The name of the step, used for tracing execution /// The period for which we pause /// The next step in the sequence, null if this is the last step. + /// The factory used to create the logger for this step. /// The data that the step operates over - public Wait(string name, TimeSpan duration, Sequential? next) - : base(name, next) + public Wait(string name, TimeSpan duration, Sequential? next, ILoggerFactory? loggerFactory = null) + : base(name, next, loggerFactory: loggerFactory) { _duration = duration; } diff --git a/src/Paramore.Brighter.Mediator/Waker.cs b/src/Paramore.Brighter.Mediator/Waker.cs index ebe0af0629..60c62908a2 100644 --- a/src/Paramore.Brighter.Mediator/Waker.cs +++ b/src/Paramore.Brighter.Mediator/Waker.cs @@ -26,7 +26,7 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Paramore.Brighter.Mediator; @@ -39,18 +39,20 @@ public class Waker private readonly TimeSpan _jobAge; private readonly Scheduler _scheduler; private readonly string _wakerName = Uuid.New().ToString("N"); - - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger>(); + + private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The age of the job to determine if it is due. /// The scheduler to trigger due jobs. - public Waker(TimeSpan jobAge, Scheduler scheduler) + /// The factory used to create the logger for this waker. + public Waker(TimeSpan jobAge, Scheduler scheduler, ILoggerFactory? loggerFactory = null) { _jobAge = jobAge; _scheduler = scheduler; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger>(); } /// @@ -61,7 +63,7 @@ public Waker(TimeSpan jobAge, Scheduler scheduler) /// A task that completes when the wake loop exits (via cancellation). public async Task RunAsync(CancellationToken cancellationToken = default(CancellationToken)) { - s_logger.LogInformation("Starting waker {WakerName}", _wakerName); + _logger.LogInformation("Starting waker {WakerName}", _wakerName); try { @@ -69,7 +71,7 @@ public Waker(TimeSpan jobAge, Scheduler scheduler) } finally { - s_logger.LogInformation("Finished waker {WakerName}", _wakerName); + _logger.LogInformation("Finished waker {WakerName}", _wakerName); } } diff --git a/src/Paramore.Brighter.MessageScheduler.Azure/AzureServiceBusScheduler.cs b/src/Paramore.Brighter.MessageScheduler.Azure/AzureServiceBusScheduler.cs index 83283ac776..59c6d4d39e 100644 --- a/src/Paramore.Brighter.MessageScheduler.Azure/AzureServiceBusScheduler.cs +++ b/src/Paramore.Brighter.MessageScheduler.Azure/AzureServiceBusScheduler.cs @@ -2,8 +2,8 @@ using System.Text.Json; using Azure.Messaging.ServiceBus; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Paramore.Brighter.Tasks; namespace Paramore.Brighter.MessageScheduler.Azure; @@ -14,13 +14,15 @@ namespace Paramore.Brighter.MessageScheduler.Azure; /// The . /// The scheduler topic or queue /// The . +/// The used to create the logger. public class AzureServiceBusScheduler( ServiceBusSender sender, RoutingKey schedulerTopic, - TimeProvider timeProvider) + TimeProvider timeProvider, + ILoggerFactory? loggerFactory = null) : IAmAMessageSchedulerAsync, IAmAMessageSchedulerSync, IAmARequestSchedulerAsync, IAmARequestSchedulerSync { - private static readonly ILogger Logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); /// public async Task ScheduleAsync(Message message, DateTimeOffset at, @@ -118,7 +120,7 @@ public async Task CancelAsync(string id, CancellationToken cancellationToken = d } else { - Logger.LogWarning("Could not cancel message as schedulerId is not a sequence number"); + _logger.LogWarning("Could not cancel message as schedulerId is not a sequence number"); } } diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/AWSMessagingGateway.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/AWSMessagingGateway.cs index 4d56dfb47a..f168e04397 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/AWSMessagingGateway.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/AWSMessagingGateway.cs @@ -36,20 +36,27 @@ THE SOFTWARE. */ using Amazon.SQS; using Amazon.SQS.Model; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Paramore.Brighter.MessagingGateway.AWSSQS.V4.Extensions; using Paramore.Brighter.Tasks; using InvalidOperationException = System.InvalidOperationException; namespace Paramore.Brighter.MessagingGateway.AWSSQS.V4; -public class AwsMessagingGateway(AWSMessagingGatewayConnection awsConnection) +public class AwsMessagingGateway { - protected static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + protected readonly ILogger _logger; - private readonly AWSClientFactory _awsClientFactory = new(awsConnection); - protected readonly AWSMessagingGatewayConnection AwsConnection = awsConnection; + private readonly AWSClientFactory _awsClientFactory; + protected readonly AWSMessagingGatewayConnection AwsConnection; + + public AwsMessagingGateway(AWSMessagingGatewayConnection awsConnection, ILoggerFactory? loggerFactory = null) + { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + _awsClientFactory = new AWSClientFactory(awsConnection); + AwsConnection = awsConnection; + } /// /// The Channel Address diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/ChannelFactory.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/ChannelFactory.cs index 798607dddd..5ba7dd9c86 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/ChannelFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/ChannelFactory.cs @@ -51,10 +51,10 @@ public partial class ChannelFactory : AwsMessagingGateway, IAmAChannelFactory /// Initializes a new instance of the class. /// /// The details of the subscription to AWS. - public ChannelFactory(AWSMessagingGatewayConnection awsConnection) - : base(awsConnection) + public ChannelFactory(AWSMessagingGatewayConnection awsConnection, ILoggerFactory? loggerFactory = null) + : base(awsConnection, loggerFactory) { - _messageConsumerFactory = new SqsMessageConsumerFactory(awsConnection); + _messageConsumerFactory = new SqsMessageConsumerFactory(awsConnection, loggerFactory); _retryPolicy = Policy .Handle() .WaitAndRetryAsync([TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10)]); @@ -169,7 +169,7 @@ await QueueExistsAsync(sqsClient, } catch (Exception) { - Log.CouldNotDeleteQueue(s_logger, queueExists.queueUrl); + Log.CouldNotDeleteQueue(_logger, queueExists.queueUrl); } } } @@ -196,7 +196,7 @@ public async Task DeleteTopicAsync() } catch (Exception) { - Log.CouldNotDeleteTopic(s_logger, ChannelTopicArn); + Log.CouldNotDeleteTopic(_logger, ChannelTopicArn); } } } @@ -311,7 +311,7 @@ private async Task UnsubscribeFromTopicAsync(AmazonSimpleNotificationServiceClie await snsClient.UnsubscribeAsync(new UnsubscribeRequest { SubscriptionArn = sub.SubscriptionArn }); if (unsubscribe.HttpStatusCode != HttpStatusCode.OK) { - Log.ErrorUnsubscribingFromTopic(s_logger, ChannelAddress, sub.SubscriptionArn); + Log.ErrorUnsubscribingFromTopic(_logger, ChannelAddress, sub.SubscriptionArn); } } } while (response.NextToken != null); diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SnsMessageProducer.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SnsMessageProducer.cs index b37b60652d..2af1f1d3af 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SnsMessageProducer.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SnsMessageProducer.cs @@ -65,10 +65,11 @@ public Publication Publication /// How do we connect to AWS in order to manage middleware /// Configuration of a producer /// - public SnsMessageProducer(AWSMessagingGatewayConnection connection, + public SnsMessageProducer(AWSMessagingGatewayConnection connection, SnsPublication publication, - InstrumentationOptions instrumentation = InstrumentationOptions.All) - : base(connection) + InstrumentationOptions instrumentation = InstrumentationOptions.All, + ILoggerFactory? loggerFactory = null) + : base(connection, loggerFactory) { _publication = publication; _clientFactory = new AWSClientFactory(connection); @@ -179,7 +180,7 @@ private async Task SendWithDelayAsync(Message message, TimeSpan? delay, bool use } BrighterTracer.WriteProducerEvent(Span, "aws_sns", message, _options); - Log.PublishingMessage(s_logger, message.Header.Topic.Value, message.Id.Value, message.Body); + Log.PublishingMessage(_logger, message.Header.Topic.Value, message.Id.Value, message.Body); await ConfirmTopicExistsAsync(message.Header.Topic, cancellationToken); @@ -195,7 +196,7 @@ private async Task SendWithDelayAsync(Message message, TimeSpan? delay, bool use throw new InvalidOperationException( $"Failed to publish message with topic {message.Header.Topic} and id {message.Id} and message: {message.Body}"); - Log.PublishedMessage(s_logger, message.Header.Topic.Value, message.Id.Value, messageId); + Log.PublishedMessage(_logger, message.Header.Topic.Value, message.Id.Value, messageId); } private static partial class Log diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SnsMessageProducerFactory.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SnsMessageProducerFactory.cs index e3a7789ac2..4e0d032d9f 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SnsMessageProducerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SnsMessageProducerFactory.cs @@ -26,6 +26,7 @@ THE SOFTWARE. */ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace Paramore.Brighter.MessagingGateway.AWSSQS.V4; @@ -33,18 +34,22 @@ public class SnsMessageProducerFactory : IAmAMessageProducerFactory { private readonly AWSMessagingGatewayConnection _connection; private readonly IEnumerable _publications; + private readonly ILoggerFactory? _loggerFactory; /// /// Creates a collection of SNS message producers from the SNS publication information /// /// The Connection to use to connect to AWS /// The publications describing the SNS topics that we want to use + /// The factory used to create loggers for the producers. public SnsMessageProducerFactory( AWSMessagingGatewayConnection connection, - IEnumerable publications) + IEnumerable publications, + ILoggerFactory? loggerFactory = null) { _connection = connection; _publications = publications; + _loggerFactory = loggerFactory; } /// @@ -60,7 +65,7 @@ public Dictionary Create() if (publication.Topic is null) throw new ConfigurationException("Missing topic on Publication"); - var producer = new SnsMessageProducer(_connection, publication); + var producer = new SnsMessageProducer(_connection, publication, loggerFactory: _loggerFactory); producer.Publication = publication; if (producer.ConfirmTopicExists()) @@ -92,7 +97,7 @@ public async Task> CreateAsync() if (publication.Topic is null) throw new ConfigurationException("Missing topic on Publication"); - var producer = new SnsMessageProducer(_connection, publication); + var producer = new SnsMessageProducer(_connection, publication, loggerFactory: _loggerFactory); producer.Publication = publication; if (await producer.ConfirmTopicExistsAsync()) diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SnsProducerRegistryFactory.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SnsProducerRegistryFactory.cs index 8f726eab47..743fe8516e 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SnsProducerRegistryFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SnsProducerRegistryFactory.cs @@ -26,6 +26,7 @@ THE SOFTWARE. */ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace Paramore.Brighter.MessagingGateway.AWSSQS.V4; @@ -36,18 +37,22 @@ public class SnsProducerRegistryFactory : IAmAProducerRegistryFactory { private readonly AWSMessagingGatewayConnection _connection; private readonly IEnumerable _snsPublications; + private readonly ILoggerFactory? _loggerFactory; /// /// Create a collection of producers from the publication information /// /// The Connection to use to connect to AWS /// The publication describing the SNS topic that we want to use + /// The factory used to create loggers for the producers. public SnsProducerRegistryFactory( AWSMessagingGatewayConnection connection, - IEnumerable snsPublications) + IEnumerable snsPublications, + ILoggerFactory? loggerFactory = null) { _connection = connection; _snsPublications = snsPublications; + _loggerFactory = loggerFactory; } /// @@ -56,7 +61,7 @@ public SnsProducerRegistryFactory( /// The with . public IAmAProducerRegistry Create() { - var producerFactory = new SnsMessageProducerFactory(_connection, _snsPublications); + var producerFactory = new SnsMessageProducerFactory(_connection, _snsPublications, _loggerFactory); return new ProducerRegistry(producerFactory.Create()); } @@ -67,7 +72,7 @@ public IAmAProducerRegistry Create() /// The with . public async Task CreateAsync(CancellationToken ct = default) { - var producerFactory = new SnsMessageProducerFactory(_connection, _snsPublications); + var producerFactory = new SnsMessageProducerFactory(_connection, _snsPublications, _loggerFactory); return new ProducerRegistry(await producerFactory.CreateAsync()); } } diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsInlineMessageCreator.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsInlineMessageCreator.cs index fbe7107db9..c7d18e7ff0 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsInlineMessageCreator.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsInlineMessageCreator.cs @@ -30,18 +30,23 @@ THE SOFTWARE. */ using Amazon; using Amazon.SQS; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; namespace Paramore.Brighter.MessagingGateway.AWSSQS.V4; internal sealed partial class SqsInlineMessageCreator : SqsMessageCreatorBase, ISqsMessageCreator { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private Dictionary _messageAttributes = new(); + public SqsInlineMessageCreator(ILoggerFactory? loggerFactory = null) + { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + } + public Message CreateMessage(Amazon.SQS.Model.Message sqsMessage) { var topic = HeaderResult.Empty(); @@ -112,12 +117,12 @@ public Message CreateMessage(Amazon.SQS.Model.Message sqsMessage) } catch (Exception e) { - Log.FailedToCreateMessageFromAwsSqsMessage(s_logger, e); + Log.FailedToCreateMessageFromAwsSqsMessage(_logger, e); return Message.FailureMessage(topic.Result, messageId.Result); } } - private static Dictionary ReadMessageAttributes(JsonDocument jsonDocument) + private Dictionary ReadMessageAttributes(JsonDocument jsonDocument) { var messageAttributes = new Dictionary(); @@ -132,7 +137,7 @@ private static Dictionary ReadMessageAttributes(JsonDocumen } catch (Exception ex) { - Log.FailedWhileDeserializingSqsMessageBody(s_logger, ex); + Log.FailedWhileDeserializingSqsMessageBody(_logger, ex); } return messageAttributes ?? new Dictionary(); @@ -393,13 +398,13 @@ private HeaderResult ReadTopic() } catch (Exception ex) { - Log.FailedToParseSqsMessageBodyToValidJsonDocument(s_logger, ex); + Log.FailedToParseSqsMessageBodyToValidJsonDocument(_logger, ex); } return new HeaderResult(null, true); } - private static MessageBody ReadMessageBody(JsonDocument jsonDocument) + private MessageBody ReadMessageBody(JsonDocument jsonDocument) { try { @@ -410,7 +415,7 @@ private static MessageBody ReadMessageBody(JsonDocument jsonDocument) } catch (Exception ex) { - Log.FailedToParseSqsMessageBodyToValidJsonDocument(s_logger, ex); + Log.FailedToParseSqsMessageBodyToValidJsonDocument(_logger, ex); } return new MessageBody(string.Empty); diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageConsumer.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageConsumer.cs index a115df23c3..fbbfc50cd8 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageConsumer.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageConsumer.cs @@ -30,8 +30,8 @@ THE SOFTWARE. */ using Amazon.SQS; using Amazon.SQS.Model; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Paramore.Brighter.Tasks; namespace Paramore.Brighter.MessagingGateway.AWSSQS.V4; @@ -41,9 +41,10 @@ namespace Paramore.Brighter.MessagingGateway.AWSSQS.V4; /// public partial class SqsMessageConsumer : IAmAMessageConsumerSync, IAmAMessageConsumerAsync { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private readonly AWSMessagingGatewayConnection _connection; + private readonly ILoggerFactory? _loggerFactory; private readonly AWSClientFactory _clientFactory; private readonly string _queueName; private readonly int _batchSize; @@ -69,6 +70,7 @@ public partial class SqsMessageConsumer : IAmAMessageConsumerSync, IAmAMessageCo /// Is the queue name a queue url? /// Do we have Raw Message Delivery enabled? /// The for the queue (used by DLQ producers for FIFO support) + /// The factory used to create a logger for this consumer public SqsMessageConsumer( AWSMessagingGatewayConnection awsConnection, string? queueName, @@ -78,11 +80,14 @@ public SqsMessageConsumer( OnMissingChannel makeChannels = OnMissingChannel.Create, bool isQueueUrl = false, bool rawMessageDelivery = true, - SqsAttributes? queueAttributes = null) + SqsAttributes? queueAttributes = null, + ILoggerFactory? loggerFactory = null) { if (string.IsNullOrEmpty(queueName)) throw new ConfigurationException("QueueName is mandatory"); + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + _loggerFactory = loggerFactory; _connection = awsConnection; _clientFactory = new AWSClientFactory(awsConnection); _queueName = queueName!; @@ -155,14 +160,14 @@ public SqsMessageConsumer( var reasonString = reason is null ? nameof(RejectionReason.DeliveryError) : reason.RejectionReason.ToString(); var description = reason is null ? "unknown" : reason.Description ?? "unknown"; - Log.RejectingMessage(s_logger, message.Id.Value, receiptHandle, _queueName, reasonString, description); + Log.RejectingMessage(_logger, message.Id.Value, receiptHandle, _queueName, reasonString, description); // If no channels configured, just delete the original message if (_deadLetterProducer == null && _invalidMessageProducer == null) { if (reason != null) { - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value, reason.RejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value, reason.RejectionReason.ToString()); } await AcknowledgeAsync(message, cancellationToken); @@ -183,7 +188,7 @@ public SqsMessageConsumer( { message.Header.Topic = routingKey!; if (isFallingBackToDlq) - Log.FallingBackToDlq(s_logger, message.Id.Value); + Log.FallingBackToDlq(_logger, message.Id.Value); if (routingKey == _invalidMessageRoutingKey) producer = _invalidMessageProducer?.Value; @@ -194,18 +199,18 @@ public SqsMessageConsumer( if (producer != null) { await producer.SendAsync(message, cancellationToken); - Log.MessageSentToRejectionChannel(s_logger, message.Id.Value, rejectionReason.ToString()); + Log.MessageSentToRejectionChannel(_logger, message.Id.Value, rejectionReason.ToString()); } else { - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value, rejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value, rejectionReason.ToString()); } } catch (Exception ex) { // Sending to DLQ failed — delete the original to prevent infinite // reprocessing. The message is lost rather than stuck in a retry loop. - Log.ErrorSendingToRejectionChannel(s_logger, ex, message.Id.Value, rejectionReason.ToString()); + Log.ErrorSendingToRejectionChannel(_logger, ex, message.Id.Value, rejectionReason.ToString()); await DeleteSourceMessageAsync(receiptHandle!, message.Id.Value, cancellationToken); return true; } @@ -228,16 +233,16 @@ public SqsMessageConsumer( try { using var client = _clientFactory.CreateSqsClient(); - Log.PurgingQueue(s_logger, _queueName); + Log.PurgingQueue(_logger, _queueName); await EnsureChannelUrl(client, cancellationToken); await client.PurgeQueueAsync(_channelUrl, cancellationToken); - Log.PurgedQueue(s_logger, _queueName); + Log.PurgedQueue(_logger, _queueName); } catch (Exception exception) { - Log.ErrorPurgingQueue(s_logger, exception, _queueName); + Log.ErrorPurgingQueue(_logger, exception, _queueName); throw; } } @@ -266,7 +271,7 @@ public async Task ReceiveAsync(TimeSpan? timeOut = null, await EnsureChannelUrl(client, cancellationToken); timeOut ??= TimeSpan.Zero; - Log.RetrievingNextMessage(s_logger,_channelUrl!); + Log.RetrievingNextMessage(_logger,_channelUrl!); var request = new ReceiveMessageRequest(_channelUrl) { @@ -282,17 +287,17 @@ public async Task ReceiveAsync(TimeSpan? timeOut = null, } catch (InvalidOperationException ioe) { - Log.CouldNotDetermineNumberOfMessagesToRetrieve(s_logger); + Log.CouldNotDetermineNumberOfMessagesToRetrieve(_logger); throw new ChannelFailureException("Error connecting to SQS, see inner exception for details", ioe); } catch (OperationCanceledException oce) { - Log.CouldNotFindMessagesToRetrieve(s_logger); + Log.CouldNotFindMessagesToRetrieve(_logger); throw new ChannelFailureException("Error connecting to SQS, see inner exception for details", oce); } catch (Exception e) { - Log.ErrorListeningToQueue(s_logger, e, _queueName); + Log.ErrorListeningToQueue(_logger, e, _queueName); throw; } finally @@ -308,8 +313,8 @@ public async Task ReceiveAsync(TimeSpan? timeOut = null, var messages = new Message[sqsMessages.Length]; for (int i = 0; i < sqsMessages.Length; i++) { - var message = SqsMessageCreatorFactory.Create(_rawMessageDelivery).CreateMessage(sqsMessages[i]); - Log.ReceivedMessageFromQueue(s_logger, _queueName, Environment.NewLine, JsonSerializer.Serialize(message, JsonSerialisationOptions.Options)); + var message = SqsMessageCreatorFactory.Create(_rawMessageDelivery, _loggerFactory).CreateMessage(sqsMessages[i]); + Log.ReceivedMessageFromQueue(_logger, _queueName, Environment.NewLine, JsonSerializer.Serialize(message, JsonSerialisationOptions.Options)); messages[i] = message; } @@ -339,7 +344,7 @@ public async Task NackAsync(Message message, CancellationToken cancellationToken try { - Log.NackingMessage(s_logger, message.Id.Value, receiptHandle, _queueName); + Log.NackingMessage(_logger, message.Id.Value, receiptHandle, _queueName); using var client = _clientFactory.CreateSqsClient(); await EnsureChannelUrl(client, cancellationToken); @@ -348,7 +353,7 @@ await client.ChangeMessageVisibilityAsync( cancellationToken ); - Log.NackedMessage(s_logger, message.Id.Value, receiptHandle, _channelUrl!); + Log.NackedMessage(_logger, message.Id.Value, receiptHandle, _channelUrl!); } catch (ReceiptHandleIsInvalidException ex) { @@ -356,11 +361,11 @@ await client.ChangeMessageVisibilityAsync( // SQS has already made the message visible again for redelivery by another consumer. // Nack sets visibility to zero for immediate redelivery — but the message is already // visible again, so the net effect is the same. Log a warning and continue. - Log.NackFailedReceiptHandleExpired(s_logger, ex, message.Id.Value, receiptHandle, _queueName); + Log.NackFailedReceiptHandleExpired(_logger, ex, message.Id.Value, receiptHandle, _queueName); } catch (Exception exception) { - Log.ErrorNackingMessage(s_logger, exception, message.Id.Value, receiptHandle, _queueName); + Log.ErrorNackingMessage(_logger, exception, message.Id.Value, receiptHandle, _queueName); throw; } } @@ -386,7 +391,7 @@ public async Task RequeueAsync(Message message, TimeSpan? delay = null, try { - Log.RequeueingMessage(s_logger, message.Id.Value); + Log.RequeueingMessage(_logger, message.Id.Value); using (var client = _clientFactory.CreateSqsClient()) { @@ -397,7 +402,7 @@ await client.ChangeMessageVisibilityAsync( ); } - Log.RequeuedMessage(s_logger, message.Id.Value); + Log.RequeuedMessage(_logger, message.Id.Value); return true; } @@ -406,12 +411,12 @@ await client.ChangeMessageVisibilityAsync( // Receipt handle is invalid (most likely because the visibility timeout elapsed). // SQS has already made the message visible again for redelivery by another consumer, // but without the intended delay. Log a warning so operators are aware. - Log.RequeueFailedReceiptHandleExpired(s_logger, ex, message.Id.Value, receiptHandle, _queueName, delay.Value); + Log.RequeueFailedReceiptHandleExpired(_logger, ex, message.Id.Value, receiptHandle, _queueName, delay.Value); return false; } catch (Exception exception) { - Log.ErrorRequeueingMessage(s_logger, exception, message.Id.Value, receiptHandle, _queueName); + Log.ErrorRequeueingMessage(_logger, exception, message.Id.Value, receiptHandle, _queueName); return false; } } @@ -458,11 +463,11 @@ public async ValueTask DisposeAsync() // We must NOT call the sync ConfirmQueueExists here because the lazy is resolved // inside RejectAsync, which may already be running inside BrighterAsyncContext.Run // from the sync Reject path — nesting would deadlock. - return new SqsMessageProducer(_connection, publication); + return new SqsMessageProducer(_connection, publication, loggerFactory: _loggerFactory); } catch (Exception e) { - Log.ErrorCreatingDlqProducerException(s_logger, e, _deadLetterRoutingKey.Value); + Log.ErrorCreatingDlqProducerException(_logger, e, _deadLetterRoutingKey.Value); return null; } } @@ -477,11 +482,11 @@ public async ValueTask DisposeAsync() try { // Queue existence is confirmed on first SendAsync via ConfirmQueueExistsAsync. - return new SqsMessageProducer(_connection, publication); + return new SqsMessageProducer(_connection, publication, loggerFactory: _loggerFactory); } catch (Exception e) { - Log.ErrorCreatingInvalidMessageProducerException(s_logger, e, _invalidMessageRoutingKey.Value); + Log.ErrorCreatingInvalidMessageProducerException(_logger, e, _invalidMessageRoutingKey.Value); return null; } } @@ -514,7 +519,7 @@ private async Task DeleteSourceMessageAsync(string receiptHandle, string message await client.DeleteMessageAsync(new DeleteMessageRequest(_channelUrl, receiptHandle), cancellationToken); - Log.DeletedMessage(s_logger, messageId, receiptHandle, _channelUrl!); + Log.DeletedMessage(_logger, messageId, receiptHandle, _channelUrl!); } catch (ReceiptHandleIsInvalidException ex) { @@ -522,11 +527,11 @@ await client.DeleteMessageAsync(new DeleteMessageRequest(_channelUrl, receiptHan // SQS has already made the message visible again for redelivery by another consumer. // This is an error because the message was not deleted and may be processed again; // handlers should be idempotent, but an operator may need to investigate. - Log.DeleteFailedReceiptHandleExpired(s_logger, ex, messageId, receiptHandle, _queueName); + Log.DeleteFailedReceiptHandleExpired(_logger, ex, messageId, receiptHandle, _queueName); } catch (Exception exception) { - Log.ErrorDeletingMessage(s_logger, exception, messageId, receiptHandle, _queueName); + Log.ErrorDeletingMessage(_logger, exception, messageId, receiptHandle, _queueName); throw; } } diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageConsumerFactory.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageConsumerFactory.cs index 1fa55c63ce..ec78a69c15 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageConsumerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageConsumerFactory.cs @@ -21,6 +21,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #endregion +using Microsoft.Extensions.Logging; + namespace Paramore.Brighter.MessagingGateway.AWSSQS.V4; /// @@ -29,13 +31,15 @@ namespace Paramore.Brighter.MessagingGateway.AWSSQS.V4; public class SqsMessageConsumerFactory : IAmAMessageConsumerFactory { private readonly AWSMessagingGatewayConnection _awsConnection; + private readonly ILoggerFactory? _loggerFactory; /// /// Initializes a new instance of the class. /// - public SqsMessageConsumerFactory(AWSMessagingGatewayConnection awsConnection) + public SqsMessageConsumerFactory(AWSMessagingGatewayConnection awsConnection, ILoggerFactory? loggerFactory = null) { _awsConnection = awsConnection; + _loggerFactory = loggerFactory; } /// @@ -87,7 +91,8 @@ private SqsMessageConsumer CreateImpl(Subscription subscription) makeChannels: sqsSubscription.MakeChannels, isQueueUrl: (sqsSubscription.FindQueueBy == QueueFindBy.Url), rawMessageDelivery: sqsSubscription.QueueAttributes.RawMessageDelivery, - queueAttributes: sqsSubscription.QueueAttributes + queueAttributes: sqsSubscription.QueueAttributes, + loggerFactory: _loggerFactory ); } } \ No newline at end of file diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageCreator.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageCreator.cs index 7d81fcd440..5d14eb3f1a 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageCreator.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageCreator.cs @@ -30,8 +30,8 @@ THE SOFTWARE. */ using Amazon.SQS; using Amazon.SQS.Model; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; using Paramore.Brighter.Transforms.Transformers; @@ -52,7 +52,12 @@ internal enum ARNAmazonSNS internal sealed partial class SqsMessageCreator : SqsMessageCreatorBase, ISqsMessageCreator { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; + + public SqsMessageCreator(ILoggerFactory? loggerFactory = null) + { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + } public Message CreateMessage(Amazon.SQS.Model.Message sqsMessage) { @@ -114,7 +119,7 @@ public Message CreateMessage(Amazon.SQS.Model.Message sqsMessage) } catch (Exception e) { - Log.FailedToCreateMessageFromAmqpMessage(s_logger, e); + Log.FailedToCreateMessageFromAmqpMessage(_logger, e); return Message.FailureMessage(topic.Result, messageId.Success ? messageId.Result : Id.Empty); } } diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageCreatorFactory.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageCreatorFactory.cs index a316431fcb..3c575ef40e 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageCreatorFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageCreatorFactory.cs @@ -21,17 +21,19 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #endregion +using Microsoft.Extensions.Logging; + namespace Paramore.Brighter.MessagingGateway.AWSSQS.V4; internal sealed class SqsMessageCreatorFactory { - public static ISqsMessageCreator Create(bool rawMessageDelivery) + public static ISqsMessageCreator Create(bool rawMessageDelivery, ILoggerFactory? loggerFactory = null) { if (rawMessageDelivery) { - return new SqsMessageCreator(); + return new SqsMessageCreator(loggerFactory); } - return new SqsInlineMessageCreator(); + return new SqsInlineMessageCreator(loggerFactory); } } \ No newline at end of file diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageProducer.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageProducer.cs index 0b88586c06..f750275f7f 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageProducer.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageProducer.cs @@ -41,6 +41,7 @@ public partial class SqsMessageProducer : AwsMessagingGateway, IAmAMessageProduc private readonly SqsPublication _publication; private readonly AWSClientFactory _clientFactory; private readonly InstrumentationOptions _instrumentation; + private readonly ILoggerFactory? _loggerFactory; /// /// The publication configuration for this producer @@ -61,16 +62,18 @@ public partial class SqsMessageProducer : AwsMessagingGateway, IAmAMessageProduc /// How do we connect to AWS in order to manage middleware /// Configuration of a producer. Required. /// - public SqsMessageProducer(AWSMessagingGatewayConnection connection, + public SqsMessageProducer(AWSMessagingGatewayConnection connection, SqsPublication publication, - InstrumentationOptions instrumentation = InstrumentationOptions.All) - : base(connection) + InstrumentationOptions instrumentation = InstrumentationOptions.All, + ILoggerFactory? loggerFactory = null) + : base(connection, loggerFactory) { _publication = publication ?? throw new ArgumentNullException(nameof(publication)); - if (_publication.ChannelName is null) + if (_publication.ChannelName is null) throw new InvalidOperationException($"We must have a valid Channel Name on the Publication, either a queue name or a Url"); _clientFactory = new AWSClientFactory(connection); _instrumentation = instrumentation; + _loggerFactory = loggerFactory; if (publication.FindQueueBy == QueueFindBy.Url) { @@ -158,12 +161,12 @@ private async Task SendWithDelayAsync(Message message, TimeSpan? delay, bool use } BrighterTracer.WriteProducerEvent(Span, MessagingSystem.AWSSQS, message, _instrumentation); - Log.PublishingMessage(s_logger, message.Header.Topic.Value, message.Id.Value, message.Body); + Log.PublishingMessage(_logger, message.Header.Topic.Value, message.Id.Value, message.Body); await ConfirmQueueExistsAsync(cancellationToken); using var client = _clientFactory.CreateSqsClient(); - var sender = new SqsMessageSender(ChannelQueueUrl!, client); + var sender = new SqsMessageSender(ChannelQueueUrl!, client, _loggerFactory); var messageId = await sender.SendAsync(message, delay, cancellationToken); if (messageId == null) @@ -172,7 +175,7 @@ private async Task SendWithDelayAsync(Message message, TimeSpan? delay, bool use $"Failed to publish message with topic {message.Header.Topic} and id {message.Id} and message: {message.Body}"); } - Log.PublishedMessage(s_logger, message.Header.Topic.Value, message.Id.Value, messageId); + Log.PublishedMessage(_logger, message.Header.Topic.Value, message.Id.Value, messageId); } public void Send(Message message) => SendWithDelay(message, null); diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageProducerFactory.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageProducerFactory.cs index 15e868033c..340c2fe45e 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageProducerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageProducerFactory.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace Paramore.Brighter.MessagingGateway.AWSSQS.V4; @@ -11,17 +12,21 @@ public class SqsMessageProducerFactory : IAmAMessageProducerFactory { private readonly AWSMessagingGatewayConnection _connection; private readonly IEnumerable _publications; + private readonly ILoggerFactory? _loggerFactory; /// /// Initialize new instance of . /// /// The . /// The collection of . + /// The factory used to create loggers for the producers. public SqsMessageProducerFactory(AWSMessagingGatewayConnection connection, - IEnumerable publications) + IEnumerable publications, + ILoggerFactory? loggerFactory = null) { _connection = connection; _publications = publications; + _loggerFactory = loggerFactory; } /// @@ -37,7 +42,7 @@ public Dictionary Create() if (publication.Topic is null) throw new ConfigurationException("Missing topic on Publication"); - var producer = new SqsMessageProducer(_connection, publication); + var producer = new SqsMessageProducer(_connection, publication, loggerFactory: _loggerFactory); if (producer.ConfirmQueueExists()) { var producerKey = new ProducerKey(publication.Topic, publication.Type); @@ -68,7 +73,7 @@ public async Task> CreateAsync() if (publication.Topic is null) throw new ConfigurationException("Missing topic on Publication"); - var producer = new SqsMessageProducer(_connection, publication); + var producer = new SqsMessageProducer(_connection, publication, loggerFactory: _loggerFactory); if (await producer.ConfirmQueueExistsAsync()) { var producerKey = new ProducerKey(publication.Topic, publication.Type); diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageSender.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageSender.cs index 2506f2171d..a9b44404ee 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageSender.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsMessageSender.cs @@ -8,9 +8,9 @@ using Amazon.SQS; using Amazon.SQS.Model; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Extensions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; namespace Paramore.Brighter.MessagingGateway.AWSSQS.V4; @@ -19,9 +19,9 @@ namespace Paramore.Brighter.MessagingGateway.AWSSQS.V4; /// public partial class SqsMessageSender { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); private static readonly TimeSpan s_maxDelay = TimeSpan.FromSeconds(900); - + + private readonly ILogger _logger; private readonly string _queueUrl; private readonly AmazonSQSClient _client; @@ -30,8 +30,10 @@ public partial class SqsMessageSender /// /// The queue ARN /// The SQS Client - public SqsMessageSender(string queueUrl, AmazonSQSClient client) + /// The factory used to create a logger for this sender + public SqsMessageSender(string queueUrl, AmazonSQSClient client, ILoggerFactory? loggerFactory = null) { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); _queueUrl = queueUrl; _client = client; } @@ -72,7 +74,7 @@ private SendMessageRequest CreateSendMessageRequest(Message message, TimeSpan? d return request; } - private static void SetMessageDelay(SendMessageRequest request, TimeSpan? delay) + private void SetMessageDelay(SendMessageRequest request, TimeSpan? delay) { delay ??= TimeSpan.Zero; if (delay > TimeSpan.Zero) @@ -80,7 +82,7 @@ private static void SetMessageDelay(SendMessageRequest request, TimeSpan? delay) if (delay.Value > s_maxDelay) { delay = s_maxDelay; - Log.DelaySetToMaximum(s_logger, delay); + Log.DelaySetToMaximum(_logger, delay); } request.DelaySeconds = (int)delay.Value.TotalSeconds; diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsProducerRegistryFactory.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsProducerRegistryFactory.cs index 7a63463a4c..2b901d6888 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsProducerRegistryFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS.V4/SqsProducerRegistryFactory.cs @@ -26,6 +26,7 @@ THE SOFTWARE. */ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace Paramore.Brighter.MessagingGateway.AWSSQS.V4; @@ -36,18 +37,22 @@ public class SqsProducerRegistryFactory : IAmAProducerRegistryFactory { private readonly AWSMessagingGatewayConnection _connection; private readonly IEnumerable _sqsPublications; + private readonly ILoggerFactory? _loggerFactory; /// /// Create a collection of producers from the publication information /// /// The Connection to use to connect to AWS /// The publication describing the SNS topic that we want to use + /// The factory used to create loggers for the producers. public SqsProducerRegistryFactory( AWSMessagingGatewayConnection connection, - IEnumerable sqsPublications) + IEnumerable sqsPublications, + ILoggerFactory? loggerFactory = null) { _connection = connection; _sqsPublications = sqsPublications; + _loggerFactory = loggerFactory; } /// @@ -56,7 +61,7 @@ public SqsProducerRegistryFactory( /// The with . public IAmAProducerRegistry Create() { - var producerFactory = new SqsMessageProducerFactory(_connection, _sqsPublications); + var producerFactory = new SqsMessageProducerFactory(_connection, _sqsPublications, _loggerFactory); return new ProducerRegistry(producerFactory.Create()); } @@ -67,7 +72,7 @@ public IAmAProducerRegistry Create() /// The with . public async Task CreateAsync(CancellationToken ct = default) { - var producerFactory = new SqsMessageProducerFactory(_connection, _sqsPublications); + var producerFactory = new SqsMessageProducerFactory(_connection, _sqsPublications, _loggerFactory); return new ProducerRegistry(await producerFactory.CreateAsync()); } } diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS/AWSMessagingGateway.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS/AWSMessagingGateway.cs index 56075ca433..7ad4525d19 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS/AWSMessagingGateway.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS/AWSMessagingGateway.cs @@ -36,20 +36,27 @@ THE SOFTWARE. */ using Amazon.SQS; using Amazon.SQS.Model; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Paramore.Brighter.MessagingGateway.AWSSQS.Extensions; using Paramore.Brighter.Tasks; using InvalidOperationException = System.InvalidOperationException; namespace Paramore.Brighter.MessagingGateway.AWSSQS; -public class AwsMessagingGateway(AWSMessagingGatewayConnection awsConnection) +public class AwsMessagingGateway { - protected static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + protected readonly ILogger _logger; - private readonly AWSClientFactory _awsClientFactory = new(awsConnection); - protected readonly AWSMessagingGatewayConnection AwsConnection = awsConnection; + private readonly AWSClientFactory _awsClientFactory; + protected readonly AWSMessagingGatewayConnection AwsConnection; + + public AwsMessagingGateway(AWSMessagingGatewayConnection awsConnection, ILoggerFactory? loggerFactory = null) + { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + _awsClientFactory = new AWSClientFactory(awsConnection); + AwsConnection = awsConnection; + } /// /// The Channel Address diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS/ChannelFactory.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS/ChannelFactory.cs index b8ba6a853e..7f6d9aa6a4 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS/ChannelFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS/ChannelFactory.cs @@ -51,10 +51,10 @@ public partial class ChannelFactory : AwsMessagingGateway, IAmAChannelFactory /// Initializes a new instance of the class. /// /// The details of the subscription to AWS. - public ChannelFactory(AWSMessagingGatewayConnection awsConnection) - : base(awsConnection) + public ChannelFactory(AWSMessagingGatewayConnection awsConnection, ILoggerFactory? loggerFactory = null) + : base(awsConnection, loggerFactory) { - _messageConsumerFactory = new SqsMessageConsumerFactory(awsConnection); + _messageConsumerFactory = new SqsMessageConsumerFactory(awsConnection, loggerFactory); _retryPolicy = Policy .Handle() .WaitAndRetryAsync([TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10)]); @@ -169,7 +169,7 @@ await QueueExistsAsync(sqsClient, } catch (Exception) { - Log.CouldNotDeleteQueue(s_logger, queueExists.queueUrl); + Log.CouldNotDeleteQueue(_logger, queueExists.queueUrl); } } } @@ -196,7 +196,7 @@ public async Task DeleteTopicAsync() } catch (Exception) { - Log.CouldNotDeleteTopic(s_logger, ChannelTopicArn); + Log.CouldNotDeleteTopic(_logger, ChannelTopicArn); } } } @@ -311,7 +311,7 @@ private async Task UnsubscribeFromTopicAsync(AmazonSimpleNotificationServiceClie await snsClient.UnsubscribeAsync(new UnsubscribeRequest { SubscriptionArn = sub.SubscriptionArn }); if (unsubscribe.HttpStatusCode != HttpStatusCode.OK) { - Log.ErrorUnsubscribingFromTopic(s_logger, ChannelAddress, sub.SubscriptionArn); + Log.ErrorUnsubscribingFromTopic(_logger, ChannelAddress, sub.SubscriptionArn); } } } while (response.NextToken != null); diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SnsMessageProducer.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SnsMessageProducer.cs index 11ca22270c..3e33e71c7e 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SnsMessageProducer.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SnsMessageProducer.cs @@ -65,10 +65,11 @@ public Publication Publication /// How do we connect to AWS in order to manage middleware /// Configuration of a producer /// - public SnsMessageProducer(AWSMessagingGatewayConnection connection, - SnsPublication publication, - InstrumentationOptions instrumentation = InstrumentationOptions.All) - : base(connection) + public SnsMessageProducer(AWSMessagingGatewayConnection connection, + SnsPublication publication, + InstrumentationOptions instrumentation = InstrumentationOptions.All, + ILoggerFactory? loggerFactory = null) + : base(connection, loggerFactory) { _publication = publication; _clientFactory = new AWSClientFactory(connection); @@ -179,7 +180,7 @@ private async Task SendWithDelayAsync(Message message, TimeSpan? delay, bool use } BrighterTracer.WriteProducerEvent(Span, "aws_sns", message, _options); - Log.PublishingMessage(s_logger, message.Header.Topic.Value, message.Id.Value, message.Body); + Log.PublishingMessage(_logger, message.Header.Topic.Value, message.Id.Value, message.Body); await ConfirmTopicExistsAsync(message.Header.Topic, cancellationToken); @@ -195,7 +196,7 @@ private async Task SendWithDelayAsync(Message message, TimeSpan? delay, bool use throw new InvalidOperationException( $"Failed to publish message with topic {message.Header.Topic} and id {message.Id} and message: {message.Body}"); - Log.PublishedMessage(s_logger, message.Header.Topic.Value, message.Id.Value, messageId); + Log.PublishedMessage(_logger, message.Header.Topic.Value, message.Id.Value, messageId); } private static partial class Log diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SnsMessageProducerFactory.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SnsMessageProducerFactory.cs index 213354fec5..0ead807ec8 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SnsMessageProducerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SnsMessageProducerFactory.cs @@ -26,6 +26,7 @@ THE SOFTWARE. */ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace Paramore.Brighter.MessagingGateway.AWSSQS; @@ -33,18 +34,22 @@ public class SnsMessageProducerFactory : IAmAMessageProducerFactory { private readonly AWSMessagingGatewayConnection _connection; private readonly IEnumerable _publications; + private readonly ILoggerFactory? _loggerFactory; /// /// Creates a collection of SNS message producers from the SNS publication information /// /// The Connection to use to connect to AWS /// The publications describing the SNS topics that we want to use + /// The factory used to create loggers for the producers. public SnsMessageProducerFactory( AWSMessagingGatewayConnection connection, - IEnumerable publications) + IEnumerable publications, + ILoggerFactory? loggerFactory = null) { _connection = connection; _publications = publications; + _loggerFactory = loggerFactory; } /// @@ -60,7 +65,7 @@ public Dictionary Create() if (publication.Topic is null) throw new ConfigurationException("Missing topic on Publication"); - var producer = new SnsMessageProducer(_connection, publication); + var producer = new SnsMessageProducer(_connection, publication, loggerFactory: _loggerFactory); producer.Publication = publication; if (producer.ConfirmTopicExists()) @@ -92,7 +97,7 @@ public async Task> CreateAsync() if (publication.Topic is null) throw new ConfigurationException("Missing topic on Publication"); - var producer = new SnsMessageProducer(_connection, publication); + var producer = new SnsMessageProducer(_connection, publication, loggerFactory: _loggerFactory); producer.Publication = publication; if (await producer.ConfirmTopicExistsAsync()) diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SnsProducerRegistryFactory.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SnsProducerRegistryFactory.cs index 7f7a2f1179..1a6da3dc7d 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SnsProducerRegistryFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SnsProducerRegistryFactory.cs @@ -26,6 +26,7 @@ THE SOFTWARE. */ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace Paramore.Brighter.MessagingGateway.AWSSQS; @@ -36,18 +37,22 @@ public class SnsProducerRegistryFactory : IAmAProducerRegistryFactory { private readonly AWSMessagingGatewayConnection _connection; private readonly IEnumerable _snsPublications; + private readonly ILoggerFactory? _loggerFactory; /// /// Create a collection of producers from the publication information /// /// The Connection to use to connect to AWS /// The publication describing the SNS topic that we want to use + /// The factory used to create loggers for the producers. public SnsProducerRegistryFactory( AWSMessagingGatewayConnection connection, - IEnumerable snsPublications) + IEnumerable snsPublications, + ILoggerFactory? loggerFactory = null) { _connection = connection; _snsPublications = snsPublications; + _loggerFactory = loggerFactory; } /// @@ -56,7 +61,7 @@ public SnsProducerRegistryFactory( /// The with . public IAmAProducerRegistry Create() { - var producerFactory = new SnsMessageProducerFactory(_connection, _snsPublications); + var producerFactory = new SnsMessageProducerFactory(_connection, _snsPublications, _loggerFactory); return new ProducerRegistry(producerFactory.Create()); } @@ -67,7 +72,7 @@ public IAmAProducerRegistry Create() /// The with . public async Task CreateAsync(CancellationToken ct = default) { - var producerFactory = new SnsMessageProducerFactory(_connection, _snsPublications); + var producerFactory = new SnsMessageProducerFactory(_connection, _snsPublications, _loggerFactory); return new ProducerRegistry(await producerFactory.CreateAsync()); } } diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsInlineMessageCreator.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsInlineMessageCreator.cs index 1d95465400..7edc57d429 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsInlineMessageCreator.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsInlineMessageCreator.cs @@ -30,18 +30,23 @@ THE SOFTWARE. */ using Amazon; using Amazon.SQS; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; namespace Paramore.Brighter.MessagingGateway.AWSSQS; internal sealed partial class SqsInlineMessageCreator : SqsMessageCreatorBase, ISqsMessageCreator { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private Dictionary _messageAttributes = new(); + public SqsInlineMessageCreator(ILoggerFactory? loggerFactory = null) + { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + } + public Message CreateMessage(Amazon.SQS.Model.Message sqsMessage) { var topic = HeaderResult.Empty(); @@ -112,12 +117,12 @@ public Message CreateMessage(Amazon.SQS.Model.Message sqsMessage) } catch (Exception e) { - Log.FailedToCreateMessageFromAwsSqsMessage(s_logger, e); + Log.FailedToCreateMessageFromAwsSqsMessage(_logger, e); return Message.FailureMessage(topic.Result, messageId.Result); } } - private static Dictionary ReadMessageAttributes(JsonDocument jsonDocument) + private Dictionary ReadMessageAttributes(JsonDocument jsonDocument) { var messageAttributes = new Dictionary(); @@ -132,7 +137,7 @@ private static Dictionary ReadMessageAttributes(JsonDocumen } catch (Exception ex) { - Log.FailedWhileDeserializingSqsMessageBody(s_logger, ex); + Log.FailedWhileDeserializingSqsMessageBody(_logger, ex); } return messageAttributes ?? new Dictionary(); @@ -429,13 +434,13 @@ private HeaderResult ReadTopic() } catch (Exception ex) { - Log.FailedToParseSqsMessageBodyToValidJsonDocument(s_logger, ex); + Log.FailedToParseSqsMessageBodyToValidJsonDocument(_logger, ex); } return new HeaderResult(null, true); } - private static MessageBody ReadMessageBody(JsonDocument jsonDocument) + private MessageBody ReadMessageBody(JsonDocument jsonDocument) { try { @@ -446,7 +451,7 @@ private static MessageBody ReadMessageBody(JsonDocument jsonDocument) } catch (Exception ex) { - Log.FailedToParseSqsMessageBodyToValidJsonDocument(s_logger, ex); + Log.FailedToParseSqsMessageBodyToValidJsonDocument(_logger, ex); } return new MessageBody(string.Empty); diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageConsumer.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageConsumer.cs index 339242f936..ab31a41dd3 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageConsumer.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageConsumer.cs @@ -31,8 +31,8 @@ THE SOFTWARE. */ using Amazon.SQS; using Amazon.SQS.Model; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Paramore.Brighter.Tasks; namespace Paramore.Brighter.MessagingGateway.AWSSQS @@ -42,9 +42,10 @@ namespace Paramore.Brighter.MessagingGateway.AWSSQS /// public partial class SqsMessageConsumer : IAmAMessageConsumerSync, IAmAMessageConsumerAsync { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private readonly AWSMessagingGatewayConnection _connection; + private readonly ILoggerFactory? _loggerFactory; private readonly AWSClientFactory _clientFactory; private readonly string _queueName; private readonly int _batchSize; @@ -70,6 +71,7 @@ public partial class SqsMessageConsumer : IAmAMessageConsumerSync, IAmAMessageCo /// Is the queue name a queue url? /// Do we have Raw Message Delivery enabled? /// The for the queue (used by DLQ producers for FIFO support) + /// The factory used to create a logger for this consumer public SqsMessageConsumer( AWSMessagingGatewayConnection awsConnection, string? queueName, @@ -79,11 +81,14 @@ public SqsMessageConsumer( OnMissingChannel makeChannels = OnMissingChannel.Create, bool isQueueUrl = false, bool rawMessageDelivery = true, - SqsAttributes? queueAttributes = null) + SqsAttributes? queueAttributes = null, + ILoggerFactory? loggerFactory = null) { if (string.IsNullOrEmpty(queueName)) throw new ConfigurationException("QueueName is mandatory"); + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + _loggerFactory = loggerFactory; _connection = awsConnection; _clientFactory = new AWSClientFactory(awsConnection); _queueName = queueName!; @@ -145,16 +150,16 @@ public SqsMessageConsumer( try { using var client = _clientFactory.CreateSqsClient(); - Log.PurgingQueue(s_logger, _queueName); + Log.PurgingQueue(_logger, _queueName); await EnsureChannelUrl(client, cancellationToken); await client.PurgeQueueAsync(_channelUrl, cancellationToken); - Log.PurgedQueue(s_logger, _queueName); + Log.PurgedQueue(_logger, _queueName); } catch (Exception exception) { - Log.ErrorPurgingQueue(s_logger, exception, _queueName); + Log.ErrorPurgingQueue(_logger, exception, _queueName); throw; } } @@ -183,7 +188,7 @@ public async Task ReceiveAsync(TimeSpan? timeOut = null, await EnsureChannelUrl(client, cancellationToken); timeOut ??= TimeSpan.Zero; - Log.RetrievingNextMessage(s_logger,_channelUrl!); + Log.RetrievingNextMessage(_logger,_channelUrl!); var request = new ReceiveMessageRequest(_channelUrl) { @@ -199,17 +204,17 @@ public async Task ReceiveAsync(TimeSpan? timeOut = null, } catch (InvalidOperationException ioe) { - Log.CouldNotDetermineNumberOfMessagesToRetrieve(s_logger); + Log.CouldNotDetermineNumberOfMessagesToRetrieve(_logger); throw new ChannelFailureException("Error connecting to SQS, see inner exception for details", ioe); } catch (OperationCanceledException oce) { - Log.CouldNotFindMessagesToRetrieve(s_logger); + Log.CouldNotFindMessagesToRetrieve(_logger); throw new ChannelFailureException("Error connecting to SQS, see inner exception for details", oce); } catch (Exception e) { - Log.ErrorListeningToQueue(s_logger, e, _queueName); + Log.ErrorListeningToQueue(_logger, e, _queueName); throw; } finally @@ -225,8 +230,8 @@ public async Task ReceiveAsync(TimeSpan? timeOut = null, var messages = new Message[sqsMessages.Length]; for (int i = 0; i < sqsMessages.Length; i++) { - var message = SqsMessageCreatorFactory.Create(_rawMessageDelivery).CreateMessage(sqsMessages[i]); - Log.ReceivedMessageFromQueue(s_logger, _queueName, Environment.NewLine, JsonSerializer.Serialize(message, JsonSerialisationOptions.Options)); + var message = SqsMessageCreatorFactory.Create(_rawMessageDelivery, _loggerFactory).CreateMessage(sqsMessages[i]); + Log.ReceivedMessageFromQueue(_logger, _queueName, Environment.NewLine, JsonSerializer.Serialize(message, JsonSerialisationOptions.Options)); messages[i] = message; } @@ -258,14 +263,14 @@ public async Task ReceiveAsync(TimeSpan? timeOut = null, var reasonString = reason is null ? nameof(RejectionReason.DeliveryError) : reason.RejectionReason.ToString(); var description = reason is null ? "unknown" : reason.Description ?? "unknown"; - Log.RejectingMessage(s_logger, message.Id.Value, receiptHandle, _queueName, reasonString, description); + Log.RejectingMessage(_logger, message.Id.Value, receiptHandle, _queueName, reasonString, description); // If no channels configured, just delete the original message if (_deadLetterProducer == null && _invalidMessageProducer == null) { if (reason != null) { - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value, reason.RejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value, reason.RejectionReason.ToString()); } await AcknowledgeAsync(message, cancellationToken); @@ -286,7 +291,7 @@ public async Task ReceiveAsync(TimeSpan? timeOut = null, { message.Header.Topic = routingKey!; if (isFallingBackToDlq) - Log.FallingBackToDlq(s_logger, message.Id.Value); + Log.FallingBackToDlq(_logger, message.Id.Value); if (routingKey == _invalidMessageRoutingKey) producer = _invalidMessageProducer?.Value; @@ -297,18 +302,18 @@ public async Task ReceiveAsync(TimeSpan? timeOut = null, if (producer != null) { await producer.SendAsync(message, cancellationToken); - Log.MessageSentToRejectionChannel(s_logger, message.Id.Value, rejectionReason.ToString()); + Log.MessageSentToRejectionChannel(_logger, message.Id.Value, rejectionReason.ToString()); } else { - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value, rejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value, rejectionReason.ToString()); } } catch (Exception ex) { // Sending to DLQ failed — delete the original to prevent infinite // reprocessing. The message is lost rather than stuck in a retry loop. - Log.ErrorSendingToRejectionChannel(s_logger, ex, message.Id.Value, rejectionReason.ToString()); + Log.ErrorSendingToRejectionChannel(_logger, ex, message.Id.Value, rejectionReason.ToString()); await DeleteSourceMessageAsync(receiptHandle!, message.Id.Value, cancellationToken); return true; } @@ -340,7 +345,7 @@ public async Task NackAsync(Message message, CancellationToken cancellationToken try { - Log.NackingMessage(s_logger, message.Id.Value, receiptHandle, _queueName); + Log.NackingMessage(_logger, message.Id.Value, receiptHandle, _queueName); using var client = _clientFactory.CreateSqsClient(); await EnsureChannelUrl(client, cancellationToken); @@ -349,7 +354,7 @@ await client.ChangeMessageVisibilityAsync( cancellationToken ); - Log.NackedMessage(s_logger, message.Id.Value, receiptHandle, _channelUrl!); + Log.NackedMessage(_logger, message.Id.Value, receiptHandle, _channelUrl!); } catch (ReceiptHandleIsInvalidException ex) { @@ -357,11 +362,11 @@ await client.ChangeMessageVisibilityAsync( // SQS has already made the message visible again for redelivery by another consumer. // Nack sets visibility to zero for immediate redelivery — but the message is already // visible again, so the net effect is the same. Log a warning and continue. - Log.NackFailedReceiptHandleExpired(s_logger, ex, message.Id.Value, receiptHandle, _queueName); + Log.NackFailedReceiptHandleExpired(_logger, ex, message.Id.Value, receiptHandle, _queueName); } catch (Exception exception) { - Log.ErrorNackingMessage(s_logger, exception, message.Id.Value, receiptHandle, _queueName); + Log.ErrorNackingMessage(_logger, exception, message.Id.Value, receiptHandle, _queueName); throw; } } @@ -393,7 +398,7 @@ public async Task RequeueAsync(Message message, TimeSpan? delay = null, try { - Log.RequeueingMessage(s_logger, message.Id.Value); + Log.RequeueingMessage(_logger, message.Id.Value); using (var client = _clientFactory.CreateSqsClient()) { @@ -404,7 +409,7 @@ await client.ChangeMessageVisibilityAsync( ); } - Log.RequeuedMessage(s_logger, message.Id.Value); + Log.RequeuedMessage(_logger, message.Id.Value); return true; } @@ -413,12 +418,12 @@ await client.ChangeMessageVisibilityAsync( // Receipt handle is invalid (most likely because the visibility timeout elapsed). // SQS has already made the message visible again for redelivery by another consumer, // but without the intended delay. Log a warning so operators are aware. - Log.RequeueFailedReceiptHandleExpired(s_logger, ex, message.Id.Value, receiptHandle, _queueName, delay.Value); + Log.RequeueFailedReceiptHandleExpired(_logger, ex, message.Id.Value, receiptHandle, _queueName, delay.Value); return false; } catch (Exception exception) { - Log.ErrorRequeueingMessage(s_logger, exception, message.Id.Value, receiptHandle, _queueName); + Log.ErrorRequeueingMessage(_logger, exception, message.Id.Value, receiptHandle, _queueName); return false; } } @@ -465,11 +470,11 @@ public async ValueTask DisposeAsync() // We must NOT call the sync ConfirmQueueExists here because the lazy is resolved // inside RejectAsync, which may already be running inside BrighterAsyncContext.Run // from the sync Reject path — nesting would deadlock. - return new SqsMessageProducer(_connection, publication); + return new SqsMessageProducer(_connection, publication, loggerFactory: _loggerFactory); } catch (Exception e) { - Log.ErrorCreatingDlqProducerException(s_logger, e, _deadLetterRoutingKey.Value); + Log.ErrorCreatingDlqProducerException(_logger, e, _deadLetterRoutingKey.Value); return null; } } @@ -484,11 +489,11 @@ public async ValueTask DisposeAsync() try { // Queue existence is confirmed on first SendAsync via ConfirmQueueExistsAsync. - return new SqsMessageProducer(_connection, publication); + return new SqsMessageProducer(_connection, publication, loggerFactory: _loggerFactory); } catch (Exception e) { - Log.ErrorCreatingInvalidMessageProducerException(s_logger, e, _invalidMessageRoutingKey.Value); + Log.ErrorCreatingInvalidMessageProducerException(_logger, e, _invalidMessageRoutingKey.Value); return null; } } @@ -521,7 +526,7 @@ private async Task DeleteSourceMessageAsync(string receiptHandle, string message await client.DeleteMessageAsync(new DeleteMessageRequest(_channelUrl, receiptHandle), cancellationToken); - Log.DeletedMessage(s_logger, messageId, receiptHandle, _channelUrl!); + Log.DeletedMessage(_logger, messageId, receiptHandle, _channelUrl!); } catch (ReceiptHandleIsInvalidException ex) { @@ -529,11 +534,11 @@ await client.DeleteMessageAsync(new DeleteMessageRequest(_channelUrl, receiptHan // SQS has already made the message visible again for redelivery by another consumer. // This is an error because the message was not deleted and may be processed again; // handlers should be idempotent, but an operator may need to investigate. - Log.DeleteFailedReceiptHandleExpired(s_logger, ex, messageId, receiptHandle, _queueName); + Log.DeleteFailedReceiptHandleExpired(_logger, ex, messageId, receiptHandle, _queueName); } catch (Exception exception) { - Log.ErrorDeletingMessage(s_logger, exception, messageId, receiptHandle, _queueName); + Log.ErrorDeletingMessage(_logger, exception, messageId, receiptHandle, _queueName); throw; } } diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageConsumerFactory.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageConsumerFactory.cs index 15ca336fe2..e4df05932b 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageConsumerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageConsumerFactory.cs @@ -21,6 +21,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #endregion +using Microsoft.Extensions.Logging; + namespace Paramore.Brighter.MessagingGateway.AWSSQS { /// @@ -29,13 +31,15 @@ namespace Paramore.Brighter.MessagingGateway.AWSSQS public class SqsMessageConsumerFactory : IAmAMessageConsumerFactory { private readonly AWSMessagingGatewayConnection _awsConnection; + private readonly ILoggerFactory? _loggerFactory; /// /// Initializes a new instance of the class. /// - public SqsMessageConsumerFactory(AWSMessagingGatewayConnection awsConnection) + public SqsMessageConsumerFactory(AWSMessagingGatewayConnection awsConnection, ILoggerFactory? loggerFactory = null) { _awsConnection = awsConnection; + _loggerFactory = loggerFactory; } /// @@ -87,7 +91,8 @@ private SqsMessageConsumer CreateImpl(Subscription subscription) makeChannels: sqsSubscription.MakeChannels, isQueueUrl: (sqsSubscription.FindQueueBy == QueueFindBy.Url), rawMessageDelivery: sqsSubscription.QueueAttributes.RawMessageDelivery, - queueAttributes: sqsSubscription.QueueAttributes + queueAttributes: sqsSubscription.QueueAttributes, + loggerFactory: _loggerFactory ); } } diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageCreator.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageCreator.cs index e9b3c9a2e2..762b2ac4c8 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageCreator.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageCreator.cs @@ -32,9 +32,9 @@ THE SOFTWARE. */ using Amazon.SQS; using Amazon.SQS.Model; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Newtonsoft.Json; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; using Paramore.Brighter.Transforms.Transformers; @@ -55,7 +55,12 @@ internal enum ARNAmazonSNS internal sealed partial class SqsMessageCreator : SqsMessageCreatorBase, ISqsMessageCreator { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; + + public SqsMessageCreator(ILoggerFactory? loggerFactory = null) + { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + } public Message CreateMessage(Amazon.SQS.Model.Message sqsMessage) { @@ -117,7 +122,7 @@ public Message CreateMessage(Amazon.SQS.Model.Message sqsMessage) } catch (Exception e) { - Log.FailedToCreateMessageFromAmqpMessage(s_logger, e); + Log.FailedToCreateMessageFromAmqpMessage(_logger, e); return Message.FailureMessage(topic.Result, messageId.Success ? messageId.Result : Id.Empty); } } diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageCreatorFactory.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageCreatorFactory.cs index a15ad868c5..18b1f9dd9e 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageCreatorFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageCreatorFactory.cs @@ -21,18 +21,20 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #endregion +using Microsoft.Extensions.Logging; + namespace Paramore.Brighter.MessagingGateway.AWSSQS { internal sealed class SqsMessageCreatorFactory { - public static ISqsMessageCreator Create(bool rawMessageDelivery) + public static ISqsMessageCreator Create(bool rawMessageDelivery, ILoggerFactory? loggerFactory = null) { if (rawMessageDelivery) { - return new SqsMessageCreator(); + return new SqsMessageCreator(loggerFactory); } - return new SqsInlineMessageCreator(); + return new SqsInlineMessageCreator(loggerFactory); } } } diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageProducer.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageProducer.cs index 6afbd0a103..ac0ec977aa 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageProducer.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageProducer.cs @@ -41,6 +41,7 @@ public partial class SqsMessageProducer : AwsMessagingGateway, IAmAMessageProduc private readonly SqsPublication _publication; private readonly AWSClientFactory _clientFactory; private readonly InstrumentationOptions _instrumentation; + private readonly ILoggerFactory? _loggerFactory; /// /// The publication configuration for this producer @@ -61,16 +62,18 @@ public partial class SqsMessageProducer : AwsMessagingGateway, IAmAMessageProduc /// How do we connect to AWS in order to manage middleware /// Configuration of a producer. Required. /// - public SqsMessageProducer(AWSMessagingGatewayConnection connection, + public SqsMessageProducer(AWSMessagingGatewayConnection connection, SqsPublication publication, - InstrumentationOptions instrumentation = InstrumentationOptions.All) - : base(connection) + InstrumentationOptions instrumentation = InstrumentationOptions.All, + ILoggerFactory? loggerFactory = null) + : base(connection, loggerFactory) { _publication = publication ?? throw new ArgumentNullException(nameof(publication)); if (_publication.ChannelName is null) throw new InvalidOperationException($"We must have a valid Channel Name on the Publication, either a queue name or a Url"); _clientFactory = new AWSClientFactory(connection); _instrumentation = instrumentation; + _loggerFactory = loggerFactory; if (publication.FindQueueBy == QueueFindBy.Url) { @@ -157,13 +160,13 @@ private async Task SendWithDelayAsync(Message message, TimeSpan? delay, bool use return; } - Log.PublishingMessage(s_logger, message.Header.Topic.Value, message.Id.Value, message.Body); + Log.PublishingMessage(_logger, message.Header.Topic.Value, message.Id.Value, message.Body); BrighterTracer.WriteProducerEvent(Span, MessagingSystem.AWSSQS, message, _instrumentation); await ConfirmQueueExistsAsync(cancellationToken); using var client = _clientFactory.CreateSqsClient(); - var sender = new SqsMessageSender(ChannelQueueUrl!, client); + var sender = new SqsMessageSender(ChannelQueueUrl!, client, _loggerFactory); var messageId = await sender.SendAsync(message, delay, cancellationToken); if (messageId == null) @@ -172,7 +175,7 @@ private async Task SendWithDelayAsync(Message message, TimeSpan? delay, bool use $"Failed to publish message with topic {message.Header.Topic} and id {message.Id} and message: {message.Body}"); } - Log.PublishedMessage(s_logger, message.Header.Topic.Value, message.Id.Value, messageId); + Log.PublishedMessage(_logger, message.Header.Topic.Value, message.Id.Value, messageId); } public void Send(Message message) => SendWithDelay(message, null); diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageProducerFactory.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageProducerFactory.cs index 9432b4f881..f0c51e540a 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageProducerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageProducerFactory.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace Paramore.Brighter.MessagingGateway.AWSSQS; @@ -11,17 +12,21 @@ public class SqsMessageProducerFactory : IAmAMessageProducerFactory { private readonly AWSMessagingGatewayConnection _connection; private readonly IEnumerable _publications; + private readonly ILoggerFactory? _loggerFactory; /// /// Initialize new instance of . /// /// The . /// The collection of . + /// The factory used to create loggers for the producers. public SqsMessageProducerFactory(AWSMessagingGatewayConnection connection, - IEnumerable publications) + IEnumerable publications, + ILoggerFactory? loggerFactory = null) { _connection = connection; _publications = publications; + _loggerFactory = loggerFactory; } /// @@ -37,7 +42,7 @@ public Dictionary Create() if (publication.Topic is null) throw new ConfigurationException("Missing topic on Publication"); - var producer = new SqsMessageProducer(_connection, publication); + var producer = new SqsMessageProducer(_connection, publication, loggerFactory: _loggerFactory); if (producer.ConfirmQueueExists()) { var producerKey = new ProducerKey(publication.Topic, publication.Type); @@ -68,7 +73,7 @@ public async Task> CreateAsync() if (publication.Topic is null) throw new ConfigurationException("Missing topic on Publication"); - var producer = new SqsMessageProducer(_connection, publication); + var producer = new SqsMessageProducer(_connection, publication, loggerFactory: _loggerFactory); if (await producer.ConfirmQueueExistsAsync()) { var producerKey = new ProducerKey(publication.Topic, publication.Type); diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageSender.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageSender.cs index fb43dd426f..dc0b18a68e 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageSender.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsMessageSender.cs @@ -9,10 +9,10 @@ using Amazon.SQS; using Amazon.SQS.Model; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Newtonsoft.Json; using Paramore.Brighter.Extensions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; namespace Paramore.Brighter.MessagingGateway.AWSSQS; @@ -21,9 +21,9 @@ namespace Paramore.Brighter.MessagingGateway.AWSSQS; /// public partial class SqsMessageSender { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); private static readonly TimeSpan s_maxDelay = TimeSpan.FromSeconds(900); - + + private readonly ILogger _logger; private readonly string _queueUrl; private readonly AmazonSQSClient _client; @@ -32,8 +32,10 @@ public partial class SqsMessageSender /// /// The queue ARN /// The SQS Client - public SqsMessageSender(string queueUrl, AmazonSQSClient client) + /// The factory used to create a logger for this sender + public SqsMessageSender(string queueUrl, AmazonSQSClient client, ILoggerFactory? loggerFactory = null) { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); _queueUrl = queueUrl; _client = client; } @@ -74,7 +76,7 @@ private SendMessageRequest CreateSendMessageRequest(Message message, TimeSpan? d return request; } - private static void SetMessageDelay(SendMessageRequest request, TimeSpan? delay) + private void SetMessageDelay(SendMessageRequest request, TimeSpan? delay) { delay ??= TimeSpan.Zero; if (delay > TimeSpan.Zero) @@ -82,7 +84,7 @@ private static void SetMessageDelay(SendMessageRequest request, TimeSpan? delay) if (delay.Value > s_maxDelay) { delay = s_maxDelay; - Log.DelaySetToMaximum(s_logger, delay); + Log.DelaySetToMaximum(_logger, delay); } request.DelaySeconds = (int)delay.Value.TotalSeconds; diff --git a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsProducerRegistryFactory.cs b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsProducerRegistryFactory.cs index eb3bc3c26f..6760a70d5d 100644 --- a/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsProducerRegistryFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.AWSSQS/SqsProducerRegistryFactory.cs @@ -26,6 +26,7 @@ THE SOFTWARE. */ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace Paramore.Brighter.MessagingGateway.AWSSQS; @@ -36,18 +37,22 @@ public class SqsProducerRegistryFactory : IAmAProducerRegistryFactory { private readonly AWSMessagingGatewayConnection _connection; private readonly IEnumerable _sqsPublications; + private readonly ILoggerFactory? _loggerFactory; /// /// Create a collection of producers from the publication information /// /// The Connection to use to connect to AWS /// The publication describing the SNS topic that we want to use + /// The factory used to create loggers for the producers. public SqsProducerRegistryFactory( AWSMessagingGatewayConnection connection, - IEnumerable sqsPublications) + IEnumerable sqsPublications, + ILoggerFactory? loggerFactory = null) { _connection = connection; _sqsPublications = sqsPublications; + _loggerFactory = loggerFactory; } /// @@ -56,7 +61,7 @@ public SqsProducerRegistryFactory( /// The with . public IAmAProducerRegistry Create() { - var producerFactory = new SqsMessageProducerFactory(_connection, _sqsPublications); + var producerFactory = new SqsMessageProducerFactory(_connection, _sqsPublications, _loggerFactory); return new ProducerRegistry(producerFactory.Create()); } @@ -67,7 +72,7 @@ public IAmAProducerRegistry Create() /// The with . public async Task CreateAsync(CancellationToken ct = default) { - var producerFactory = new SqsMessageProducerFactory(_connection, _sqsPublications); + var producerFactory = new SqsMessageProducerFactory(_connection, _sqsPublications, _loggerFactory); return new ProducerRegistry(await producerFactory.CreateAsync()); } } diff --git a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusConsumer.cs b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusConsumer.cs index 2bd1e92f16..8f0355e2ce 100644 --- a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusConsumer.cs +++ b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusConsumer.cs @@ -57,11 +57,13 @@ public abstract partial class AzureServiceBusConsumer : IAmAMessageConsumerSync, /// The producer we want to send via /// The admin client for ASB /// Whether the consumer is async + /// The used to create loggers protected AzureServiceBusConsumer( - AzureServiceBusSubscription subscription, + AzureServiceBusSubscription subscription, IAmAMessageProducer messageProducer, IAdministrationClientWrapper administrationClientWrapper, - bool isAsync = false + bool isAsync = false, + ILoggerFactory? loggerFactory = null ) { Subscription = subscription; @@ -70,7 +72,7 @@ protected AzureServiceBusConsumer( SubscriptionConfiguration = subscription.Configuration ?? new AzureServiceBusSubscriptionConfiguration(); _messageProducer = messageProducer; AdministrationClientWrapper = administrationClientWrapper; - _azureServiceBusMesssageCreator = new AzureServiceBusMesssageCreator(subscription); + _azureServiceBusMesssageCreator = new AzureServiceBusMesssageCreator(subscription, loggerFactory); } /// diff --git a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusConsumerFactory.cs b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusConsumerFactory.cs index 077956cf5e..2ff9d65010 100644 --- a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusConsumerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusConsumerFactory.cs @@ -23,6 +23,7 @@ THE SOFTWARE. */ #endregion using System; +using Microsoft.Extensions.Logging; using Paramore.Brighter.MessagingGateway.AzureServiceBus.AzureServiceBusWrappers; using Paramore.Brighter.MessagingGateway.AzureServiceBus.ClientProvider; using IServiceBusClientProvider = Paramore.Brighter.MessagingGateway.AzureServiceBus.ClientProvider.IServiceBusClientProvider; @@ -35,22 +36,26 @@ namespace Paramore.Brighter.MessagingGateway.AzureServiceBus; public class AzureServiceBusConsumerFactory : IAmAMessageConsumerFactory { private readonly IServiceBusClientProvider _clientProvider; + private readonly ILoggerFactory? _loggerFactory; /// /// Factory to create an Azure Service Bus Consumer /// /// The configuration to connect to - public AzureServiceBusConsumerFactory(AzureServiceBusConfiguration configuration) - : this(new ServiceBusConnectionStringClientProvider(configuration.ConnectionString)) + /// The used to create loggers + public AzureServiceBusConsumerFactory(AzureServiceBusConfiguration configuration, ILoggerFactory? loggerFactory = null) + : this(new ServiceBusConnectionStringClientProvider(configuration.ConnectionString), loggerFactory) { } /// /// Factory to create an Azure Service Bus Consumer /// /// A client Provider to determine how to connect to ASB - public AzureServiceBusConsumerFactory(IServiceBusClientProvider clientProvider) + /// The used to create loggers + public AzureServiceBusConsumerFactory(IServiceBusClientProvider clientProvider, ILoggerFactory? loggerFactory = null) { _clientProvider = clientProvider; + _loggerFactory = loggerFactory; } /// @@ -60,39 +65,43 @@ public AzureServiceBusConsumerFactory(IServiceBusClientProvider clientProvider) /// IAmAMessageConsumerSync public IAmAMessageConsumerSync Create(Subscription subscription) { - var nameSpaceManagerWrapper = new AdministrationClientWrapper(_clientProvider); + var nameSpaceManagerWrapper = new AdministrationClientWrapper(_clientProvider, _loggerFactory); if (!(subscription is AzureServiceBusSubscription sub)) throw new ArgumentException("Subscription is not of type AzureServiceBusSubscription.", nameof(subscription)); - var receiverProvider = new ServiceBusReceiverProvider(_clientProvider); + var receiverProvider = new ServiceBusReceiverProvider(_clientProvider, _loggerFactory); if (sub.Configuration.UseServiceBusQueue) { var messageProducer = new AzureServiceBusQueueMessageProducer( nameSpaceManagerWrapper, new ServiceBusSenderProvider(_clientProvider), - new AzureServiceBusPublication { MakeChannels = subscription.MakeChannels }); + new AzureServiceBusPublication { MakeChannels = subscription.MakeChannels }, + loggerFactory: _loggerFactory); return new AzureServiceBusQueueConsumer( sub, messageProducer, nameSpaceManagerWrapper, - receiverProvider); + receiverProvider, + _loggerFactory); } else { var messageProducer = new AzureServiceBusTopicMessageProducer( nameSpaceManagerWrapper, new ServiceBusSenderProvider(_clientProvider), - new AzureServiceBusPublication { MakeChannels = subscription.MakeChannels }); + new AzureServiceBusPublication { MakeChannels = subscription.MakeChannels }, + loggerFactory: _loggerFactory); return new AzureServiceBusTopicConsumer( sub, messageProducer, nameSpaceManagerWrapper, - receiverProvider); + receiverProvider, + _loggerFactory); } } diff --git a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusMessageProducerFactory.cs b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusMessageProducerFactory.cs index 8f88814aa8..029e91e9a8 100644 --- a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusMessageProducerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusMessageProducerFactory.cs @@ -27,6 +27,7 @@ THE SOFTWARE. */ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Paramore.Brighter.MessagingGateway.AzureServiceBus.AzureServiceBusWrappers; using Paramore.Brighter.MessagingGateway.AzureServiceBus.ClientProvider; @@ -41,6 +42,7 @@ public class AzureServiceBusMessageProducerFactory : IAmAMessageProducerFactory private readonly IServiceBusClientProvider _clientProvider; private readonly IEnumerable _publications; private readonly int _bulkSendBatchSize; + private readonly ILoggerFactory? _loggerFactory; /// /// Factory to create a dictionary of Azure Service Bus Producers indexed by topic name @@ -48,14 +50,17 @@ public class AzureServiceBusMessageProducerFactory : IAmAMessageProducerFactory /// The connection to ASB /// A set of publications - topics on the server - to configure /// The maximum size to chunk messages when dispatching to ASB + /// The used to create loggers public AzureServiceBusMessageProducerFactory( IServiceBusClientProvider clientProvider, IEnumerable publications, - int bulkSendBatchSize) + int bulkSendBatchSize, + ILoggerFactory? loggerFactory = null) { _clientProvider = clientProvider; _publications = publications; _bulkSendBatchSize = bulkSendBatchSize; + _loggerFactory = loggerFactory; } /// @@ -66,7 +71,7 @@ public AzureServiceBusMessageProducerFactory( public Dictionary Create() { - var nameSpaceManagerWrapper = new AdministrationClientWrapper(_clientProvider); + var nameSpaceManagerWrapper = new AdministrationClientWrapper(_clientProvider, _loggerFactory); var topicClientProvider = new ServiceBusSenderProvider(_clientProvider); var producers = new Dictionary(); @@ -74,16 +79,16 @@ public Dictionary Create() { if (publication.Topic is null) throw new ArgumentException("Publication must have a Topic."); - + if (publication.UseServiceBusQueue) { - var producer = new AzureServiceBusQueueMessageProducer(nameSpaceManagerWrapper, topicClientProvider, publication, _bulkSendBatchSize); + var producer = new AzureServiceBusQueueMessageProducer(nameSpaceManagerWrapper, topicClientProvider, publication, _bulkSendBatchSize, _loggerFactory); producer.Publication = publication; RegisterProducer(publication, producers, producer); } else { - var producer = new AzureServiceBusTopicMessageProducer(nameSpaceManagerWrapper, topicClientProvider, publication, _bulkSendBatchSize); + var producer = new AzureServiceBusTopicMessageProducer(nameSpaceManagerWrapper, topicClientProvider, publication, _bulkSendBatchSize, _loggerFactory); producer.Publication = publication; RegisterProducer(publication, producers, producer); diff --git a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusMesssageCreator.cs b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusMesssageCreator.cs index ad314943c6..5111dd00ea 100644 --- a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusMesssageCreator.cs +++ b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusMesssageCreator.cs @@ -26,7 +26,7 @@ THE SOFTWARE. */ using System; using System.Net.Mime; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.MessagingGateway.AzureServiceBus.AzureServiceBusWrappers; using Paramore.Brighter.Observability; @@ -36,10 +36,11 @@ namespace Paramore.Brighter.MessagingGateway.AzureServiceBus; /// Creates a Brighter from an Azure Service Bus message. /// /// Subscription information, used to help populate the message -public partial class AzureServiceBusMesssageCreator(AzureServiceBusSubscription subscription) +/// The used to create the logger. +public partial class AzureServiceBusMesssageCreator(AzureServiceBusSubscription subscription, ILoggerFactory? loggerFactory = null) { private readonly RoutingKey _topic = subscription.RoutingKey; - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); /// /// Maps an Azure Service Bus message to a Brighter . @@ -50,21 +51,21 @@ public Message MapToBrighterMessage(IBrokeredMessageWrapper? azureServiceBusMess { if (azureServiceBusMessage is null) { - Log.NullMessageReceived(s_logger, _topic, subscription.Name); + Log.NullMessageReceived(_logger, _topic, subscription.Name); return Message.FailureMessage(_topic); } if (azureServiceBusMessage!.MessageBodyValue is null) { - Log.NullMessageBodyReceived(s_logger, _topic, subscription.Name); + Log.NullMessageBodyReceived(_logger, _topic, subscription.Name); } var bodyMemory = azureServiceBusMessage.MessageBodyMemory; #if NETSTANDARD2_0 - Log.ReceivedMessage(s_logger, _topic, subscription.Name, System.Text.Encoding.UTF8.GetString(bodyMemory.ToArray())); + Log.ReceivedMessage(_logger, _topic, subscription.Name, System.Text.Encoding.UTF8.GetString(bodyMemory.ToArray())); #else - Log.ReceivedMessage(s_logger, _topic, subscription.Name, System.Text.Encoding.UTF8.GetString(bodyMemory.Span)); + Log.ReceivedMessage(_logger, _topic, subscription.Name, System.Text.Encoding.UTF8.GetString(bodyMemory.Span)); #endif //TODO: Switch these to use the option type HeaderResult for consistency with the rest of the codebase. @@ -122,7 +123,7 @@ private Baggage GetBaggage(IBrokeredMessageWrapper azureServiceBusMessage) { if (!azureServiceBusMessage.ApplicationProperties.TryGetValue(ASBConstants.Baggage, out object? property)) { - Log.NoBaggageFound(s_logger, _topic, subscription.Name); + Log.NoBaggageFound(_logger, _topic, subscription.Name); return new Baggage(); } @@ -138,7 +139,7 @@ private Uri GetCloudEventsDataSchema(IBrokeredMessageWrapper azureServiceBusMess var defaultSchemaUri = new Uri("http://goparamore.io"); // Default schema URI if (!azureServiceBusMessage.ApplicationProperties.TryGetValue(ASBConstants.CloudEventsSchema, out object? property)) { - Log.NoCloudEventsDataSchema(s_logger, _topic, subscription.Name); + Log.NoCloudEventsDataSchema(_logger, _topic, subscription.Name); return defaultSchemaUri; } @@ -146,7 +147,7 @@ private Uri GetCloudEventsDataSchema(IBrokeredMessageWrapper azureServiceBusMess if (string.IsNullOrEmpty(dataSchema)) { - Log.EmptyCloudEventsDataSchema(s_logger, _topic, subscription.Name); + Log.EmptyCloudEventsDataSchema(_logger, _topic, subscription.Name); return defaultSchemaUri; } @@ -157,7 +158,7 @@ private string GetCloudEventsSubject(IBrokeredMessageWrapper azureServiceBusMess { if (!azureServiceBusMessage.ApplicationProperties.TryGetValue(ASBConstants.CloudEventsSubject, out object? property)) { - Log.NoCloudEventsSubject(s_logger, _topic, subscription.Name); + Log.NoCloudEventsSubject(_logger, _topic, subscription.Name); return string.Empty; } @@ -170,7 +171,7 @@ private DateTimeOffset GetCloudEventsTime(IBrokeredMessageWrapper azureServiceBu { if (!azureServiceBusMessage.ApplicationProperties.TryGetValue(ASBConstants.CloudEventsTime, out object? property)) { - Log.NoCloudEventsTime(s_logger, _topic, subscription.Name); + Log.NoCloudEventsTime(_logger, _topic, subscription.Name); return DateTimeOffset.UtcNow; } @@ -181,7 +182,7 @@ private DateTimeOffset GetCloudEventsTime(IBrokeredMessageWrapper azureServiceBu return parsedTime; } - Log.InvalidCloudEventsTimeFormat(s_logger, _topic, subscription.Name); + Log.InvalidCloudEventsTimeFormat(_logger, _topic, subscription.Name); return DateTimeOffset.UtcNow; } @@ -189,7 +190,7 @@ private PartitionKey GetPartitionKey(IBrokeredMessageWrapper azureServiceBusMess { if (!azureServiceBusMessage.ApplicationProperties.TryGetValue(ASBConstants.CloudEventsParitionKey, out object? property)) { - Log.NoCloudEventsPartitionKey(s_logger, _topic, subscription.Name); + Log.NoCloudEventsPartitionKey(_logger, _topic, subscription.Name); return PartitionKey.Empty; } @@ -200,7 +201,7 @@ private CloudEventsType GetCloudEventsType(IBrokeredMessageWrapper azureServiceB { if (!azureServiceBusMessage.ApplicationProperties.TryGetValue(ASBConstants.CloudEventsType, out object? property)) { - Log.NoCloudEventsType(s_logger, _topic, subscription.Name); + Log.NoCloudEventsType(_logger, _topic, subscription.Name); return CloudEventsType.Empty; } @@ -252,13 +253,13 @@ private Uri GetSource(IBrokeredMessageWrapper azureServiceBusMessage) var defaultSourceUri = new Uri("http://goparamore.io"); // Default source URI if (!azureServiceBusMessage.ApplicationProperties.TryGetValue(ASBConstants.CloudEventsSource, out object? property)) { - Log.NoSourceFound(s_logger, _topic, subscription.Name); + Log.NoSourceFound(_logger, _topic, subscription.Name); return defaultSourceUri; } if (property is not string sourceString || string.IsNullOrEmpty(sourceString)) { - Log.EmptyOrInvalidSource(s_logger, _topic, subscription.Name); + Log.EmptyOrInvalidSource(_logger, _topic, subscription.Name); return defaultSourceUri; } @@ -271,7 +272,7 @@ private TraceParent GetTraceParent(IBrokeredMessageWrapper azureServiceBusMessag { if (!azureServiceBusMessage.ApplicationProperties.TryGetValue(ASBConstants.TraceParent, out object?property)) { - Log.NoTraceParentFound(s_logger, _topic, subscription.Name); + Log.NoTraceParentFound(_logger, _topic, subscription.Name); return new TraceParent(string.Empty); } @@ -284,7 +285,7 @@ private TraceState GetTraceState(IBrokeredMessageWrapper azureServiceBusMessage) { if (!azureServiceBusMessage.ApplicationProperties.TryGetValue(ASBConstants.TraceState, out object? property)) { - Log.NoTraceStateFound(s_logger, _topic, subscription.Name); + Log.NoTraceStateFound(_logger, _topic, subscription.Name); return new TraceState(string.Empty); } diff --git a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusProducerRegistryFactory.cs b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusProducerRegistryFactory.cs index 929b86713a..e518a66a4a 100644 --- a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusProducerRegistryFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusProducerRegistryFactory.cs @@ -27,6 +27,7 @@ THE SOFTWARE. */ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Paramore.Brighter.MessagingGateway.AzureServiceBus.ClientProvider; namespace Paramore.Brighter.MessagingGateway.AzureServiceBus; @@ -36,19 +37,23 @@ public class AzureServiceBusProducerRegistryFactory : IAmAProducerRegistryFactor private readonly IServiceBusClientProvider _clientProvider; private readonly IEnumerable _asbPublications; private readonly int _bulkSendBatchSize; + private readonly ILoggerFactory? _loggerFactory; /// /// Creates a producer registry initialized with producers for ASB derived from the publications /// /// The configuration of the connection to ASB /// A set of publications - topics on the server - to configure + /// The used to create loggers public AzureServiceBusProducerRegistryFactory( - AzureServiceBusConfiguration configuration, - IEnumerable asbPublications) + AzureServiceBusConfiguration configuration, + IEnumerable asbPublications, + ILoggerFactory? loggerFactory = null) { _clientProvider = new ServiceBusConnectionStringClientProvider(configuration.ConnectionString); _asbPublications = asbPublications; _bulkSendBatchSize = configuration.BulkSendBatchSize; + _loggerFactory = loggerFactory; } /// @@ -57,14 +62,17 @@ public AzureServiceBusProducerRegistryFactory( /// The connection to ASB /// A set of publications - topics on the server - to configure /// The maximum size to chunk messages when dispatching to ASB + /// The used to create loggers public AzureServiceBusProducerRegistryFactory( IServiceBusClientProvider clientProvider, IEnumerable asbPublications, - int bulkSendBatchSize = 10) + int bulkSendBatchSize = 10, + ILoggerFactory? loggerFactory = null) { _clientProvider = clientProvider; _asbPublications = asbPublications; _bulkSendBatchSize = bulkSendBatchSize; + _loggerFactory = loggerFactory; } /// @@ -73,7 +81,7 @@ public AzureServiceBusProducerRegistryFactory( /// A has of middleware clients by topic, for sending messages to the middleware public IAmAProducerRegistry Create() { - var producerFactory = new AzureServiceBusMessageProducerFactory(_clientProvider, _asbPublications, _bulkSendBatchSize); + var producerFactory = new AzureServiceBusMessageProducerFactory(_clientProvider, _asbPublications, _bulkSendBatchSize, _loggerFactory); return new ProducerRegistry(producerFactory.Create()); } diff --git a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusQueueConsumer.cs b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusQueueConsumer.cs index 1510bbdeb2..d97bf2ca62 100644 --- a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusQueueConsumer.cs +++ b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusQueueConsumer.cs @@ -24,11 +24,11 @@ THE SOFTWARE. */ #endregion using System; -using System.Threading; +using System.Threading; using System.Threading.Tasks; using Azure.Messaging.ServiceBus; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.MessagingGateway.AzureServiceBus.AzureServiceBusWrappers; using Paramore.Brighter.Tasks; @@ -40,12 +40,12 @@ namespace Paramore.Brighter.MessagingGateway.AzureServiceBus; public partial class AzureServiceBusQueueConsumer : AzureServiceBusConsumer { protected override string SubscriptionName => "Queue"; - protected override ILogger Logger => s_logger; - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + protected override ILogger Logger => _logger; + private readonly ILogger _logger; private readonly IServiceBusReceiverProvider _serviceBusReceiverProvider; private bool _queueCreated = false; - + /// /// Initializes an Instance of for Service Bus Queus /// @@ -53,25 +53,28 @@ public partial class AzureServiceBusQueueConsumer : AzureServiceBusConsumer /// An instance of the Messaging Producer used for Requeue. /// An Instance of Administration Client Wrapper. /// An Instance of . + /// The used to create the logger. public AzureServiceBusQueueConsumer(AzureServiceBusSubscription subscription, IAmAMessageProducerSync messageProducer, IAdministrationClientWrapper administrationClientWrapper, - IServiceBusReceiverProvider serviceBusReceiverProvider) : base(subscription, - messageProducer, administrationClientWrapper) + IServiceBusReceiverProvider serviceBusReceiverProvider, + ILoggerFactory? loggerFactory = null) : base(subscription, + messageProducer, administrationClientWrapper, loggerFactory: loggerFactory) { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); _serviceBusReceiverProvider = serviceBusReceiverProvider; } protected override async Task GetMessageReceiverProviderAsync() { - Log.GettingMessageReceiverProviderAsync(s_logger, Topic); + Log.GettingMessageReceiverProviderAsync(_logger, Topic); try { ServiceBusReceiver = await _serviceBusReceiverProvider.GetAsync(Topic, SubscriptionConfiguration.RequireSession); } catch (Exception e) { - Log.FailedToGetMessageReceiverProviderAsync(s_logger, Topic, e); + Log.FailedToGetMessageReceiverProviderAsync(_logger, Topic, e); } } @@ -80,7 +83,7 @@ protected override async Task GetMessageReceiverProviderAsync() /// public override async Task PurgeAsync(CancellationToken cancellationToken = default(CancellationToken)) { - Log.PurgingMessagesFromQueueAsync(s_logger, Topic); + Log.PurgingMessagesFromQueueAsync(_logger, Topic); await AdministrationClientWrapper.DeleteQueueAsync(Topic); await EnsureChannelAsync(); @@ -111,7 +114,7 @@ protected override async Task EnsureChannelAsync() { if (ex.Reason == ServiceBusFailureReason.MessagingEntityAlreadyExists) { - Log.MessageEntityAlreadyExists(s_logger, Topic); + Log.MessageEntityAlreadyExists(_logger, Topic); _queueCreated = true; } else @@ -121,7 +124,7 @@ protected override async Task EnsureChannelAsync() } catch (Exception e) { - Log.FailingToCheckOrCreateSubscription(s_logger, e); + Log.FailingToCheckOrCreateSubscription(_logger, e); //The connection to Azure Service bus may have failed so we re-establish the connection. AdministrationClientWrapper.Reset(); diff --git a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusQueueMessageProducer.cs b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusQueueMessageProducer.cs index f7a2fd8db8..b4735ddd57 100644 --- a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusQueueMessageProducer.cs +++ b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusQueueMessageProducer.cs @@ -26,7 +26,7 @@ THE SOFTWARE. */ using System; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.MessagingGateway.AzureServiceBus.AzureServiceBusWrappers; namespace Paramore.Brighter.MessagingGateway.AzureServiceBus @@ -36,10 +36,10 @@ namespace Paramore.Brighter.MessagingGateway.AzureServiceBus /// public partial class AzureServiceBusQueueMessageProducer : AzureServiceBusMessageProducer { - protected override ILogger Logger => s_logger; - - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); - + protected override ILogger Logger => _logger; + + private readonly ILogger _logger; + private readonly IAdministrationClientWrapper _administrationClientWrapper; /// @@ -49,13 +49,16 @@ public partial class AzureServiceBusQueueMessageProducer : AzureServiceBusMessag /// The provider to use when producing messages. /// Configuration of a producer /// When sending more than one message using the MessageProducer, the max amount to send in a single transmission. + /// The used to create the logger. public AzureServiceBusQueueMessageProducer( IAdministrationClientWrapper administrationClientWrapper, IServiceBusSenderProvider serviceBusSenderProvider, AzureServiceBusPublication publication, - int bulkSendBatchSize = 10 + int bulkSendBatchSize = 10, + ILoggerFactory? loggerFactory = null ) : base(serviceBusSenderProvider, publication, bulkSendBatchSize) { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); _administrationClientWrapper = administrationClientWrapper; } @@ -84,7 +87,7 @@ protected override async Task EnsureChannelExistsAsync(string channelName) { //The connection to Azure Service bus may have failed so we re-establish the connection. _administrationClientWrapper.Reset(); - Log.FailingToCheckOrCreateQueue(s_logger, e); + Log.FailingToCheckOrCreateQueue(_logger, e); throw; } } diff --git a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusTopicConsumer.cs b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusTopicConsumer.cs index ba36f72fb3..e124d47252 100644 --- a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusTopicConsumer.cs +++ b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusTopicConsumer.cs @@ -28,7 +28,7 @@ THE SOFTWARE. */ using System.Threading.Tasks; using Azure.Messaging.ServiceBus; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.MessagingGateway.AzureServiceBus.AzureServiceBusWrappers; using Paramore.Brighter.Tasks; @@ -39,9 +39,9 @@ namespace Paramore.Brighter.MessagingGateway.AzureServiceBus; /// public partial class AzureServiceBusTopicConsumer : AzureServiceBusConsumer { - protected override ILogger Logger => s_logger; + protected override ILogger Logger => _logger; - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private bool _subscriptionCreated; private readonly string _subscriptionName; private readonly IServiceBusReceiverProvider _serviceBusReceiverProvider; @@ -54,13 +54,16 @@ public partial class AzureServiceBusTopicConsumer : AzureServiceBusConsumer /// An instance of the Messaging Producer used for Requeue. /// An Instance of Administration Client Wrapper. /// An Instance of . + /// The used to create the logger. public AzureServiceBusTopicConsumer( AzureServiceBusSubscription subscription, IAmAMessageProducer messageProducer, IAdministrationClientWrapper administrationClientWrapper, - IServiceBusReceiverProvider serviceBusReceiverProvider) - : base(subscription, messageProducer, administrationClientWrapper) + IServiceBusReceiverProvider serviceBusReceiverProvider, + ILoggerFactory? loggerFactory = null) + : base(subscription, messageProducer, administrationClientWrapper, loggerFactory: loggerFactory) { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); _subscriptionName = subscription.ChannelName.Value; _serviceBusReceiverProvider = serviceBusReceiverProvider; } @@ -70,7 +73,7 @@ public AzureServiceBusTopicConsumer( /// public override async Task PurgeAsync(CancellationToken ct = default) { - Log.PurgingMessagesFromSubscriptionOnTopic(s_logger, SubscriptionName, Topic); + Log.PurgingMessagesFromSubscriptionOnTopic(_logger, SubscriptionName, Topic); await AdministrationClientWrapper.DeleteTopicAsync(Topic); await EnsureChannelAsync(); @@ -102,7 +105,7 @@ protected override async Task EnsureChannelAsync() { if (ex.Reason == ServiceBusFailureReason.MessagingEntityAlreadyExists) { - Log.MessageEntityAlreadyExists(s_logger, Topic, _subscriptionName); + Log.MessageEntityAlreadyExists(_logger, Topic, _subscriptionName); _subscriptionCreated = true; } else @@ -112,7 +115,7 @@ protected override async Task EnsureChannelAsync() } catch (Exception e) { - Log.FailingToCheckOrCreateSubscription(s_logger, e); + Log.FailingToCheckOrCreateSubscription(_logger, e); //The connection to Azure Service bus may have failed so we re-establish the connection. AdministrationClientWrapper.Reset(); @@ -123,7 +126,7 @@ protected override async Task EnsureChannelAsync() protected override async Task GetMessageReceiverProviderAsync() { - Log.GettingMessageReceiverProviderForTopicAndSubscription(s_logger, Topic, _subscriptionName); + Log.GettingMessageReceiverProviderForTopicAndSubscription(_logger, Topic, _subscriptionName); try { ServiceBusReceiver = await _serviceBusReceiverProvider.GetAsync(Topic, _subscriptionName, @@ -131,7 +134,7 @@ protected override async Task GetMessageReceiverProviderAsync() } catch (Exception e) { - Log.FailedToGetMessageReceiverProviderForTopicAndSubscription(s_logger, e, Topic, _subscriptionName); + Log.FailedToGetMessageReceiverProviderForTopicAndSubscription(_logger, e, Topic, _subscriptionName); } } diff --git a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusTopicMessageProducer.cs b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusTopicMessageProducer.cs index ebfa9d70d5..0ecede349f 100644 --- a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusTopicMessageProducer.cs +++ b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusTopicMessageProducer.cs @@ -26,7 +26,7 @@ THE SOFTWARE. */ using System; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.MessagingGateway.AzureServiceBus.AzureServiceBusWrappers; namespace Paramore.Brighter.MessagingGateway.AzureServiceBus; @@ -36,10 +36,10 @@ namespace Paramore.Brighter.MessagingGateway.AzureServiceBus; /// public partial class AzureServiceBusTopicMessageProducer : AzureServiceBusMessageProducer { - protected override ILogger Logger => s_logger; - - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); - + protected override ILogger Logger => _logger; + + private readonly ILogger _logger; + private readonly IAdministrationClientWrapper _administrationClientWrapper; /// @@ -49,13 +49,16 @@ public partial class AzureServiceBusTopicMessageProducer : AzureServiceBusMessag /// The provider to use when producing messages. /// Configuration of a producer /// When sending more than one message using the MessageProducer, the max amount to send in a single transmission. + /// The used to create the logger. public AzureServiceBusTopicMessageProducer( IAdministrationClientWrapper administrationClientWrapper, IServiceBusSenderProvider serviceBusSenderProvider, AzureServiceBusPublication publication, - int bulkSendBatchSize = 10 + int bulkSendBatchSize = 10, + ILoggerFactory? loggerFactory = null ) : base(serviceBusSenderProvider, publication, bulkSendBatchSize) { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); _administrationClientWrapper = administrationClientWrapper; } @@ -84,7 +87,7 @@ protected override async Task EnsureChannelExistsAsync(string channelName) { //The connection to Azure Service bus may have failed so we re-establish the connection. _administrationClientWrapper.Reset(); - Log.FailingToCheckOrCreateTopic(s_logger, e); + Log.FailingToCheckOrCreateTopic(_logger, e); throw; } } diff --git a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusWrappers/AdministrationClientWrapper.cs b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusWrappers/AdministrationClientWrapper.cs index c73dfe4298..f581ea3dab 100644 --- a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusWrappers/AdministrationClientWrapper.cs +++ b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusWrappers/AdministrationClientWrapper.cs @@ -26,7 +26,7 @@ THE SOFTWARE. */ using System.Threading.Tasks; using Azure.Messaging.ServiceBus.Administration; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.MessagingGateway.AzureServiceBus.ClientProvider; using Paramore.Brighter.Tasks; @@ -39,16 +39,18 @@ public partial class AdministrationClientWrapper : IAdministrationClientWrapper { private readonly IServiceBusClientProvider _clientProvider; private ServiceBusAdministrationClient _administrationClient; - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; /// /// Initializes an Instance of /// /// - public AdministrationClientWrapper(IServiceBusClientProvider clientProvider) + /// The used to create the logger. + public AdministrationClientWrapper(IServiceBusClientProvider clientProvider, ILoggerFactory? loggerFactory = null) { _clientProvider = clientProvider; _administrationClient = _clientProvider.GetServiceBusAdministrationClient(); + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } /// @@ -56,7 +58,7 @@ public AdministrationClientWrapper(IServiceBusClientProvider clientProvider) /// public void Reset() { - Log.ResettingManagementClientWrapper(s_logger); + Log.ResettingManagementClientWrapper(_logger); Initialise(); } @@ -68,7 +70,7 @@ public void Reset() /// Ma message size in kilobytes : Only available in premium public async Task CreateQueueAsync(string queueName, TimeSpan? autoDeleteOnIdle = null, long? maxMessageSizeInKilobytes = default) { - Log.CreatingTopic(s_logger, queueName); + Log.CreatingTopic(_logger, queueName); try { @@ -80,11 +82,11 @@ await _administrationClient.CreateQueueAsync(new CreateQueueOptions(queueName) } catch (Exception e) { - Log.FailedToCreateQueue(s_logger, e, queueName); + Log.FailedToCreateQueue(_logger, e, queueName); throw; } - Log.QueueCreated(s_logger, queueName); + Log.QueueCreated(_logger, queueName); } /// @@ -96,7 +98,7 @@ await _administrationClient.CreateQueueAsync(new CreateQueueOptions(queueName) /// The configuration options for the subscriptions. public async Task CreateSubscriptionAsync(string topicName, string subscriptionName, AzureServiceBusSubscriptionConfiguration subscriptionConfiguration) { - Log.CreatingSubscriptionForTopic(s_logger, subscriptionName, topicName); + Log.CreatingSubscriptionForTopic(_logger, subscriptionName, topicName); if (!await TopicExistsAsync(topicName)) { @@ -122,11 +124,11 @@ public async Task CreateSubscriptionAsync(string topicName, string subscriptionN } catch (Exception e) { - Log.FailedToCreateSubscriptionForTopic(s_logger, e, subscriptionName, topicName); + Log.FailedToCreateSubscriptionForTopic(_logger, e, subscriptionName, topicName); throw; } - Log.SubscriptionForTopicCreated(s_logger, subscriptionName, topicName); + Log.SubscriptionForTopicCreated(_logger, subscriptionName, topicName); } @@ -139,7 +141,7 @@ public async Task CreateSubscriptionAsync(string topicName, string subscriptionN /// Ma message size in kilobytes : Only available in premium public async Task CreateTopicAsync(string topicName, TimeSpan? autoDeleteOnIdle = null, long? maxMessageSizeInKilobytes = default) { - Log.CreatingTopic(s_logger, topicName); + Log.CreatingTopic(_logger, topicName); try { @@ -151,11 +153,11 @@ await _administrationClient.CreateTopicAsync(new CreateTopicOptions(topicName) } catch (Exception e) { - Log.FailedToCreateTopic(s_logger, e, topicName); + Log.FailedToCreateTopic(_logger, e, topicName); throw; } - Log.TopicCreated(s_logger, topicName); + Log.TopicCreated(_logger, topicName); } @@ -165,15 +167,15 @@ await _administrationClient.CreateTopicAsync(new CreateTopicOptions(topicName) /// The name of the Queue public async Task DeleteQueueAsync(string queueName) { - Log.DeletingQueue(s_logger, queueName); + Log.DeletingQueue(_logger, queueName); try { await _administrationClient.DeleteQueueAsync(queueName); - Log.QueueSuccessfullyDeleted(s_logger, queueName); + Log.QueueSuccessfullyDeleted(_logger, queueName); } catch (Exception e) { - Log.FailedToDeleteQueue(s_logger, e, queueName); + Log.FailedToDeleteQueue(_logger, e, queueName); } } @@ -183,15 +185,15 @@ public async Task DeleteQueueAsync(string queueName) /// The name of the Topic public async Task DeleteTopicAsync(string topicName) { - Log.DeletingTopic(s_logger, topicName); + Log.DeletingTopic(_logger, topicName); try { await _administrationClient.DeleteTopicAsync(topicName); - Log.TopicSuccessfullyDeleted(s_logger, topicName); + Log.TopicSuccessfullyDeleted(_logger, topicName); } catch (Exception e) { - Log.FailedToDeleteTopic(s_logger, e, topicName); + Log.FailedToDeleteTopic(_logger, e, topicName); } } @@ -215,7 +217,7 @@ public async Task GetSubscriptionAsync(string topicName, /// True if the Queue exists. public async Task QueueExistsAsync(string queueName) { - Log.CheckingIfQueueExists(s_logger, queueName); + Log.CheckingIfQueueExists(_logger, queueName); bool result; @@ -225,17 +227,17 @@ public async Task QueueExistsAsync(string queueName) } catch (Exception e) { - Log.FailedToCheckIfQueueExists(s_logger, e, queueName); + Log.FailedToCheckIfQueueExists(_logger, e, queueName); throw; } if (result) { - Log.QueueExists(s_logger, queueName); + Log.QueueExists(_logger, queueName); } else { - Log.QueueDoesNotExist(s_logger, queueName); + Log.QueueDoesNotExist(_logger, queueName); } return result; @@ -249,7 +251,7 @@ public async Task QueueExistsAsync(string queueName) /// True if the subscription exists on the specified Topic. public async Task SubscriptionExistsAsync(string topicName, string subscriptionName) { - Log.CheckingIfSubscriptionForTopicExists(s_logger, subscriptionName, topicName); + Log.CheckingIfSubscriptionForTopicExists(_logger, subscriptionName, topicName); bool result; @@ -259,17 +261,17 @@ public async Task SubscriptionExistsAsync(string topicName, string subscri } catch (Exception e) { - Log.FailedToCheckIfSubscriptionForTopicExists(s_logger, e, subscriptionName, topicName); + Log.FailedToCheckIfSubscriptionForTopicExists(_logger, e, subscriptionName, topicName); throw; } if (result) { - Log.SubscriptionForTopicExists(s_logger, subscriptionName, topicName); + Log.SubscriptionForTopicExists(_logger, subscriptionName, topicName); } else { - Log.SubscriptionForTopicDoesNotExist(s_logger, subscriptionName, topicName); + Log.SubscriptionForTopicDoesNotExist(_logger, subscriptionName, topicName); } return result; @@ -283,7 +285,7 @@ public async Task SubscriptionExistsAsync(string topicName, string subscri /// True if the Topic exists. public async Task TopicExistsAsync(string topicName) { - Log.CheckingIfTopicExists(s_logger, topicName); + Log.CheckingIfTopicExists(_logger, topicName); bool result; @@ -293,17 +295,17 @@ public async Task TopicExistsAsync(string topicName) } catch (Exception e) { - Log.FailedToCheckIfTopicExists(s_logger, e, topicName); + Log.FailedToCheckIfTopicExists(_logger, e, topicName); throw; } if (result) { - Log.TopicExists(s_logger, topicName); + Log.TopicExists(_logger, topicName); } else { - Log.TopicDoesNotExist(s_logger, topicName); + Log.TopicDoesNotExist(_logger, topicName); } return result; @@ -311,7 +313,7 @@ public async Task TopicExistsAsync(string topicName) private void Initialise() { - Log.InitialisingNewManagementClientWrapper(s_logger); + Log.InitialisingNewManagementClientWrapper(_logger); try { @@ -319,11 +321,11 @@ private void Initialise() } catch (Exception e) { - Log.FailedToInitialiseNewManagementClientWrapper(s_logger, e); + Log.FailedToInitialiseNewManagementClientWrapper(_logger, e); throw; } - Log.NewManagementClientWrapperInitialised(s_logger); + Log.NewManagementClientWrapperInitialised(_logger); } private static partial class Log diff --git a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusWrappers/ServiceBusReceiverProvider.cs b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusWrappers/ServiceBusReceiverProvider.cs index 50acb7b19c..5186563202 100644 --- a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusWrappers/ServiceBusReceiverProvider.cs +++ b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusWrappers/ServiceBusReceiverProvider.cs @@ -23,13 +23,15 @@ THE SOFTWARE. */ using System.Threading.Tasks; using Azure.Messaging.ServiceBus; +using Microsoft.Extensions.Logging; using Paramore.Brighter.MessagingGateway.AzureServiceBus.ClientProvider; namespace Paramore.Brighter.MessagingGateway.AzureServiceBus.AzureServiceBusWrappers { - internal sealed class ServiceBusReceiverProvider(IServiceBusClientProvider clientProvider) : IServiceBusReceiverProvider + internal sealed class ServiceBusReceiverProvider(IServiceBusClientProvider clientProvider, ILoggerFactory? loggerFactory = null) : IServiceBusReceiverProvider { private readonly ServiceBusClient _client = clientProvider.GetServiceBusClient(); + private readonly ILoggerFactory? _loggerFactory = loggerFactory; /// /// Gets a for a Service Bus Queue @@ -45,7 +47,7 @@ internal sealed class ServiceBusReceiverProvider(IServiceBusClientProvider clien try { return new ServiceBusReceiverWrapper(await _client.AcceptNextSessionAsync(queueName, - new ServiceBusSessionReceiverOptions() {ReceiveMode = ServiceBusReceiveMode.PeekLock})); + new ServiceBusSessionReceiverOptions() {ReceiveMode = ServiceBusReceiveMode.PeekLock}), _loggerFactory); } catch (ServiceBusException e) { @@ -61,7 +63,7 @@ internal sealed class ServiceBusReceiverProvider(IServiceBusClientProvider clien else { return new ServiceBusReceiverWrapper(_client.CreateReceiver(queueName, - new ServiceBusReceiverOptions { ReceiveMode = ServiceBusReceiveMode.PeekLock })); + new ServiceBusReceiverOptions { ReceiveMode = ServiceBusReceiveMode.PeekLock }), _loggerFactory); } } @@ -80,7 +82,7 @@ internal sealed class ServiceBusReceiverProvider(IServiceBusClientProvider clien try { return new ServiceBusReceiverWrapper(await _client.AcceptNextSessionAsync(topicName, subscriptionName, - new ServiceBusSessionReceiverOptions() {ReceiveMode = ServiceBusReceiveMode.PeekLock})); + new ServiceBusSessionReceiverOptions() {ReceiveMode = ServiceBusReceiveMode.PeekLock}), _loggerFactory); } catch (ServiceBusException e) { @@ -96,7 +98,7 @@ internal sealed class ServiceBusReceiverProvider(IServiceBusClientProvider clien else { return new ServiceBusReceiverWrapper(_client.CreateReceiver(topicName, subscriptionName, - new ServiceBusReceiverOptions { ReceiveMode = ServiceBusReceiveMode.PeekLock })); + new ServiceBusReceiverOptions { ReceiveMode = ServiceBusReceiveMode.PeekLock }), _loggerFactory); } } } diff --git a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusWrappers/ServiceBusReceiverWrapper.cs b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusWrappers/ServiceBusReceiverWrapper.cs index 0c8996faf4..cb4c19a706 100644 --- a/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusWrappers/ServiceBusReceiverWrapper.cs +++ b/src/Paramore.Brighter.MessagingGateway.AzureServiceBus/AzureServiceBusWrappers/ServiceBusReceiverWrapper.cs @@ -27,7 +27,7 @@ THE SOFTWARE. */ using System.Threading.Tasks; using Azure.Messaging.ServiceBus; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Paramore.Brighter.MessagingGateway.AzureServiceBus.AzureServiceBusWrappers { @@ -37,15 +37,17 @@ namespace Paramore.Brighter.MessagingGateway.AzureServiceBus.AzureServiceBusWrap internal sealed partial class ServiceBusReceiverWrapper : IServiceBusReceiverWrapper { private readonly ServiceBusReceiver _messageReceiver; - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The to wrap. - public ServiceBusReceiverWrapper(ServiceBusReceiver messageReceiver) + /// The used to create the logger. + public ServiceBusReceiverWrapper(ServiceBusReceiver messageReceiver, ILoggerFactory? loggerFactory = null) { _messageReceiver = messageReceiver; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } /// @@ -70,16 +72,16 @@ public async Task> ReceiveAsync(int batchSi /// public void Close() { - Log.ClosingMessageReceiverConnection(s_logger); + Log.ClosingMessageReceiverConnection(_logger); _messageReceiver.CloseAsync().GetAwaiter().GetResult(); - Log.MessageReceiverConnectionStopped(s_logger); + Log.MessageReceiverConnectionStopped(_logger); } public async Task CloseAsync() { - Log.ClosingMessageReceiverConnection(s_logger); + Log.ClosingMessageReceiverConnection(_logger); await _messageReceiver.CloseAsync().ConfigureAwait(false); - Log.MessageReceiverConnectionStopped(s_logger); + Log.MessageReceiverConnectionStopped(_logger); } /// diff --git a/src/Paramore.Brighter.MessagingGateway.GcpPubSub/GcpPubSubConsumerFactory.cs b/src/Paramore.Brighter.MessagingGateway.GcpPubSub/GcpPubSubConsumerFactory.cs index 6809efdeff..0d8c4523e2 100644 --- a/src/Paramore.Brighter.MessagingGateway.GcpPubSub/GcpPubSubConsumerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.GcpPubSub/GcpPubSubConsumerFactory.cs @@ -1,6 +1,7 @@ using System.Collections.Concurrent; using Google.Api.Gax; using Google.Cloud.PubSub.V1; +using Microsoft.Extensions.Logging; using Paramore.Brighter.Tasks; namespace Paramore.Brighter.MessagingGateway.GcpPubSub; @@ -12,10 +13,12 @@ namespace Paramore.Brighter.MessagingGateway.GcpPubSub; /// dedicated pull consumers, as well as ensuring the underlying subscription exists. /// /// The connection details for the Google Cloud Pub/Sub gateway. -public class GcpPubSubConsumerFactory(GcpMessagingGatewayConnection connection) +/// The used to create loggers for the consumers. +public class GcpPubSubConsumerFactory(GcpMessagingGatewayConnection connection, ILoggerFactory? loggerFactory = null) : GcpPubSubMessageGateway(connection), IAmAMessageConsumerFactory { private readonly GcpMessagingGatewayConnection _connection = connection; + private readonly ILoggerFactory? _loggerFactory = loggerFactory; /// /// Creates a synchronous message consumer for the given subscription. @@ -81,7 +84,7 @@ private async Task CreateAsync(GcpPubSubSubscription pubSubSubscription) { // Create a new, non-shared consumer that uses the Pull API for each request return new GcpPullMessageConsumer(_connection, subscriptionName, - pubSubSubscription.BufferSize, pubSubSubscription.TimeProvider); + pubSubSubscription.BufferSize, pubSubSubscription.TimeProvider, _loggerFactory); } // If not Pull, use Stream mode. Stream mode consumers are shared per subscription to manage @@ -100,7 +103,8 @@ private async Task CreateAsync(GcpPubSubSubscription pubSubSubscription) _connection, consumer, subscriptionName, - pubSubSubscription.TimeProvider); + pubSubSubscription.TimeProvider, + _loggerFactory); } private Google.Cloud.PubSub.V1.SubscriberClient CreateSubscriberClient(Google.Cloud.PubSub.V1.SubscriptionName subscriptionName, diff --git a/src/Paramore.Brighter.MessagingGateway.GcpPubSub/GcpPubSubStreamMessageConsumer.cs b/src/Paramore.Brighter.MessagingGateway.GcpPubSub/GcpPubSubStreamMessageConsumer.cs index fc30338d24..254f0ef3b9 100644 --- a/src/Paramore.Brighter.MessagingGateway.GcpPubSub/GcpPubSubStreamMessageConsumer.cs +++ b/src/Paramore.Brighter.MessagingGateway.GcpPubSub/GcpPubSubStreamMessageConsumer.cs @@ -1,7 +1,7 @@ using Google.Cloud.PubSub.V1; using Google.Protobuf.WellKnownTypes; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Tasks; namespace Paramore.Brighter.MessagingGateway.GcpPubSub; @@ -19,10 +19,11 @@ public partial class GcpPubSubStreamMessageConsumer( GcpMessagingGatewayConnection connection, GcpStreamConsumer consumer, Google.Cloud.PubSub.V1.SubscriptionName subscriptionName, - TimeProvider timeProvider) : IAmAMessageConsumerSync, IAmAMessageConsumerAsync + TimeProvider timeProvider, + ILoggerFactory? loggerFactory = null) : IAmAMessageConsumerSync, IAmAMessageConsumerAsync { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); /// /// Synchronously acknowledges a message, signalling the Pub/Sub service that the message @@ -37,7 +38,7 @@ public void Acknowledge(Message message) } gcpStreamMessage.Accepted(); - Log.AcknowledgeSuccess(s_logger, message.Id.Value, "", subscriptionName.ToString()); + Log.AcknowledgeSuccess(_logger, message.Id.Value, "", subscriptionName.ToString()); } /// @@ -89,7 +90,7 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) } gcpStreamMessage.Accepted(); - Log.RejectMessage(s_logger, message.Id.Value, "", subscriptionName.ToString()); + Log.RejectMessage(_logger, message.Id.Value, "", subscriptionName.ToString()); return true; } @@ -117,18 +118,18 @@ public void Purge() { var client = connection.GetOrCreateSubscriberServiceApiClient(); - Log.PurgeStart(s_logger, subscriptionName.ToString()); + Log.PurgeStart(_logger, subscriptionName.ToString()); client.Seek(new SeekRequest { Time = Timestamp.FromDateTimeOffset(timeProvider.GetUtcNow().AddMinutes(1)) }); - Log.PurgeComplete(s_logger, subscriptionName.ToString()); + Log.PurgeComplete(_logger, subscriptionName.ToString()); } catch (Exception ex) { - Log.PurgeError(s_logger, ex, subscriptionName.ToString()); + Log.PurgeError(_logger, ex, subscriptionName.ToString()); throw; } } @@ -146,17 +147,17 @@ public async Task PurgeAsync(CancellationToken cancellationToken = default) { var client = await connection.CreateSubscriberServiceApiClientAsync(); - Log.PurgeStart(s_logger, subscriptionName.ToString()); + Log.PurgeStart(_logger, subscriptionName.ToString()); await client.SeekAsync( new SeekRequest { Time = Timestamp.FromDateTimeOffset(timeProvider.GetUtcNow().AddMinutes(1)) }, cancellationToken); - Log.PurgeComplete(s_logger, subscriptionName.ToString()); + Log.PurgeComplete(_logger, subscriptionName.ToString()); } catch (Exception ex) { - Log.PurgeError(s_logger, ex, subscriptionName.ToString()); + Log.PurgeError(_logger, ex, subscriptionName.ToString()); throw; } } @@ -222,7 +223,7 @@ public bool Requeue(Message message, TimeSpan? delay = null) } gcpStreamMessage.Reject(); - Log.RequeueComplete(s_logger, message.Id.Value); + Log.RequeueComplete(_logger, message.Id.Value); return true; } diff --git a/src/Paramore.Brighter.MessagingGateway.GcpPubSub/GcpPullMessageConsumer.cs b/src/Paramore.Brighter.MessagingGateway.GcpPubSub/GcpPullMessageConsumer.cs index 88dce2619b..0ca38f467a 100644 --- a/src/Paramore.Brighter.MessagingGateway.GcpPubSub/GcpPullMessageConsumer.cs +++ b/src/Paramore.Brighter.MessagingGateway.GcpPubSub/GcpPullMessageConsumer.cs @@ -2,7 +2,7 @@ using Google.Protobuf.WellKnownTypes; using Grpc.Core; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Paramore.Brighter.MessagingGateway.GcpPubSub; @@ -15,10 +15,11 @@ public partial class GcpPullMessageConsumer( GcpMessagingGatewayConnection connection, Google.Cloud.PubSub.V1.SubscriptionName subscriptionName, int batchSize, - TimeProvider timeProvider) + TimeProvider timeProvider, + ILoggerFactory? loggerFactory = null) : IAmAMessageConsumerAsync, IAmAMessageConsumerSync { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); /// /// Synchronously acknowledges a message. @@ -35,11 +36,11 @@ public void Acknowledge(Message message) { var client = connection.GetOrCreateSubscriberServiceApiClient(); client.Acknowledge(subscriptionName, [ackId]); - Log.AcknowledgeSuccess(s_logger, message.Id.Value, ackId, subscriptionName.ToString()); + Log.AcknowledgeSuccess(_logger, message.Id.Value, ackId, subscriptionName.ToString()); } catch (Exception ex) { - Log.AcknowledgeError(s_logger, ex, message.Id.Value, ackId, subscriptionName.ToString()); + Log.AcknowledgeError(_logger, ex, message.Id.Value, ackId, subscriptionName.ToString()); throw; } } @@ -61,11 +62,11 @@ public async Task AcknowledgeAsync(Message message, CancellationToken cancellati { var client = await connection.CreateSubscriberServiceApiClientAsync(); await client.AcknowledgeAsync(subscriptionName, [ackId], cancellationToken); - Log.AcknowledgeSuccess(s_logger, message.Id.Value, ackId, subscriptionName.ToString()); + Log.AcknowledgeSuccess(_logger, message.Id.Value, ackId, subscriptionName.ToString()); } catch (Exception ex) { - Log.AcknowledgeError(s_logger, ex, message.Id.Value, ackId, subscriptionName.ToString()); + Log.AcknowledgeError(_logger, ex, message.Id.Value, ackId, subscriptionName.ToString()); throw; } } @@ -98,14 +99,14 @@ public void Purge() { var client = connection.GetOrCreateSubscriberServiceApiClient(); - Log.PurgeStart(s_logger, subscriptionName.ToString()); + Log.PurgeStart(_logger, subscriptionName.ToString()); client.Seek( new SeekRequest { Time = Timestamp.FromDateTimeOffset(timeProvider.GetUtcNow().AddMinutes(1)) }); - Log.PurgeComplete(s_logger, subscriptionName.ToString()); + Log.PurgeComplete(_logger, subscriptionName.ToString()); } catch (Exception ex) { - Log.PurgeError(s_logger, ex, subscriptionName.ToString()); + Log.PurgeError(_logger, ex, subscriptionName.ToString()); throw; } } @@ -122,17 +123,17 @@ public async Task PurgeAsync(CancellationToken cancellationToken = default) { var client = await connection.CreateSubscriberServiceApiClientAsync(); - Log.PurgeStart(s_logger, subscriptionName.ToString()); + Log.PurgeStart(_logger, subscriptionName.ToString()); await client.SeekAsync( new SeekRequest { Time = Timestamp.FromDateTimeOffset(timeProvider.GetUtcNow().AddMinutes(1)) }, cancellationToken); - Log.PurgeComplete(s_logger, subscriptionName.ToString()); + Log.PurgeComplete(_logger, subscriptionName.ToString()); } catch (Exception ex) { - Log.PurgeError(s_logger, ex, subscriptionName.ToString()); + Log.PurgeError(_logger, ex, subscriptionName.ToString()); throw; } } @@ -164,13 +165,13 @@ public async Task ReceiveAsync(TimeSpan? timeOut = null, Cancellation } catch (RpcException rcpException) when (rcpException.Status.StatusCode == StatusCode.Unavailable) { - Log.ReceiveConnectionError(s_logger); + Log.ReceiveConnectionError(_logger); throw new ChannelFailureException("Error connecting to Pub/Sub, see inner exception for details", rcpException); } catch (Exception e) { - Log.ReceiveError(s_logger, e, subscriptionName.ToString()); + Log.ReceiveError(_logger, e, subscriptionName.ToString()); throw; } @@ -202,13 +203,13 @@ public Message[] Receive(TimeSpan? timeOut = null) } catch (RpcException rcpException) when (rcpException.Status.StatusCode == StatusCode.Unavailable) { - Log.ReceiveConnectionError(s_logger); + Log.ReceiveConnectionError(_logger); throw new ChannelFailureException("Error connecting to Pub/Sub, see inner exception for details", rcpException); } catch (Exception e) { - Log.ReceiveError(s_logger, e, subscriptionName.ToString()); + Log.ReceiveError(_logger, e, subscriptionName.ToString()); throw; } @@ -232,12 +233,12 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) { var client = connection.GetOrCreateSubscriberServiceApiClient(); - Log.RejectMessage(s_logger, message.Id.Value, ackId, subscriptionName.ToString()); + Log.RejectMessage(_logger, message.Id.Value, ackId, subscriptionName.ToString()); client.Acknowledge(subscriptionName, [ackId]); } catch (Exception ex) { - Log.RejectError(s_logger, ex, message.Id.Value, ackId, subscriptionName.ToString()); + Log.RejectError(_logger, ex, message.Id.Value, ackId, subscriptionName.ToString()); throw; } @@ -261,12 +262,12 @@ public async Task RejectAsync(Message message, MessageRejectionReason? rea try { var client = await connection.CreateSubscriberServiceApiClientAsync(); - Log.RejectMessage(s_logger, message.Id.Value, ackId, subscriptionName.ToString()); + Log.RejectMessage(_logger, message.Id.Value, ackId, subscriptionName.ToString()); await client.AcknowledgeAsync(subscriptionName, [ackId], cancellationToken); } catch (Exception ex) { - Log.RejectError(s_logger, ex, message.Id.Value, ackId, subscriptionName.ToString()); + Log.RejectError(_logger, ex, message.Id.Value, ackId, subscriptionName.ToString()); throw; } @@ -291,17 +292,17 @@ public bool Requeue(Message message, TimeSpan? delay = null) { var client = connection.GetOrCreateSubscriberServiceApiClient(); - Log.RequeueStart(s_logger, message.Id.Value); + Log.RequeueStart(_logger, message.Id.Value); // The requeue policy is defined by subscription, during its creation client.ModifyAckDeadline(subscriptionName, [ackId], 0); - Log.RequeueComplete(s_logger, message.Id.Value); + Log.RequeueComplete(_logger, message.Id.Value); return true; } catch (Exception ex) { - Log.RequeueError(s_logger, ex, message.Id.Value, ackId, subscriptionName.ToString()); + Log.RequeueError(_logger, ex, message.Id.Value, ackId, subscriptionName.ToString()); return false; } } @@ -326,7 +327,7 @@ public async Task RequeueAsync(Message message, TimeSpan? delay = null, { var client = await connection.CreateSubscriberServiceApiClientAsync(); - Log.RequeueStart(s_logger, message.Id.Value); + Log.RequeueStart(_logger, message.Id.Value); // The requeue policy is defined by subscription, during its creation await client.ModifyAckDeadlineAsync(new ModifyAckDeadlineRequest @@ -336,12 +337,12 @@ await client.ModifyAckDeadlineAsync(new ModifyAckDeadlineRequest AckDeadlineSeconds = 0 }, cancellationToken); - Log.RequeueComplete(s_logger, message.Id.Value); + Log.RequeueComplete(_logger, message.Id.Value); return true; } catch (Exception ex) { - Log.RequeueError(s_logger, ex, message.Id.Value, ackId, subscriptionName.ToString()); + Log.RequeueError(_logger, ex, message.Id.Value, ackId, subscriptionName.ToString()); return false; } } diff --git a/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageConsumer.cs b/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageConsumer.cs index 8cb742e50f..65d5b5d15a 100644 --- a/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageConsumer.cs +++ b/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageConsumer.cs @@ -108,6 +108,7 @@ public partial class KafkaMessageConsumer : KafkaMessagingGateway, IAmAMessageCo /// If we support an invalid message topic what is the /// Optional scheduler for delayed requeue operations. When provided, the lazily-created /// requeue producer will use this scheduler for delayed sends. + /// The used to create loggers for this consumer and the producers it creates. /// Throws an exception if required parameters missing public KafkaMessageConsumer( KafkaMessagingGatewayConfiguration configuration, @@ -129,8 +130,10 @@ public KafkaMessageConsumer( RoutingKey? deadLetterRoutingKey = null, RoutingKey? invalidMessageRoutingKey = null, TimeProvider? timeProvider = null, - IAmAMessageScheduler? scheduler = null + IAmAMessageScheduler? scheduler = null, + ILoggerFactory? loggerFactory = null ) + : base(loggerFactory) { if (groupId is null) throw new ConfigurationException("You must set a GroupId for the consumer"); @@ -227,7 +230,7 @@ public KafkaMessageConsumer( { var partitions = list.Select(p => $"{p.Topic} : {p.Partition.Value}"); - Log.PartitionAdded(s_logger, String.Join(",", partitions)); + Log.PartitionAdded(_logger, String.Join(",", partitions)); _partitions.AddRange(list); }) @@ -238,7 +241,7 @@ public KafkaMessageConsumer( var revokedPartitionInfo = list.Select(tpo => $"{tpo.Topic} : {tpo.Partition}").ToList(); - Log.PartitionsRevoked(s_logger, string.Join(",", revokedPartitionInfo)); + Log.PartitionsRevoked(_logger, string.Join(",", revokedPartitionInfo)); _partitions = _partitions.Where(tp => list.All(tpo => tpo.TopicPartition != tp)).ToList(); }) @@ -246,7 +249,7 @@ public KafkaMessageConsumer( { var lostPartitions = list.Select(tpo => $"{tpo.Topic} : {tpo.Partition}").ToList(); - Log.PartitionsLost(s_logger, string.Join(",", lostPartitions)); + Log.PartitionsLost(_logger, string.Join(",", lostPartitions)); _partitions = _partitions.Where(tp => list.All(tpo => tpo.TopicPartition != tp)).ToList(); }) @@ -255,16 +258,16 @@ public KafkaMessageConsumer( _hasFatalError = error.IsFatal; if (_hasFatalError ) - Log.FatalError(s_logger, error.Code, error.Reason, true); + Log.FatalError(_logger, error.Code, error.Reason, true); else - Log.NonFatalError(s_logger, error.Code, error.Reason, false); + Log.NonFatalError(_logger, error.Code, error.Reason, false); }) .Build(); - Log.SubscribingToTopic(s_logger, Topic); + Log.SubscribingToTopic(_logger, Topic); _consumer.Subscribe([Topic.Value]); - _creator = new KafkaMessageCreator(); + _creator = new KafkaMessageCreator(_loggerFactory?.CreateLogger()); MakeChannels = makeChannels; Topic = routingKey; @@ -298,7 +301,7 @@ public void Acknowledge(Message message) { if (!message.Header.Bag.TryGetValue(HeaderNames.PARTITION_OFFSET, out var bagData)) { - Log.CannotAcknowledgeMessage(s_logger, message.Id.Value); + Log.CannotAcknowledgeMessage(_logger, message.Id.Value); return; } @@ -307,26 +310,26 @@ public void Acknowledge(Message message) var topicPartitionOffset = bagData as TopicPartitionOffset; if (topicPartitionOffset == null) { - Log.CannotAcknowledgeMessage(s_logger, message.Id.Value); + Log.CannotAcknowledgeMessage(_logger, message.Id.Value); return; } var offset = new TopicPartitionOffset(topicPartitionOffset.TopicPartition, new Offset(topicPartitionOffset.Offset + 1)); - Log.StoringOffset(s_logger, new Offset(topicPartitionOffset.Offset + 1).Value, topicPartitionOffset.TopicPartition.Topic, topicPartitionOffset.TopicPartition.Partition.Value); + Log.StoringOffset(_logger, new Offset(topicPartitionOffset.Offset + 1).Value, topicPartitionOffset.TopicPartition.Topic, topicPartitionOffset.TopicPartition.Partition.Value); _offsetStorage.Add(offset); if (_offsetStorage.Count % _maxBatchSize == 0) FlushOffsets(); - Log.CurrentKafkaBatchCount(s_logger, _offsetStorage.Count.ToString(), _maxBatchSize.ToString()); + Log.CurrentKafkaBatchCount(_logger, _offsetStorage.Count.ToString(), _maxBatchSize.ToString()); } catch (TopicPartitionException tpe) { var results = tpe.Results.Select(r => $"Error committing topic {r.Topic} for partition {r.Partition.Value.ToString()} because {r.Error.Reason}"); var errorString = string.Join(Environment.NewLine, results); - Log.ErrorCommittingOffsetsAsDebug(s_logger, errorString); + Log.ErrorCommittingOffsetsAsDebug(_logger, errorString); } } @@ -358,7 +361,7 @@ public void Nack(Message message) { if (!message.Header.Bag.TryGetValue(HeaderNames.PARTITION_OFFSET, out var bagData)) { - Log.CannotNackMessage(s_logger, message.Id.Value); + Log.CannotNackMessage(_logger, message.Id.Value); return; } @@ -367,17 +370,17 @@ public void Nack(Message message) var topicPartitionOffset = bagData as TopicPartitionOffset; if (topicPartitionOffset == null) { - Log.CannotNackMessageTypeMismatch(s_logger, message.Id.Value, bagData?.GetType().FullName ?? "null"); + Log.CannotNackMessageTypeMismatch(_logger, message.Id.Value, bagData?.GetType().FullName ?? "null"); return; } - Log.NackingMessage(s_logger, topicPartitionOffset.Offset.Value, topicPartitionOffset.Topic, topicPartitionOffset.Partition.Value); + Log.NackingMessage(_logger, topicPartitionOffset.Offset.Value, topicPartitionOffset.Topic, topicPartitionOffset.Partition.Value); _consumer.Seek(topicPartitionOffset); } catch (Exception ex) when (ex is KafkaException or InvalidOperationException) { - Log.ErrorSeekingOffsetForNack(s_logger, ex.Message); + Log.ErrorSeekingOffsetForNack(_logger, ex.Message); } } @@ -414,13 +417,13 @@ public void Close() } else { - Log.SkippedCommittingOffsetsBeforeClose(s_logger); + Log.SkippedCommittingOffsetsBeforeClose(_logger); } } catch (Exception ex) { //Close anyway, we just will get replay of those messages - Log.ErrorCommittingOffsetBeforeClosing(s_logger, ex.Message); + Log.ErrorCommittingOffsetBeforeClosing(_logger, ex.Message); } finally { @@ -501,37 +504,37 @@ public Message[] Receive(TimeSpan? timeOut = null) LogOffSets(); - Log.ConsumingMessages(s_logger, timeOut.Value.TotalMilliseconds); + Log.ConsumingMessages(_logger, timeOut.Value.TotalMilliseconds); var consumeResult = _consumer.Consume(timeOut.Value); if (consumeResult == null) { CheckHasPartitions(); - Log.NoMessagesAvailable(s_logger); + Log.NoMessagesAvailable(_logger); return [new Message()]; } if (consumeResult.IsPartitionEOF) { - Log.EndOfPartition(s_logger, _consumer.MemberId); + Log.EndOfPartition(_logger, _consumer.MemberId); return [new Message()]; } - Log.UsableMessageRetrieved(s_logger, consumeResult.Message.Value); - Log.PartitionOffsetValue(s_logger, consumeResult.Partition, consumeResult.Offset, consumeResult.Message.Value); + Log.UsableMessageRetrieved(_logger, consumeResult.Message.Value); + Log.PartitionOffsetValue(_logger, consumeResult.Partition, consumeResult.Offset, consumeResult.Message.Value); return [_creator.CreateMessage(consumeResult)]; } catch (ConsumeException consumeException) { - Log.ErrorListeningToTopic(s_logger, consumeException, Topic ?? RoutingKey.Empty, _consumerConfig.GroupId, _consumerConfig.BootstrapServers); + Log.ErrorListeningToTopic(_logger, consumeException, Topic ?? RoutingKey.Empty, _consumerConfig.GroupId, _consumerConfig.BootstrapServers); throw new ChannelFailureException("Error connecting to Kafka, see inner exception for details", consumeException); } catch (KafkaException kafkaException) { - Log.ErrorListeningToTopic(s_logger, kafkaException, Topic ?? RoutingKey.Empty, _consumerConfig.GroupId, _consumerConfig.BootstrapServers); + Log.ErrorListeningToTopic(_logger, kafkaException, Topic ?? RoutingKey.Empty, _consumerConfig.GroupId, _consumerConfig.BootstrapServers); if (kafkaException.Error.IsFatal) //this can't be recovered and requires a new consumer throw; @@ -539,7 +542,7 @@ public Message[] Receive(TimeSpan? timeOut = null) } catch (Exception exception) { - Log.ErrorListeningToTopic(s_logger, exception, Topic ?? RoutingKey.Empty, _consumerConfig.GroupId, _consumerConfig.BootstrapServers); + Log.ErrorListeningToTopic(_logger, exception, Topic ?? RoutingKey.Empty, _consumerConfig.GroupId, _consumerConfig.BootstrapServers); throw; } } @@ -597,7 +600,7 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) { if (reason != null) { - Log.NoChannelsConfiguredForRejection(s_logger, message.Header.MessageId.Value, reason.RejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Header.MessageId.Value, reason.RejectionReason.ToString()); } Acknowledge(message); return true; @@ -619,7 +622,7 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) { message.Header.Topic = routingKey!; if (isFallingBackToDlq) - Log.FallingBackToDLQ(s_logger, message.Header.MessageId.Value); + Log.FallingBackToDLQ(_logger, message.Header.MessageId.Value); // Get the appropriate producer based on routing producer = GetRejectionProducer(routingKey); @@ -628,16 +631,16 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) if (producer != null) { producer.Send(message); - Log.MessageSentToRejectionChannel(s_logger, message.Header.MessageId.Value, rejectionReason.ToString()); + Log.MessageSentToRejectionChannel(_logger, message.Header.MessageId.Value, rejectionReason.ToString()); } else { - Log.NoChannelsConfiguredForRejection(s_logger, message.Header.MessageId.Value, rejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Header.MessageId.Value, rejectionReason.ToString()); } } catch (Exception ex) { - Log.ErrorSendingToRejectionChannel(s_logger, ex, message.Header.MessageId.Value, rejectionReason.ToString()); + Log.ErrorSendingToRejectionChannel(_logger, ex, message.Header.MessageId.Value, rejectionReason.ToString()); Acknowledge(message); return true; } @@ -663,7 +666,7 @@ public async Task RejectAsync(Message message, MessageRejectionReason? rea { if (reason != null) { - Log.NoChannelsConfiguredForRejection(s_logger, message.Header.MessageId.Value, reason.RejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Header.MessageId.Value, reason.RejectionReason.ToString()); } await AcknowledgeAsync(message, cancellationToken); return true; @@ -685,7 +688,7 @@ public async Task RejectAsync(Message message, MessageRejectionReason? rea { message.Header.Topic = routingKey!; if (isFallingBackToDlq) - Log.FallingBackToDLQ(s_logger, message.Header.MessageId.Value); + Log.FallingBackToDLQ(_logger, message.Header.MessageId.Value); // Get the appropriate producer based on routing producer = GetRejectionProducer(routingKey); @@ -694,16 +697,16 @@ public async Task RejectAsync(Message message, MessageRejectionReason? rea if (producer != null) { await producer.SendAsync(message, cancellationToken); - Log.MessageSentToRejectionChannel(s_logger, message.Header.MessageId.Value, rejectionReason.ToString()); + Log.MessageSentToRejectionChannel(_logger, message.Header.MessageId.Value, rejectionReason.ToString()); } else { - Log.NoChannelsConfiguredForRejection(s_logger, message.Header.MessageId.Value, rejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Header.MessageId.Value, rejectionReason.ToString()); } } catch (Exception ex) { - Log.ErrorSendingToRejectionChannel(s_logger, ex, message.Header.MessageId.Value, rejectionReason.ToString()); + Log.ErrorSendingToRejectionChannel(_logger, ex, message.Header.MessageId.Value, rejectionReason.ToString()); await AcknowledgeAsync(message, cancellationToken); return true; } @@ -780,7 +783,7 @@ public bool Requeue(Message message, TimeSpan? delay = null) private void CheckHasPartitions() { if (_partitions.Count <= 0) - Log.NoPartitionsAllocated(s_logger); + Log.NoPartitionsAllocated(_logger); } @@ -805,13 +808,13 @@ private void LogOffSets() foreach (KeyValuePair pair in highestReadOffset) { var topicPartition = pair.Key; - Log.OffsetToConsumeFrom(s_logger, pair.Value.ToString(), topicPartition.Partition.Value.ToString(), topicPartition.Topic); + Log.OffsetToConsumeFrom(_logger, pair.Value.ToString(), topicPartition.Partition.Value.ToString(), topicPartition.Topic); } } catch (KafkaException ke) { //This is only login for debug, so skip errors here - Log.KafkaErrorLoggingOffsets(s_logger, ke.Message); + Log.KafkaErrorLoggingOffsets(_logger, ke.Message); } } @@ -844,12 +847,12 @@ private void CommitOffsets() } - if (s_logger.IsEnabled(LogLevel.Information)) + if (_logger.IsEnabled(LogLevel.Information)) { var offsets = listOffsets.Select(tpo => $"Topic: {tpo.Topic} Partition: {tpo.Partition.Value} Offset: {tpo.Offset.Value}"); var offsetAsString = string.Join(Environment.NewLine, offsets); - Log.CommittingOffsets(s_logger, Environment.NewLine, offsetAsString); + Log.CommittingOffsets(_logger, Environment.NewLine, offsetAsString); } _consumer.Commit(listOffsets); @@ -857,7 +860,7 @@ private void CommitOffsets() catch(Exception ex) { //may happen if the consumer is not valid when the thread runs - Log.ErrorCommittingOffsetsAsWarning(s_logger, ex.Message); + Log.ErrorCommittingOffsetsAsWarning(_logger, ex.Message); } finally { @@ -876,7 +879,7 @@ private void CommitOffsetsFor(List revokedPartitions) //wait for any in-flight background commit to finish before we commit revoked offsets if (!_flushToken.Wait(s_commitSyncTimeout)) { - Log.SkippedCommittingOffsetsForRevokedPartitions(s_logger); + Log.SkippedCommittingOffsetsForRevokedPartitions(_logger); return; } @@ -908,7 +911,7 @@ private void CommitOffsetsFor(List revokedPartitions) } catch (KafkaException error) { - Log.ErrorCommittingOffsetsDuringPartitionRevoke(s_logger, error.Message, error.Error.Code, error.Error.Reason, error.Error.IsFatal); + Log.ErrorCommittingOffsetsDuringPartitionRevoke(_logger, error.Message, error.Error.Code, error.Error.Reason, error.Error.IsFatal); } } @@ -916,10 +919,10 @@ private void CommitOffsetsFor(List revokedPartitions) [DebuggerStepThrough] private void LogOffSetCommitRevokedPartitions(List revokedOffsetsToCommit) { - Log.SavingRevokedPartitionOffsets(s_logger, revokedOffsetsToCommit.Count); + Log.SavingRevokedPartitionOffsets(_logger, revokedOffsetsToCommit.Count); foreach (var offset in revokedOffsetsToCommit) { - Log.SavingRevokedPartitionOffset(s_logger, offset.Offset.Value.ToString(), offset.Partition.Value.ToString(), offset.Topic); + Log.SavingRevokedPartitionOffset(_logger, offset.Offset.Value.ToString(), offset.Partition.Value.ToString(), offset.Topic); } } @@ -940,12 +943,12 @@ private void CommitAllOffsets(DateTime flushTime) } - if (s_logger.IsEnabled(LogLevel.Information) && listOffsets.Count != 0) + if (_logger.IsEnabled(LogLevel.Information) && listOffsets.Count != 0) { var offsets = listOffsets.Select(tpo => $"Topic: {tpo.Topic} Partition: {tpo.Partition.Value} Offset: {tpo.Offset.Value}"); var offsetAsString = string.Join(Environment.NewLine, offsets); - Log.SweepingOffsets(s_logger, Environment.NewLine, offsetAsString); + Log.SweepingOffsets(_logger, Environment.NewLine, offsetAsString); } _consumer.Commit(listOffsets); @@ -987,13 +990,13 @@ private void EnsureRequeueProducer() try { - var producer = new KafkaMessageProducer(_configuration, publication); + var producer = new KafkaMessageProducer(_configuration, publication, loggerFactory: _loggerFactory); producer.Init(); return producer; } catch (Exception e) { - logError?.Invoke(s_logger, e); + logError?.Invoke(_logger, e); return null; } } @@ -1027,7 +1030,7 @@ private void AcknowledgeOffset(TopicPartitionOffset? partitionOffset) var offset = new TopicPartitionOffset(partitionOffset.TopicPartition, new Offset(partitionOffset.Offset + 1)); - Log.StoringOffset(s_logger, new Offset(partitionOffset.Offset + 1).Value, partitionOffset.TopicPartition.Topic, partitionOffset.TopicPartition.Partition.Value); + Log.StoringOffset(_logger, new Offset(partitionOffset.Offset + 1).Value, partitionOffset.TopicPartition.Topic, partitionOffset.TopicPartition.Partition.Value); _offsetStorage.Add(offset); if (_offsetStorage.Count % _maxBatchSize == 0) @@ -1118,7 +1121,7 @@ private void FlushOffsets() } else { - Log.SkippedCommittingOffsets(s_logger); + Log.SkippedCommittingOffsets(_logger); } } @@ -1150,7 +1153,7 @@ private void SweepOffsets() } else { - Log.SkippedSweepingOffsets(s_logger); + Log.SkippedSweepingOffsets(_logger); } } diff --git a/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageConsumerFactory.cs b/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageConsumerFactory.cs index e89a943873..1fee74fb64 100644 --- a/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageConsumerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageConsumerFactory.cs @@ -21,16 +21,19 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #endregion +using Microsoft.Extensions.Logging; + namespace Paramore.Brighter.MessagingGateway.Kafka { /// /// - /// A factory for creating a Kafka message consumer from a > + /// A factory for creating a Kafka message consumer from a > /// - + public class KafkaMessageConsumerFactory : IAmAMessageConsumerFactory { private readonly KafkaMessagingGatewayConfiguration _configuration; + private readonly ILoggerFactory? _loggerFactory; private IAmAMessageScheduler? _scheduler; /// @@ -48,13 +51,16 @@ public IAmAMessageScheduler? Scheduler /// /// The used to connect to the Broker /// The optional message scheduler for delayed requeue support + /// The used to create loggers for the consumers public KafkaMessageConsumerFactory( KafkaMessagingGatewayConfiguration configuration, - IAmAMessageScheduler? scheduler = null + IAmAMessageScheduler? scheduler = null, + ILoggerFactory? loggerFactory = null ) { _configuration = configuration; _scheduler = scheduler; + _loggerFactory = loggerFactory; } /// @@ -102,7 +108,8 @@ public IAmAMessageConsumerSync Create(Subscription subscription) deadLetterRoutingKey: deadLetterRoutingKey, invalidMessageRoutingKey: invalidMessageRoutingKey, timeProvider: kafkaSubscription.TimeProvider, - scheduler: _scheduler + scheduler: _scheduler, + loggerFactory: _loggerFactory ); } diff --git a/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageCreator.cs b/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageCreator.cs index 949ffbea7d..66e8f19815 100644 --- a/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageCreator.cs +++ b/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageCreator.cs @@ -30,8 +30,8 @@ THE SOFTWARE. */ using System.Text; using Confluent.Kafka; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Extensions; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; namespace Paramore.Brighter.MessagingGateway.Kafka @@ -45,7 +45,12 @@ namespace Paramore.Brighter.MessagingGateway.Kafka /// public partial class KafkaMessageCreator { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; + + public KafkaMessageCreator(ILogger? logger = null) + { + _logger = logger ?? NullLogger.Instance; + } private sealed class MessageHeaderResults { @@ -72,17 +77,17 @@ public Message CreateMessage(ConsumeResult consumeResult) { try { - var headerResults = KafkaMessageCreator.ReadAllHeaders(consumeResult); + var headerResults = ReadAllHeaders(consumeResult); return CreateMessageFromHeaders(headerResults, consumeResult); } catch (Exception e) { - Log.FailedToCreateMessageFromKafkaOffset(s_logger, e); + Log.FailedToCreateMessageFromKafkaOffset(_logger, e); return Message.FailureMessage(RoutingKey.Empty, Id.Empty); } } - private static MessageHeaderResults ReadAllHeaders(ConsumeResult consumeResult) + private MessageHeaderResults ReadAllHeaders(ConsumeResult consumeResult) { var result = new MessageHeaderResults { @@ -122,7 +127,7 @@ private Message CreateMessageFromHeaders(MessageHeaderResults headers, ConsumeRe return message; } - private static Message SuccessMessage(MessageHeaderResults headers, ConsumeResult consumeResult) + private Message SuccessMessage(MessageHeaderResults headers, ConsumeResult consumeResult) { var messageHeader = new MessageHeader( messageId: (headers.MessageId.Success ? headers.MessageId.Result : Id.Empty)!, @@ -158,7 +163,7 @@ private void AddCustomHeaders(Message message, Headers headers) headers.Each(header => ReadBagEntry(header, message)); } - private static HeaderResult ReadContentType(Headers headers) + private HeaderResult ReadContentType(Headers headers) { var contentType = ReadHeader(headers, HeaderNames.CLOUD_EVENTS_DATA_CONTENT_TYPE, true); @@ -172,14 +177,14 @@ private void AddCustomHeaders(Message message, Headers headers) return new HeaderResult(null, false); } - private static HeaderResult ReadCorrelationId(Headers headers) + private HeaderResult ReadCorrelationId(Headers headers) { return ReadHeader(headers, HeaderNames.CORRELATION_ID) .Map(correlationId => { if (string.IsNullOrEmpty(correlationId)) { - Log.NoCorrelationIdFoundInMessage(s_logger); + Log.NoCorrelationIdFoundInMessage(_logger); return new HeaderResult(Id.Empty, true); } @@ -187,14 +192,14 @@ private void AddCustomHeaders(Message message, Headers headers) }); } - private static HeaderResult ReadDelay(Headers headers) + private HeaderResult ReadDelay(Headers headers) { return ReadHeader(headers, HeaderNames.DELAYED_MILLISECONDS) .Map(s => { if (string.IsNullOrEmpty(s)) { - Log.NoDelayMillisecondsFoundInMessage(s_logger); + Log.NoDelayMillisecondsFoundInMessage(_logger); return new HeaderResult(TimeSpan.Zero, true); } @@ -203,19 +208,19 @@ private static HeaderResult ReadDelay(Headers headers) return new HeaderResult(TimeSpan.FromMilliseconds(delayMilliseconds), true); } - Log.CouldNotParseMessageDelayMilliseconds(s_logger, s!); + Log.CouldNotParseMessageDelayMilliseconds(_logger, s!); return new HeaderResult(TimeSpan.Zero, false); }); } - private static HeaderResult ReadHandledCount(Headers headers) + private HeaderResult ReadHandledCount(Headers headers) { return ReadHeader(headers, HeaderNames.HANDLED_COUNT) .Map(s => { if (string.IsNullOrEmpty(s)) { - Log.NoHandledCountFoundInMessage(s_logger); + Log.NoHandledCountFoundInMessage(_logger); return new HeaderResult(0, true); } @@ -224,19 +229,19 @@ private static HeaderResult ReadHandledCount(Headers headers) return new HeaderResult(handledCount, true); } - Log.CouldNotParseMessageHandledCount(s_logger, s!); + Log.CouldNotParseMessageHandledCount(_logger, s!); return new HeaderResult(0, false); }); } - private static HeaderResult ReadReplyTo(Headers headers) + private HeaderResult ReadReplyTo(Headers headers) { return ReadHeader(headers, HeaderNames.REPLY_TO) .Map(s => { if (string.IsNullOrEmpty(s)) { - Log.NoReplyToFoundInMessage(s_logger); + Log.NoReplyToFoundInMessage(_logger); return new HeaderResult(RoutingKey.Empty, true); } @@ -244,7 +249,7 @@ private static HeaderResult ReadHandledCount(Headers headers) }); } - private static HeaderResult ReadTimeStamp(Headers headers) + private HeaderResult ReadTimeStamp(Headers headers) { if (headers.TryGetLastBytesIgnoreCase(HeaderNames.TIMESTAMP, out var lastHeader)) { @@ -270,7 +275,7 @@ private static HeaderResult ReadTimeStamp(Headers headers) : new HeaderResult(DateTimeOffset.UtcNow, true)); } - private static HeaderResult ReadMessageType(Headers headers) + private HeaderResult ReadMessageType(Headers headers) { return ReadHeader(headers, HeaderNames.MESSAGE_TYPE) .Map(s => @@ -285,12 +290,12 @@ private static HeaderResult ReadMessageType(Headers headers) }); } - private static HeaderResult ReadTopic(string topic) + private HeaderResult ReadTopic(string topic) { return new HeaderResult(new RoutingKey(topic), true); } - private static HeaderResult ReadMessageId(Headers headers) + private HeaderResult ReadMessageId(Headers headers) { var id = ReadHeader(headers, HeaderNames.CLOUD_EVENTS_ID, true) .Map(messageId => new HeaderResult(string.IsNullOrEmpty(messageId) ? Id.Random() : Id.Create(messageId), true)); @@ -306,7 +311,7 @@ private static HeaderResult ReadTopic(string topic) { if (string.IsNullOrEmpty(messageId)) { - Log.NoMessageIdFoundInMessage(s_logger, newMessageId); + Log.NoMessageIdFoundInMessage(_logger, newMessageId); return new HeaderResult(Id.Random(), true); } @@ -314,7 +319,7 @@ private static HeaderResult ReadTopic(string topic) }); } - private static HeaderResult ReadPartitionKey(Message message) + private HeaderResult ReadPartitionKey(Message message) { var pKey = ReadHeader(message.Headers, HeaderNames.PARTITIONKEY) @@ -322,7 +327,7 @@ private static HeaderResult ReadTopic(string topic) { if (string.IsNullOrEmpty(s)) { - Log.NoPartitionKeyFoundInMessage(s_logger); + Log.NoPartitionKeyFoundInMessage(_logger); return new HeaderResult(PartitionKey.Empty, false); } @@ -346,28 +351,28 @@ private static HeaderResult ReadTopic(string topic) return new HeaderResult(PartitionKey.Empty, false); } - private static HeaderResult ReadSubject(Headers headers) + private HeaderResult ReadSubject(Headers headers) => ReadHeader(headers, HeaderNames.CLOUD_EVENTS_SUBJECT); - private static HeaderResult ReadType(Headers headers) + private HeaderResult ReadType(Headers headers) => ReadHeader(headers, HeaderNames.CLOUD_EVENTS_TYPE) .Map(x =>x is not null ? new HeaderResult(new CloudEventsType(x), true) : new HeaderResult(CloudEventsType.Empty, true)); - private static HeaderResult ReadDataSchema(Headers headers) => + private HeaderResult ReadDataSchema(Headers headers) => ReadHeader(headers, HeaderNames.CLOUD_EVENTS_DATA_SCHEMA, true) .Map(x => Uri.TryCreate(x, UriKind.RelativeOrAbsolute, out var dataSchema) ? new HeaderResult(dataSchema, true) : new HeaderResult(null, false)); - private static HeaderResult ReadSource(Headers headers) => + private HeaderResult ReadSource(Headers headers) => ReadHeader(headers, HeaderNames.CLOUD_EVENTS_SOURCE) .Map(x => Uri.TryCreate(x, UriKind.RelativeOrAbsolute, out var dataSchema) ? new HeaderResult(dataSchema, true) : new HeaderResult(new Uri("http://goparamore.io"), true)); - private static HeaderResult ReadTraceParent(Headers headers) + private HeaderResult ReadTraceParent(Headers headers) { return ReadHeader(headers, HeaderNames.CLOUD_EVENTS_TRACE_PARENT) .Map(s => @@ -381,7 +386,7 @@ private static HeaderResult ReadTopic(string topic) }); } - private static HeaderResult ReadTraceState(Headers headers) + private HeaderResult ReadTraceState(Headers headers) { return ReadHeader(headers, HeaderNames.CLOUD_EVENTS_TRACE_STATE) .Map(s => @@ -395,7 +400,7 @@ private static HeaderResult ReadTopic(string topic) }); } - private static HeaderResult ReadBaggage(Headers headers) + private HeaderResult ReadBaggage(Headers headers) { return ReadHeader(headers, HeaderNames.W3C_BAGGAGE) .Map(s => @@ -411,7 +416,7 @@ private static HeaderResult ReadTopic(string topic) }); } - private static HeaderResult ReadHeader(Headers headers, string key, bool dieOnMissing = false) + private HeaderResult ReadHeader(Headers headers, string key, bool dieOnMissing = false) { if (headers.TryGetLastBytesIgnoreCase(key, out byte[]? lastHeader)) { @@ -423,7 +428,7 @@ private static HeaderResult ReadTopic(string topic) catch (Exception e) { var firstTwentyBytes = BitConverter.ToString(lastHeader!.Take(20).ToArray()); - Log.FailedToReadTheValueOfHeader(s_logger, e, key, firstTwentyBytes); + Log.FailedToReadTheValueOfHeader(_logger, e, key, firstTwentyBytes); return new HeaderResult(null, false); } } diff --git a/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageProducer.cs b/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageProducer.cs index f576b618fb..1acbefe4ed 100644 --- a/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageProducer.cs +++ b/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageProducer.cs @@ -61,9 +61,11 @@ public partial class KafkaMessageProducer : KafkaMessagingGateway, IAmAMessagePr private readonly InstrumentationOptions _instrumentation; public KafkaMessageProducer( - KafkaMessagingGatewayConfiguration configuration, + KafkaMessagingGatewayConfiguration configuration, KafkaPublication publication, - InstrumentationOptions instrumentation = InstrumentationOptions.All) + InstrumentationOptions instrumentation = InstrumentationOptions.All, + ILoggerFactory? loggerFactory = null) + : base(loggerFactory) { if (publication is null) throw new ArgumentNullException(nameof(publication)); @@ -178,9 +180,9 @@ public void Init() _hasFatalProducerError = error.IsFatal; if (_hasFatalProducerError) - Log.FatalProducerError(s_logger, error.Code, error.Reason, true); + Log.FatalProducerError(_logger, error.Code, error.Reason, true); else - Log.NonFatalProducerError(s_logger, error.Code, error.Reason, false); + Log.NonFatalProducerError(_logger, error.Code, error.Reason, false); }) .Build(); @@ -256,29 +258,29 @@ public void SendWithDelay(Message message, TimeSpan? delay = null) try { BrighterTracer.WriteProducerEvent(Span, MessagingSystem.Kafka, message, _instrumentation); - Log.SendingMessageToKafka(s_logger, _producerConfig.BootstrapServers, message.Header.Topic.Value, message.Body.Value); + Log.SendingMessageToKafka(_logger, _producerConfig.BootstrapServers, message.Header.Topic.Value, message.Body.Value); _publisher.PublishMessage(message, report => PublishResults(report.Status, report.Headers)); } catch (ProduceException pe) { - Log.ErrorSendingMessageToKafka(s_logger, pe, _producerConfig.BootstrapServers, pe.Error.Reason); + Log.ErrorSendingMessageToKafka(_logger, pe, _producerConfig.BootstrapServers, pe.Error.Reason); throw new ChannelFailureException("Error talking to the broker, see inner exception for details", pe); } catch (InvalidOperationException ioe) { - Log.ErrorSendingMessageToKafka(s_logger, ioe, _producerConfig.BootstrapServers, ioe.Message); + Log.ErrorSendingMessageToKafka(_logger, ioe, _producerConfig.BootstrapServers, ioe.Message); throw new ChannelFailureException("Error talking to the broker, see inner exception for details", ioe); } catch (ArgumentException ae) { - Log.ErrorSendingMessageToKafka(s_logger, ae, _producerConfig.BootstrapServers, ae.Message); + Log.ErrorSendingMessageToKafka(_logger, ae, _producerConfig.BootstrapServers, ae.Message); throw new ChannelFailureException("Error talking to the broker, see inner exception for details", ae); } catch (KafkaException kafkaException) { - Log.KafkaExceptionError(s_logger, kafkaException, Topic?.Value ?? RoutingKey.Empty.Value); + Log.KafkaExceptionError(_logger, kafkaException, Topic?.Value ?? RoutingKey.Empty.Value); if (kafkaException.Error.IsFatal) //this can't be recovered and requires a new producer throw; @@ -329,24 +331,24 @@ public async Task SendWithDelayAsync(Message message, TimeSpan? delay, Cancellat try { BrighterTracer.WriteProducerEvent(Span, MessagingSystem.Kafka, message, _instrumentation); - Log.SendingMessageToKafka(s_logger, _producerConfig.BootstrapServers, message.Header.Topic.Value, message.Body.Value); + Log.SendingMessageToKafka(_logger, _producerConfig.BootstrapServers, message.Header.Topic.Value, message.Body.Value); await _publisher.PublishMessageAsync(message, result => PublishResults(result.Status, result.Headers), cancellationToken); } catch (ProduceException pe) { - Log.ErrorSendingMessageToKafka(s_logger, pe, _producerConfig.BootstrapServers, pe.Error.Reason); + Log.ErrorSendingMessageToKafka(_logger, pe, _producerConfig.BootstrapServers, pe.Error.Reason); throw new ChannelFailureException("Error talking to the broker, see inner exception for details", pe); } catch (InvalidOperationException ioe) { - Log.ErrorSendingMessageToKafka(s_logger, ioe, _producerConfig.BootstrapServers, ioe.Message); + Log.ErrorSendingMessageToKafka(_logger, ioe, _producerConfig.BootstrapServers, ioe.Message); throw new ChannelFailureException("Error talking to the broker, see inner exception for details", ioe); } catch (ArgumentException ae) { - Log.ErrorSendingMessageToKafka(s_logger, ae, _producerConfig.BootstrapServers, ae.Message); + Log.ErrorSendingMessageToKafka(_logger, ae, _producerConfig.BootstrapServers, ae.Message); throw new ChannelFailureException("Error talking to the broker, see inner exception for details", ae); } diff --git a/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageProducerFactory.cs b/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageProducerFactory.cs index 692afe6fca..9e54c3e75d 100644 --- a/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageProducerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessageProducerFactory.cs @@ -25,6 +25,7 @@ THE SOFTWARE. */ using System.Collections.Generic; using System.Threading.Tasks; using Confluent.Kafka; +using Microsoft.Extensions.Logging; namespace Paramore.Brighter.MessagingGateway.Kafka { @@ -36,22 +37,26 @@ public class KafkaMessageProducerFactory : IAmAMessageProducerFactory { private readonly KafkaMessagingGatewayConfiguration _globalConfiguration; private readonly IEnumerable _publications; + private readonly ILoggerFactory? _loggerFactory; private Action? _configHook; /// /// This constructs a which can be used to create a dictionary of /// instances indexed by topic name. - /// It takes a dependency on a to connect to the broker, and a collection of + /// It takes a dependency on a to connect to the broker, and a collection of /// instances that determine how we publish to Kafka and the parameters of any topics if required. /// /// Configures how we connect to the broker /// The list of topics that we want to publish to + /// The used to create loggers for the producers public KafkaMessageProducerFactory( - KafkaMessagingGatewayConfiguration globalConfiguration, - IEnumerable publications) + KafkaMessagingGatewayConfiguration globalConfiguration, + IEnumerable publications, + ILoggerFactory? loggerFactory = null) { _globalConfiguration = globalConfiguration; _publications = publications; + _loggerFactory = loggerFactory; _configHook = null; } @@ -65,7 +70,7 @@ public Dictionary Create() foreach (var publication in _publications) { if (publication.Topic is null) continue; - var producer = new KafkaMessageProducer(_globalConfiguration, publication); + var producer = new KafkaMessageProducer(_globalConfiguration, publication, loggerFactory: _loggerFactory); if (_configHook != null) producer.ConfigHook(_configHook); producer.Init(); diff --git a/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessagingGateway.cs b/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessagingGateway.cs index 47ceffa079..3bfe04530f 100644 --- a/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessagingGateway.cs +++ b/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaMessagingGateway.cs @@ -29,7 +29,7 @@ THE SOFTWARE. */ using Confluent.Kafka; using Confluent.Kafka.Admin; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Tasks; namespace Paramore.Brighter.MessagingGateway.Kafka @@ -41,7 +41,8 @@ namespace Paramore.Brighter.MessagingGateway.Kafka /// public partial class KafkaMessagingGateway { - protected static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + protected readonly ILogger _logger; + protected readonly ILoggerFactory? _loggerFactory; protected ClientConfig? ClientConfig; protected OnMissingChannel MakeChannels; protected RoutingKey? Topic; @@ -49,6 +50,16 @@ public partial class KafkaMessagingGateway protected short ReplicationFactor; protected TimeSpan TopicFindTimeout; + /// + /// Initializes a new instance of the class. + /// + /// The used to create loggers for the gateway and any producers it creates. + protected KafkaMessagingGateway(ILoggerFactory? loggerFactory = null) + { + _loggerFactory = loggerFactory; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + } + /// /// Ensure that the topic exists, behaviour based on the MakeChannels flag of the publication /// Sync over async, but alright as we in topic creation @@ -99,7 +110,7 @@ await adminClient.CreateTopicsAsync(new List $"An error occured creating topic {Topic.Value}: {e.Results[0].Error.Reason}"); } - Log.TopicAlreadyExists(s_logger, Topic.Value); + Log.TopicAlreadyExists(_logger, Topic.Value); } } @@ -153,13 +164,13 @@ private bool FindTopic() $"topic is misconfigured => ReplicationFactor should be {ReplicationFactor} but is {matchingTopic.Partitions[0].Replicas.Length};"; } - Log.TopicMisconfiguredWarning(s_logger, error); + Log.TopicMisconfiguredWarning(_logger, error); } } } if (found) - Log.TopicExists(s_logger, Topic.Value); + Log.TopicExists(_logger, Topic.Value); return found; } diff --git a/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaProducerRegistryFactory.cs b/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaProducerRegistryFactory.cs index 91e799bf51..9f7654ec18 100644 --- a/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaProducerRegistryFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.Kafka/KafkaProducerRegistryFactory.cs @@ -26,6 +26,7 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Confluent.Kafka; +using Microsoft.Extensions.Logging; namespace Paramore.Brighter.MessagingGateway.Kafka { @@ -37,6 +38,7 @@ public class KafkaProducerRegistryFactory : IAmAProducerRegistryFactory { private readonly KafkaMessagingGatewayConfiguration _globalConfiguration; private readonly IEnumerable _publications; + private readonly ILoggerFactory? _loggerFactory; private Action? _configHook; /// @@ -46,15 +48,18 @@ public class KafkaProducerRegistryFactory : IAmAProducerRegistryFactory /// /// Configures how we connect to the broker /// The list of topics that we want to publish to + /// The used to create loggers for the producers public KafkaProducerRegistryFactory( - KafkaMessagingGatewayConfiguration globalConfiguration, - IEnumerable publications) + KafkaMessagingGatewayConfiguration globalConfiguration, + IEnumerable publications, + ILoggerFactory? loggerFactory = null) { _globalConfiguration = globalConfiguration; _publications = publications; + _loggerFactory = loggerFactory; _configHook = null; } - + /// /// Create a producer registry from the and instances supplied /// to the constructor @@ -62,7 +67,7 @@ public KafkaProducerRegistryFactory( /// An that represents a collection of Kafka Message Producers public IAmAProducerRegistry Create() { - var producerFactory = new KafkaMessageProducerFactory(_globalConfiguration, _publications); + var producerFactory = new KafkaMessageProducerFactory(_globalConfiguration, _publications, _loggerFactory); producerFactory.SetConfigHook(_configHook); return new ProducerRegistry(producerFactory.Create()); diff --git a/src/Paramore.Brighter.MessagingGateway.MQTT/MQTTMessageConsumer.cs b/src/Paramore.Brighter.MessagingGateway.MQTT/MQTTMessageConsumer.cs index 45adfb7cfe..8b876b1e7d 100644 --- a/src/Paramore.Brighter.MessagingGateway.MQTT/MQTTMessageConsumer.cs +++ b/src/Paramore.Brighter.MessagingGateway.MQTT/MQTTMessageConsumer.cs @@ -5,12 +5,12 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using MQTTnet; using MQTTnet.Client; using MQTTnet.Packets; using MQTTnet.Protocol; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; namespace Paramore.Brighter.MessagingGateway.MQTT @@ -25,7 +25,8 @@ public partial class MqttMessageConsumer : IAmAMessageConsumerSync, IAmAMessageC private readonly string _topic; private readonly MqttMessagingGatewayConsumerConfiguration _configuration; private readonly ConcurrentQueue _messageQueue = new(); - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; + private readonly ILoggerFactory? _loggerFactory; private readonly Message _noopMessage = new(); private readonly IMqttClient _mqttClient; private readonly MqttClientOptions _mqttClientOptions; @@ -51,6 +52,7 @@ public partial class MqttMessageConsumer : IAmAMessageConsumerSync, IAmAMessageC /// /// The routing key for the dead letter queue, if using Brighter-managed DLQ. /// The routing key for the invalid message queue, if using Brighter-managed invalid message handling. + /// The used to create loggers for this consumer and the producers it creates. /// /// Thrown when the is null. /// @@ -65,8 +67,11 @@ public MqttMessageConsumer( MqttMessagingGatewayConsumerConfiguration configuration, IAmAMessageScheduler? scheduler = null, RoutingKey? deadLetterRoutingKey = null, - RoutingKey? invalidMessageRoutingKey = null) + RoutingKey? invalidMessageRoutingKey = null, + ILoggerFactory? loggerFactory = null) { + _loggerFactory = loggerFactory; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); _configuration = configuration; _scheduler = scheduler; _deadLetterRoutingKey = deadLetterRoutingKey; @@ -106,7 +111,7 @@ public MqttMessageConsumer( _mqttClient.ApplicationMessageReceivedAsync += e => { - Log.MqttMessageConsumerReceivedMessage(s_logger, configuration.TopicPrefix); + Log.MqttMessageConsumerReceivedMessage(_logger, configuration.TopicPrefix); var message = JsonSerializer.Deserialize(e.ApplicationMessage.PayloadSegment.ToArray(), JsonSerialisationOptions.Options); _messageQueue.Enqueue(message!); @@ -234,14 +239,14 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) try { producer.Send(message); - Log.MessageSentToRejectionChannel(s_logger, message.Id.Value, routingKey.Value); + Log.MessageSentToRejectionChannel(_logger, message.Id.Value, routingKey.Value); } catch (Exception ex) { // DLQ send failed — MQTT fire-and-forget model means the source message // only exists in memory and cannot be requeued. Return true to prevent // requeue loops (per ADR 0034). - Log.ErrorSendingToRejectionChannel(s_logger, ex, message.Id.Value, routingKey.Value); + Log.ErrorSendingToRejectionChannel(_logger, ex, message.Id.Value, routingKey.Value); return true; } @@ -263,14 +268,14 @@ public async Task RejectAsync(Message message, MessageRejectionReason? rea try { await producer.SendAsync(message, cancellationToken); - Log.MessageSentToRejectionChannel(s_logger, message.Id.Value, routingKey.Value); + Log.MessageSentToRejectionChannel(_logger, message.Id.Value, routingKey.Value); } catch (Exception ex) { // DLQ send failed — MQTT fire-and-forget model means the source message // only exists in memory and cannot be requeued. Return true to prevent // requeue loops (per ADR 0034). - Log.ErrorSendingToRejectionChannel(s_logger, ex, message.Id.Value, routingKey.Value); + Log.ErrorSendingToRejectionChannel(_logger, ex, message.Id.Value, routingKey.Value); return true; } @@ -281,7 +286,7 @@ public async Task RejectAsync(Message message, MessageRejectionReason? rea { if (_deadLetterProducer == null && _invalidMessageProducer == null) { - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value); return (null, null); } @@ -290,11 +295,11 @@ public async Task RejectAsync(Message message, MessageRejectionReason? rea var (routingKey, hasProducer, isFallingBackToDlq) = DetermineRejectionRoute(reason); if (isFallingBackToDlq) - Log.FallingBackToDlq(s_logger, message.Id.Value); + Log.FallingBackToDlq(_logger, message.Id.Value); if (!hasProducer) { - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value); return (null, null); } @@ -378,7 +383,7 @@ private void EnsureRequeueProducer() CleanSession = _configuration.CleanSession, Username = _configuration.Username, Password = _configuration.Password - }); + }, _loggerFactory); return new MqttMessageProducer(publisher, new Publication()) { Scheduler = _scheduler @@ -431,12 +436,12 @@ private static void RefreshMetadata(Message message, MessageRejectionReason? rea ClientID = string.IsNullOrEmpty(_configuration.ClientID) ? null : $"{_configuration.ClientID}-dlq", TopicPrefix = _deadLetterRoutingKey.Value }; - var publisher = new MqttMessagePublisher(config); + var publisher = new MqttMessagePublisher(config, _loggerFactory); return new MqttMessageProducer(publisher, new Publication { Topic = _deadLetterRoutingKey }); } catch (Exception ex) { - Log.ErrorCreatingDlqProducer(s_logger, ex, _deadLetterRoutingKey.Value); + Log.ErrorCreatingDlqProducer(_logger, ex, _deadLetterRoutingKey.Value); return null; } } @@ -457,12 +462,12 @@ private static void RefreshMetadata(Message message, MessageRejectionReason? rea ClientID = string.IsNullOrEmpty(_configuration.ClientID) ? null : $"{_configuration.ClientID}-invalid", TopicPrefix = _invalidMessageRoutingKey.Value }; - var publisher = new MqttMessagePublisher(config); + var publisher = new MqttMessagePublisher(config, _loggerFactory); return new MqttMessageProducer(publisher, new Publication { Topic = _invalidMessageRoutingKey }); } catch (Exception ex) { - Log.ErrorCreatingInvalidMessageProducer(s_logger, ex, _invalidMessageRoutingKey.Value); + Log.ErrorCreatingInvalidMessageProducer(_logger, ex, _invalidMessageRoutingKey.Value); return null; } } @@ -474,16 +479,16 @@ private async Task Connect(int connectionAttempts) try { await _mqttClient.ConnectAsync(_mqttClientOptions, CancellationToken.None); - Log.MqttConsumerClientConnected(s_logger); + Log.MqttConsumerClientConnected(_logger); await _mqttClient.SubscribeAsync(new MqttTopicFilter { Topic = _topic, QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce }); - Log.SubscribedToTopic(s_logger, _topic); + Log.SubscribedToTopic(_logger, _topic); return; } catch (Exception ex) { - Log.UnableToConnectMqttConsumerClient(s_logger, ex); + Log.UnableToConnectMqttConsumerClient(_logger, ex); } } } diff --git a/src/Paramore.Brighter.MessagingGateway.MQTT/MQTTMessagePublisher.cs b/src/Paramore.Brighter.MessagingGateway.MQTT/MQTTMessagePublisher.cs index 00013ac5ea..d25a495451 100644 --- a/src/Paramore.Brighter.MessagingGateway.MQTT/MQTTMessagePublisher.cs +++ b/src/Paramore.Brighter.MessagingGateway.MQTT/MQTTMessagePublisher.cs @@ -3,11 +3,11 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using MQTTnet; using MQTTnet.Client; using MQTTnet.Protocol; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Paramore.Brighter.Tasks; namespace Paramore.Brighter.MessagingGateway.MQTT @@ -17,7 +17,7 @@ namespace Paramore.Brighter.MessagingGateway.MQTT /// public partial class MqttMessagePublisher : IDisposable, IAsyncDisposable { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private readonly MqttMessagingGatewayConfiguration _config; private readonly IMqttClient _mqttClient; private readonly MqttClientOptions _mqttClientOptions; @@ -27,8 +27,10 @@ public partial class MqttMessagePublisher : IDisposable, IAsyncDisposable /// Sync over async, but necessary as we are in the ctor /// /// The Publisher configuration. - public MqttMessagePublisher(MqttMessagingGatewayConfiguration config) + /// The used to create the logger. + public MqttMessagePublisher(MqttMessagingGatewayConfiguration config, ILoggerFactory? loggerFactory = null) { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); _config = config; _mqttClient = new MqttFactory().CreateMqttClient(); @@ -113,12 +115,12 @@ private async Task ConnectAsync() try { await _mqttClient.ConnectAsync(_mqttClientOptions, CancellationToken.None); - Log.ConnectedToHost(s_logger, _config.Hostname, _config.Port); + Log.ConnectedToHost(_logger, _config.Hostname, _config.Port); return; } catch (Exception) { - Log.UnableToConnectToHost(s_logger, _config.Hostname!, _config.Port); + Log.UnableToConnectToHost(_logger, _config.Hostname!, _config.Port); } } } diff --git a/src/Paramore.Brighter.MessagingGateway.MQTT/MqttMessageConsumerFactory.cs b/src/Paramore.Brighter.MessagingGateway.MQTT/MqttMessageConsumerFactory.cs index 3790452423..ec328b56c4 100644 --- a/src/Paramore.Brighter.MessagingGateway.MQTT/MqttMessageConsumerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.MQTT/MqttMessageConsumerFactory.cs @@ -22,6 +22,8 @@ THE SOFTWARE. */ #endregion +using Microsoft.Extensions.Logging; + namespace Paramore.Brighter.MessagingGateway.MQTT { /// @@ -30,6 +32,7 @@ namespace Paramore.Brighter.MessagingGateway.MQTT public class MqttMessageConsumerFactory : IAmAMessageConsumerFactory { private readonly MqttMessagingGatewayConsumerConfiguration _configuration; + private readonly ILoggerFactory? _loggerFactory; private IAmAMessageScheduler? _scheduler; /// @@ -47,12 +50,15 @@ public IAmAMessageScheduler? Scheduler /// /// The MQTT consumer configuration containing broker connection details. /// The optional message scheduler for delayed requeue support + /// The used to create loggers for the consumers. public MqttMessageConsumerFactory( MqttMessagingGatewayConsumerConfiguration configuration, - IAmAMessageScheduler? scheduler = null) + IAmAMessageScheduler? scheduler = null, + ILoggerFactory? loggerFactory = null) { _configuration = configuration; _scheduler = scheduler; + _loggerFactory = loggerFactory; } /// @@ -65,7 +71,7 @@ public IAmAMessageConsumerSync Create(Subscription subscription) var deadLetterRoutingKey = (subscription as IUseBrighterDeadLetterSupport)?.DeadLetterRoutingKey; var invalidMessageRoutingKey = (subscription as IUseBrighterInvalidMessageSupport)?.InvalidMessageRoutingKey; - return new MqttMessageConsumer(_configuration, _scheduler, deadLetterRoutingKey, invalidMessageRoutingKey); + return new MqttMessageConsumer(_configuration, _scheduler, deadLetterRoutingKey, invalidMessageRoutingKey, _loggerFactory); } /// @@ -78,7 +84,7 @@ public IAmAMessageConsumerAsync CreateAsync(Subscription subscription) var deadLetterRoutingKey = (subscription as IUseBrighterDeadLetterSupport)?.DeadLetterRoutingKey; var invalidMessageRoutingKey = (subscription as IUseBrighterInvalidMessageSupport)?.InvalidMessageRoutingKey; - return new MqttMessageConsumer(_configuration, _scheduler, deadLetterRoutingKey, invalidMessageRoutingKey); + return new MqttMessageConsumer(_configuration, _scheduler, deadLetterRoutingKey, invalidMessageRoutingKey, _loggerFactory); } } } diff --git a/src/Paramore.Brighter.MessagingGateway.MsSql/ChannelFactory.cs b/src/Paramore.Brighter.MessagingGateway.MsSql/ChannelFactory.cs index 1bbfe14a2c..da55ac3f4d 100644 --- a/src/Paramore.Brighter.MessagingGateway.MsSql/ChannelFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.MsSql/ChannelFactory.cs @@ -2,7 +2,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Paramore.Brighter.MessagingGateway.MsSql; @@ -11,7 +11,7 @@ namespace Paramore.Brighter.MessagingGateway.MsSql; /// public partial class ChannelFactory : IAmAChannelFactory, IAmAChannelFactoryWithScheduler { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private readonly MsSqlMessageConsumerFactory _msSqlMessageConsumerFactory; /// @@ -28,11 +28,13 @@ public IAmAMessageScheduler? Scheduler /// Initializes a new instance of the class. /// /// The factory for creating MS SQL message consumers. + /// The optional logger; defaults to a no-op logger. /// Thrown when the msSqlMessageConsumerFactory is null. - public ChannelFactory(MsSqlMessageConsumerFactory msSqlMessageConsumerFactory) + public ChannelFactory(MsSqlMessageConsumerFactory msSqlMessageConsumerFactory, ILogger? logger = null) { _msSqlMessageConsumerFactory = msSqlMessageConsumerFactory ?? throw new ArgumentNullException(nameof(msSqlMessageConsumerFactory)); + _logger = logger ?? NullLogger.Instance; } /// @@ -47,7 +49,7 @@ public IAmAChannelSync CreateSyncChannel(Subscription subscription) if (rmqSubscription == null) throw new ConfigurationException("MS SQL ChannelFactory We expect an MsSqlSubscription or MsSqlSubscription as a parameter"); - Log.MsSqlInputChannelFactoryCreateInputChannel(s_logger, subscription.ChannelName, subscription.RoutingKey.Value); + Log.MsSqlInputChannelFactoryCreateInputChannel(_logger, subscription.ChannelName, subscription.RoutingKey.Value); return new Channel( subscription.ChannelName, subscription.RoutingKey, @@ -67,7 +69,7 @@ public IAmAChannelAsync CreateAsyncChannel(Subscription subscription) if (rmqSubscription == null) throw new ConfigurationException("MS SQL ChannelFactory We expect an MsSqlSubscription or MsSqlSubscription as a parameter"); - Log.MsSqlInputChannelFactoryCreateInputChannel(s_logger, subscription.ChannelName, subscription.RoutingKey.Value); + Log.MsSqlInputChannelFactoryCreateInputChannel(_logger, subscription.ChannelName, subscription.RoutingKey.Value); return new ChannelAsync( subscription.ChannelName, subscription.RoutingKey, @@ -89,7 +91,7 @@ public async Task CreateAsyncChannelAsync(Subscription subscri if (rmqSubscription == null) throw new ConfigurationException("MS SQL ChannelFactory We expect an MsSqlSubscription or MsSqlSubscription as a parameter"); - Log.MsSqlInputChannelFactoryCreateInputChannel(s_logger, subscription.ChannelName, subscription.RoutingKey.Value); + Log.MsSqlInputChannelFactoryCreateInputChannel(_logger, subscription.ChannelName, subscription.RoutingKey.Value); var channel = new ChannelAsync( subscription.ChannelName, subscription.RoutingKey, diff --git a/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlMessageConsumer.cs b/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlMessageConsumer.cs index 3114409b44..2210729e33 100644 --- a/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlMessageConsumer.cs +++ b/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlMessageConsumer.cs @@ -2,7 +2,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.MessagingGateway.MsSql.SqlQueues; using Paramore.Brighter.MsSql; @@ -11,7 +11,8 @@ namespace Paramore.Brighter.MessagingGateway.MsSql public partial class MsSqlMessageConsumer : IAmAMessageConsumerSync, IAmAMessageConsumerAsync { private readonly string _topic; - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; + private readonly ILoggerFactory? _loggerFactory; private readonly MsSqlMessageQueue _sqlMessageQueue; private readonly RelationalDatabaseConfiguration _msSqlConfiguration; private readonly RoutingKey? _deadLetterRoutingKey; @@ -29,11 +30,14 @@ public MsSqlMessageConsumer( RelationalDbConnectionProvider connectionProvider, IAmAMessageScheduler? scheduler = null, RoutingKey? deadLetterRoutingKey = null, - RoutingKey? invalidMessageRoutingKey = null) + RoutingKey? invalidMessageRoutingKey = null, + ILoggerFactory? loggerFactory = null) { + _loggerFactory = loggerFactory; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); _topic = topic ?? throw new ArgumentNullException(nameof(topic)); _msSqlConfiguration = msSqlConfiguration ?? throw new ArgumentNullException(nameof(msSqlConfiguration)); - _sqlMessageQueue = new MsSqlMessageQueue(msSqlConfiguration, connectionProvider); + _sqlMessageQueue = new MsSqlMessageQueue(msSqlConfiguration, connectionProvider, loggerFactory); _scheduler = scheduler; _deadLetterRoutingKey = deadLetterRoutingKey; _invalidMessageRoutingKey = invalidMessageRoutingKey; @@ -52,8 +56,9 @@ public MsSqlMessageConsumer( string topic, IAmAMessageScheduler? scheduler = null, RoutingKey? deadLetterRoutingKey = null, - RoutingKey? invalidMessageRoutingKey = null) - : this(msSqlConfiguration, topic, new MsSqlConnectionProvider(msSqlConfiguration), scheduler, deadLetterRoutingKey, invalidMessageRoutingKey) + RoutingKey? invalidMessageRoutingKey = null, + ILoggerFactory? loggerFactory = null) + : this(msSqlConfiguration, topic, new MsSqlConnectionProvider(msSqlConfiguration), scheduler, deadLetterRoutingKey, invalidMessageRoutingKey, loggerFactory) {} /// @@ -89,13 +94,13 @@ public Task NackAsync(Message message, CancellationToken cancellationToken = def /// public void Purge() { - Log.PurgingQueue(s_logger); + Log.PurgingQueue(_logger); _sqlMessageQueue.Purge(); } public async Task PurgeAsync(CancellationToken cancellationToken = default(CancellationToken)) { - Log.PurgingQueue(s_logger); + Log.PurgingQueue(_logger); await Task.Run( () => _sqlMessageQueue.Purge(), cancellationToken); } @@ -149,7 +154,7 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) if (_deadLetterProducer == null && _invalidMessageProducer == null) { if (reason != null) - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value, reason.RejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value, reason.RejectionReason.ToString()); return true; } @@ -168,7 +173,7 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) { message.Header.Topic = routingKey!; if (isFallingBackToDlq) - Log.FallingBackToDlq(s_logger, message.Id.Value); + Log.FallingBackToDlq(_logger, message.Id.Value); if (routingKey == _invalidMessageRoutingKey) producer = _invalidMessageProducer?.Value; @@ -179,11 +184,11 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) if (producer != null) { producer.Send(message); - Log.MessageSentToRejectionChannel(s_logger, message.Id.Value, rejectionReason.ToString()); + Log.MessageSentToRejectionChannel(_logger, message.Id.Value, rejectionReason.ToString()); } else { - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value, rejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value, rejectionReason.ToString()); } } catch (Exception ex) @@ -191,7 +196,7 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) // DLQ send failed — the message was already atomically deleted from the source // queue on Receive, so we cannot requeue it. Log and return true to prevent the // message pump from retrying endlessly. - Log.ErrorSendingToRejectionChannel(s_logger, ex, message.Id.Value, rejectionReason.ToString()); + Log.ErrorSendingToRejectionChannel(_logger, ex, message.Id.Value, rejectionReason.ToString()); return true; } @@ -213,7 +218,7 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) if (_deadLetterProducer == null && _invalidMessageProducer == null) { if (reason != null) - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value, reason.RejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value, reason.RejectionReason.ToString()); return true; } @@ -232,7 +237,7 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) { message.Header.Topic = routingKey!; if (isFallingBackToDlq) - Log.FallingBackToDlq(s_logger, message.Id.Value); + Log.FallingBackToDlq(_logger, message.Id.Value); if (routingKey == _invalidMessageRoutingKey) producer = _invalidMessageProducer?.Value; @@ -243,11 +248,11 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) if (producer != null) { await producer.SendAsync(message, cancellationToken); - Log.MessageSentToRejectionChannel(s_logger, message.Id.Value, rejectionReason.ToString()); + Log.MessageSentToRejectionChannel(_logger, message.Id.Value, rejectionReason.ToString()); } else { - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value, rejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value, rejectionReason.ToString()); } } catch (Exception ex) @@ -255,7 +260,7 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) // DLQ send failed — the message was already atomically deleted from the source // queue on ReceiveAsync, so we cannot requeue it. Log and return true to prevent // the message pump from retrying endlessly. - Log.ErrorSendingToRejectionChannel(s_logger, ex, message.Id.Value, rejectionReason.ToString()); + Log.ErrorSendingToRejectionChannel(_logger, ex, message.Id.Value, rejectionReason.ToString()); return true; } @@ -273,7 +278,7 @@ public bool Requeue(Message message, TimeSpan? delay = null) delay ??= TimeSpan.Zero; var topic = message.Header.Topic; - Log.RequeuingMessage(s_logger, topic.Value, message.Id.ToString()); + Log.RequeuingMessage(_logger, topic.Value, message.Id.ToString()); if (delay > TimeSpan.Zero) { @@ -298,7 +303,7 @@ public bool Requeue(Message message, TimeSpan? delay = null) delay ??= TimeSpan.Zero; var topic = message.Header.Topic; - Log.RequeuingMessage(s_logger, topic.Value, message.Id.ToString()); + Log.RequeuingMessage(_logger, topic.Value, message.Id.ToString()); if (delay > TimeSpan.Zero) { @@ -329,7 +334,7 @@ public async ValueTask DisposeAsync() private void EnsureRequeueProducer() { LazyInitializer.EnsureInitialized(ref _requeueProducer, ref _requeueProducerInitialized, - ref _requeueProducerLock, () => new MsSqlMessageProducer(_msSqlConfiguration) + ref _requeueProducerLock, () => new MsSqlMessageProducer(_msSqlConfiguration, loggerFactory: _loggerFactory) { Scheduler = _scheduler }); @@ -342,11 +347,11 @@ private void EnsureRequeueProducer() try { return new MsSqlMessageProducer(_msSqlConfiguration, - new Publication { Topic = _deadLetterRoutingKey }); + new Publication { Topic = _deadLetterRoutingKey }, _loggerFactory); } catch (Exception e) { - Log.ErrorCreatingDlqProducer(s_logger, e, _deadLetterRoutingKey.Value); + Log.ErrorCreatingDlqProducer(_logger, e, _deadLetterRoutingKey.Value); return null; } } @@ -358,11 +363,11 @@ private void EnsureRequeueProducer() try { return new MsSqlMessageProducer(_msSqlConfiguration, - new Publication { Topic = _invalidMessageRoutingKey }); + new Publication { Topic = _invalidMessageRoutingKey }, _loggerFactory); } catch (Exception e) { - Log.ErrorCreatingInvalidMessageProducer(s_logger, e, _invalidMessageRoutingKey.Value); + Log.ErrorCreatingInvalidMessageProducer(_logger, e, _invalidMessageRoutingKey.Value); return null; } } diff --git a/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlMessageConsumerFactory.cs b/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlMessageConsumerFactory.cs index 1b21ea5638..ee192392b7 100644 --- a/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlMessageConsumerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlMessageConsumerFactory.cs @@ -1,13 +1,14 @@ using System; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.MsSql; namespace Paramore.Brighter.MessagingGateway.MsSql { public partial class MsSqlMessageConsumerFactory : IAmAMessageConsumerFactory { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; + private readonly ILoggerFactory? _loggerFactory; private readonly RelationalDatabaseConfiguration _msSqlConfiguration; private IAmAMessageScheduler? _scheduler; @@ -26,10 +27,13 @@ public IAmAMessageScheduler? Scheduler /// /// The configuration for connecting to the MsSql database /// The optional message scheduler for delayed requeue support - public MsSqlMessageConsumerFactory(RelationalDatabaseConfiguration msSqlConfiguration, IAmAMessageScheduler? scheduler = null) + /// The optional used to create loggers + public MsSqlMessageConsumerFactory(RelationalDatabaseConfiguration msSqlConfiguration, IAmAMessageScheduler? scheduler = null, ILoggerFactory? loggerFactory = null) { _msSqlConfiguration = msSqlConfiguration ?? throw new ArgumentNullException(nameof(msSqlConfiguration)); _scheduler = scheduler; + _loggerFactory = loggerFactory; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } /// @@ -44,8 +48,8 @@ public IAmAMessageConsumerSync Create(Subscription subscription) var deadLetterRoutingKey = (subscription as IUseBrighterDeadLetterSupport)?.DeadLetterRoutingKey; var invalidMessageRoutingKey = (subscription as IUseBrighterInvalidMessageSupport)?.InvalidMessageRoutingKey; - Log.MsSqlMessageConsumerFactoryCreate(s_logger, subscription.ChannelName); - return new MsSqlMessageConsumer(_msSqlConfiguration, subscription.ChannelName!, _scheduler, deadLetterRoutingKey, invalidMessageRoutingKey); + Log.MsSqlMessageConsumerFactoryCreate(_logger, subscription.ChannelName); + return new MsSqlMessageConsumer(_msSqlConfiguration, subscription.ChannelName!, _scheduler, deadLetterRoutingKey, invalidMessageRoutingKey, _loggerFactory); } public IAmAMessageConsumerAsync CreateAsync(Subscription subscription) @@ -55,8 +59,8 @@ public IAmAMessageConsumerAsync CreateAsync(Subscription subscription) var deadLetterRoutingKey = (subscription as IUseBrighterDeadLetterSupport)?.DeadLetterRoutingKey; var invalidMessageRoutingKey = (subscription as IUseBrighterInvalidMessageSupport)?.InvalidMessageRoutingKey; - Log.MsSqlMessageConsumerFactoryCreateAsync(s_logger, subscription.ChannelName); - return new MsSqlMessageConsumer(_msSqlConfiguration, subscription.ChannelName!, _scheduler, deadLetterRoutingKey, invalidMessageRoutingKey); + Log.MsSqlMessageConsumerFactoryCreateAsync(_logger, subscription.ChannelName); + return new MsSqlMessageConsumer(_msSqlConfiguration, subscription.ChannelName!, _scheduler, deadLetterRoutingKey, invalidMessageRoutingKey, _loggerFactory); } private static partial class Log diff --git a/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlMessageProducer.cs b/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlMessageProducer.cs index 47bee5bb9f..437c13572e 100644 --- a/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlMessageProducer.cs +++ b/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlMessageProducer.cs @@ -28,7 +28,7 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.MessagingGateway.MsSql.SqlQueues; using Paramore.Brighter.MsSql; using Paramore.Brighter.Observability; @@ -41,7 +41,7 @@ namespace Paramore.Brighter.MessagingGateway.MsSql /// public partial class MsSqlMessageProducer : IAmAMessageProducerSync, IAmAMessageProducerAsync { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private readonly InstrumentationOptions _instrumentation; private readonly MsSqlMessageQueue _sqlQ; @@ -69,10 +69,12 @@ public MsSqlMessageProducer( RelationalDatabaseConfiguration msSqlConfiguration, IAmARelationalDbConnectionProvider connectonProvider, Publication? publication = null, - InstrumentationOptions instrumentation = InstrumentationOptions.All + InstrumentationOptions instrumentation = InstrumentationOptions.All, + ILoggerFactory? loggerFactory = null ) { - _sqlQ = new MsSqlMessageQueue(msSqlConfiguration, connectonProvider); + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + _sqlQ = new MsSqlMessageQueue(msSqlConfiguration, connectonProvider, loggerFactory); _instrumentation = instrumentation; Publication = publication ?? new Publication { MakeChannels = OnMissingChannel.Create }; } @@ -84,8 +86,9 @@ public MsSqlMessageProducer( /// The publication configuration. public MsSqlMessageProducer( RelationalDatabaseConfiguration msSqlConfiguration, - Publication? publication = null) - : this(msSqlConfiguration, new MsSqlConnectionProvider(msSqlConfiguration), publication) + Publication? publication = null, + ILoggerFactory? loggerFactory = null) + : this(msSqlConfiguration, new MsSqlConnectionProvider(msSqlConfiguration), publication, loggerFactory: loggerFactory) { } @@ -139,7 +142,7 @@ public void SendWithDelay(Message message, TimeSpan? delay = null) BrighterTracer.WriteProducerEvent(Span, "microsoft_sql_server", message, _instrumentation); var topic = message.Header.Topic; - Log.SendMessage(s_logger, topic.Value, message.Id.Value); + Log.SendMessage(_logger, topic.Value, message.Id.Value); _sqlQ.Send(message, topic); } @@ -176,7 +179,7 @@ public async Task SendWithDelayAsync(Message message, TimeSpan? delay, Cancellat BrighterTracer.WriteProducerEvent(Span, "microsoft_sql_server", message, _instrumentation); var topic = message.Header.Topic; - Log.SendMessageAsync(s_logger, topic.Value, message.Id.Value); + Log.SendMessageAsync(_logger, topic.Value, message.Id.Value); await _sqlQ.SendAsync(message, topic.Value, TimeSpan.Zero, cancellationToken); } diff --git a/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlMessageProducerFactory.cs b/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlMessageProducerFactory.cs index 60f036b96e..dc3fbb3953 100644 --- a/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlMessageProducerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlMessageProducerFactory.cs @@ -24,6 +24,7 @@ THE SOFTWARE. */ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace Paramore.Brighter.MessagingGateway.MsSql { @@ -31,21 +32,25 @@ public class MsSqlMessageProducerFactory : IAmAMessageProducerFactory { private readonly RelationalDatabaseConfiguration _msSqlConfiguration; private readonly IEnumerable _publications; + private readonly ILoggerFactory? _loggerFactory; /// /// Creates a collection of MsSQL message producers from the MsSQL publication information /// /// The connection to use to connect to MsSQL /// The publications describing the MySQL topics that we want to use + /// The optional used to create loggers public MsSqlMessageProducerFactory( RelationalDatabaseConfiguration msSqlConfiguration, - IEnumerable publications) + IEnumerable publications, + ILoggerFactory? loggerFactory = null) { - _msSqlConfiguration = + _msSqlConfiguration = msSqlConfiguration ?? throw new ArgumentNullException(nameof(msSqlConfiguration)); if (string.IsNullOrEmpty(msSqlConfiguration.QueueStoreTable)) throw new ArgumentNullException(nameof(msSqlConfiguration.QueueStoreTable)); _publications = publications; + _loggerFactory = loggerFactory; } /// @@ -60,7 +65,7 @@ public Dictionary Create() foreach (var publication in _publications) { if (publication.Topic is null) throw new ConfigurationException("MS SQL Message Producer Factory: Topic is missing from the publication"); - var producer = new MsSqlMessageProducer(_msSqlConfiguration, publication); + var producer = new MsSqlMessageProducer(_msSqlConfiguration, publication, _loggerFactory); producer.Publication = publication; var producerKey = new ProducerKey(publication.Topic, publication.Type); if (producers.ContainsKey(producerKey)) diff --git a/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlProducerRegistryFactory.cs b/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlProducerRegistryFactory.cs index afbd86f2aa..4a3ef02cad 100644 --- a/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlProducerRegistryFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.MsSql/MsSqlProducerRegistryFactory.cs @@ -3,25 +3,29 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Paramore.Brighter.MessagingGateway.MsSql { public partial class MsSqlProducerRegistryFactory : IAmAProducerRegistryFactory { private readonly RelationalDatabaseConfiguration _msSqlConfiguration; - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; + private readonly ILoggerFactory? _loggerFactory; private readonly IEnumerable _publications; //-- placeholder for future use public MsSqlProducerRegistryFactory( RelationalDatabaseConfiguration msSqlConfiguration, - IEnumerable publications) + IEnumerable publications, + ILoggerFactory? loggerFactory = null) { - _msSqlConfiguration = + _msSqlConfiguration = msSqlConfiguration ?? throw new ArgumentNullException(nameof(msSqlConfiguration)); if (string.IsNullOrEmpty(msSqlConfiguration.QueueStoreTable)) throw new ArgumentNullException(nameof(msSqlConfiguration.QueueStoreTable)); _publications = publications; + _loggerFactory = loggerFactory; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } /// @@ -30,9 +34,9 @@ public MsSqlProducerRegistryFactory( /// A registry of middleware clients by topic, for sending messages to the middleware public IAmAProducerRegistry Create() { - Log.MsSqlMessageProducerFactoryCreateProducer(s_logger); + Log.MsSqlMessageProducerFactoryCreateProducer(_logger); - var producerFactory = new MsSqlMessageProducerFactory(_msSqlConfiguration, _publications); + var producerFactory = new MsSqlMessageProducerFactory(_msSqlConfiguration, _publications, _loggerFactory); return new ProducerRegistry(producerFactory.Create()); } diff --git a/src/Paramore.Brighter.MessagingGateway.MsSql/SqlQueues/MsSqlMessageQueue.cs b/src/Paramore.Brighter.MessagingGateway.MsSql/SqlQueues/MsSqlMessageQueue.cs index 797c7b5531..1631d85097 100644 --- a/src/Paramore.Brighter.MessagingGateway.MsSql/SqlQueues/MsSqlMessageQueue.cs +++ b/src/Paramore.Brighter.MessagingGateway.MsSql/SqlQueues/MsSqlMessageQueue.cs @@ -6,8 +6,8 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; namespace Paramore.Brighter.MessagingGateway.MsSql.SqlQueues { @@ -18,7 +18,7 @@ namespace Paramore.Brighter.MessagingGateway.MsSql.SqlQueues public partial class MsSqlMessageQueue { private const int RetryDelay = 100; - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger>(); + private readonly ILogger _logger; private readonly RelationalDatabaseConfiguration _configuration; private readonly IAmARelationalDbConnectionProvider _connectionProvider; @@ -27,11 +27,12 @@ public partial class MsSqlMessageQueue /// /// /// - public MsSqlMessageQueue(RelationalDatabaseConfiguration configuration, IAmARelationalDbConnectionProvider connectionProvider) + public MsSqlMessageQueue(RelationalDatabaseConfiguration configuration, IAmARelationalDbConnectionProvider connectionProvider, ILoggerFactory? loggerFactory = null) { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger>(); _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); _connectionProvider = connectionProvider; - Log.MsSqlMessageQueueCtor(s_logger, _configuration.ConnectionString, _configuration.QueueStoreTable); + Log.MsSqlMessageQueueCtor(_logger, _configuration.ConnectionString, _configuration.QueueStoreTable); ContinueOnCapturedContext = false; } @@ -55,7 +56,7 @@ public void Send(T message, RoutingKey topic, TimeSpan? timeOut = null) { timeOut ??= TimeSpan.FromMilliseconds(-1); - Log.Send(s_logger, typeof(T).FullName, topic); + Log.Send(_logger, typeof(T).FullName, topic); var parameters = InitAddDbParameters(topic.Value, message); @@ -74,7 +75,7 @@ public void Send(T message, RoutingKey topic, TimeSpan? timeOut = null) /// public async Task SendAsync(T message, string topic, TimeSpan? timeOut, CancellationToken cancellationToken = default) { - Log.SendAsync(s_logger, typeof(T).FullName, topic); + Log.SendAsync(_logger, typeof(T).FullName, topic); timeOut ??= TimeSpan.FromMilliseconds(-1); @@ -96,7 +97,7 @@ public ReceivedResult TryReceive(string topic, TimeSpan? timeout = null) { timeout ??= TimeSpan.FromMilliseconds(-1); - Log.TryReceive(s_logger, typeof(T).FullName, timeout.Value.TotalMilliseconds); + Log.TryReceive(_logger, typeof(T).FullName, timeout.Value.TotalMilliseconds); var rc = TryReceive(topic); var timeLeft = timeout.Value.TotalMilliseconds; @@ -117,7 +118,7 @@ public ReceivedResult TryReceive(string topic, TimeSpan? timeout = null) /// The message received -or- ReceivedResult<T>.Empty when no message is waiting private ReceivedResult TryReceive(string topic) { - Log.TryReceiveInner(s_logger, typeof(T).FullName); + Log.TryReceiveInner(_logger, typeof(T).FullName); var parameters = InitRemoveDbParameters(topic); @@ -142,7 +143,7 @@ private ReceivedResult TryReceive(string topic) public async Task> TryReceiveAsync(string topic, CancellationToken cancellationToken = default) { - Log.TryReceiveAsync(s_logger, typeof(T).FullName); + Log.TryReceiveAsync(_logger, typeof(T).FullName); var parameters = InitRemoveDbParameters(topic); @@ -182,7 +183,7 @@ public int NumberOfMessageReady(string topic) /// public void Purge() { - Log.Purge(s_logger); + Log.Purge(_logger); using var connection = _connectionProvider.GetConnection(); var sqlCmd = InitPurgeDbCommand(connection); diff --git a/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresChannelFactory.cs b/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresChannelFactory.cs index 4531e5d5e7..3ed81af088 100644 --- a/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresChannelFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresChannelFactory.cs @@ -1,5 +1,6 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace Paramore.Brighter.MessagingGateway.Postgres; @@ -8,9 +9,9 @@ namespace Paramore.Brighter.MessagingGateway.Postgres; /// This factory is responsible for ensuring the underlying queue store exists and for creating channels /// configured according to the provided . /// -public class PostgresChannelFactory(PostgresMessagingGatewayConnection connection): PostgresMessagingGateway(connection), IAmAChannelFactory +public class PostgresChannelFactory(PostgresMessagingGatewayConnection connection, ILoggerFactory? loggerFactory = null): PostgresMessagingGateway(connection), IAmAChannelFactory { - private readonly PostgresConsumerFactory _factory = new(connection); + private readonly PostgresConsumerFactory _factory = new(connection, loggerFactory); /// public IAmAChannelSync CreateSyncChannel(Subscription subscription) diff --git a/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresConsumerFactory.cs b/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresConsumerFactory.cs index b90884f681..2f4a4bd24a 100644 --- a/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresConsumerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresConsumerFactory.cs @@ -1,3 +1,5 @@ +using Microsoft.Extensions.Logging; + namespace Paramore.Brighter.MessagingGateway.Postgres; /// @@ -5,7 +7,7 @@ namespace Paramore.Brighter.MessagingGateway.Postgres; /// This factory is responsible for instantiating instances based on the /// provided configuration. /// -public class PostgresConsumerFactory(PostgresMessagingGatewayConnection connection) : IAmAMessageConsumerFactory +public class PostgresConsumerFactory(PostgresMessagingGatewayConnection connection, ILoggerFactory? loggerFactory = null) : IAmAMessageConsumerFactory { /// public IAmAMessageConsumerSync Create(Subscription subscription) @@ -29,6 +31,7 @@ private PostgresMessageConsumer CreateMessageConsumer(Subscription subscription) connection.Configuration, postgresSubscription, deadLetterRoutingKey, - invalidMessageRoutingKey); + invalidMessageRoutingKey, + loggerFactory); } } diff --git a/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresMessageConsumer.cs b/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresMessageConsumer.cs index 7244615ad9..74760a4dec 100644 --- a/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresMessageConsumer.cs +++ b/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresMessageConsumer.cs @@ -5,10 +5,10 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Npgsql; using NpgsqlTypes; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Paramore.Brighter.PostgreSql; using JsonSerializer = System.Text.Json.JsonSerializer; @@ -22,10 +22,11 @@ public partial class PostgresMessageConsumer( RelationalDatabaseConfiguration configuration, PostgresSubscription subscription, RoutingKey? deadLetterRoutingKey = null, - RoutingKey? invalidMessageRoutingKey = null + RoutingKey? invalidMessageRoutingKey = null, + ILoggerFactory? loggerFactory = null ) : IAmAMessageConsumerAsync, IAmAMessageConsumerSync { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); private readonly RelationalDatabaseConfiguration _configuration = configuration; private readonly PostgreSqlConnectionProvider _connectionProvider = new(configuration); private readonly RoutingKey? _deadLetterRoutingKey = deadLetterRoutingKey; @@ -34,9 +35,9 @@ public partial class PostgresMessageConsumer( // thread-safety mode is needed. None does not cache exceptions, allowing the factory // to retry on the next .Value access after a transient failure. private readonly Lazy? _deadLetterProducer = - deadLetterRoutingKey != null ? new Lazy(() => CreateProducer(configuration, deadLetterRoutingKey), LazyThreadSafetyMode.None) : null; + deadLetterRoutingKey != null ? new Lazy(() => CreateProducer(configuration, deadLetterRoutingKey, loggerFactory), LazyThreadSafetyMode.None) : null; private readonly Lazy? _invalidMessageProducer = - invalidMessageRoutingKey != null ? new Lazy(() => CreateProducer(configuration, invalidMessageRoutingKey), LazyThreadSafetyMode.None) : null; + invalidMessageRoutingKey != null ? new Lazy(() => CreateProducer(configuration, invalidMessageRoutingKey, loggerFactory), LazyThreadSafetyMode.None) : null; private string SchemaName => subscription.SchemaName ?? _configuration.SchemaName ?? "public"; private string TableName => subscription.QueueStoreTable ?? _configuration.QueueStoreTable; @@ -63,11 +64,11 @@ public void Acknowledge(Message message) command.CommandText = $"DELETE FROM \"{SchemaName}\".\"{TableName}\" WHERE \"id\" = $1"; command.Parameters.Add(new NpgsqlParameter { Value = receiptHandle }); command.ExecuteNonQuery(); - Log.DeletedMessage(s_logger, message.Id.Value, receiptHandle, QueueName); + Log.DeletedMessage(_logger, message.Id.Value, receiptHandle, QueueName); } catch (Exception exception) { - Log.ErrorDeletingMessage(s_logger, exception, message.Id.Value, receiptHandle, QueueName); + Log.ErrorDeletingMessage(_logger, exception, message.Id.Value, receiptHandle, QueueName); throw; } } @@ -87,11 +88,11 @@ public async Task AcknowledgeAsync(Message message, CancellationToken cancellati command.CommandText = $"DELETE FROM \"{SchemaName}\".\"{TableName}\" WHERE \"id\" = $1"; command.Parameters.Add(new NpgsqlParameter { Value = receiptHandle }); await command.ExecuteNonQueryAsync(cancellationToken); - Log.DeletedMessage(s_logger, message.Id.Value, receiptHandle, QueueName); + Log.DeletedMessage(_logger, message.Id.Value, receiptHandle, QueueName); } catch (Exception exception) { - Log.ErrorDeletingMessage(s_logger, exception, message.Id.Value, receiptHandle, QueueName); + Log.ErrorDeletingMessage(_logger, exception, message.Id.Value, receiptHandle, QueueName); throw; } } @@ -101,18 +102,18 @@ public async Task PurgeAsync(CancellationToken cancellationToken = default) { try { - Log.PurgingQueue(s_logger, TableName); + Log.PurgingQueue(_logger, TableName); await using var connection = await _connectionProvider.GetConnectionAsync(cancellationToken); await using var command = connection.CreateCommand(); command.CommandText = $"DELETE FROM \"{SchemaName}\".\"{TableName}\" WHERE \"queue\" = $1"; command.Parameters.Add(new NpgsqlParameter { Value = QueueName }); await command.ExecuteNonQueryAsync(cancellationToken); - Log.PurgedQueue(s_logger, QueueName); + Log.PurgedQueue(_logger, QueueName); } catch (Exception exception) { - Log.ErrorPurgingQueue(s_logger, exception, QueueName); + Log.ErrorPurgingQueue(_logger, exception, QueueName); throw; } @@ -123,7 +124,7 @@ public async Task ReceiveAsync(TimeSpan? timeOut = null, Cancellation { try { - Log.RetrievingNextMessage(s_logger, QueueName); + Log.RetrievingNextMessage(_logger, QueueName); await using var connection = await _connectionProvider.GetConnectionAsync(cancellationToken); await using var command = connection.CreateCommand(); if (timeOut != null && timeOut != TimeSpan.Zero) @@ -171,7 +172,7 @@ FOR UPDATE SKIP LOCKED } catch (Exception exception) { - Log.ErrorListeningToQueue(s_logger, exception, QueueName); + Log.ErrorListeningToQueue(_logger, exception, QueueName); throw; } } @@ -197,12 +198,12 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) return false; } - Log.RejectingMessage(s_logger, message.Id.Value, receiptHandle, QueueName); + Log.RejectingMessage(_logger, message.Id.Value, receiptHandle, QueueName); if (_deadLetterProducer == null && _invalidMessageProducer == null) { if (reason != null) - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value, reason.RejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value, reason.RejectionReason.ToString()); DeleteSourceMessage(receiptHandle); return true; @@ -222,7 +223,7 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) { message.Header.Topic = routingKey!; if (isFallingBackToDlq) - Log.FallingBackToDlq(s_logger, message.Id.Value); + Log.FallingBackToDlq(_logger, message.Id.Value); if (routingKey == _invalidMessageRoutingKey) producer = _invalidMessageProducer?.Value; @@ -233,18 +234,18 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) if (producer != null) { producer.Send(message); - Log.MessageSentToRejectionChannel(s_logger, message.Id.Value, rejectionReason.ToString()); + Log.MessageSentToRejectionChannel(_logger, message.Id.Value, rejectionReason.ToString()); } else { - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value, rejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value, rejectionReason.ToString()); } } catch (Exception ex) { // DLQ send failed — delete the source message (in finally) and return true // to prevent the message pump from retrying endlessly. - Log.ErrorSendingToRejectionChannel(s_logger, ex, message.Id.Value, rejectionReason.ToString()); + Log.ErrorSendingToRejectionChannel(_logger, ex, message.Id.Value, rejectionReason.ToString()); return true; } finally @@ -263,12 +264,12 @@ public async Task RejectAsync(Message message, MessageRejectionReason? rea return false; } - Log.RejectingMessage(s_logger, message.Id.Value, receiptHandle, QueueName); + Log.RejectingMessage(_logger, message.Id.Value, receiptHandle, QueueName); if (_deadLetterProducer == null && _invalidMessageProducer == null) { if (reason != null) - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value, reason.RejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value, reason.RejectionReason.ToString()); await DeleteSourceMessageAsync(receiptHandle, cancellationToken); return true; @@ -288,7 +289,7 @@ public async Task RejectAsync(Message message, MessageRejectionReason? rea { message.Header.Topic = routingKey!; if (isFallingBackToDlq) - Log.FallingBackToDlq(s_logger, message.Id.Value); + Log.FallingBackToDlq(_logger, message.Id.Value); if (routingKey == _invalidMessageRoutingKey) producer = _invalidMessageProducer?.Value; @@ -299,18 +300,18 @@ public async Task RejectAsync(Message message, MessageRejectionReason? rea if (producer != null) { await producer.SendAsync(message, cancellationToken); - Log.MessageSentToRejectionChannel(s_logger, message.Id.Value, rejectionReason.ToString()); + Log.MessageSentToRejectionChannel(_logger, message.Id.Value, rejectionReason.ToString()); } else { - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value, rejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value, rejectionReason.ToString()); } } catch (Exception ex) { // DLQ send failed — delete the source message and return true // to prevent the message pump from retrying endlessly. - Log.ErrorSendingToRejectionChannel(s_logger, ex, message.Id.Value, rejectionReason.ToString()); + Log.ErrorSendingToRejectionChannel(_logger, ex, message.Id.Value, rejectionReason.ToString()); return true; } finally @@ -333,7 +334,7 @@ public async Task RequeueAsync(Message message, TimeSpan? delay = null, Ca try { - Log.RequeueingMessage(s_logger, message.Id.Value); + Log.RequeueingMessage(_logger, message.Id.Value); await using var connection = await _connectionProvider.GetConnectionAsync(cancellationToken); await using var command = connection.CreateCommand(); @@ -344,13 +345,13 @@ public async Task RequeueAsync(Message message, TimeSpan? delay = null, Ca command.Parameters.Add(new NpgsqlParameter { Value = receiptHandle }); await command.ExecuteNonQueryAsync(cancellationToken); - Log.RequeuedMessage(s_logger, message.Id.Value); + Log.RequeuedMessage(_logger, message.Id.Value); return true; } catch (Exception exception) { - Log.ErrorRequeueingMessage(s_logger, exception, message.Id.Value, receiptHandle, QueueName); + Log.ErrorRequeueingMessage(_logger, exception, message.Id.Value, receiptHandle, QueueName); return false; } } @@ -363,17 +364,17 @@ public void Purge() { try { - Log.PurgingQueue(s_logger, QueueName); + Log.PurgingQueue(_logger, QueueName); using var connection = _connectionProvider.GetConnection(); using var command = connection.CreateCommand(); command.CommandText = $"DELETE FROM \"{SchemaName}\".\"{TableName}\" WHERE \"queue\" = $1"; command.Parameters.Add(new NpgsqlParameter { Value = QueueName }); command.ExecuteNonQuery(); - Log.PurgedQueue(s_logger, QueueName); + Log.PurgedQueue(_logger, QueueName); } catch (Exception exception) { - Log.ErrorPurgingQueue(s_logger, exception, QueueName); + Log.ErrorPurgingQueue(_logger, exception, QueueName); throw; } } @@ -383,7 +384,7 @@ public Message[] Receive(TimeSpan? timeOut = null) { try { - Log.RetrievingNextMessage(s_logger, QueueName); + Log.RetrievingNextMessage(_logger, QueueName); using var connection = _connectionProvider.GetConnection(); using var command = connection.CreateCommand(); @@ -425,7 +426,7 @@ FOR UPDATE SKIP LOCKED } catch (Exception exception) { - Log.ErrorListeningToQueue(s_logger, exception, QueueName); + Log.ErrorListeningToQueue(_logger, exception, QueueName); throw; } } @@ -440,7 +441,7 @@ public bool Requeue(Message message, TimeSpan? delay = null) try { - Log.RequeueingMessage(s_logger, message.Id.Value); + Log.RequeueingMessage(_logger, message.Id.Value); using var connection = _connectionProvider.GetConnection(); using var command = connection.CreateCommand(); @@ -452,13 +453,13 @@ public bool Requeue(Message message, TimeSpan? delay = null) command.Parameters.Add(new NpgsqlParameter { Value = receiptHandle }); command.ExecuteNonQuery(); - Log.RequeuedMessage(s_logger, message.Id.Value); + Log.RequeuedMessage(_logger, message.Id.Value); return true; } catch (Exception exception) { - Log.ErrorRequeueingMessage(s_logger, exception, message.Id.Value, receiptHandle, QueueName); + Log.ErrorRequeueingMessage(_logger, exception, message.Id.Value, receiptHandle, QueueName); return false; } } @@ -517,15 +518,16 @@ private async Task ToLargeMessageAsync(DbDataReader reader, Cancellatio return message; } - private static PostgresMessageProducer? CreateProducer(RelationalDatabaseConfiguration config, RoutingKey routingKey) + private static PostgresMessageProducer? CreateProducer(RelationalDatabaseConfiguration config, RoutingKey routingKey, ILoggerFactory? loggerFactory) { try { - return new PostgresMessageProducer(config, new PostgresPublication { Topic = routingKey }); + return new PostgresMessageProducer(config, new PostgresPublication { Topic = routingKey }, loggerFactory: loggerFactory); } catch (Exception e) { - Log.ErrorCreatingProducer(s_logger, e, routingKey.Value); + var logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + Log.ErrorCreatingProducer(logger, e, routingKey.Value); return null; } } @@ -575,11 +577,11 @@ private void DeleteSourceMessage(object receiptHandle) command.CommandText = $"DELETE FROM \"{SchemaName}\".\"{TableName}\" WHERE \"id\" = $1"; command.Parameters.Add(new NpgsqlParameter { Value = receiptHandle }); command.ExecuteNonQuery(); - Log.DeletedMessage(s_logger, "source", receiptHandle, QueueName); + Log.DeletedMessage(_logger, "source", receiptHandle, QueueName); } catch (Exception exception) { - Log.ErrorDeletingMessage(s_logger, exception, "source", receiptHandle, QueueName); + Log.ErrorDeletingMessage(_logger, exception, "source", receiptHandle, QueueName); throw; } } @@ -593,11 +595,11 @@ private async Task DeleteSourceMessageAsync(object receiptHandle, CancellationTo command.CommandText = $"DELETE FROM \"{SchemaName}\".\"{TableName}\" WHERE \"id\" = $1"; command.Parameters.Add(new NpgsqlParameter { Value = receiptHandle }); await command.ExecuteNonQueryAsync(cancellationToken); - Log.DeletedMessage(s_logger, "source", receiptHandle, QueueName); + Log.DeletedMessage(_logger, "source", receiptHandle, QueueName); } catch (Exception exception) { - Log.ErrorDeletingMessage(s_logger, exception, "source", receiptHandle, QueueName); + Log.ErrorDeletingMessage(_logger, exception, "source", receiptHandle, QueueName); throw; } } diff --git a/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresMessageProducer.cs b/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresMessageProducer.cs index 9491829b31..7e9ee08ff5 100644 --- a/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresMessageProducer.cs +++ b/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresMessageProducer.cs @@ -4,10 +4,10 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Npgsql; using NpgsqlTypes; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; using Paramore.Brighter.PostgreSql; @@ -21,9 +21,10 @@ namespace Paramore.Brighter.MessagingGateway.Postgres; public partial class PostgresMessageProducer( RelationalDatabaseConfiguration configuration, PostgresPublication publication, - InstrumentationOptions instrumentations = InstrumentationOptions.All) : IAmAMessageProducerAsync, IAmAMessageProducerSync + InstrumentationOptions instrumentations = InstrumentationOptions.All, + ILoggerFactory? loggerFactory = null) : IAmAMessageProducerAsync, IAmAMessageProducerSync { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); private readonly PostgreSqlConnectionProvider _connectionProvider = new(configuration); private PostgresPublication _publication = publication; @@ -56,7 +57,7 @@ public async Task SendAsync(Message message, CancellationToken cancellationToken } BrighterTracer.WriteProducerEvent(Span, "postgres", message, instrumentations); - Log.PublishingMessage(s_logger, message.Header.Topic.Value, message.Id.Value, message.Body); + Log.PublishingMessage(_logger, message.Header.Topic.Value, message.Id.Value, message.Body); await using var connection = await _connectionProvider.GetConnectionAsync(cancellationToken); await using var command = connection.CreateCommand(); @@ -66,7 +67,7 @@ public async Task SendAsync(Message message, CancellationToken cancellationToken command.Parameters.Add(new NpgsqlParameter { Value = JsonSerializer.Serialize(message, JsonSerialisationOptions.Options), NpgsqlDbType = MessagePayloadDbType}); var id = await command.ExecuteScalarAsync(cancellationToken); - Log.PublishedMessage(s_logger, message.Header.Topic.Value, message.Id.Value, Convert.ToInt64(id)); + Log.PublishedMessage(_logger, message.Header.Topic.Value, message.Id.Value, Convert.ToInt64(id)); } /// @@ -84,7 +85,7 @@ public async Task SendWithDelayAsync(Message message, TimeSpan? delay, Cancellat } BrighterTracer.WriteProducerEvent(Span, "postgres", message, instrumentations); - Log.PublishingMessage(s_logger, message.Header.Topic.Value, message.Id.Value, message.Body); + Log.PublishingMessage(_logger, message.Header.Topic.Value, message.Id.Value, message.Body); await using var connection = await _connectionProvider.GetConnectionAsync(cancellationToken); await using var command = connection.CreateCommand(); @@ -95,7 +96,7 @@ public async Task SendWithDelayAsync(Message message, TimeSpan? delay, Cancellat command.Parameters.Add(new NpgsqlParameter { Value = JsonSerializer.Serialize(message, JsonSerialisationOptions.Options), NpgsqlDbType = MessagePayloadDbType}); var id = await command.ExecuteScalarAsync(cancellationToken); - Log.PublishedMessage(s_logger, message.Header.Topic.Value, message.Id.Value, Convert.ToInt64(id)); + Log.PublishedMessage(_logger, message.Header.Topic.Value, message.Id.Value, Convert.ToInt64(id)); } /// @@ -107,7 +108,7 @@ public void Send(Message message) } BrighterTracer.WriteProducerEvent(Span, "postgres", message, instrumentations); - Log.PublishingMessage(s_logger, message.Header.Topic.Value, message.Id.Value, message.Body); + Log.PublishingMessage(_logger, message.Header.Topic.Value, message.Id.Value, message.Body); using var connection = _connectionProvider.GetConnection(); using var command = connection.CreateCommand(); @@ -117,7 +118,7 @@ public void Send(Message message) command.Parameters.Add(new NpgsqlParameter { Value = JsonSerializer.Serialize(message, JsonSerialisationOptions.Options), NpgsqlDbType = MessagePayloadDbType}); var id = command.ExecuteScalar(); - Log.PublishedMessage(s_logger, message.Header.Topic.Value, message.Id.Value, Convert.ToInt64(id)); + Log.PublishedMessage(_logger, message.Header.Topic.Value, message.Id.Value, Convert.ToInt64(id)); } /// @@ -135,7 +136,7 @@ public void SendWithDelay(Message message, TimeSpan? delay) } BrighterTracer.WriteProducerEvent(Span, "postgres", message, instrumentations); - Log.PublishingMessage(s_logger, message.Header.Topic.Value, message.Id.Value, message.Body); + Log.PublishingMessage(_logger, message.Header.Topic.Value, message.Id.Value, message.Body); using var connection = _connectionProvider.GetConnection(); using var command = connection.CreateCommand(); @@ -146,7 +147,7 @@ public void SendWithDelay(Message message, TimeSpan? delay) command.Parameters.Add(new NpgsqlParameter { Value = JsonSerializer.Serialize(message, JsonSerialisationOptions.Options), NpgsqlDbType = MessagePayloadDbType}); var id = command.ExecuteScalar(); - Log.PublishedMessage(s_logger, message.Header.Topic.Value, message.Id.Value, Convert.ToInt64(id)); + Log.PublishedMessage(_logger, message.Header.Topic.Value, message.Id.Value, Convert.ToInt64(id)); } diff --git a/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresMessageProducerFactory.cs b/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresMessageProducerFactory.cs index d19f88e7c8..2149e06fed 100644 --- a/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresMessageProducerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresMessageProducerFactory.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace Paramore.Brighter.MessagingGateway.Postgres; @@ -10,7 +11,7 @@ namespace Paramore.Brighter.MessagingGateway.Postgres; /// ensures the underlying queue store exists for each publication, and creates a corresponding /// instance, keyed by the publication's topic. /// -public class PostgresMessageProducerFactory(PostgresMessagingGatewayConnection connection, IEnumerable publications) : PostgresMessagingGateway(connection), IAmAMessageProducerFactory +public class PostgresMessageProducerFactory(PostgresMessagingGatewayConnection connection, IEnumerable publications, ILoggerFactory? loggerFactory = null) : PostgresMessagingGateway(connection), IAmAMessageProducerFactory { /// /// Creates a dictionary of in-memory message producers. @@ -31,7 +32,7 @@ public Dictionary Create() EnsureQueueStoreExists(schemaName, tableName, binaryMessagePayload, publication.MakeChannels); - var producer = new PostgresMessageProducer(Connection.Configuration, publication); + var producer = new PostgresMessageProducer(Connection.Configuration, publication, loggerFactory: loggerFactory); producer.Publication = publication; var producerKey = new ProducerKey(publication.Topic, publication.Type); @@ -64,7 +65,7 @@ public async Task> CreateAsync() await EnsureQueueStoreExistsAsync(schemaName, tableName, binaryMessagePayload, publication.MakeChannels, CancellationToken.None); - var producer = new PostgresMessageProducer(Connection.Configuration, publication); + var producer = new PostgresMessageProducer(Connection.Configuration, publication, loggerFactory: loggerFactory); producer.Publication = publication; var producerKey = new ProducerKey(publication.Topic, publication.Type); diff --git a/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresProducerRegistryFactory.cs b/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresProducerRegistryFactory.cs index e2291edd2e..5e937851da 100644 --- a/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresProducerRegistryFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.Postgres/PostgresProducerRegistryFactory.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace Paramore.Brighter.MessagingGateway.Postgres; @@ -9,19 +10,19 @@ namespace Paramore.Brighter.MessagingGateway.Postgres; /// to publish messages to a PostgreSQL message queue. This factory takes a connection configuration and a collection /// of configurations to build the registry. /// -public class PostgresProducerRegistryFactory(PostgresMessagingGatewayConnection connection, IEnumerable publications) : IAmAProducerRegistryFactory +public class PostgresProducerRegistryFactory(PostgresMessagingGatewayConnection connection, IEnumerable publications, ILoggerFactory? loggerFactory = null) : IAmAProducerRegistryFactory { /// public IAmAProducerRegistry Create() - { - var producerFactory = new PostgresMessageProducerFactory(connection, publications); + { + var producerFactory = new PostgresMessageProducerFactory(connection, publications, loggerFactory); return new ProducerRegistry(producerFactory.Create()); } /// public async Task CreateAsync(CancellationToken ct = default) { - var producerFactory = new PostgresMessageProducerFactory(connection, publications); + var producerFactory = new PostgresMessageProducerFactory(connection, publications, loggerFactory); return new ProducerRegistry(await producerFactory.CreateAsync()); } } diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/ConnectionPolicyFactory.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/ConnectionPolicyFactory.cs index bcc3ab3256..8369912cbb 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/ConnectionPolicyFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/ConnectionPolicyFactory.cs @@ -24,7 +24,7 @@ THE SOFTWARE. */ using System; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Polly; using RabbitMQ.Client.Exceptions; @@ -35,22 +35,25 @@ namespace Paramore.Brighter.MessagingGateway.RMQ.Async /// public partial class ConnectionPolicyFactory { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// - public ConnectionPolicyFactory() - : this(new RmqMessagingGatewayConnection()) + public ConnectionPolicyFactory(ILoggerFactory? loggerFactory = null) + : this(new RmqMessagingGatewayConnection(), loggerFactory) {} /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// Use if you need to inject a test logger /// /// - public ConnectionPolicyFactory(RmqMessagingGatewayConnection connection) + /// The used to create a logger; defaults to + public ConnectionPolicyFactory(RmqMessagingGatewayConnection connection, ILoggerFactory? loggerFactory = null) { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + if (connection.Exchange is null) throw new ConfigurationException("RabbitMQ Exchange is not set"); if (connection.AmpqUri is null) throw new ConfigurationException("RabbitMQ Broker URL is not set"); @@ -68,11 +71,11 @@ public ConnectionPolicyFactory(RmqMessagingGatewayConnection connection) { if (exception is BrokerUnreachableException) { - Log.BrokerUnreachableException(s_logger, exception, context["queueName"].ToString(), connection.Exchange.Name, connection.AmpqUri.GetSanitizedUri(), retries); + Log.BrokerUnreachableException(_logger, exception, context["queueName"].ToString(), connection.Exchange.Name, connection.AmpqUri.GetSanitizedUri(), retries); } else { - Log.ExceptionOnSubscription(s_logger, exception, context["queueName"].ToString(), connection.Exchange.Name, connection.AmpqUri.GetSanitizedUri()); + Log.ExceptionOnSubscription(_logger, exception, context["queueName"].ToString(), connection.Exchange.Name, connection.AmpqUri.GetSanitizedUri()); throw new ChannelFailureException($"RMQMessagingGateway: Exception on subscription to queue { context["queueName"]} via exchange {connection.Exchange.Name} on subscription {connection.AmpqUri.GetSanitizedUri()}", exception); } diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/PullConsumer.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/PullConsumer.cs index eac9c84205..5e50506c31 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/PullConsumer.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/PullConsumer.cs @@ -28,15 +28,15 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using RabbitMQ.Client; using RabbitMQ.Client.Events; namespace Paramore.Brighter.MessagingGateway.RMQ.Async; -public partial class PullConsumer(IChannel channel) : AsyncDefaultBasicConsumer(channel) +public partial class PullConsumer(IChannel channel, ILoggerFactory? loggerFactory = null) : AsyncDefaultBasicConsumer(channel) { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); //we do end up creating a second buffer to the Brighter Channel, but controlling the flow from RMQ depends //on us being able to buffer up to the set QoS and then pull. This matches other implementations. @@ -131,7 +131,7 @@ protected override async Task OnCancelAsync(string[] consumerTags, catch (Exception e) { //don't impede shutdown, just log - Log.NackUnhandledMessagesOnShutdownFailed(s_logger, e.Message); + Log.NackUnhandledMessagesOnShutdownFailed(_logger, e.Message); } await base.OnCancelAsync(consumerTags, cancellationToken); diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageConsumer.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageConsumer.cs index e68a73eeab..eede4110c1 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageConsumer.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageConsumer.cs @@ -31,8 +31,8 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Paramore.Brighter.Tasks; using Polly.CircuitBreaker; using RabbitMQ.Client.Exceptions; @@ -47,7 +47,8 @@ namespace Paramore.Brighter.MessagingGateway.RMQ.Async; /// public partial class RmqMessageConsumer : RmqMessageGateway, IAmAMessageConsumerSync, IAmAMessageConsumerAsync { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; + private readonly RmqMessageCreator _messageCreator; private PullConsumer? _consumer; private RmqMessageProducer? _requeueProducer; @@ -85,6 +86,7 @@ public partial class RmqMessageConsumer : RmqMessageGateway, IAmAMessageConsumer /// Should we validate, or create missing channels /// The type of queue to use - Classic or Quorum; defaults to Classic /// Optional scheduler for delayed requeue operations + /// The used to create a logger; defaults to public RmqMessageConsumer( RmqMessagingGatewayConnection connection, ChannelName queueName, @@ -98,9 +100,10 @@ public RmqMessageConsumer( int? maxQueueLength = null, OnMissingChannel makeChannels = OnMissingChannel.Create, QueueType queueType = QueueType.Classic, - IAmAMessageScheduler? scheduler = null) + IAmAMessageScheduler? scheduler = null, + ILoggerFactory? loggerFactory = null) : this(connection, queueName, new RoutingKeys(routingKey), isDurable, highAvailability, - batchSize, deadLetterQueueName, deadLetterRoutingKey, ttl, maxQueueLength, makeChannels, queueType, scheduler) + batchSize, deadLetterQueueName, deadLetterRoutingKey, ttl, maxQueueLength, makeChannels, queueType, scheduler, loggerFactory) { } @@ -120,6 +123,7 @@ public RmqMessageConsumer( /// Should we validate or create missing channels /// The type of queue to use - Classic or Quorum; defaults to Classic /// Optional scheduler for delayed requeue operations + /// The used to create a logger; defaults to public RmqMessageConsumer( RmqMessagingGatewayConnection connection, ChannelName queueName, @@ -133,9 +137,12 @@ public RmqMessageConsumer( int? maxQueueLength = null, OnMissingChannel makeChannels = OnMissingChannel.Create, QueueType queueType = QueueType.Classic, - IAmAMessageScheduler? scheduler = null) - : base(connection) + IAmAMessageScheduler? scheduler = null, + ILoggerFactory? loggerFactory = null) + : base(connection, loggerFactory) { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + _messageCreator = new RmqMessageCreator(LoggerFactory.CreateLogger()); _queueName = queueName; _routingKeys = routingKeys; _isDurable = isDurable; @@ -176,12 +183,12 @@ public async Task AcknowledgeAsync(Message message, CancellationToken cancellati if (Channel is null) throw new ChannelFailureException($"RmqMessageConsumer: channel {_queueName.Value} is null"); - Log.AcknowledgingMessage(s_logger, message.Id.Value, deliveryTag); + Log.AcknowledgingMessage(_logger, message.Id.Value, deliveryTag); await Channel.BasicAckAsync(deliveryTag, false, cancellationToken); } catch (Exception exception) { - Log.ErrorAcknowledgingMessage(s_logger, exception, message.Id.Value, deliveryTag); + Log.ErrorAcknowledgingMessage(_logger, exception, message.Id.Value, deliveryTag); throw; } } @@ -200,7 +207,7 @@ public async Task PurgeAsync(CancellationToken cancellationToken = default) if (Channel is null) throw new ChannelFailureException($"RmqMessageConsumer: channel {_queueName.Value} is null"); - Log.PurgingChannel(s_logger, _queueName.Value); + Log.PurgingChannel(_logger, _queueName.Value); try { @@ -218,7 +225,7 @@ public async Task PurgeAsync(CancellationToken cancellationToken = default) } catch (Exception exception) { - Log.ErrorPurgingChannel(s_logger, exception, _queueName.Value); + Log.ErrorPurgingChannel(_logger, exception, _queueName.Value); throw; } } @@ -255,7 +262,7 @@ public async Task PurgeAsync(CancellationToken cancellationToken = default) if (Connection.Exchange is null) throw new ConfigurationException($"RmqMessageConsumer: exchange for {_queueName.Value} is null"); if (Connection.AmpqUri is null) throw new ConfigurationException($"RmqMessageConsumer: ampqUri for {_queueName.Value} is null"); - Log.RetrievingNextMessage(s_logger, _queueName.Value, + Log.RetrievingNextMessage(_logger, _queueName.Value, string.Join(";", _routingKeys.Select(rk => rk.Value)), Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri()); @@ -267,10 +274,10 @@ public async Task PurgeAsync(CancellationToken cancellationToken = default) var messages = new Message[resultCount]; for (var i = 0; i < resultCount; i++) { - var message = RmqMessageCreator.CreateMessage(results![i]); + var message = _messageCreator.CreateMessage(results![i]); messages[i] = message; - Log.ReceivedMessage(s_logger, _queueName.Value, + Log.ReceivedMessage(_logger, _queueName.Value, string.Join(";", _routingKeys.Select(rk => rk.Value)), Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri(), @@ -322,12 +329,12 @@ public async Task NackAsync(Message message, CancellationToken cancellationToken if (Channel is null) throw new ChannelFailureException($"RmqMessageConsumer: channel {_queueName.Value} is null"); - Log.NackingMessage(s_logger, message.Id.Value, deliveryTag); + Log.NackingMessage(_logger, message.Id.Value, deliveryTag); await Channel.BasicNackAsync(deliveryTag, false, true, cancellationToken); } catch (Exception exception) { - Log.ErrorNackingMessage(s_logger, exception, message.Id.Value, deliveryTag); + Log.ErrorNackingMessage(_logger, exception, message.Id.Value, deliveryTag); throw; } } @@ -356,7 +363,7 @@ public async Task RejectAsync(Message message, MessageRejectionReason? rea var reasonString = reason is null ? nameof(RejectionReason.DeliveryError) : reason.RejectionReason.ToString(); var description = reason is null ? "unknown" : reason.Description ?? "unknown"; - Log.NoAckMessage(s_logger, message.Id.Value, message.DeliveryTag, reasonString, description); + Log.NoAckMessage(_logger, message.Id.Value, message.DeliveryTag, reasonString, description); //if we have a DLQ, this will force over to the DLQ await Channel.BasicRejectAsync(message.DeliveryTag, false, cancellationToken); @@ -364,7 +371,7 @@ public async Task RejectAsync(Message message, MessageRejectionReason? rea } catch (Exception exception) { - Log.ErrorNoAckMessage(s_logger, exception, message.Id.Value); + Log.ErrorNoAckMessage(_logger, exception, message.Id.Value); throw; } } @@ -423,7 +430,7 @@ public async Task RequeueAsync(Message message, TimeSpan? timeout = null, try { - Log.RequeueingMessage(s_logger, message.Id.Value, timeout.Value.TotalMilliseconds); + Log.RequeueingMessage(_logger, message.Id.Value, timeout.Value.TotalMilliseconds); await EnsureChannelAsync(cancellationToken); @@ -434,7 +441,7 @@ public async Task RequeueAsync(Message message, TimeSpan? timeout = null, // timeout is guaranteed non-null here due to the ??= TimeSpan.Zero coalescing at the top of this method if (DelaySupported || timeout <= TimeSpan.Zero) { - var rmqMessagePublisher = new RmqMessagePublisher(Channel, Connection); + var rmqMessagePublisher = new RmqMessagePublisher(Channel, Connection, LoggerFactory); await rmqMessagePublisher.RequeueMessageAsync(message, _queueName, timeout.Value, cancellationToken); } else @@ -447,14 +454,14 @@ public async Task RequeueAsync(Message message, TimeSpan? timeout = null, // If this fails after a successful publish, the message may be duplicated (not lost). // Consumers should be idempotent to handle potential duplicates. var deliveryTag = message.DeliveryTag; - Log.DeletingMessage(s_logger, message.Id.Value, deliveryTag); + Log.DeletingMessage(_logger, message.Id.Value, deliveryTag); await Channel.BasicAckAsync(deliveryTag, false, cancellationToken); return true; } catch (Exception exception) { - Log.ErrorRequeueingMessage(s_logger, exception, message.Id.Value); + Log.ErrorRequeueingMessage(_logger, exception, message.Id.Value); return false; } } @@ -485,7 +492,7 @@ protected virtual async Task EnsureChannelAsync(CancellationToken cancellationTo if (Connection.Exchange is null) throw new ConfigurationException($"RmqMessageConsumer: exchange for {_queueName.Value} is null"); if (Connection.AmpqUri is null) throw new ConfigurationException($"RmqMessageConsumer: ampqUri for {_queueName.Value} is null"); - Log.CreatedChannel(s_logger, Channel.ChannelNumber, _queueName.Value, + Log.CreatedChannel(_logger, Channel.ChannelNumber, _queueName.Value, string.Join(";", _routingKeys.Select(rk => rk.Value)), Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri()); @@ -511,7 +518,7 @@ private async Task CreateConsumerAsync(CancellationToken cancellationToken) if (Connection.Exchange is null) throw new ConfigurationException($"RmqMessageConsumer: exchange for {_queueName.Value} is null"); if (Connection.AmpqUri is null) throw new ConfigurationException($"RmqMessageConsumer: ampqUri for {_queueName.Value} is null"); - _consumer = new PullConsumer(Channel); + _consumer = new PullConsumer(Channel, LoggerFactory); if (_consumer is null) throw new InvalidOperationException($"RmqMessageConsumer: consumer for {_queueName.Value} is null"); await _consumer.SetChannelBatchSizeAsync(_batchSize); @@ -525,7 +532,7 @@ await Channel.BasicConsumeAsync(_queueName.Value, _consumer, cancellationToken: cancellationToken); - Log.CreatedConsumer(s_logger, _queueName.Value, + Log.CreatedConsumer(_logger, _queueName.Value, string.Join(";", _routingKeys.Select(rk => rk.Value)), Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri()); @@ -537,7 +544,7 @@ private async Task CreateQueueAsync(CancellationToken cancellationToken) if (Connection.Exchange is null) throw new ConfigurationException($"RmqMessageConsumer: exchange for {_queueName.Value} is null"); if (Connection.AmpqUri is null) throw new ConfigurationException($"RmqMessageConsumer: ampqUri for {_queueName.Value} is null"); - Log.CreatingQueue(s_logger, _queueName.Value, Connection.AmpqUri.GetSanitizedUri()); + Log.CreatingQueue(_logger, _queueName.Value, Connection.AmpqUri.GetSanitizedUri()); await Channel.QueueDeclareAsync(_queueName.Value, _isDurable, false, false, SetQueueArguments(), cancellationToken: cancellationToken); @@ -572,7 +579,7 @@ private async Task HandleExceptionAsync(Exception exception, bool resetConnectio if (Connection.Exchange is null) throw new ConfigurationException($"RmqMessageConsumer: exchange for {_queueName.Value} is null", exception); if (Connection.AmpqUri is null) throw new ConfigurationException($"RmqMessageConsumer: ampqUri for {_queueName.Value} is null", exception); - Log.ErrorListeningToQueue(s_logger, exception, _queueName.Value, + Log.ErrorListeningToQueue(_logger, exception, _queueName.Value, string.Join(";", _routingKeys.Select(rk => rk.Value)), Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri()); @@ -587,7 +594,7 @@ private async Task ValidateQueueAsync(CancellationToken cancellationToken) if (Connection.Exchange is null) throw new ConfigurationException($"RmqMessageConsumer: exchange for {_queueName.Value} is null"); if (Connection.AmpqUri is null) throw new ConfigurationException($"RmqMessageConsumer: ampqUri for {_queueName.Value} is null"); - Log.ValidatingQueue(s_logger, _queueName.Value, Connection.AmpqUri.GetSanitizedUri()); + Log.ValidatingQueue(_logger, _queueName.Value, Connection.AmpqUri.GetSanitizedUri()); try { @@ -641,7 +648,7 @@ private void EnsureProducer() { #pragma warning disable CS0420 // LazyInitializer handles the memory barrier for the volatile field LazyInitializer.EnsureInitialized(ref _requeueProducer, ref _requeueProducerInitialized, - ref _requeueProducerLock, () => new RmqMessageProducer(Connection) + ref _requeueProducerLock, () => new RmqMessageProducer(Connection, loggerFactory: LoggerFactory) { Scheduler = _scheduler }); diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageConsumerFactory.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageConsumerFactory.cs index bb74d35498..5b0430a539 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageConsumerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageConsumerFactory.cs @@ -22,11 +22,14 @@ THE SOFTWARE. */ #endregion +using Microsoft.Extensions.Logging; + namespace Paramore.Brighter.MessagingGateway.RMQ.Async { public class RmqMessageConsumerFactory : IAmAMessageConsumerFactory { private readonly RmqMessagingGatewayConnection _rmqConnection; + private readonly ILoggerFactory? _loggerFactory; private IAmAMessageScheduler? _scheduler; /// @@ -44,10 +47,12 @@ public IAmAMessageScheduler? Scheduler /// /// The subscription to the broker hosting the queue /// Optional scheduler for delayed requeue operations - public RmqMessageConsumerFactory(RmqMessagingGatewayConnection rmqConnection, IAmAMessageScheduler? scheduler = null) + /// The used to create loggers for the consumers + public RmqMessageConsumerFactory(RmqMessagingGatewayConnection rmqConnection, IAmAMessageScheduler? scheduler = null, ILoggerFactory? loggerFactory = null) { _rmqConnection = rmqConnection; _scheduler = scheduler; + _loggerFactory = loggerFactory; } /// @@ -80,7 +85,8 @@ public IAmAMessageConsumerSync Create(Subscription subscription) rmqSubscription.MaxQueueLength, subscription.MakeChannels, rmqSubscription.QueueType, - scheduler: _scheduler); + scheduler: _scheduler, + loggerFactory: _loggerFactory); } public IAmAMessageConsumerAsync CreateAsync(Subscription subscription) @@ -102,7 +108,8 @@ public IAmAMessageConsumerAsync CreateAsync(Subscription subscription) rmqSubscription.MaxQueueLength, subscription.MakeChannels, rmqSubscription.QueueType, - scheduler: _scheduler); + scheduler: _scheduler, + loggerFactory: _loggerFactory); } } } diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageCreator.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageCreator.cs index 9701cae16d..5b0b90cf71 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageCreator.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageCreator.cs @@ -29,8 +29,8 @@ THE SOFTWARE. */ using System.Net.Mime; using System.Text; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Extensions; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; using RabbitMQ.Client; using RabbitMQ.Client.Events; @@ -39,9 +39,14 @@ namespace Paramore.Brighter.MessagingGateway.RMQ.Async; internal sealed partial class RmqMessageCreator { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; - public static Message CreateMessage(BasicDeliverEventArgs fromQueue) + public RmqMessageCreator(ILogger? logger = null) + { + _logger = logger ?? NullLogger.Instance; + } + + public Message CreateMessage(BasicDeliverEventArgs fromQueue) { var headers = fromQueue.BasicProperties.Headers ?? new Dictionary(); var topic = HeaderResult.Empty(); @@ -62,14 +67,14 @@ public static Message CreateMessage(BasicDeliverEventArgs fromQueue) } catch (Exception e) { - Log.FailedToCreateMessageFromAmqpMessage(s_logger, e); + Log.FailedToCreateMessageFromAmqpMessage(_logger, e); message = Message.FailureMessage(topic.Result, messageId.Result); } return message; } - private static MessageHeader CreateMessageHeader(BasicDeliverEventArgs fromQueue, IDictionary headers, + private MessageHeader CreateMessageHeader(BasicDeliverEventArgs fromQueue, IDictionary headers, HeaderResult topic, HeaderResult messageId) { var timeStamp = ReadTimeStamp(fromQueue.BasicProperties); @@ -115,12 +120,12 @@ private static MessageHeader CreateMessageHeader(BasicDeliverEventArgs fromQueue ); } - private static void ProcessHeaderBag(IDictionary headers, Message message) + private void ProcessHeaderBag(IDictionary headers, Message message) { headers.Each(header => message.Header.Bag.Add(header.Key, ParseHeaderValue(header.Value))); } - private static void SetMessageMetadata(BasicDeliverEventArgs fromQueue, Message message) + private void SetMessageMetadata(BasicDeliverEventArgs fromQueue, Message message) { var deliveryTag = ReadDeliveryTag(fromQueue.DeliveryTag); var redelivered = ReadRedeliveredFlag(fromQueue.Redelivered); @@ -130,7 +135,7 @@ private static void SetMessageMetadata(BasicDeliverEventArgs fromQueue, Message message.Persist = fromQueue.BasicProperties.DeliveryMode == DeliveryModes.Persistent; } - private static HeaderResult ReadHeader(IDictionary dict, string key, bool dieOnMissing = false) + private HeaderResult ReadHeader(IDictionary dict, string key, bool dieOnMissing = false) { if (false == dict.TryGetValue(key, out object? value)) { @@ -139,7 +144,7 @@ private static void SetMessageMetadata(BasicDeliverEventArgs fromQueue, Message if (!(value is byte[] bytes)) { - Log.HeaderValueCouldNotBeCastToByteArray(s_logger, key); + Log.HeaderValueCouldNotBeCastToByteArray(_logger, key); return new HeaderResult(null, false); } @@ -151,12 +156,12 @@ private static void SetMessageMetadata(BasicDeliverEventArgs fromQueue, Message catch (Exception e) { var firstTwentyBytes = BitConverter.ToString(bytes.Take(20).ToArray()); - Log.FailedToReadHeaderValueAsUtf8(s_logger, key, firstTwentyBytes, e); + Log.FailedToReadHeaderValueAsUtf8(_logger, key, firstTwentyBytes, e); return new HeaderResult(null, false); } } - private static HeaderResult ReadCorrelationId(IDictionary headers) + private HeaderResult ReadCorrelationId(IDictionary headers) { if (headers.TryGetValue(HeaderNames.CORRELATION_ID, out object? correlationHeader)) { @@ -167,12 +172,12 @@ private static void SetMessageMetadata(BasicDeliverEventArgs fromQueue, Message return new HeaderResult(null, false); } - private static HeaderResult ReadDeliveryTag(ulong deliveryTag) + private HeaderResult ReadDeliveryTag(ulong deliveryTag) { return new HeaderResult(deliveryTag, true); } - private static HeaderResult ReadTimeStamp(IReadOnlyBasicProperties basicProperties) + private HeaderResult ReadTimeStamp(IReadOnlyBasicProperties basicProperties) { if (basicProperties.IsTimestampPresent()) { @@ -190,7 +195,7 @@ private static HeaderResult ReadTimeStamp(IReadOnlyBasicProperti return new HeaderResult(DateTimeOffset.UtcNow, true); } - private static HeaderResult ReadMessageType(IDictionary headers) + private HeaderResult ReadMessageType(IDictionary headers) { return ReadHeader(headers, HeaderNames.MESSAGE_TYPE) .Map(s => @@ -205,7 +210,7 @@ private static HeaderResult ReadMessageType(IDictionary ReadHandledCount(IDictionary headers) + private HeaderResult ReadHandledCount(IDictionary headers) { if (headers.TryGetValue(HeaderNames.HANDLED_COUNT, out object? header) == false) { @@ -226,7 +231,7 @@ private static HeaderResult ReadHandledCount(IDictionary h } } - private static HeaderResult ReadDelay(IDictionary headers) + private HeaderResult ReadDelay(IDictionary headers) { if (headers.TryGetValue(HeaderNames.DELAYED_MILLISECONDS, out var delayedMsHeader) == false) { @@ -276,7 +281,7 @@ private static HeaderResult ReadDelay(IDictionary hea return new HeaderResult(TimeSpan.FromMilliseconds(delayedMilliseconds), true); } - private static HeaderResult ReadTopic(BasicDeliverEventArgs fromQueue, IDictionary headers) + private HeaderResult ReadTopic(BasicDeliverEventArgs fromQueue, IDictionary headers) { var res = ReadHeader(headers, HeaderNames.TOPIC).Map(s => { @@ -292,24 +297,24 @@ private static HeaderResult ReadDelay(IDictionary hea return new HeaderResult(new RoutingKey(fromQueue.RoutingKey), true); } - private static HeaderResult ReadMessageId(string? messageId) + private HeaderResult ReadMessageId(string? messageId) { if (string.IsNullOrEmpty(messageId)) { var newMessageId = Id.Random(); - Log.NoMessageIdFoundInMessage(s_logger, newMessageId.Value); + Log.NoMessageIdFoundInMessage(_logger, newMessageId.Value); return new HeaderResult(newMessageId, true); } return new HeaderResult(Id.Create(messageId), true); } - private static HeaderResult ReadRedeliveredFlag(bool redelivered) + private HeaderResult ReadRedeliveredFlag(bool redelivered) { return new HeaderResult(redelivered, true); } - private static HeaderResult ReadReplyTo(IReadOnlyBasicProperties basicProperties) + private HeaderResult ReadReplyTo(IReadOnlyBasicProperties basicProperties) { if (basicProperties.IsReplyToPresent()) { @@ -319,7 +324,7 @@ private static HeaderResult ReadRedeliveredFlag(bool redelivered) return new HeaderResult(null, true); } - private static HeaderResult ReadSource(IDictionary headers) + private HeaderResult ReadSource(IDictionary headers) { if (headers.TryGetValue(HeaderNames.CLOUD_EVENTS_SOURCE, out var source) && source is byte[] val @@ -331,7 +336,7 @@ private static HeaderResult ReadSource(IDictionary headers return new HeaderResult(new Uri(MessageHeader.DefaultSource), true); } - private static HeaderResult ReadType(IDictionary headers) + private HeaderResult ReadType(IDictionary headers) { if (headers.TryGetValue(HeaderNames.CLOUD_EVENTS_TYPE, out var type) && type is byte[] typeArray) @@ -342,7 +347,7 @@ private static HeaderResult ReadType(IDictionary(CloudEventsType.Empty, true); } - private static HeaderResult ReadSubject(IDictionary headers) + private HeaderResult ReadSubject(IDictionary headers) { if (headers.TryGetValue(HeaderNames.CLOUD_EVENTS_SUBJECT, out var subject) && subject is byte[] subjectArray) @@ -353,7 +358,7 @@ private static HeaderResult ReadType(IDictionary(null, true); } - private static HeaderResult ReadDataSchema(IDictionary headers) + private HeaderResult ReadDataSchema(IDictionary headers) { if (headers.TryGetValue(HeaderNames.CLOUD_EVENTS_DATA_SCHEMA, out var dataSchema) && dataSchema is byte[] dataSchemaArray @@ -365,7 +370,7 @@ private static HeaderResult ReadType(IDictionary(null, true); } - private static HeaderResult ReadTraceParent(IDictionary headers) + private HeaderResult ReadTraceParent(IDictionary headers) { if (headers.TryGetValue(HeaderNames.CLOUD_EVENTS_TRACE_PARENT, out var traceParent) && traceParent is byte[] traceParentArray) @@ -376,7 +381,7 @@ private static HeaderResult ReadType(IDictionary(string.Empty, true); } - private static HeaderResult ReadTraceState(IDictionary headers) + private HeaderResult ReadTraceState(IDictionary headers) { object? traceState = null; if (headers.TryGetValue(HeaderNames.CLOUD_EVENTS_TRACE_STATE, out traceState) @@ -396,7 +401,7 @@ private static HeaderResult ReadType(IDictionary(string.Empty, true); } - private static HeaderResult ReadBaggage(IDictionary headers) + private HeaderResult ReadBaggage(IDictionary headers) { if (headers.TryGetValue(HeaderNames.W3C_BAGGAGE, out var traceParent) && traceParent is byte[] traceParentArray) @@ -407,7 +412,7 @@ private static HeaderResult ReadType(IDictionary(string.Empty, true); } - private static object ParseHeaderValue(object? value) + private object ParseHeaderValue(object? value) { if (value == null) return string.Empty; diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageGateway.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageGateway.cs index 0d09049b12..5842bf2931 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageGateway.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageGateway.cs @@ -28,7 +28,7 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Tasks; using Polly; using RabbitMQ.Client; @@ -54,11 +54,12 @@ namespace Paramore.Brighter.MessagingGateway.RMQ.Async; /// public partial class RmqMessageGateway : IDisposable, IAsyncDisposable { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private readonly AsyncPolicy _circuitBreakerPolicy; private readonly ConnectionFactory _connectionFactory; private readonly AsyncPolicy _retryPolicy; private int _disposed; + protected readonly ILoggerFactory LoggerFactory; protected readonly RmqMessagingGatewayConnection Connection; protected IChannel? Channel; @@ -67,11 +68,14 @@ public partial class RmqMessageGateway : IDisposable, IAsyncDisposable /// Use if you need to inject a test logger /// /// The amqp uri and exchange to connect to - protected RmqMessageGateway(RmqMessagingGatewayConnection connection) + /// The used to create a logger; defaults to + protected RmqMessageGateway(RmqMessagingGatewayConnection connection, ILoggerFactory? loggerFactory = null) { + LoggerFactory = loggerFactory ?? NullLoggerFactory.Instance; + _logger = LoggerFactory.CreateLogger(); Connection = connection; - var connectionPolicyFactory = new ConnectionPolicyFactory(Connection); + var connectionPolicyFactory = new ConnectionPolicyFactory(Connection, LoggerFactory); _retryPolicy = connectionPolicyFactory.RetryPolicyAsync; _circuitBreakerPolicy = connectionPolicyFactory.CircuitBreakerPolicyAsync; @@ -140,7 +144,7 @@ protected virtual async Task ConnectToBrokerAsync(OnMissingChannel makeExchange, { if (Channel == null || Channel.IsClosed) { - var connection = await new RmqMessageGatewayConnectionPool(Connection.Name, Connection.Heartbeat) + var connection = await new RmqMessageGatewayConnectionPool(Connection.Name, Connection.Heartbeat, LoggerFactory) .GetConnectionAsync(_connectionFactory, cancellationToken); if (Connection.AmpqUri is null) throw new ConfigurationException("RMQMessagingGateway: No AMPQ URI specified"); @@ -148,7 +152,7 @@ protected virtual async Task ConnectToBrokerAsync(OnMissingChannel makeExchange, connection.ConnectionBlockedAsync += HandleBlockedAsync; connection.ConnectionUnblockedAsync += HandleUnBlockedAsync; - Log.OpeningChannelToRabbitMq(s_logger, Connection.AmpqUri.GetSanitizedUri()); + Log.OpeningChannelToRabbitMq(_logger, Connection.AmpqUri.GetSanitizedUri()); Channel = await connection.CreateChannelAsync( new CreateChannelOptions( @@ -165,7 +169,7 @@ private Task HandleBlockedAsync(object sender, ConnectionBlockedEventArgs args) { if (Connection.AmpqUri is null) throw new ConfigurationException("RMQMessagingGateway: No AMPQ URI specified"); - Log.SubscriptionBlocked(s_logger, Connection.AmpqUri.GetSanitizedUri(), args.Reason); + Log.SubscriptionBlocked(_logger, Connection.AmpqUri.GetSanitizedUri(), args.Reason); return Task.CompletedTask; } @@ -174,13 +178,13 @@ private Task HandleUnBlockedAsync(object sender, AsyncEventArgs args) { if (Connection.AmpqUri is null) throw new ConfigurationException("RMQMessagingGateway: No AMPQ URI specified"); - Log.SubscriptionUnblocked(s_logger, Connection.AmpqUri.GetSanitizedUri()); + Log.SubscriptionUnblocked(_logger, Connection.AmpqUri.GetSanitizedUri()); return Task.CompletedTask; } protected async Task ResetConnectionToBrokerAsync(CancellationToken cancellationToken = default) { - await new RmqMessageGatewayConnectionPool(Connection.Name, Connection.Heartbeat).ResetConnectionAsync(_connectionFactory, cancellationToken); + await new RmqMessageGatewayConnectionPool(Connection.Name, Connection.Heartbeat, LoggerFactory).ResetConnectionAsync(_connectionFactory, cancellationToken); } ~RmqMessageGateway() @@ -199,7 +203,7 @@ public virtual async ValueTask DisposeAsync() Channel = null; } - await new RmqMessageGatewayConnectionPool(Connection.Name, Connection.Heartbeat).RemoveConnectionAsync(_connectionFactory); + await new RmqMessageGatewayConnectionPool(Connection.Name, Connection.Heartbeat, LoggerFactory).RemoveConnectionAsync(_connectionFactory); } protected virtual void Dispose(bool disposing) @@ -212,7 +216,7 @@ protected virtual void Dispose(bool disposing) Channel?.Dispose(); Channel = null; - new RmqMessageGatewayConnectionPool(Connection.Name, Connection.Heartbeat).RemoveConnectionAsync(_connectionFactory) + new RmqMessageGatewayConnectionPool(Connection.Name, Connection.Heartbeat, LoggerFactory).RemoveConnectionAsync(_connectionFactory) .GetAwaiter() .GetResult(); } diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageGatewayConnectionPool.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageGatewayConnectionPool.cs index 9d7c6b37be..a7a9ea223e 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageGatewayConnectionPool.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageGatewayConnectionPool.cs @@ -28,7 +28,7 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Tasks; using RabbitMQ.Client; using RabbitMQ.Client.Events; @@ -39,12 +39,12 @@ namespace Paramore.Brighter.MessagingGateway.RMQ.Async; /// /// Class MessageGatewayConnectionPool. /// -public partial class RmqMessageGatewayConnectionPool(string connectionName, ushort connectionHeartbeat) +public partial class RmqMessageGatewayConnectionPool(string connectionName, ushort connectionHeartbeat, ILoggerFactory? loggerFactory = null) { private static readonly Dictionary s_connectionPool = new(); private static readonly SemaphoreSlim s_lock = new SemaphoreSlim(1, 1); - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); private static readonly Random jitter = new Random(); /// @@ -99,7 +99,7 @@ public async Task ResetConnectionAsync(ConnectionFactory connectionFactory, Canc } catch (BrokerUnreachableException exception) { - Log.FailedToResetSubscriptionToRabbitMqEndpoint(s_logger, connectionFactory.Endpoint, exception); + Log.FailedToResetSubscriptionToRabbitMqEndpoint(_logger, connectionFactory.Endpoint, exception); } } finally @@ -133,7 +133,7 @@ private async Task CreateConnectionAsync(ConnectionFactory con await TryRemoveConnectionAsync(connectionId).ConfigureAwait(false); - Log.CreatingSubscriptionToRabbitMqEndpoint(s_logger, connectionFactory.Endpoint); + Log.CreatingSubscriptionToRabbitMqEndpoint(_logger, connectionFactory.Endpoint); connectionFactory.RequestedHeartbeat = TimeSpan.FromSeconds(connectionHeartbeat); connectionFactory.RequestedConnectionTimeout = TimeSpan.FromMilliseconds(5000); @@ -142,12 +142,12 @@ private async Task CreateConnectionAsync(ConnectionFactory con var connection = await connectionFactory.CreateConnectionAsync(connectionName, cancellationToken).ConfigureAwait(false); - Log.NewConnectedToAddedToPool(s_logger, connection.Endpoint, connection.ClientProvidedName); + Log.NewConnectedToAddedToPool(_logger, connection.Endpoint, connection.ClientProvidedName); async Task ShutdownHandler(object sender, ShutdownEventArgs e) { - Log.SubscriptionHasBeenShutdown(s_logger, connection.Endpoint, e.ToString()); + Log.SubscriptionHasBeenShutdown(_logger, connection.Endpoint, e.ToString()); try { diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageProducer.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageProducer.cs index c60d759c61..0537876a9d 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageProducer.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageProducer.cs @@ -32,8 +32,8 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; using Paramore.Brighter.Tasks; using RabbitMQ.Client; @@ -49,7 +49,7 @@ namespace Paramore.Brighter.MessagingGateway.RMQ.Async; public partial class RmqMessageProducer : RmqMessageGateway, IAmAMessageProducerSync, IAmAMessageProducerAsync, ISupportPublishConfirmation { private readonly InstrumentationOptions _instrumentationOptions; - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; // Used to bound the active-send wait when the user opts out of confirms (timeout=0). // Active sends in flight at dispose time should not be aborted: outbox would mark them Dispatched @@ -97,9 +97,10 @@ public Publication Publication /// /// The subscription information needed to talk to RMQ /// The for how deep should the instrumentation go? + /// The used to create a logger; defaults to /// Make Channels = Create - public RmqMessageProducer(RmqMessagingGatewayConnection connection, InstrumentationOptions instrumentationOptions = InstrumentationOptions.All) - : this(connection, new RmqPublication { MakeChannels = OnMissingChannel.Create }) + public RmqMessageProducer(RmqMessagingGatewayConnection connection, InstrumentationOptions instrumentationOptions = InstrumentationOptions.All, ILoggerFactory? loggerFactory = null) + : this(connection, new RmqPublication { MakeChannels = OnMissingChannel.Create }, loggerFactory) { _instrumentationOptions = instrumentationOptions; } @@ -111,9 +112,11 @@ public RmqMessageProducer(RmqMessagingGatewayConnection connection, Instrumentat /// How should we configure this producer. If not provided use default behaviours: /// Make Channels = Create /// - public RmqMessageProducer(RmqMessagingGatewayConnection connection, RmqPublication? publication) - : base(connection) + /// The used to create a logger; defaults to + public RmqMessageProducer(RmqMessagingGatewayConnection connection, RmqPublication? publication, ILoggerFactory? loggerFactory = null) + : base(connection, loggerFactory) { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); _publication = publication ?? new RmqPublication { MakeChannels = OnMissingChannel.Create }; _waitForConfirmsTimeOutInMilliseconds = _publication.WaitForConfirmsTimeOutInMilliseconds; } @@ -161,7 +164,7 @@ private async Task SendWithDelayAsync(Message message, TimeSpan? delay, bool use delay ??= TimeSpan.Zero; - Log.PreparingToSendAsync(s_logger, Connection.Exchange.Name); + Log.PreparingToSendAsync(_logger, Connection.Exchange.Name); var channelInitialized = Channel is not null; await EnsureBrokerAsync(makeExchange: _publication.MakeChannels, cancellationToken: cancellationToken); @@ -177,12 +180,12 @@ private async Task SendWithDelayAsync(Message message, TimeSpan? delay, bool use BrighterTracer.WriteProducerEvent(Span, MessagingSystem.RabbitMQ, message, _instrumentationOptions); - Log.PublishingMessageAsync(s_logger, Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri(), delay.Value.TotalMilliseconds, + Log.PublishingMessageAsync(_logger, Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri(), delay.Value.TotalMilliseconds, message.Header.Topic.Value, message.Persist, message.Id.Value, message.Body.Value); if (PublishesOnChannel(delay.Value)) { - var rmqMessagePublisher = new RmqMessagePublisher(Channel, Connection); + var rmqMessagePublisher = new RmqMessagePublisher(Channel, Connection, LoggerFactory); var deliveryTag = await Channel.GetNextPublishSequenceNumberAsync(cancellationToken); AddPendingConfirmation(deliveryTag, message.Id.Value); pendingDeliveryTag = deliveryTag; @@ -201,13 +204,13 @@ private async Task SendWithDelayAsync(Message message, TimeSpan? delay, bool use schedulerSync.Schedule(message, delay.Value); } - Log.PublishedMessageAsync(s_logger, Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri(), delay, + Log.PublishedMessageAsync(_logger, Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri(), delay, message.Header.Topic.Value, message.Persist, message.Id.Value, JsonSerializer.Serialize(message, JsonSerialisationOptions.Options), DateTime.UtcNow); } catch (IOException io) { - Log.ErrorTalkingToSocketAsync(s_logger, io, Connection.AmpqUri!.GetSanitizedUri()); + Log.ErrorTalkingToSocketAsync(_logger, io, Connection.AmpqUri!.GetSanitizedUri()); ClearPendingConfirmations(); // ClearPendingConfirmations removed the orphan; suppress the per-tag cleanup in finally. pendingDeliveryTag = null; @@ -349,7 +352,7 @@ private async Task WaitForActiveSendsAsync() if (activeSends == 0) return; - Log.FailedToAwaitActiveSends(s_logger, activeSends, waitMilliseconds); + Log.FailedToAwaitActiveSends(_logger, activeSends, waitMilliseconds); } private void WaitForPendingPublisherConfirmations() => BrighterAsyncContext.Run(WaitForPendingPublisherConfirmationsAsync); @@ -391,7 +394,7 @@ private async Task WaitForPendingPublisherConfirmationsAsync() if (pendingConfirmations == 0) return; - Log.FailedToAwaitPublisherConfirms(s_logger, pendingConfirmations, waitMilliseconds); + Log.FailedToAwaitPublisherConfirms(_logger, pendingConfirmations, waitMilliseconds); } private void AddPendingConfirmation(ulong deliveryTag, string messageId) @@ -478,7 +481,7 @@ private Task OnPublishFailed(object sender, BasicNackEventArgs e) foreach (var messageId in RemovePendingConfirmations(e.DeliveryTag, e.Multiple)) { OnMessagePublished?.Invoke(false, messageId); - Log.FailedToPublishMessageAsync(s_logger, messageId); + Log.FailedToPublishMessageAsync(_logger, messageId); } return Task.CompletedTask; @@ -489,7 +492,7 @@ private Task OnPublishSucceeded(object sender, BasicAckEventArgs e) foreach (var messageId in RemovePendingConfirmations(e.DeliveryTag, e.Multiple)) { OnMessagePublished?.Invoke(true, messageId); - Log.PublishedMessage(s_logger, messageId); + Log.PublishedMessage(_logger, messageId); } return Task.CompletedTask; diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageProducerFactory.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageProducerFactory.cs index 4fc906555d..12acbd1af8 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageProducerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessageProducerFactory.cs @@ -26,6 +26,7 @@ THE SOFTWARE. */ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace Paramore.Brighter.MessagingGateway.RMQ.Async { @@ -34,9 +35,11 @@ namespace Paramore.Brighter.MessagingGateway.RMQ.Async /// /// The connection to use to connect to RabbitMQ /// The publications describing the RabbitMQ topics that we want to use + /// The used to create loggers for the producers public class RmqMessageProducerFactory( RmqMessagingGatewayConnection connection, - IEnumerable publications) + IEnumerable publications, + ILoggerFactory? loggerFactory = null) : IAmAMessageProducerFactory { /// @@ -51,7 +54,7 @@ public Dictionary Create() { if (publication.Topic is null) throw new ConfigurationException("RmqMessageProducerFactory.Create => An RmqPublication must have a topic/routing key"); - var messageProducer = new RmqMessageProducer(connection, publication); + var messageProducer = new RmqMessageProducer(connection, publication, loggerFactory); messageProducer.Publication = publication; var producerKey = new ProducerKey(publication.Topic, publication.Type); if (producers.ContainsKey(producerKey)) diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessagePublisher.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessagePublisher.cs index 00400e3864..75e752e6d0 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessagePublisher.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Async/RmqMessagePublisher.cs @@ -31,8 +31,8 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Extensions; -using Paramore.Brighter.Logging; using RabbitMQ.Client; namespace Paramore.Brighter.MessagingGateway.RMQ.Async; @@ -42,7 +42,7 @@ namespace Paramore.Brighter.MessagingGateway.RMQ.Async; /// internal sealed partial class RmqMessagePublisher { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private static readonly HashSet _headersToReset = [ @@ -62,12 +62,13 @@ internal sealed partial class RmqMessagePublisher /// /// The channel. /// The exchange we want to talk to. + /// The used to create a logger; defaults to /// /// channel /// or /// exchangeName /// - public RmqMessagePublisher(IChannel channel, RmqMessagingGatewayConnection connection) + public RmqMessagePublisher(IChannel channel, RmqMessagingGatewayConnection connection, ILoggerFactory? loggerFactory = null) { if (channel is null) { @@ -79,6 +80,8 @@ public RmqMessagePublisher(IChannel channel, RmqMessagingGatewayConnection conne throw new ArgumentNullException(nameof(connection)); } + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + _connection = connection; _channel = channel; @@ -131,7 +134,7 @@ public async Task RequeueMessageAsync(Message message, ChannelName queueName, Ti var messageId = Uuid.NewAsString(); const string deliveryTag = "1"; - Log.RegeneratingMessage(s_logger, message.Id.Value, deliveryTag, messageId, 1); + Log.RegeneratingMessage(_logger, message.Id.Value, deliveryTag, messageId, 1); Dictionary headers = AddCloudEventsHeaders(message); diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/ConnectionPoolFactory.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/ConnectionPoolFactory.cs index 9600423173..c8faceb5d0 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/ConnectionPoolFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/ConnectionPoolFactory.cs @@ -24,7 +24,7 @@ THE SOFTWARE. */ using System; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Polly; using RabbitMQ.Client.Exceptions; @@ -35,22 +35,25 @@ namespace Paramore.Brighter.MessagingGateway.RMQ.Sync /// public partial class ConnectionPolicyFactory { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// - public ConnectionPolicyFactory() - : this(new RmqMessagingGatewayConnection()) + public ConnectionPolicyFactory(ILoggerFactory? loggerFactory = null) + : this(new RmqMessagingGatewayConnection(), loggerFactory) {} /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// Use if you need to inject a test logger /// /// - public ConnectionPolicyFactory(RmqMessagingGatewayConnection connection) + /// The used to create a logger; defaults to + public ConnectionPolicyFactory(RmqMessagingGatewayConnection connection, ILoggerFactory? loggerFactory = null) { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + if (connection.AmpqUri is null) throw new ConfigurationException("ConnectionPolicyFactory ctor: RmqMessagingGatewayConnection.AmpqUri is not set"); @@ -71,11 +74,11 @@ public ConnectionPolicyFactory(RmqMessagingGatewayConnection connection) { if (exception is BrokerUnreachableException) { - Log.BrokerUnreachableException(s_logger, exception, context["queueName"].ToString(), connection.Exchange.Name, connection.AmpqUri.GetSanitizedUri(), retries); + Log.BrokerUnreachableException(_logger, exception, context["queueName"].ToString(), connection.Exchange.Name, connection.AmpqUri.GetSanitizedUri(), retries); } else { - Log.ExceptionOnSubscription(s_logger, exception, context["queueName"].ToString(), connection.Exchange.Name, connection.AmpqUri.GetSanitizedUri()); + Log.ExceptionOnSubscription(_logger, exception, context["queueName"].ToString(), connection.Exchange.Name, connection.AmpqUri.GetSanitizedUri()); throw new ChannelFailureException($"RMQMessagingGateway: Exception on subscription to queue { context["queueName"]} via exchange {connection.Exchange.Name} on subscription {connection.AmpqUri.GetSanitizedUri()}", exception); } diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/PullConsumer.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/PullConsumer.cs index 5013a06af8..d67a923e58 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/PullConsumer.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/PullConsumer.cs @@ -27,7 +27,7 @@ THE SOFTWARE. */ using System.Collections.Concurrent; using System.Threading; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using RabbitMQ.Client; using RabbitMQ.Client.Events; @@ -35,15 +35,17 @@ namespace Paramore.Brighter.MessagingGateway.RMQ.Sync { public partial class PullConsumer : DefaultBasicConsumer { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); - + private readonly ILogger _logger; + //we do end up creating a second buffer to the Brighter Channel, but controlling the flow from RMQ depends //on us being able to buffer up to the set QoS and then pull. This matches other implementations. private readonly ConcurrentQueue _messages = new ConcurrentQueue(); - public PullConsumer(IModel channel, ushort batchSize) + public PullConsumer(IModel channel, ushort batchSize, ILoggerFactory? loggerFactory = null) : base(channel) { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + //set the number of messages to fetch -- defaults to 1 unless set on subscription, no impact on //BasicGet, only works on BasicConsume channel.BasicQos(0, batchSize, false); @@ -122,7 +124,7 @@ public override void OnCancel(params string[] consumerTags) catch (Exception e) { //don't impede shutdown, just log - Log.NackUnhandledMessagesOnShutdownFailed(s_logger, e.Message); + Log.NackUnhandledMessagesOnShutdownFailed(_logger, e.Message); } base.OnCancel(); diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageConsumer.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageConsumer.cs index d09a39a5b7..79fd34c901 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageConsumer.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageConsumer.cs @@ -31,8 +31,8 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Polly.CircuitBreaker; using RabbitMQ.Client.Exceptions; @@ -49,7 +49,8 @@ namespace Paramore.Brighter.MessagingGateway.RMQ.Sync /// public partial class RmqMessageConsumer : RmqMessageGateway, IAmAMessageConsumerSync { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; + private readonly RmqMessageCreator _messageCreator; private PullConsumer? _consumer; private RmqMessageProducer? _requeueProducer; @@ -85,6 +86,7 @@ public partial class RmqMessageConsumer : RmqMessageGateway, IAmAMessageConsumer /// How lare can the buffer grow before we stop accepting new work? /// Should we validate, or create missing channels /// Optional scheduler for delayed message delivery when native delay is not supported + /// The used to create a logger; defaults to public RmqMessageConsumer( RmqMessagingGatewayConnection connection, ChannelName queueName, @@ -97,9 +99,10 @@ public RmqMessageConsumer( TimeSpan? ttl = null, int? maxQueueLength = null, OnMissingChannel makeChannels = OnMissingChannel.Create, - IAmAMessageScheduler? scheduler = null) + IAmAMessageScheduler? scheduler = null, + ILoggerFactory? loggerFactory = null) : this(connection, queueName, new RoutingKeys([routingKey]), isDurable, highAvailability, - batchSize, deadLetterQueueName, deadLetterRoutingKey, ttl, maxQueueLength, makeChannels, scheduler) + batchSize, deadLetterQueueName, deadLetterRoutingKey, ttl, maxQueueLength, makeChannels, scheduler, loggerFactory) { } @@ -118,6 +121,7 @@ public RmqMessageConsumer( /// The maximum number of messages on the queue before we begin to reject publication of messages /// Should we validate or create missing channels /// Optional scheduler for delayed message delivery when native delay is not supported + /// The used to create a logger; defaults to public RmqMessageConsumer( RmqMessagingGatewayConnection connection, ChannelName queueName, @@ -130,9 +134,12 @@ public RmqMessageConsumer( TimeSpan? ttl = null, int? maxQueueLength = null, OnMissingChannel makeChannels = OnMissingChannel.Create, - IAmAMessageScheduler? scheduler = null) - : base(connection) + IAmAMessageScheduler? scheduler = null, + ILoggerFactory? loggerFactory = null) + : base(connection, loggerFactory) { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + _messageCreator = new RmqMessageCreator(LoggerFactory.CreateLogger()); _queueName = queueName; _routingKeys = routingKeys; _isDurable = isDurable; @@ -159,13 +166,13 @@ public void Acknowledge(Message message) try { EnsureBroker(); - Log.AcknowledgingMessage(s_logger, message.Id.Value, deliveryTag); + Log.AcknowledgingMessage(_logger, message.Id.Value, deliveryTag); //NOTE: Ensure Broker will create a channel if it is not already created Channel!.BasicAck(deliveryTag, false); } catch (Exception exception) { - Log.ErrorAcknowledgingMessage(s_logger, exception, message.Id.Value, deliveryTag); + Log.ErrorAcknowledgingMessage(_logger, exception, message.Id.Value, deliveryTag); throw; } } @@ -179,7 +186,7 @@ public void Purge() { //Why bind a queue? Because we use purge to initialize a queue for RPC EnsureChannel(); - Log.PurgingChannel(s_logger, _queueName.Value); + Log.PurgingChannel(_logger, _queueName.Value); //NOTE: Ensure Broker will create a channel if it is not already created try { Channel!.QueuePurge(_queueName.Value); } @@ -192,7 +199,7 @@ public void Purge() } catch (Exception exception) { - Log.ErrorPurgingChannel(s_logger, exception, _queueName.Value); + Log.ErrorPurgingChannel(_logger, exception, _queueName.Value); throw; } } @@ -207,12 +214,12 @@ public void Nack(Message message) try { EnsureBroker(); - Log.NackingMessage(s_logger, message.Id.Value, deliveryTag); + Log.NackingMessage(_logger, message.Id.Value, deliveryTag); Channel!.BasicNack(deliveryTag, false, true); } catch (Exception exception) { - Log.ErrorNackingMessage(s_logger, exception, message.Id.Value, deliveryTag); + Log.ErrorNackingMessage(_logger, exception, message.Id.Value, deliveryTag); throw; } } @@ -232,7 +239,7 @@ public Message[] Receive(TimeSpan? timeOut = null) if (Connection.AmpqUri is null) throw new InvalidOperationException("RmqMessageConsumer.Receive - value of Connection.AmpqUri cannot be null"); - Log.PreparingToRetrieveMessage(s_logger, _queueName.Value, string.Join(";", _routingKeys.Select(rk => rk.Value)), Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri()); + Log.PreparingToRetrieveMessage(_logger, _queueName.Value, string.Join(";", _routingKeys.Select(rk => rk.Value)), Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri()); timeOut ??= TimeSpan.FromMilliseconds(5); @@ -248,10 +255,10 @@ public Message[] Receive(TimeSpan? timeOut = null) var messages = new Message[resultCount]; for (var i = 0; i < resultCount; i++) { - var message = RmqMessageCreator.CreateMessage(results[i]); + var message = _messageCreator.CreateMessage(results[i]); messages[i] = message; - Log.ReceivedMessage(s_logger, _queueName.Value, string.Join(";", _routingKeys.Select(rk => rk.Value)), Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri(), JsonSerializer.Serialize(message, JsonSerialisationOptions.Options)); + Log.ReceivedMessage(_logger, _queueName.Value, string.Join(";", _routingKeys.Select(rk => rk.Value)), Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri(), JsonSerializer.Serialize(message, JsonSerialisationOptions.Options)); } return messages; @@ -296,7 +303,7 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) var reasonString = reason is null ? nameof(RejectionReason.DeliveryError) : reason.RejectionReason.ToString(); var description = reason is null ? "unknown" : reason.Description ?? "unknown"; - Log.NoAckMessage(s_logger, message.Id.Value, message.DeliveryTag, reasonString, description); + Log.NoAckMessage(_logger, message.Id.Value, message.DeliveryTag, reasonString, description); //if we have a DLQ, this will force over to the DLQ Channel!.BasicReject(message.DeliveryTag, false); @@ -304,7 +311,7 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) } catch (Exception exception) { - Log.ErrorNoAckMessage(s_logger, exception, message.Id.Value); + Log.ErrorNoAckMessage(_logger, exception, message.Id.Value); throw; } } @@ -337,7 +344,7 @@ public bool Requeue(Message message, TimeSpan? timeout = null) try { - Log.RequeueingMessage(s_logger, message.Id.Value, timeout.Value.TotalMilliseconds); + Log.RequeueingMessage(_logger, message.Id.Value, timeout.Value.TotalMilliseconds); EnsureBroker(_queueName); // Step 1: Publish the message back to the queue first. @@ -345,7 +352,7 @@ public bool Requeue(Message message, TimeSpan? timeout = null) // timeout is guaranteed non-null here due to the ??= TimeSpan.Zero coalescing at the top of this method if (DelaySupported || timeout <= TimeSpan.Zero) { - var rmqMessagePublisher = new RmqMessagePublisher(Channel!, Connection); + var rmqMessagePublisher = new RmqMessagePublisher(Channel!, Connection, LoggerFactory); rmqMessagePublisher.RequeueMessage(message, _queueName, timeout.Value); } else @@ -358,7 +365,7 @@ public bool Requeue(Message message, TimeSpan? timeout = null) // If this fails after a successful publish, the message may be duplicated (not lost). // Consumers should be idempotent to handle potential duplicates. var deliveryTag = message.DeliveryTag; - Log.DeletingMessage(s_logger, message.Id.Value, deliveryTag); + Log.DeletingMessage(_logger, message.Id.Value, deliveryTag); Channel!.BasicAck(deliveryTag, false); @@ -366,7 +373,7 @@ public bool Requeue(Message message, TimeSpan? timeout = null) } catch (Exception exception) { - Log.ErrorRequeueingMessage(s_logger, exception, message.Id.Value); + Log.ErrorRequeueingMessage(_logger, exception, message.Id.Value); return false; } } @@ -377,7 +384,7 @@ private void EnsureProducer() { #pragma warning disable CS0420 // LazyInitializer handles the memory barrier for the volatile field LazyInitializer.EnsureInitialized(ref _requeueProducer, ref _requeueProducerInitialized, - ref _requeueProducerLock, () => new RmqMessageProducer(Connection) + ref _requeueProducerLock, () => new RmqMessageProducer(Connection, loggerFactory: LoggerFactory) { Scheduler = _scheduler }); @@ -413,7 +420,7 @@ protected virtual void EnsureChannel() CreateConsumer(); - Log.CreatedChannel(s_logger, Channel!.ChannelNumber, _queueName.Value, string.Join(";", _routingKeys.Select(rk => rk.Value)), Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri()); + Log.CreatedChannel(_logger, Channel!.ChannelNumber, _queueName.Value, string.Join(";", _routingKeys.Select(rk => rk.Value)), Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri()); } } @@ -441,11 +448,11 @@ private void CreateConsumer() if (Connection.AmpqUri is null) throw new InvalidOperationException("RmqMessageConsumer.CreateConsumer - value of Connection.AmpqUri cannot be null"); - _consumer = new PullConsumer(Channel, _batchSize); + _consumer = new PullConsumer(Channel, _batchSize, LoggerFactory); Channel.BasicConsume(_queueName.Value, false, _consumerTag, false, false, SetQueueArguments(), _consumer); - Log.CreatedConsumer(s_logger, _queueName.Value, string.Join(";", _routingKeys.Select(rk => rk.Value)), Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri()); + Log.CreatedConsumer(_logger, _queueName.Value, string.Join(";", _routingKeys.Select(rk => rk.Value)), Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri()); } private void CreateQueue() @@ -456,7 +463,7 @@ private void CreateQueue() if (Connection.AmpqUri is null) throw new InvalidOperationException("RmqMessageConsumer.CreateQueue - value of Connection.AmpqUri cannot be null"); - Log.CreatingQueue(s_logger, _queueName.Value, Connection.AmpqUri.GetSanitizedUri()); + Log.CreatingQueue(_logger, _queueName.Value, Connection.AmpqUri.GetSanitizedUri()); Channel.QueueDeclare(_queueName.Value, _isDurable, false, false, SetQueueArguments()); //NOTE: hasDlq cannot be true if _deadLetterQueuename is null if (_hasDlq) Channel.QueueDeclare(_deadLetterQueueName!.Value, _isDurable, false, false, new Dictionary()); @@ -482,7 +489,7 @@ private void BindQueue() private void HandleException(Exception exception, bool resetConnection = false) { - Log.ErrorListeningToQueue(s_logger, exception, _queueName.Value, string.Join(";", _routingKeys.Select(rk => rk.Value)), Connection.Exchange?.Name ?? string.Empty, Connection.AmpqUri?.GetSanitizedUri() ?? string.Empty); + Log.ErrorListeningToQueue(_logger, exception, _queueName.Value, string.Join(";", _routingKeys.Select(rk => rk.Value)), Connection.Exchange?.Name ?? string.Empty, Connection.AmpqUri?.GetSanitizedUri() ?? string.Empty); if (resetConnection) ResetConnectionToBroker(); throw new ChannelFailureException("Error connecting to RabbitMQ, see inner exception for details", exception); } @@ -492,7 +499,7 @@ private void ValidateQueue() if (Channel == null) throw new InvalidOperationException("RmqMessageConsumer.ValidateQueue - value of Channel cannot be null"); - Log.ValidatingQueue(s_logger, _queueName.Value, Connection.AmpqUri!.GetSanitizedUri()); + Log.ValidatingQueue(_logger, _queueName.Value, Connection.AmpqUri!.GetSanitizedUri()); try { diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageConsumerFactory.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageConsumerFactory.cs index 8dd8bfecee..6f14fcaea2 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageConsumerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageConsumerFactory.cs @@ -22,11 +22,14 @@ THE SOFTWARE. */ #endregion +using Microsoft.Extensions.Logging; + namespace Paramore.Brighter.MessagingGateway.RMQ.Sync { public class RmqMessageConsumerFactory : IAmAMessageConsumerFactory { private readonly RmqMessagingGatewayConnection _rmqConnection; + private readonly ILoggerFactory? _loggerFactory; private IAmAMessageScheduler? _scheduler; /// @@ -44,10 +47,12 @@ public IAmAMessageScheduler? Scheduler /// /// The subscription to the broker hosting the queue /// The optional message scheduler for delayed requeue support - public RmqMessageConsumerFactory(RmqMessagingGatewayConnection rmqConnection, IAmAMessageScheduler? scheduler = null) + /// The used to create loggers for the consumers + public RmqMessageConsumerFactory(RmqMessagingGatewayConnection rmqConnection, IAmAMessageScheduler? scheduler = null, ILoggerFactory? loggerFactory = null) { _rmqConnection = rmqConnection; _scheduler = scheduler; + _loggerFactory = loggerFactory; } /// @@ -73,7 +78,8 @@ public IAmAMessageConsumerSync Create(Subscription subscription) rmqSubscription.Ttl, rmqSubscription.MaxQueueLength, subscription.MakeChannels, - scheduler: _scheduler); + scheduler: _scheduler, + loggerFactory: _loggerFactory); } /// diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageCreator.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageCreator.cs index 5ba3d94fd2..ee2f582846 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageCreator.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageCreator.cs @@ -29,8 +29,8 @@ THE SOFTWARE. */ using System.Net.Mime; using System.Text; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Extensions; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; using RabbitMQ.Client; using RabbitMQ.Client.Events; @@ -39,9 +39,14 @@ namespace Paramore.Brighter.MessagingGateway.RMQ.Sync; internal sealed partial class RmqMessageCreator { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; - public static Message CreateMessage(BasicDeliverEventArgs fromQueue) + public RmqMessageCreator(ILogger? logger = null) + { + _logger = logger ?? NullLogger.Instance; + } + + public Message CreateMessage(BasicDeliverEventArgs fromQueue) { var headers = fromQueue.BasicProperties.Headers ?? new Dictionary(); var topic = HeaderResult.Empty(); @@ -62,14 +67,14 @@ public static Message CreateMessage(BasicDeliverEventArgs fromQueue) } catch (Exception e) { - Log.FailedToCreateMessageFromAmqpMessage(s_logger, e); + Log.FailedToCreateMessageFromAmqpMessage(_logger, e); message = Message.FailureMessage(topic.Result, messageId.Result); } return message; } - private static MessageHeader CreateMessageHeader(BasicDeliverEventArgs fromQueue, IDictionary headers, + private MessageHeader CreateMessageHeader(BasicDeliverEventArgs fromQueue, IDictionary headers, HeaderResult topic, HeaderResult messageId) { var timeStamp = ReadTimeStamp(fromQueue.BasicProperties); @@ -113,12 +118,12 @@ private static MessageHeader CreateMessageHeader(BasicDeliverEventArgs fromQueue ); } - private static void ProcessHeaderBag(IDictionary headers, Message message) + private void ProcessHeaderBag(IDictionary headers, Message message) { headers.Each(header => message.Header.Bag.Add(header.Key, ParseHeaderValue(header.Value))); } - private static void SetMessageMetadata(BasicDeliverEventArgs fromQueue, Message message) + private void SetMessageMetadata(BasicDeliverEventArgs fromQueue, Message message) { var deliveryTag = ReadDeliveryTag(fromQueue.DeliveryTag); var redelivered = ReadRedeliveredFlag(fromQueue.Redelivered); @@ -128,7 +133,7 @@ private static void SetMessageMetadata(BasicDeliverEventArgs fromQueue, Message message.Persist = fromQueue.BasicProperties.DeliveryMode == 2; } - private static HeaderResult ReadHeader(IDictionary dict, string key, bool dieOnMissing = false) + private HeaderResult ReadHeader(IDictionary dict, string key, bool dieOnMissing = false) { if (false == dict.TryGetValue(key, out object? value)) { @@ -137,7 +142,7 @@ private static void SetMessageMetadata(BasicDeliverEventArgs fromQueue, Message if (!(value is byte[] bytes)) { - Log.HeaderValueCouldNotBeCastToByteArray(s_logger, key); + Log.HeaderValueCouldNotBeCastToByteArray(_logger, key); return new HeaderResult(null, false); } @@ -149,12 +154,12 @@ private static void SetMessageMetadata(BasicDeliverEventArgs fromQueue, Message catch (Exception e) { var firstTwentyBytes = BitConverter.ToString(bytes.Take(20).ToArray()); - Log.FailedToReadHeaderValueAsUtf8(s_logger, key, firstTwentyBytes, e); + Log.FailedToReadHeaderValueAsUtf8(_logger, key, firstTwentyBytes, e); return new HeaderResult(null, false); } } - private static HeaderResult ReadCorrelationId(IDictionary headers) + private HeaderResult ReadCorrelationId(IDictionary headers) { if (headers.TryGetValue(HeaderNames.CORRELATION_ID, out object? correlationHeader)) { @@ -165,12 +170,12 @@ private static void SetMessageMetadata(BasicDeliverEventArgs fromQueue, Message return new HeaderResult(null, false); } - private static HeaderResult ReadDeliveryTag(ulong deliveryTag) + private HeaderResult ReadDeliveryTag(ulong deliveryTag) { return new HeaderResult(deliveryTag, true); } - private static HeaderResult ReadTimeStamp(IBasicProperties basicProperties) + private HeaderResult ReadTimeStamp(IBasicProperties basicProperties) { if (basicProperties.IsTimestampPresent()) { @@ -188,7 +193,7 @@ private static HeaderResult ReadTimeStamp(IBasicProperties basic return new HeaderResult(DateTimeOffset.UtcNow, true); } - private static HeaderResult ReadMessageType(IDictionary headers) + private HeaderResult ReadMessageType(IDictionary headers) { return ReadHeader(headers, HeaderNames.MESSAGE_TYPE) .Map(s => @@ -203,7 +208,7 @@ private static HeaderResult ReadMessageType(IDictionary ReadHandledCount(IDictionary headers) + private HeaderResult ReadHandledCount(IDictionary headers) { if (headers.TryGetValue(HeaderNames.HANDLED_COUNT, out object? header) == false) { @@ -224,7 +229,7 @@ private static HeaderResult ReadHandledCount(IDictionary h } } - private static HeaderResult ReadDelay(IDictionary headers) + private HeaderResult ReadDelay(IDictionary headers) { if (headers.TryGetValue(HeaderNames.DELAYED_MILLISECONDS, out var delayedMsHeader) == false) { @@ -274,7 +279,7 @@ private static HeaderResult ReadDelay(IDictionary hea return new HeaderResult(TimeSpan.FromMilliseconds(delayedMilliseconds), true); } - private static HeaderResult ReadTopic(BasicDeliverEventArgs fromQueue, IDictionary headers) + private HeaderResult ReadTopic(BasicDeliverEventArgs fromQueue, IDictionary headers) { var res = ReadHeader(headers, HeaderNames.TOPIC).Map(s => { @@ -290,24 +295,24 @@ private static HeaderResult ReadDelay(IDictionary hea return new HeaderResult(new RoutingKey(fromQueue.RoutingKey), true); } - private static HeaderResult ReadMessageId(string? messageId) + private HeaderResult ReadMessageId(string? messageId) { if (string.IsNullOrEmpty(messageId)) { var newMessageId = Id.Random(); - Log.NoMessageIdFoundInMessage(s_logger, newMessageId.Value); + Log.NoMessageIdFoundInMessage(_logger, newMessageId.Value); return new HeaderResult(newMessageId, true); } return new HeaderResult(Id.Create(messageId), true); } - private static HeaderResult ReadRedeliveredFlag(bool redelivered) + private HeaderResult ReadRedeliveredFlag(bool redelivered) { return new HeaderResult(redelivered, true); } - private static HeaderResult ReadReplyTo(IBasicProperties basicProperties) + private HeaderResult ReadReplyTo(IBasicProperties basicProperties) { if (basicProperties.IsReplyToPresent()) { @@ -317,7 +322,7 @@ private static HeaderResult ReadRedeliveredFlag(bool redelivered) return new HeaderResult(null, true); } - private static HeaderResult ReadSource(IDictionary headers) + private HeaderResult ReadSource(IDictionary headers) { if (headers.TryGetValue(HeaderNames.CLOUD_EVENTS_SOURCE, out var source) && source is byte[] val @@ -329,7 +334,7 @@ private static HeaderResult ReadSource(IDictionary headers return new HeaderResult(new Uri(MessageHeader.DefaultSource), true); } - private static HeaderResult ReadType(IDictionary headers) + private HeaderResult ReadType(IDictionary headers) { if (headers.TryGetValue(HeaderNames.CLOUD_EVENTS_TYPE, out var type) && type is byte[] typeArray) @@ -340,7 +345,7 @@ private static HeaderResult ReadType(IDictionary(CloudEventsType.Empty, true); } - private static HeaderResult ReadSubject(IDictionary headers) + private HeaderResult ReadSubject(IDictionary headers) { if (headers.TryGetValue(HeaderNames.CLOUD_EVENTS_SUBJECT, out var subject) && subject is byte[] subjectArray) @@ -351,7 +356,7 @@ private static HeaderResult ReadType(IDictionary(null, true); } - private static HeaderResult ReadDataSchema(IDictionary headers) + private HeaderResult ReadDataSchema(IDictionary headers) { if (headers.TryGetValue(HeaderNames.CLOUD_EVENTS_DATA_SCHEMA, out var dataSchema) && dataSchema is byte[] dataSchemaArray @@ -363,7 +368,7 @@ private static HeaderResult ReadType(IDictionary(null, true); } - private static HeaderResult ReadTraceParent(IDictionary headers) + private HeaderResult ReadTraceParent(IDictionary headers) { if (headers.TryGetValue(HeaderNames.CLOUD_EVENTS_TRACE_PARENT, out var traceParent) && traceParent is byte[] traceParentArray) @@ -374,7 +379,7 @@ private static HeaderResult ReadType(IDictionary(string.Empty, true); } - private static HeaderResult ReadTraceState(IDictionary headers) + private HeaderResult ReadTraceState(IDictionary headers) { if (headers.TryGetValue(HeaderNames.CLOUD_EVENTS_TRACE_STATE, out var traceState) && traceState is byte[] traceParentArray) @@ -385,7 +390,7 @@ private static HeaderResult ReadType(IDictionary(string.Empty, true); } - private static HeaderResult ReadBaggage(IDictionary headers) + private HeaderResult ReadBaggage(IDictionary headers) { if (headers.TryGetValue(HeaderNames.W3C_BAGGAGE, out var traceParent) && traceParent is byte[] traceParentArray) @@ -396,7 +401,7 @@ private static HeaderResult ReadType(IDictionary(string.Empty, true); } - private static object ParseHeaderValue(object? value) + private object ParseHeaderValue(object? value) { if (value == null) return string.Empty; diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageGateway.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageGateway.cs index b1771e6389..59cad52f34 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageGateway.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageGateway.cs @@ -26,7 +26,7 @@ THE SOFTWARE. */ using System; using System.Collections.Generic; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Polly; using RabbitMQ.Client; using RabbitMQ.Client.Events; @@ -51,10 +51,11 @@ namespace Paramore.Brighter.MessagingGateway.RMQ.Sync /// public partial class RmqMessageGateway : IDisposable { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private readonly Policy _circuitBreakerPolicy; private readonly ConnectionFactory _connectionFactory; private readonly Policy _retryPolicy; + protected readonly ILoggerFactory LoggerFactory; protected readonly RmqMessagingGatewayConnection Connection; protected IModel? Channel; @@ -63,17 +64,20 @@ public partial class RmqMessageGateway : IDisposable /// Use if you need to inject a test logger /// /// The amqp uri and exchange to connect to - protected RmqMessageGateway(RmqMessagingGatewayConnection connection) + /// The used to create a logger; defaults to + protected RmqMessageGateway(RmqMessagingGatewayConnection connection, ILoggerFactory? loggerFactory = null) { + LoggerFactory = loggerFactory ?? NullLoggerFactory.Instance; + _logger = LoggerFactory.CreateLogger(); Connection = connection ?? throw new ArgumentNullException(nameof(connection)); - + if (Connection.AmpqUri is null) throw new InvalidOperationException("RMQMessagingGateway: Connection must have an AMPQ URI"); - + if (Connection.Exchange is null) throw new InvalidOperationException("RMQMessagingGateway: Connection must have an Exchange"); - var connectionPolicyFactory = new ConnectionPolicyFactory(Connection); + var connectionPolicyFactory = new ConnectionPolicyFactory(Connection, LoggerFactory); _retryPolicy = connectionPolicyFactory.RetryPolicy; _circuitBreakerPolicy = connectionPolicyFactory.CircuitBreakerPolicy; @@ -138,15 +142,15 @@ protected virtual void ConnectToBroker(OnMissingChannel makeExchange) if (Connection.AmpqUri is null) throw new InvalidOperationException("RMQMessagingGateway: Connection must have an AMPQ URI"); - var connection = new RmqMessageGatewayConnectionPool(Connection.Name, Connection.Heartbeat).GetConnection(_connectionFactory); - + var connection = new RmqMessageGatewayConnectionPool(Connection.Name, Connection.Heartbeat, LoggerFactory).GetConnection(_connectionFactory); + if (connection is null) throw new InvalidOperationException($"RMQMessagingGateway: Connection to {Connection.AmpqUri.GetSanitizedUri()} failed" ); connection.ConnectionBlocked += HandleBlocked; connection.ConnectionUnblocked += HandleUnBlocked; - Log.OpeningChannelToRabbitMq(s_logger, Connection.AmpqUri.GetSanitizedUri()); + Log.OpeningChannelToRabbitMq(_logger, Connection.AmpqUri.GetSanitizedUri()); Channel = connection.CreateModel(); @@ -157,12 +161,12 @@ protected virtual void ConnectToBroker(OnMissingChannel makeExchange) private void HandleBlocked(object? sender, ConnectionBlockedEventArgs args) { - Log.SubscriptionBlocked(s_logger, Connection.AmpqUri!.GetSanitizedUri(), args.Reason); + Log.SubscriptionBlocked(_logger, Connection.AmpqUri!.GetSanitizedUri(), args.Reason); } private void HandleUnBlocked(object? sender, EventArgs args) - { - Log.SubscriptionUnblocked(s_logger, Connection.AmpqUri!.GetSanitizedUri()); + { + Log.SubscriptionUnblocked(_logger, Connection.AmpqUri!.GetSanitizedUri()); } protected void ResetConnectionToBroker() @@ -170,7 +174,7 @@ protected void ResetConnectionToBroker() if (Connection.Name is null) throw new InvalidOperationException("RMQMessagingGateway: Connection must have a name"); - new RmqMessageGatewayConnectionPool(Connection.Name, Connection.Heartbeat).ResetConnection(_connectionFactory); + new RmqMessageGatewayConnectionPool(Connection.Name, Connection.Heartbeat, LoggerFactory).ResetConnection(_connectionFactory); } ~RmqMessageGateway() @@ -188,7 +192,7 @@ protected virtual void Dispose(bool disposing) Channel = null; if (Connection.Name is not null) - new RmqMessageGatewayConnectionPool(Connection.Name, Connection.Heartbeat).RemoveConnection(_connectionFactory); + new RmqMessageGatewayConnectionPool(Connection.Name, Connection.Heartbeat, LoggerFactory).RemoveConnection(_connectionFactory); } } diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageGatewayConnectionPool.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageGatewayConnectionPool.cs index ef4c1ed74a..2f69168fc5 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageGatewayConnectionPool.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageGatewayConnectionPool.cs @@ -26,7 +26,7 @@ THE SOFTWARE. */ using System.Collections.Generic; using System.Threading; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using RabbitMQ.Client; using RabbitMQ.Client.Exceptions; @@ -39,15 +39,16 @@ public partial class RmqMessageGatewayConnectionPool { private readonly string _connectionName; private readonly ushort _connectionHeartbeat; + private readonly ILogger _logger; private static readonly Dictionary s_connectionPool = new Dictionary(); private static readonly object s_lock = new object(); - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); private static readonly Random jitter = new Random(); - public RmqMessageGatewayConnectionPool(string connectionName, ushort connectionHeartbeat) + public RmqMessageGatewayConnectionPool(string connectionName, ushort connectionHeartbeat, ILoggerFactory? loggerFactory = null) { _connectionName = connectionName; _connectionHeartbeat = connectionHeartbeat; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } /// @@ -85,7 +86,7 @@ public void ResetConnection(ConnectionFactory connectionFactory) } catch (BrokerUnreachableException exception) { - Log.FailedToResetSubscriptionToRabbitMqEndpoint(s_logger, connectionFactory.Endpoint, exception); + Log.FailedToResetSubscriptionToRabbitMqEndpoint(_logger, connectionFactory.Endpoint, exception); } } } @@ -96,7 +97,7 @@ private PooledConnection CreateConnection(ConnectionFactory connectionFactory) TryRemoveConnection(connectionId); - Log.CreatingSubscriptionToRabbitMqEndpoint(s_logger, connectionFactory.Endpoint); + Log.CreatingSubscriptionToRabbitMqEndpoint(_logger, connectionFactory.Endpoint); connectionFactory.RequestedHeartbeat = TimeSpan.FromSeconds(_connectionHeartbeat); connectionFactory.RequestedConnectionTimeout = TimeSpan.FromMilliseconds(5000); @@ -105,12 +106,12 @@ private PooledConnection CreateConnection(ConnectionFactory connectionFactory) var connection = connectionFactory.CreateConnection(_connectionName); - Log.NewConnectedToAddedToPool(s_logger, connection.Endpoint, connection.ClientProvidedName); + Log.NewConnectedToAddedToPool(_logger, connection.Endpoint, connection.ClientProvidedName); void ShutdownHandler(object? sender, ShutdownEventArgs e) { - Log.SubscriptionHasBeenShutdown(s_logger, connection.Endpoint, e.ToString()); + Log.SubscriptionHasBeenShutdown(_logger, connection.Endpoint, e.ToString()); lock (s_lock) { diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageProducer.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageProducer.cs index 26ea0288bc..38a12ad899 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageProducer.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageProducer.cs @@ -31,8 +31,8 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; using Paramore.Brighter.Tasks; using RabbitMQ.Client.Events; @@ -51,7 +51,7 @@ namespace Paramore.Brighter.MessagingGateway.RMQ.Sync public partial class RmqMessageProducer : RmqMessageGateway, IAmAMessageProducerSync, IAmAMessageProducerAsync, ISupportPublishConfirmation { private readonly InstrumentationOptions _instrumentationOptions; - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; static readonly object s_lock = new(); private RmqPublication _publication; @@ -88,9 +88,10 @@ public Publication Publication /// /// The subscription information needed to talk to RMQ /// The for how deep should the instrumentation go? + /// The used to create a logger; defaults to /// Make Channels = Create - public RmqMessageProducer(RmqMessagingGatewayConnection connection, InstrumentationOptions instrumentationOptions = InstrumentationOptions.All) - : this(connection, new RmqPublication { MakeChannels = OnMissingChannel.Create }) + public RmqMessageProducer(RmqMessagingGatewayConnection connection, InstrumentationOptions instrumentationOptions = InstrumentationOptions.All, ILoggerFactory? loggerFactory = null) + : this(connection, new RmqPublication { MakeChannels = OnMissingChannel.Create }, loggerFactory) { _instrumentationOptions = instrumentationOptions; } @@ -102,9 +103,11 @@ public RmqMessageProducer(RmqMessagingGatewayConnection connection, Instrumentat /// How should we configure this producer. If not provided use default behaviours: /// Make Channels = Create /// - public RmqMessageProducer(RmqMessagingGatewayConnection connection, RmqPublication publication) - : base(connection) + /// The used to create a logger; defaults to + public RmqMessageProducer(RmqMessagingGatewayConnection connection, RmqPublication publication, ILoggerFactory? loggerFactory = null) + : base(connection, loggerFactory) { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); _publication = publication ?? new RmqPublication { MakeChannels = OnMissingChannel.Create }; _waitForConfirmsTimeOutInMilliseconds = _publication.WaitForConfirmsTimeOutInMilliseconds; } @@ -135,9 +138,9 @@ private void SendWithDelay(Message message, TimeSpan? delay, bool useSchedulerAs { EnsureBroker(makeExchange: _publication.MakeChannels); //NOTE: EnsureBroker will create a channel if one does not exist - Log.PreparingToSend(s_logger, Connection.Exchange!.Name); + Log.PreparingToSend(_logger, Connection.Exchange!.Name); - var rmqMessagePublisher = new RmqMessagePublisher(Channel!, Connection); + var rmqMessagePublisher = new RmqMessagePublisher(Channel!, Connection, LoggerFactory); message.Persist = Connection.PersistMessages; Channel!.BasicAcks += OnPublishSucceeded; @@ -147,7 +150,7 @@ private void SendWithDelay(Message message, TimeSpan? delay, bool useSchedulerAs BrighterTracer.WriteProducerEvent(Span, MessagingSystem.RabbitMQ, message, _instrumentationOptions); - Log.PublishingMessage(s_logger, Connection.Exchange.Name, Connection.AmpqUri!.GetSanitizedUri(), delay.Value.TotalMilliseconds, + Log.PublishingMessage(_logger, Connection.Exchange.Name, Connection.AmpqUri!.GetSanitizedUri(), delay.Value.TotalMilliseconds, message.Header.Topic.Value, message.Persist, message.Id.Value, message.Body.Value); _pendingConfirmations.TryAdd(Channel.NextPublishSeqNo, message.Id.Value); @@ -167,14 +170,14 @@ private void SendWithDelay(Message message, TimeSpan? delay, bool useSchedulerAs schedulerSync.Schedule(message, delay.Value); } - Log.PublishedMessage(s_logger, Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri(), delay, + Log.PublishedMessage(_logger, Connection.Exchange.Name, Connection.AmpqUri.GetSanitizedUri(), delay, message.Header.Topic.Value, message.Persist, message.Id.Value, JsonSerializer.Serialize(message, JsonSerialisationOptions.Options), DateTime.UtcNow); } } catch (IOException io) { - Log.ErrorTalkingToSocket(s_logger, io, Connection.AmpqUri!.GetSanitizedUri()); + Log.ErrorTalkingToSocket(_logger, io, Connection.AmpqUri!.GetSanitizedUri()); ResetConnectionToBroker(); throw new ChannelFailureException("Error talking to the broker, see inner exception for details", io); } @@ -221,7 +224,7 @@ protected override void Dispose(bool disposing) //As we are disposing, just let that happen Channel.WaitForConfirms(TimeSpan.FromMilliseconds(_waitForConfirmsTimeOutInMilliseconds), out bool timedOut); if (timedOut) - Log.FailedToAwaitPublisherConfirms(s_logger); + Log.FailedToAwaitPublisherConfirms(_logger); } } @@ -234,7 +237,7 @@ private void OnPublishFailed(object? sender, BasicNackEventArgs e) { OnMessagePublished?.Invoke(false, messageId); _pendingConfirmations.TryRemove(e.DeliveryTag, out string? _); - Log.FailedToPublishMessage(s_logger, messageId); + Log.FailedToPublishMessage(_logger, messageId); } } @@ -244,7 +247,7 @@ private void OnPublishSucceeded(object? sender, BasicAckEventArgs e) { OnMessagePublished?.Invoke(true, messageId); _pendingConfirmations.TryRemove(e.DeliveryTag, out string? _); - Log.PublishedMessageInformation(s_logger, messageId); + Log.PublishedMessageInformation(_logger, messageId); } } diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageProducerFactory.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageProducerFactory.cs index 05569d19ce..8aa7f107a7 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageProducerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessageProducerFactory.cs @@ -24,6 +24,7 @@ THE SOFTWARE. */ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace Paramore.Brighter.MessagingGateway.RMQ.Sync { @@ -36,9 +37,11 @@ namespace Paramore.Brighter.MessagingGateway.RMQ.Sync /// /// The connection to use to connect to RabbitMQ /// The publications describing the RabbitMQ topics that we want to use + /// The used to create loggers for the producers public class RmqMessageProducerFactory( RmqMessagingGatewayConnection connection, - IEnumerable publications) + IEnumerable publications, + ILoggerFactory? loggerFactory = null) : IAmAMessageProducerFactory { /// @@ -53,7 +56,7 @@ public Dictionary Create() { if (publication.Topic is null) throw new ConfigurationException("RmqMessageProducerFactory.Create => An RmqPublication must have a topic/routing key"); - var messageProducer = new RmqMessageProducer(connection, publication); + var messageProducer = new RmqMessageProducer(connection, publication, loggerFactory); messageProducer.Publication = publication; var producerKey = new ProducerKey(publication.Topic, publication.Type); if (producers.ContainsKey(producerKey)) diff --git a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessagePublisher.cs b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessagePublisher.cs index 645315051c..fb4b5ac886 100644 --- a/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessagePublisher.cs +++ b/src/Paramore.Brighter.MessagingGateway.RMQ.Sync/RmqMessagePublisher.cs @@ -28,8 +28,8 @@ THE SOFTWARE. */ using System.Linq; using System.Net.Mime; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Extensions; -using Paramore.Brighter.Logging; using RabbitMQ.Client; namespace Paramore.Brighter.MessagingGateway.RMQ.Sync @@ -39,7 +39,7 @@ namespace Paramore.Brighter.MessagingGateway.RMQ.Sync /// internal sealed partial class RmqMessagePublisher { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private static readonly HashSet _headersToReset = [ HeaderNames.DELAY_MILLISECONDS, @@ -58,15 +58,17 @@ internal sealed partial class RmqMessagePublisher /// /// The channel. /// The exchange we want to talk to. + /// The used to create a logger; defaults to /// /// channel /// or /// exchangeName /// - public RmqMessagePublisher(IModel channel, RmqMessagingGatewayConnection connection) + public RmqMessagePublisher(IModel channel, RmqMessagingGatewayConnection connection, ILoggerFactory? loggerFactory = null) { _connection = connection ?? throw new ArgumentNullException(nameof(connection)); _channel = channel ?? throw new ArgumentNullException(nameof(channel)); + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } /// @@ -116,7 +118,7 @@ public void RequeueMessage(Message message, ChannelName queueName, TimeSpan time var messageId = Uuid.NewAsString(); const string deliveryTag = "1"; - Log.RequeueMessageInformation(s_logger, message.Id.Value, deliveryTag, messageId, 1); + Log.RequeueMessageInformation(_logger, message.Id.Value, deliveryTag, messageId, 1); Dictionary headers = AddCloudEventHeaders(message); diff --git a/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageConsumer.cs b/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageConsumer.cs index 41b0c3a55f..a6fa8c8df2 100644 --- a/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageConsumer.cs +++ b/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageConsumer.cs @@ -30,8 +30,8 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using ServiceStack.Redis; namespace Paramore.Brighter.MessagingGateway.Redis @@ -41,7 +41,9 @@ public partial class RedisMessageConsumer : RedisMessageGateway, IAmAMessageCons /* see RedisMessageProducer to understand how we are using a dynamic recipient list model with Redis */ - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; + private readonly ILoggerFactory? _loggerFactory; + private readonly RedisMessageCreator _messageCreator; private const string QUEUES = "queues"; private readonly ChannelName _queueName; @@ -65,15 +67,20 @@ public partial class RedisMessageConsumer : RedisMessageGateway, IAmAMessageCons /// The topic that the list subscribes to /// The routing key for the dead letter queue, if using Brighter-managed DLQ /// The routing key for the invalid message queue, if using Brighter-managed invalid message handling + /// The used to create loggers for this consumer and the producers it creates public RedisMessageConsumer( RedisMessagingGatewayConfiguration redisMessagingGatewayConfiguration, ChannelName queueName, RoutingKey topic, IAmAMessageScheduler? scheduler = null, RoutingKey? deadLetterRoutingKey = null, - RoutingKey? invalidMessageRoutingKey = null) + RoutingKey? invalidMessageRoutingKey = null, + ILoggerFactory? loggerFactory = null) :base(redisMessagingGatewayConfiguration, topic) { + _loggerFactory = loggerFactory; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + _messageCreator = new RedisMessageCreator((_loggerFactory ?? NullLoggerFactory.Instance).CreateLogger()); _queueName = queueName; _redisConfiguration = redisMessagingGatewayConfiguration; _scheduler = scheduler; @@ -104,7 +111,7 @@ public RedisMessageConsumer( /// public void Acknowledge(Message message) { - Log.AcknowledgingMessage(s_logger, message.Id.Value); + Log.AcknowledgingMessage(_logger, message.Id.Value); _inflight.Remove(message.Id.Value); } @@ -192,7 +199,7 @@ public async ValueTask DisposeAsync() /// public void Purge() { - Log.PurgingChannel(s_logger, _queueName); + Log.PurgingChannel(_logger, _queueName); using var client = GetClient(); if (client == null) @@ -208,7 +215,7 @@ public void Purge() /// The cancellation token public async Task PurgeAsync(CancellationToken cancellationToken = default(CancellationToken)) { - Log.PurgingChannel(s_logger, _queueName); + Log.PurgingChannel(_logger, _queueName); await using var client = await GetClientAsync(cancellationToken); if (client == null) @@ -225,11 +232,11 @@ public void Purge() /// The message read from the list public Message[] Receive(TimeSpan? timeOut = null) { - Log.RetrievingNextMessage(s_logger, _queueName, Topic); + Log.RetrievingNextMessage(_logger, _queueName, Topic); if (_inflight.Any()) { - Log.UnackedMessageInFlight(s_logger, _queueName); + Log.UnackedMessageInFlight(_logger, _queueName); throw new ChannelFailureException($"Unacked message still in flight with id: {_inflight.Keys.First()}"); } @@ -249,7 +256,7 @@ public Message[] Receive(TimeSpan? timeOut = null) if (redisMessage.msgId == null || string.IsNullOrEmpty(redisMessage.rawMsg)) return []; - var message = RedisMessageCreator.CreateMessage(redisMessage.rawMsg); + var message = _messageCreator.CreateMessage(redisMessage.rawMsg); if (message.Header.MessageType != MessageType.MT_NONE && message.Header.MessageType != MessageType.MT_UNACCEPTABLE) { _inflight.Add(message.Id.Value, redisMessage.msgId); @@ -259,12 +266,12 @@ public Message[] Receive(TimeSpan? timeOut = null) } catch (TimeoutException te) { - Log.CouldNotConnectToRedisClient(s_logger, timeOut.Value.TotalMilliseconds.ToString(CultureInfo.CurrentCulture)); + Log.CouldNotConnectToRedisClient(_logger, timeOut.Value.TotalMilliseconds.ToString(CultureInfo.CurrentCulture)); throw new ChannelFailureException($"Could not connect to Redis client within {timeOut.Value.TotalMilliseconds.ToString(CultureInfo.InvariantCulture)} milliseconds", te); } catch (RedisException re) { - Log.CouldNotConnectToRedis(s_logger, re.Message); + Log.CouldNotConnectToRedis(_logger, re.Message); throw new ChannelFailureException("Could not connect to Redis client - see inner exception for details", re); } } @@ -277,11 +284,11 @@ public Message[] Receive(TimeSpan? timeOut = null) /// The message read from the list public async Task ReceiveAsync(TimeSpan? timeOut = null, CancellationToken cancellationToken = default(CancellationToken)) { - Log.RetrievingNextMessage(s_logger, _queueName, Topic); + Log.RetrievingNextMessage(_logger, _queueName, Topic); if (_inflight.Any()) { - Log.UnackedMessageInFlight(s_logger, _queueName); + Log.UnackedMessageInFlight(_logger, _queueName); throw new ChannelFailureException($"Unacked message still in flight with id: {_inflight.Keys.First()}"); } @@ -297,7 +304,7 @@ public Message[] Receive(TimeSpan? timeOut = null) if (redisMessage.msgId == null || string.IsNullOrEmpty(redisMessage.rawMsg)) return []; - var message = RedisMessageCreator.CreateMessage(redisMessage.rawMsg); + var message = _messageCreator.CreateMessage(redisMessage.rawMsg); if (message.Header.MessageType != MessageType.MT_NONE && message.Header.MessageType != MessageType.MT_UNACCEPTABLE) { @@ -308,12 +315,12 @@ public Message[] Receive(TimeSpan? timeOut = null) } catch (TimeoutException te) { - Log.CouldNotConnectToRedisClient(s_logger, timeOut.Value.TotalMilliseconds.ToString(CultureInfo.InvariantCulture)); + Log.CouldNotConnectToRedisClient(_logger, timeOut.Value.TotalMilliseconds.ToString(CultureInfo.InvariantCulture)); throw new ChannelFailureException($"Could not connect to Redis client within {timeOut.Value.TotalMilliseconds.ToString(CultureInfo.InvariantCulture)} milliseconds", te); } catch (RedisException re) { - Log.CouldNotConnectToRedis(s_logger, re.Message); + Log.CouldNotConnectToRedis(_logger, re.Message); throw new ChannelFailureException("Could not connect to Redis client - see inner exception for details", re); } } @@ -328,7 +335,7 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) if (_deadLetterProducer == null && _invalidMessageProducer == null) { if (reason != null) - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value, reason.RejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value, reason.RejectionReason.ToString()); _inflight.Remove(message.Id.Value); return true; @@ -348,7 +355,7 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) { message.Header.Topic = routingKey!; if (isFallingBackToDlq) - Log.FallingBackToDlq(s_logger, message.Id.Value); + Log.FallingBackToDlq(_logger, message.Id.Value); if (routingKey == _invalidMessageRoutingKey) producer = _invalidMessageProducer?.Value; @@ -359,18 +366,18 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) if (producer != null) { producer.Send(message); - Log.MessageSentToRejectionChannel(s_logger, message.Id.Value, rejectionReason.ToString()); + Log.MessageSentToRejectionChannel(_logger, message.Id.Value, rejectionReason.ToString()); } else { - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value, rejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value, rejectionReason.ToString()); } } catch (Exception ex) { // DLQ send failed — the message was already popped from Redis so we cannot // requeue it. Remove from inflight to prevent blocking subsequent receives. - Log.ErrorSendingToRejectionChannel(s_logger, ex, message.Id.Value, rejectionReason.ToString()); + Log.ErrorSendingToRejectionChannel(_logger, ex, message.Id.Value, rejectionReason.ToString()); _inflight.Remove(message.Id.Value); return true; } @@ -390,7 +397,7 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) if (_deadLetterProducer == null && _invalidMessageProducer == null) { if (reason != null) - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value, reason.RejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value, reason.RejectionReason.ToString()); _inflight.Remove(message.Id.Value); return true; @@ -410,7 +417,7 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) { message.Header.Topic = routingKey!; if (isFallingBackToDlq) - Log.FallingBackToDlq(s_logger, message.Id.Value); + Log.FallingBackToDlq(_logger, message.Id.Value); if (routingKey == _invalidMessageRoutingKey) producer = _invalidMessageProducer?.Value; @@ -421,18 +428,18 @@ public bool Reject(Message message, MessageRejectionReason? reason = null) if (producer != null) { await producer.SendAsync(message, cancellationToken); - Log.MessageSentToRejectionChannel(s_logger, message.Id.Value, rejectionReason.ToString()); + Log.MessageSentToRejectionChannel(_logger, message.Id.Value, rejectionReason.ToString()); } else { - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value, rejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value, rejectionReason.ToString()); } } catch (Exception ex) { // DLQ send failed — the message was already popped from Redis so we cannot // requeue it. Remove from inflight to prevent blocking subsequent receives. - Log.ErrorSendingToRejectionChannel(s_logger, ex, message.Id.Value, rejectionReason.ToString()); + Log.ErrorSendingToRejectionChannel(_logger, ex, message.Id.Value, rejectionReason.ToString()); _inflight.Remove(message.Id.Value); return true; } @@ -489,7 +496,7 @@ public bool Requeue(Message message, TimeSpan? delay = null) } else { - Log.MessageNotFoundInFlight(s_logger, message.Id.Value); + Log.MessageNotFoundInFlight(_logger, message.Id.Value); return false; } } @@ -543,7 +550,7 @@ public bool Requeue(Message message, TimeSpan? delay = null) } else { - Log.MessageNotFoundInFlight(s_logger, message.Id.Value); + Log.MessageNotFoundInFlight(_logger, message.Id.Value); return false; } } @@ -553,7 +560,8 @@ private void EnsureRequeueProducer() LazyInitializer.EnsureInitialized(ref _requeueProducer, ref _requeueProducerInitialized, ref _requeueProducerLock, () => new RedisMessageProducer( _redisConfiguration, - new RedisMessagePublication { Topic = Topic }) + new RedisMessagePublication { Topic = Topic }, + loggerFactory: _loggerFactory) { Scheduler = _scheduler }); @@ -566,11 +574,12 @@ private void EnsureRequeueProducer() try { return new RedisMessageProducer(_redisConfiguration, - new RedisMessagePublication { Topic = _deadLetterRoutingKey }); + new RedisMessagePublication { Topic = _deadLetterRoutingKey }, + loggerFactory: _loggerFactory); } catch (Exception e) { - Log.ErrorCreatingDlqProducer(s_logger, e, _deadLetterRoutingKey.Value); + Log.ErrorCreatingDlqProducer(_logger, e, _deadLetterRoutingKey.Value); return null; } } @@ -582,11 +591,12 @@ private void EnsureRequeueProducer() try { return new RedisMessageProducer(_redisConfiguration, - new RedisMessagePublication { Topic = _invalidMessageRoutingKey }); + new RedisMessagePublication { Topic = _invalidMessageRoutingKey }, + loggerFactory: _loggerFactory); } catch (Exception e) { - Log.ErrorCreatingInvalidMessageProducer(s_logger, e, _invalidMessageRoutingKey.Value); + Log.ErrorCreatingInvalidMessageProducer(_logger, e, _invalidMessageRoutingKey.Value); return null; } } @@ -671,7 +681,7 @@ private static void RefreshMetadata(Message message, MessageRejectionReason? rea private void EnsureConnection(IRedisClient client) { - Log.CreatingQueue(s_logger, _queueName); + Log.CreatingQueue(_logger, _queueName); //what is the queue list key var key = Topic + "." + QUEUES; //subscribe us @@ -680,7 +690,7 @@ private void EnsureConnection(IRedisClient client) private async Task EnsureConnectionAsync(IRedisClientAsync client) { - Log.CreatingQueue(s_logger, _queueName); + Log.CreatingQueue(_logger, _queueName); //what is the queue list key var key = Topic + "." + QUEUES; //subscribe us @@ -695,11 +705,11 @@ private async Task EnsureConnectionAsync(IRedisClientAsync client) { var key = Topic + "." + latestId; msg = client.GetValue(key); - Log.ReceivedMessageFromQueue(s_logger, _queueName, Topic, JsonSerializer.Serialize(msg, JsonSerialisationOptions.Options)); + Log.ReceivedMessageFromQueue(_logger, _queueName, Topic, JsonSerializer.Serialize(msg, JsonSerialisationOptions.Options)); } else { - Log.TimeoutWithoutReceivingMessage(s_logger, _queueName, Topic); + Log.TimeoutWithoutReceivingMessage(_logger, _queueName, Topic); } return (latestId, msg); } @@ -718,16 +728,16 @@ private async Task EnsureConnectionAsync(IRedisClientAsync client) { var key = Topic + "." + latestId; msg = await client.GetValueAsync(key); - Log.ReceivedMessageFromQueue(s_logger, _queueName, Topic, JsonSerializer.Serialize(msg, JsonSerialisationOptions.Options)); + Log.ReceivedMessageFromQueue(_logger, _queueName, Topic, JsonSerializer.Serialize(msg, JsonSerialisationOptions.Options)); } } catch (OperationCanceledException) { - Log.TimeoutWithoutReceivingMessage(s_logger, _queueName, Topic); + Log.TimeoutWithoutReceivingMessage(_logger, _queueName, Topic); } catch (RedisException re) when (re.InnerException is OperationCanceledException) { - Log.TimeoutWithoutReceivingMessage(s_logger, _queueName, Topic); + Log.TimeoutWithoutReceivingMessage(_logger, _queueName, Topic); } return (latestId, msg); diff --git a/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageConsumerFactory.cs b/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageConsumerFactory.cs index 1b87556343..b5420b5b80 100644 --- a/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageConsumerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageConsumerFactory.cs @@ -22,11 +22,14 @@ THE SOFTWARE. */ #endregion +using Microsoft.Extensions.Logging; + namespace Paramore.Brighter.MessagingGateway.Redis { public class RedisMessageConsumerFactory : IAmAMessageConsumerFactory { private readonly RedisMessagingGatewayConfiguration _configuration; + private readonly ILoggerFactory? _loggerFactory; private IAmAMessageScheduler? _scheduler; /// @@ -44,10 +47,12 @@ public IAmAMessageScheduler? Scheduler /// /// The Redis messaging gateway configuration /// The optional message scheduler for delayed requeue support - public RedisMessageConsumerFactory(RedisMessagingGatewayConfiguration configuration, IAmAMessageScheduler? scheduler = null) + /// The used to create loggers for the consumers + public RedisMessageConsumerFactory(RedisMessagingGatewayConfiguration configuration, IAmAMessageScheduler? scheduler = null, ILoggerFactory? loggerFactory = null) { _configuration = configuration; _scheduler = scheduler; + _loggerFactory = loggerFactory; } @@ -69,7 +74,8 @@ public IAmAMessageConsumerSync Create(Subscription subscription) subscription.RoutingKey, _scheduler, deadLetterRoutingKey, - invalidMessageRoutingKey); + invalidMessageRoutingKey, + _loggerFactory); } private static void RequireQueueName(Subscription subscription) @@ -96,7 +102,8 @@ public IAmAMessageConsumerAsync CreateAsync(Subscription subscription) subscription.RoutingKey, _scheduler, deadLetterRoutingKey, - invalidMessageRoutingKey); + invalidMessageRoutingKey, + _loggerFactory); } } } diff --git a/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageCreator.cs b/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageCreator.cs index b6ff8dd350..2b518e10a7 100644 --- a/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageCreator.cs +++ b/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageCreator.cs @@ -28,8 +28,8 @@ THE SOFTWARE. */ using System.Net.Mime; using System.Text.Json; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; using ServiceStack; @@ -37,8 +37,13 @@ namespace Paramore.Brighter.MessagingGateway.Redis { public partial class RedisMessageCreator { - private static readonly ILogger s_logger= ApplicationLogging.CreateLogger(); - + private readonly ILogger _logger; + + public RedisMessageCreator(ILogger? logger = null) + { + _logger = logger ?? NullLogger.Instance; + } + /// /// Create a Brighter Message from the Redis raw content /// Expected message shape is: @@ -72,7 +77,7 @@ public partial class RedisMessageCreator /// /// The raw message read from the wire /// - public static Message CreateMessage(string redisMessage) + public Message CreateMessage(string redisMessage) { var message = new Message(); if (redisMessage.IsNullOrEmpty()) @@ -84,44 +89,44 @@ public static Message CreateMessage(string redisMessage) var header = reader.ReadLine(); if (header is null || header.TrimEnd() != " /// The raw header JSON /// - private static MessageHeader ReadHeader(string? headersJson) + private MessageHeader ReadHeader(string? headersJson) { if (headersJson is null) return MessageHeader.FailureMessageHeader(RoutingKey.Empty, Id.Empty); @@ -198,7 +203,7 @@ private static MessageHeader ReadHeader(string? headersJson) return messageHeader; } - private static HeaderResult ReadBaggage(Dictionary headers) + private HeaderResult ReadBaggage(Dictionary headers) { if (headers.TryGetValue(HeaderNames.W3C_BAGGAGE, out string? header)) { @@ -209,7 +214,7 @@ private static HeaderResult ReadBaggage(Dictionary head return new HeaderResult(new Baggage(), false); } - private static HeaderResult ReadContentType(Dictionary headers) + private HeaderResult ReadContentType(Dictionary headers) { if (headers.TryGetValue(HeaderNames.CONTENT_TYPE, out string? header)) { @@ -219,7 +224,7 @@ private static HeaderResult ReadBaggage(Dictionary head return new HeaderResult(null, false); } - private static HeaderResult ReadCorrelationId(Dictionary headers) + private HeaderResult ReadCorrelationId(Dictionary headers) { var newCorrelationId = string.Empty; @@ -231,7 +236,7 @@ private static HeaderResult ReadCorrelationId(Dictionary return new HeaderResult(newCorrelationId, false); } - private static HeaderResult ReadDataSchema(Dictionary headers) + private HeaderResult ReadDataSchema(Dictionary headers) { if (headers.TryGetValue(HeaderNames.CLOUD_EVENTS_DATA_SCHEMA, out string? header) && Uri.TryCreate(header, UriKind.RelativeOrAbsolute, out Uri? dataSchema)) @@ -242,7 +247,7 @@ private static HeaderResult ReadCorrelationId(Dictionary return new HeaderResult(null, false); } - private static HeaderResult ReadDelay(Dictionary headers) + private HeaderResult ReadDelay(Dictionary headers) { if (headers.TryGetValue(HeaderNames.DELAYED_MILLISECONDS, out string? header)) { @@ -254,7 +259,7 @@ private static HeaderResult ReadDelay(Dictionary heade return new HeaderResult(TimeSpan.Zero, true); } - private static HeaderResult ReadHandledCount(Dictionary headers) + private HeaderResult ReadHandledCount(Dictionary headers) { if (headers.TryGetValue(HeaderNames.HANDLED_COUNT, out string? header)) { @@ -275,7 +280,7 @@ private static HeaderResult ReadHandledCount(Dictionary hea /// /// The raw json /// A dictionary, either empty if key missing or matching contents if present (could be mepty) - private static HeaderResult> ReadMessageBag(Dictionary headers) + private HeaderResult> ReadMessageBag(Dictionary headers) { if (headers.TryGetValue(HeaderNames.BAG, out string? header)) { @@ -289,7 +294,7 @@ private static HeaderResult> ReadMessageBag(Dictionar return new HeaderResult>(new Dictionary(), false); } - private static HeaderResult ReadMessageType(Dictionary headers) + private HeaderResult ReadMessageType(Dictionary headers) { if (headers.TryGetValue(HeaderNames.MESSAGE_TYPE, out string? header)) { @@ -302,7 +307,7 @@ private static HeaderResult ReadMessageType(Dictionary(MessageType.MT_EVENT, true); } - private static HeaderResult ReadMessageId(IDictionary headers) + private HeaderResult ReadMessageId(IDictionary headers) { if (headers.TryGetValue(HeaderNames.MESSAGE_ID, out string? header) && !string.IsNullOrEmpty(header)) { @@ -312,7 +317,7 @@ private static HeaderResult ReadMessageId(IDictionary header return new HeaderResult(Id.Random(), true); } - private static HeaderResult ReadReplyTo(Dictionary headers) + private HeaderResult ReadReplyTo(Dictionary headers) { if (headers.TryGetValue(HeaderNames.REPLY_TO, out string? header)) { @@ -321,7 +326,7 @@ private static HeaderResult ReadReplyTo(Dictionary h return new HeaderResult(RoutingKey.Empty, false); } - private static HeaderResult ReadSource(Dictionary headers) + private HeaderResult ReadSource(Dictionary headers) { if (headers.TryGetValue(HeaderNames.CLOUD_EVENTS_SOURCE, out string? header) && Uri.TryCreate(header, UriKind.RelativeOrAbsolute, out var source)) @@ -331,7 +336,7 @@ private static HeaderResult ReadReplyTo(Dictionary h return new HeaderResult(new Uri(MessageHeader.DefaultSource), true); } - private static HeaderResult ReadSubject(Dictionary headers) + private HeaderResult ReadSubject(Dictionary headers) { if (headers.TryGetValue(HeaderNames.CLOUD_EVENTS_SUBJECT, out string? header)) { @@ -345,7 +350,7 @@ private static HeaderResult ReadSubject(Dictionary heade /// /// The collection of headers /// The result, always a success because we don't break for missing timestamp, just use now - private static HeaderResult ReadTimeStamp(Dictionary headers) + private HeaderResult ReadTimeStamp(Dictionary headers) { if (headers.TryGetValue(HeaderNames.TIMESTAMP, out string? header) && DateTimeOffset.TryParse(header, out var timestamp)) @@ -356,7 +361,7 @@ private static HeaderResult ReadTimeStamp(Dictionary(DateTimeOffset.UtcNow, true); } - private static HeaderResult ReadTraceParent(Dictionary headers) + private HeaderResult ReadTraceParent(Dictionary headers) { if (headers.TryGetValue(HeaderNames.CLOUD_EVENTS_TRACE_PARENT, out string? header)) { @@ -365,7 +370,7 @@ private static HeaderResult ReadTraceParent(Dictionary(TraceParent.Empty, false); } - private static HeaderResult ReadTraceState(Dictionary headers) + private HeaderResult ReadTraceState(Dictionary headers) { if (headers.TryGetValue(HeaderNames.CLOUD_EVENTS_TRACE_STATE, out string? header)) { @@ -374,7 +379,7 @@ private static HeaderResult ReadTraceState(Dictionary(TraceState.Empty, false); } - private static HeaderResult ReadType(Dictionary headers) + private HeaderResult ReadType(Dictionary headers) { if (headers.TryGetValue(HeaderNames.CLOUD_EVENTS_TYPE, out string? header)) { @@ -383,7 +388,7 @@ private static HeaderResult ReadType(Dictionary return new HeaderResult(CloudEventsType.Empty, false); } - private static HeaderResult ReadTopic(Dictionary headers) + private HeaderResult ReadTopic(Dictionary headers) { var topic = string.Empty; if (headers.TryGetValue(HeaderNames.TOPIC, out string? header)) diff --git a/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageProducer.cs b/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageProducer.cs index 22276a5959..1d8d2b038e 100644 --- a/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageProducer.cs +++ b/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageProducer.cs @@ -28,7 +28,7 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Observability; using Paramore.Brighter.Tasks; using ServiceStack.Redis; @@ -56,12 +56,13 @@ We end with a public partial class RedisMessageProducer( RedisMessagingGatewayConfiguration redisMessagingGatewayConfiguration, RedisMessagePublication publication, - InstrumentationOptions instrumentation = InstrumentationOptions.All) + InstrumentationOptions instrumentation = InstrumentationOptions.All, + ILoggerFactory? loggerFactory = null) : RedisMessageGateway(redisMessagingGatewayConfiguration, publication.Topic!), IAmAMessageProducerSync, IAmAMessageProducerAsync { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); - private Publication _publication = publication; + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + private Publication _publication = publication; private const string NEXT_ID = "nextid"; private const string QUEUES = "queues"; @@ -144,18 +145,18 @@ public void SendWithDelay(Message message, TimeSpan? delay = null) Topic = message.Header.Topic; BrighterTracer.WriteProducerEvent(Span, "redis", message, instrumentation); - Log.PreparingToSend(s_logger); + Log.PreparingToSend(_logger); var redisMessage = CreateRedisMessage(message); - Log.PublishingMessage(s_logger, message.Header.Topic.Value, message.Id.ToString(), message.Body.Value); + Log.PublishingMessage(_logger, message.Header.Topic.Value, message.Id.ToString(), message.Body.Value); //increment a counter to get the next message id var nextMsgId = IncrementMessageCounter(client); //store the message, against that id StoreMessage(client, redisMessage, nextMsgId); //If there are subscriber queues, push the message to the subscriber queues var pushedTo = PushToQueues(client, nextMsgId); - Log.PublishedMessage(s_logger, message.Header.Topic.Value, message.Id.ToString(), message.Body.Value, string.Join(", ", pushedTo)); + Log.PublishedMessage(_logger, message.Header.Topic.Value, message.Id.ToString(), message.Body.Value, string.Join(", ", pushedTo)); } /// @@ -193,18 +194,18 @@ public async Task SendWithDelayAsync(Message message, TimeSpan? delay, Cancellat Topic = message.Header.Topic; BrighterTracer.WriteProducerEvent(Span, "redis", message, instrumentation); - Log.PreparingToSend(s_logger); + Log.PreparingToSend(_logger); var redisMessage = CreateRedisMessage(message); - Log.PublishingMessage(s_logger, message.Header.Topic.Value, message.Id.ToString(), message.Body.Value); + Log.PublishingMessage(_logger, message.Header.Topic.Value, message.Id.ToString(), message.Body.Value); //increment a counter to get the next message id var nextMsgId = await IncrementMessageCounterAsync(client, cancellationToken); //store the message, against that id await StoreMessageAsync(client, redisMessage, nextMsgId); //If there are subscriber queues, push the message to the subscriber queues var pushedTo = await PushToQueuesAsync(client, nextMsgId, cancellationToken); - Log.PublishedMessage(s_logger, message.Header.Topic.Value, message.Id.ToString(), message.Body.Value, string.Join(", ", pushedTo)); + Log.PublishedMessage(_logger, message.Header.Topic.Value, message.Id.ToString(), message.Body.Value, string.Join(", ", pushedTo)); } private HashSet PushToQueues(IRedisClient client, long nextMsgId) diff --git a/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageProducerFactory.cs b/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageProducerFactory.cs index 3a240796d6..e903b98551 100644 --- a/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageProducerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.Redis/RedisMessageProducerFactory.cs @@ -24,6 +24,7 @@ THE SOFTWARE. */ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace Paramore.Brighter.MessagingGateway.Redis { @@ -35,18 +36,22 @@ public class RedisMessageProducerFactory : IAmAMessageProducerFactory { private readonly RedisMessagingGatewayConfiguration _redisConfiguration; private readonly IEnumerable _publications; + private readonly ILoggerFactory? _loggerFactory; /// /// Initializes a new instance of the class. /// /// The configuration settings for connecting to Redis. /// The collection of Redis message publications. + /// The used to create loggers for the producers. public RedisMessageProducerFactory( RedisMessagingGatewayConfiguration redisConfiguration, - IEnumerable publications) + IEnumerable publications, + ILoggerFactory? loggerFactory = null) { _redisConfiguration = redisConfiguration; _publications = publications; + _loggerFactory = loggerFactory; } /// @@ -63,7 +68,7 @@ public Dictionary Create() if (publication.Topic is null) throw new ConfigurationException("RmqMessageProducerFactory.Create => An RmqPublication must have a topic/routing key"); - var messageProducer = new RedisMessageProducer(_redisConfiguration, publication); + var messageProducer = new RedisMessageProducer(_redisConfiguration, publication, loggerFactory: _loggerFactory); messageProducer.Publication = publication; var producerKey = new ProducerKey(publication.Topic, publication.Type); diff --git a/src/Paramore.Brighter.MessagingGateway.Redis/RedisProducerRegistryFactory.cs b/src/Paramore.Brighter.MessagingGateway.Redis/RedisProducerRegistryFactory.cs index 00ae75887b..6bf2deead8 100644 --- a/src/Paramore.Brighter.MessagingGateway.Redis/RedisProducerRegistryFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.Redis/RedisProducerRegistryFactory.cs @@ -1,12 +1,14 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace Paramore.Brighter.MessagingGateway.Redis { public class RedisProducerRegistryFactory( RedisMessagingGatewayConfiguration redisConfiguration, - IEnumerable publications) + IEnumerable publications, + ILoggerFactory? loggerFactory = null) : IAmAProducerRegistryFactory { /// @@ -15,7 +17,7 @@ public class RedisProducerRegistryFactory( /// A has of middleware clients by topic, for sending messages to the middleware public IAmAProducerRegistry Create() { - var producerFactory = new RedisMessageProducerFactory(redisConfiguration, publications); + var producerFactory = new RedisMessageProducerFactory(redisConfiguration, publications, loggerFactory); return new ProducerRegistry(producerFactory.Create()); } diff --git a/src/Paramore.Brighter.MessagingGateway.RocketMQ/RocketMessageConsumer.cs b/src/Paramore.Brighter.MessagingGateway.RocketMQ/RocketMessageConsumer.cs index 75e4582dfc..9d2ecfa760 100644 --- a/src/Paramore.Brighter.MessagingGateway.RocketMQ/RocketMessageConsumer.cs +++ b/src/Paramore.Brighter.MessagingGateway.RocketMQ/RocketMessageConsumer.cs @@ -5,9 +5,9 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Org.Apache.Rocketmq; using Paramore.Brighter.Extensions; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; using Paramore.Brighter.Tasks; @@ -23,15 +23,17 @@ namespace Paramore.Brighter.MessagingGateway.RocketMQ; /// The gateway connection configuration, used for lazy DLQ producer creation. /// The routing key for the dead letter queue topic. /// The routing key for the invalid message topic. +/// The used to create the logger. public partial class RocketMessageConsumer(SimpleConsumer consumer, int bufferSize, TimeSpan invisibilityTimeout, RocketMessagingGatewayConnection? connection = null, RoutingKey? deadLetterRoutingKey = null, - RoutingKey? invalidMessageRoutingKey = null) + RoutingKey? invalidMessageRoutingKey = null, + ILoggerFactory? loggerFactory = null) : IAmAMessageConsumerAsync, IAmAMessageConsumerSync { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); private readonly RocketMessagingGatewayConnection? _connection = connection; private readonly RoutingKey? _deadLetterRoutingKey = deadLetterRoutingKey; @@ -123,12 +125,12 @@ public async Task RejectAsync(Message message, MessageRejectionReason? rea return false; } - Log.RejectingMessage(s_logger, message.Id.Value); + Log.RejectingMessage(_logger, message.Id.Value); if (_deadLetterRoutingKey == null && _invalidMessageRoutingKey == null) { if (reason != null) - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value, reason.RejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value, reason.RejectionReason.ToString()); await consumer.Ack(view); return true; @@ -147,7 +149,7 @@ public async Task RejectAsync(Message message, MessageRejectionReason? rea { message.Header.Topic = routingKey!; if (isFallingBackToDlq) - Log.FallingBackToDlq(s_logger, message.Id.Value); + Log.FallingBackToDlq(_logger, message.Id.Value); producer = await GetProducerForRouteAsync(routingKey!); } @@ -155,16 +157,16 @@ public async Task RejectAsync(Message message, MessageRejectionReason? rea if (producer != null) { await producer.SendAsync(message, cancellationToken); - Log.MessageSentToRejectionChannel(s_logger, message.Id.Value, rejectionReason.ToString()); + Log.MessageSentToRejectionChannel(_logger, message.Id.Value, rejectionReason.ToString()); } else { - Log.NoChannelsConfiguredForRejection(s_logger, message.Id.Value, rejectionReason.ToString()); + Log.NoChannelsConfiguredForRejection(_logger, message.Id.Value, rejectionReason.ToString()); } } catch (Exception ex) { - Log.ErrorSendingToRejectionChannel(s_logger, ex, message.Id.Value, rejectionReason.ToString()); + Log.ErrorSendingToRejectionChannel(_logger, ex, message.Id.Value, rejectionReason.ToString()); return true; } finally @@ -206,7 +208,7 @@ private async Task AckSourceMessageSafeAsync(MessageView view) } catch (Exception ackEx) { - Log.ErrorAckingSourceMessage(s_logger, ackEx); + Log.ErrorAckingSourceMessage(_logger, ackEx); return false; } } @@ -240,7 +242,7 @@ private async Task AckSourceMessageSafeAsync(MessageView view) } catch (Exception ex) { - Log.ErrorCreatingProducer(s_logger, ex, routingKey.Value); + Log.ErrorCreatingProducer(_logger, ex, routingKey.Value); return null; } } diff --git a/src/Paramore.Brighter.MessagingGateway.RocketMQ/RocketMessageConsumerFactory.cs b/src/Paramore.Brighter.MessagingGateway.RocketMQ/RocketMessageConsumerFactory.cs index c328696c29..ff93d206c6 100644 --- a/src/Paramore.Brighter.MessagingGateway.RocketMQ/RocketMessageConsumerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.RocketMQ/RocketMessageConsumerFactory.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; using Org.Apache.Rocketmq; using Paramore.Brighter.Tasks; @@ -9,7 +10,7 @@ namespace Paramore.Brighter.MessagingGateway.RocketMQ; /// RocketMQ message producer implementation for Brighter. /// Integrates RocketMQ's producer group pattern and transactional message support. /// -public class RocketMessageConsumerFactory(RocketMessagingGatewayConnection connection) : IAmAMessageConsumerFactory +public class RocketMessageConsumerFactory(RocketMessagingGatewayConnection connection, ILoggerFactory? loggerFactory = null) : IAmAMessageConsumerFactory { /// public IAmAMessageConsumerSync Create(Subscription subscription) @@ -41,6 +42,6 @@ internal async Task CreateConsumerAsync(Subscription subs var consumer = await builder.Build(); return new RocketMessageConsumer(consumer, rocketSubscription.BufferSize, - rocketSubscription.InvisibilityTimeout, connection, deadLetterRoutingKey, invalidMessageRoutingKey); + rocketSubscription.InvisibilityTimeout, connection, deadLetterRoutingKey, invalidMessageRoutingKey, loggerFactory); } } diff --git a/src/Paramore.Brighter.MessagingGateway.RocketMQ/RocketMessageProducerFactory.cs b/src/Paramore.Brighter.MessagingGateway.RocketMQ/RocketMessageProducerFactory.cs index c13257969e..1afcc8c976 100644 --- a/src/Paramore.Brighter.MessagingGateway.RocketMQ/RocketMessageProducerFactory.cs +++ b/src/Paramore.Brighter.MessagingGateway.RocketMQ/RocketMessageProducerFactory.cs @@ -2,8 +2,8 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Org.Apache.Rocketmq; -using Paramore.Brighter.Logging; using Paramore.Brighter.Tasks; namespace Paramore.Brighter.MessagingGateway.RocketMQ; @@ -12,9 +12,12 @@ namespace Paramore.Brighter.MessagingGateway.RocketMQ; /// Factory class for creating RocketMQ message producers in Brighter. /// Implements RocketMQ's producer group pattern and transactional message support. /// -public partial class RocketMessageProducerFactory(RocketMessagingGatewayConnection connection, IEnumerable publications) : IAmAMessageProducerFactory +/// The gateway connection configuration. +/// The publications to create producers for. +/// The used to create the logger. +public partial class RocketMessageProducerFactory(RocketMessagingGatewayConnection connection, IEnumerable publications, ILoggerFactory? loggerFactory = null) : IAmAMessageProducerFactory { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); /// public Dictionary Create() @@ -34,7 +37,7 @@ public async Task> CreateAsync() if (publication.MakeChannels == OnMissingChannel.Create) { - Log.CreateTopicIsNotSupported(s_logger, publication.Topic!.Value); + Log.CreateTopicIsNotSupported(_logger, publication.Topic!.Value); } producers[new ProducerKey(publication.Topic, publication.Type)] = new RocketMqMessageProducer(connection, diff --git a/src/Paramore.Brighter.Outbox.Hosting/HostedServiceCollectionExtensions.cs b/src/Paramore.Brighter.Outbox.Hosting/HostedServiceCollectionExtensions.cs index 4bee42857b..eb25fd047f 100644 --- a/src/Paramore.Brighter.Outbox.Hosting/HostedServiceCollectionExtensions.cs +++ b/src/Paramore.Brighter.Outbox.Hosting/HostedServiceCollectionExtensions.cs @@ -25,6 +25,7 @@ THE SOFTWARE. */ using System; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; using Paramore.Brighter.Extensions.DependencyInjection; using Paramore.Brighter.Observability; @@ -63,7 +64,8 @@ public static IBrighterBuilder UseOutboxArchiver(this IBrighterBui provider.GetService(), options.ArchiveBatchSize, provider.GetService(), - options.Instrumentation)); + options.Instrumentation, + provider.GetService())); brighterBuilder.Services.AddHostedService>(); diff --git a/src/Paramore.Brighter.Outbox.Hosting/TimedOutboxArchiver.cs b/src/Paramore.Brighter.Outbox.Hosting/TimedOutboxArchiver.cs index 57549766d7..276bad1216 100644 --- a/src/Paramore.Brighter.Outbox.Hosting/TimedOutboxArchiver.cs +++ b/src/Paramore.Brighter.Outbox.Hosting/TimedOutboxArchiver.cs @@ -27,7 +27,7 @@ THE SOFTWARE. */ using System.Threading.Tasks; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; // ReSharper disable StaticMemberInGenericType namespace Paramore.Brighter.Outbox.Hosting @@ -37,7 +37,7 @@ namespace Paramore.Brighter.Outbox.Hosting /// public partial class TimedOutboxArchiver : IHostedService, IDisposable where TMessage : Message { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private Timer? _timer; private readonly OutboxArchiver _archiver; private readonly IDistributedLock _distributedLock; @@ -49,14 +49,17 @@ public partial class TimedOutboxArchiver : IHostedServic /// The archiver to use /// Used to ensure that only one instance of the is running /// The that control how the archiver runs, such as interval + /// The logger; defaults to a no-op logger when not supplied public TimedOutboxArchiver( OutboxArchiver archiver, IDistributedLock distributedLock, - TimedOutboxArchiverOptions options) + TimedOutboxArchiverOptions options, + ILogger>? logger = null) { _archiver = archiver; _distributedLock = distributedLock; _options = options; + _logger = logger ?? NullLogger>.Instance; } private const string LockingResourceName = "Archiver"; @@ -68,7 +71,7 @@ public TimedOutboxArchiver( /// A completed task to allow other background services to run public Task StartAsync(CancellationToken cancellationToken) { - Log.OutboxArchiverServiceIsStarting(s_logger); + Log.OutboxArchiverServiceIsStarting(_logger); _timer = new Timer(_ => Archive(cancellationToken).GetAwaiter().GetResult(), null, @@ -85,7 +88,7 @@ public Task StartAsync(CancellationToken cancellationToken) /// A completed task to allow other background services to run public Task StopAsync(CancellationToken cancellationToken) { - Log.OutboxArchiverServiceIsStopping(s_logger); + Log.OutboxArchiverServiceIsStopping(_logger); _timer?.Change(Timeout.Infinite, 0); @@ -107,11 +110,11 @@ private async Task Archive(CancellationToken cancellationToken) var lockId = await _distributedLock.ObtainLockAsync(LockingResourceName, cancellationToken); if (lockId == null) { - Log.OutboxArchiverIsStillRunningAbandoningAttempt(s_logger); + Log.OutboxArchiverIsStillRunningAbandoningAttempt(_logger); return; } - Log.OutboxArchiverLookingForMessagesToArchive(s_logger); + Log.OutboxArchiverLookingForMessagesToArchive(_logger); try { if (_archiver.HasAsyncOutbox()) @@ -119,7 +122,7 @@ private async Task Archive(CancellationToken cancellationToken) else if (_archiver.HasOutbox()) await Task.Run(() => _archiver.Archive(_options.MinimumAge, new RequestContext()), cancellationToken); else - Log.NoOutboxConfigured(s_logger); + Log.NoOutboxConfigured(_logger); } finally { @@ -128,11 +131,11 @@ private async Task Archive(CancellationToken cancellationToken) } catch (Exception e) { - Log.ErrorWhileSweepingTheOutbox(s_logger, e); + Log.ErrorWhileSweepingTheOutbox(_logger, e); } finally { - Log.OutboxSweeperSleeping(s_logger); + Log.OutboxSweeperSleeping(_logger); } } diff --git a/src/Paramore.Brighter.Outbox.Hosting/TimedOutboxSweeper.cs b/src/Paramore.Brighter.Outbox.Hosting/TimedOutboxSweeper.cs index b35b1a43d7..7a2702331e 100644 --- a/src/Paramore.Brighter.Outbox.Hosting/TimedOutboxSweeper.cs +++ b/src/Paramore.Brighter.Outbox.Hosting/TimedOutboxSweeper.cs @@ -30,7 +30,7 @@ THE SOFTWARE. */ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using ILogger = Microsoft.Extensions.Logging.ILogger; namespace Paramore.Brighter.Outbox.Hosting @@ -44,7 +44,7 @@ public partial class TimedOutboxSweeper : IHostedService, IDisposable private readonly IServiceScopeFactory _serviceScopeFactory; private readonly IDistributedLock _distributedLock; private readonly TimedOutboxSweeperOptions _options; - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private Timer? _timer; private const string LockingResourceName = "OutboxSweeper"; @@ -54,15 +54,18 @@ public partial class TimedOutboxSweeper : IHostedService, IDisposable /// Needed to create a scope within which to create a /// Used to ensure that only one instance of the is running /// The that can be used to configure how this runs, such as interval or age + /// The logger; defaults to a no-op logger when not supplied public TimedOutboxSweeper( IServiceScopeFactory serviceScopeFactory, IDistributedLock distributedLock, - TimedOutboxSweeperOptions options + TimedOutboxSweeperOptions options, + ILogger? logger = null ) { _serviceScopeFactory = serviceScopeFactory; _distributedLock = distributedLock; _options = options; + _logger = logger ?? NullLogger.Instance; } /// @@ -72,7 +75,7 @@ TimedOutboxSweeperOptions options /// A completed task to allow other background services to be run public Task StartAsync(CancellationToken cancellationToken) { - Log.OutboxSweeperServiceIsStarting(s_logger); + Log.OutboxSweeperServiceIsStarting(_logger); _timer = new Timer(Sweep, null, TimeSpan.Zero, TimeSpan.FromSeconds(_options.TimerInterval)); @@ -86,7 +89,7 @@ public Task StartAsync(CancellationToken cancellationToken) /// A completed task to allow other background services to be stopped public Task StopAsync(CancellationToken cancellationToken) { - Log.OutboxSweeperServiceIsStopping(s_logger); + Log.OutboxSweeperServiceIsStopping(_logger); _timer?.Change(Timeout.Infinite, 0); @@ -106,7 +109,7 @@ private async void Sweep(object? state) var lockId = await _distributedLock.ObtainLockAsync(LockingResourceName, CancellationToken.None); if (lockId != null) { - Log.OutboxSweeperLookingForUnsentMessages(s_logger); + Log.OutboxSweeperLookingForUnsentMessages(_logger); var scope = _serviceScopeFactory.CreateScope(); try @@ -133,10 +136,10 @@ private async void Sweep(object? state) } else { - Log.OutboxSweeperIsStillRunningAbandoningAttempt(s_logger); + Log.OutboxSweeperIsStillRunningAbandoningAttempt(_logger); } - Log.OutboxSweeperSleeping(s_logger); + Log.OutboxSweeperSleeping(_logger); } private static partial class Log diff --git a/src/Paramore.Brighter.Outbox.MsSql/MsSqlOutbox.cs b/src/Paramore.Brighter.Outbox.MsSql/MsSqlOutbox.cs index 266995bf9c..0f8eea931c 100644 --- a/src/Paramore.Brighter.Outbox.MsSql/MsSqlOutbox.cs +++ b/src/Paramore.Brighter.Outbox.MsSql/MsSqlOutbox.cs @@ -28,7 +28,8 @@ THE SOFTWARE. */ using System.Data.Common; using System.Linq; using Microsoft.Data.SqlClient; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.MsSql; using Paramore.Brighter.Observability; @@ -47,10 +48,12 @@ public class MsSqlOutbox : RelationDatabaseOutbox /// /// The configuration. /// The connection factory. + /// The logger to use; defaults to a null logger when not supplied public MsSqlOutbox(IAmARelationalDatabaseConfiguration configuration, - IAmARelationalDbConnectionProvider connectionProvider) + IAmARelationalDbConnectionProvider connectionProvider, + ILogger? logger = null) : base(DbSystem.MsSql, configuration, connectionProvider, - new MsSqlQueries(), ApplicationLogging.CreateLogger()) + new MsSqlQueries(), logger ?? NullLogger.Instance) { } @@ -58,8 +61,9 @@ public MsSqlOutbox(IAmARelationalDatabaseConfiguration configuration, /// Initializes a new instance of the class. /// /// The configuration. - public MsSqlOutbox(IAmARelationalDatabaseConfiguration configuration) : this(configuration, - new MsSqlConnectionProvider(configuration)) + /// The logger to use; defaults to a null logger when not supplied + public MsSqlOutbox(IAmARelationalDatabaseConfiguration configuration, ILogger? logger = null) : this(configuration, + new MsSqlConnectionProvider(configuration), logger) { } diff --git a/src/Paramore.Brighter.Outbox.MySql/MySqlOutbox.cs b/src/Paramore.Brighter.Outbox.MySql/MySqlOutbox.cs index e29b7c7091..7e8a6bb825 100644 --- a/src/Paramore.Brighter.Outbox.MySql/MySqlOutbox.cs +++ b/src/Paramore.Brighter.Outbox.MySql/MySqlOutbox.cs @@ -26,8 +26,9 @@ THE SOFTWARE. */ using System; using System.Data; using System.Data.Common; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using MySqlConnector; -using Paramore.Brighter.Logging; using Paramore.Brighter.MySql; using Paramore.Brighter.Observability; @@ -45,10 +46,12 @@ public class MySqlOutbox : RelationDatabaseOutbox /// /// The configuration to connect to this data store /// Provides a connection to the Db that allows us to enlist in an ambient transaction + /// The logger to use; defaults to a null logger when not supplied public MySqlOutbox(IAmARelationalDatabaseConfiguration configuration, - IAmARelationalDbConnectionProvider connectionProvider) - : base(DbSystem.MySql, configuration, connectionProvider, - new MySqlQueries(), ApplicationLogging.CreateLogger()) + IAmARelationalDbConnectionProvider connectionProvider, + ILogger? logger = null) + : base(DbSystem.MySql, configuration, connectionProvider, + new MySqlQueries(), logger ?? NullLogger.Instance) { } @@ -56,8 +59,9 @@ public MySqlOutbox(IAmARelationalDatabaseConfiguration configuration, /// Initializes a new instance of the class. /// /// The configuration to connect to this data store - public MySqlOutbox(IAmARelationalDatabaseConfiguration configuration) - : this(configuration, new MySqlConnectionProvider(configuration)) + /// The logger to use; defaults to a null logger when not supplied + public MySqlOutbox(IAmARelationalDatabaseConfiguration configuration, ILogger? logger = null) + : this(configuration, new MySqlConnectionProvider(configuration), logger) { } diff --git a/src/Paramore.Brighter.Outbox.PostgreSql/PostgreSqlOutbox.cs b/src/Paramore.Brighter.Outbox.PostgreSql/PostgreSqlOutbox.cs index e4240b5a7a..f9853f04f5 100644 --- a/src/Paramore.Brighter.Outbox.PostgreSql/PostgreSqlOutbox.cs +++ b/src/Paramore.Brighter.Outbox.PostgreSql/PostgreSqlOutbox.cs @@ -26,8 +26,9 @@ THE SOFTWARE. */ using System; using System.Data; using System.Linq; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Npgsql; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; using Paramore.Brighter.PostgreSql; @@ -43,11 +44,13 @@ public class PostgreSqlOutbox : RelationDatabaseOutbox /// /// The configuration to connect to this data store /// Provides a connection to the Db that allows us to enlist in an ambient transaction + /// The logger to use; defaults to a null logger when not supplied public PostgreSqlOutbox( IAmARelationalDatabaseConfiguration configuration, - IAmARelationalDbConnectionProvider connectionProvider) - : base(DbSystem.Postgresql, configuration, connectionProvider, - new PostgreSqlQueries(), ApplicationLogging.CreateLogger()) + IAmARelationalDbConnectionProvider connectionProvider, + ILogger? logger = null) + : base(DbSystem.Postgresql, configuration, connectionProvider, + new PostgreSqlQueries(), logger ?? NullLogger.Instance) { } @@ -58,10 +61,12 @@ public PostgreSqlOutbox( /// From v7.0 Npgsql uses an Npgsql data source, leave null to have Brighter manage /// connections; Brighter will not manage type mapping for you in this case so you must register them /// globally + /// The logger to use; defaults to a null logger when not supplied public PostgreSqlOutbox( IAmARelationalDatabaseConfiguration configuration, - NpgsqlDataSource? dataSource = null) - : this(configuration, new PostgreSqlConnectionProvider(configuration, dataSource)) + NpgsqlDataSource? dataSource = null, + ILogger? logger = null) + : this(configuration, new PostgreSqlConnectionProvider(configuration, dataSource), logger) { } diff --git a/src/Paramore.Brighter.Outbox.Spanner/SpannerOutbox.cs b/src/Paramore.Brighter.Outbox.Spanner/SpannerOutbox.cs index 200cad4aaa..64b9b0dd84 100644 --- a/src/Paramore.Brighter.Outbox.Spanner/SpannerOutbox.cs +++ b/src/Paramore.Brighter.Outbox.Spanner/SpannerOutbox.cs @@ -6,7 +6,7 @@ using Google.Cloud.Spanner.Data; using Grpc.Core; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Observability; using Paramore.Brighter.Spanner; @@ -29,11 +29,9 @@ namespace Paramore.Brighter.Outbox.Spanner; /// encapsulates the Spanner-specific SQL queries for outbox operations. /// /// -public class SpannerOutbox(IAmARelationalDatabaseConfiguration configuration, IAmARelationalDbConnectionProvider connectionProvider) - : RelationDatabaseOutbox(DbSystem.Spanner, configuration, connectionProvider, new SpannerQueries(), s_logger) +public class SpannerOutbox(IAmARelationalDatabaseConfiguration configuration, IAmARelationalDbConnectionProvider connectionProvider, ILogger? logger = null) + : RelationDatabaseOutbox(DbSystem.Spanner, configuration, connectionProvider, new SpannerQueries(), logger ?? NullLogger.Instance) { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); - /// /// Initializes a new instance of the class with only /// the database configuration. This constructor internally creates a default @@ -41,10 +39,11 @@ public class SpannerOutbox(IAmARelationalDatabaseConfiguration configuration, IA /// /// The configuration settings specific to the relational database, /// including connection string, database name, and outbox table name. - public SpannerOutbox(IAmARelationalDatabaseConfiguration configuration) - : this(configuration, new SpannerConnectionProvider(configuration)) + /// The logger to use; defaults to a null logger when not supplied + public SpannerOutbox(IAmARelationalDatabaseConfiguration configuration, ILogger? logger = null) + : this(configuration, new SpannerConnectionProvider(configuration), logger) { - + } /// diff --git a/src/Paramore.Brighter.Outbox.Sqlite/SqliteOutbox.cs b/src/Paramore.Brighter.Outbox.Sqlite/SqliteOutbox.cs index 4ab1a4b746..0d86843910 100644 --- a/src/Paramore.Brighter.Outbox.Sqlite/SqliteOutbox.cs +++ b/src/Paramore.Brighter.Outbox.Sqlite/SqliteOutbox.cs @@ -28,7 +28,8 @@ THE SOFTWARE. */ using System.Data.Common; using System.Globalization; using Microsoft.Data.Sqlite; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Observability; using Paramore.Brighter.Sqlite; @@ -47,12 +48,14 @@ public class SqliteOutbox : RelationDatabaseOutbox /// /// The configuration to connect to this data store /// Provides a connection to the Db that allows us to enlist in an ambient transaction + /// The logger to use; defaults to a null logger when not supplied public SqliteOutbox( IAmARelationalDatabaseConfiguration configuration, - IAmARelationalDbConnectionProvider connectionProvider + IAmARelationalDbConnectionProvider connectionProvider, + ILogger? logger = null ) - : base(DbSystem.Sqlite, configuration, connectionProvider, - new SqliteQueries(), ApplicationLogging.CreateLogger()) + : base(DbSystem.Sqlite, configuration, connectionProvider, + new SqliteQueries(), logger ?? NullLogger.Instance) { } @@ -60,8 +63,9 @@ IAmARelationalDbConnectionProvider connectionProvider /// Initializes a new instance of the class. /// /// The configuration to connect to this data store - public SqliteOutbox(IAmARelationalDatabaseConfiguration configuration) - : this(configuration, new SqliteConnectionProvider(configuration)) + /// The logger to use; defaults to a null logger when not supplied + public SqliteOutbox(IAmARelationalDatabaseConfiguration configuration, ILogger? logger = null) + : this(configuration, new SqliteConnectionProvider(configuration), logger) { } diff --git a/src/Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection/ServiceCollectionExtensions.cs b/src/Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection/ServiceCollectionExtensions.cs index aaff49b3cd..468f066bf3 100644 --- a/src/Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection/ServiceCollectionExtensions.cs +++ b/src/Paramore.Brighter.ServiceActivator.Extensions.DependencyInjection/ServiceCollectionExtensions.cs @@ -2,8 +2,8 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Extensions.DependencyInjection; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; using Paramore.Brighter.ServiceActivator.Validation; using Paramore.Brighter.Validation; @@ -135,11 +135,10 @@ public static IBrighterBuilder AddConsumers( private static Dispatcher BuildDispatcher(IServiceProvider serviceProvider) { - var loggerFactory = serviceProvider.GetService(); - //if not supplied, use the default logger factory, which has no providers - if (loggerFactory != null) - ApplicationLogging.LoggerFactory = loggerFactory; - + //Resolve the container's logger factory and flow it through the builder as an instance, + //rather than copying it into a process-wide static (which would be disposed with the container). + var loggerFactory = serviceProvider.GetService() ?? NullLoggerFactory.Instance; + var options = serviceProvider.GetRequiredService(); var commandProcessor = serviceProvider.GetRequiredService(); @@ -178,6 +177,7 @@ private static Dispatcher BuildDispatcher(IServiceProvider serviceProvider) .ChannelFactory(channelFactory) .Subscriptions(options.Subscriptions) .ConfigureInstrumentation(tracer, options.InstrumentationOptions) + .ConfigureLogging(loggerFactory) .Build(); } diff --git a/src/Paramore.Brighter.ServiceActivator/ConsumerFactory.cs b/src/Paramore.Brighter.ServiceActivator/ConsumerFactory.cs index 27d343c6a8..e14fe2d4f5 100644 --- a/src/Paramore.Brighter.ServiceActivator/ConsumerFactory.cs +++ b/src/Paramore.Brighter.ServiceActivator/ConsumerFactory.cs @@ -23,6 +23,7 @@ THE SOFTWARE. */ #endregion using System; +using Microsoft.Extensions.Logging; using Paramore.Brighter.Observability; namespace Paramore.Brighter.ServiceActivator @@ -40,6 +41,7 @@ internal sealed class ConsumerFactory : IConsumerFactory private readonly IAmAMessageMapperRegistryAsync? _messageMapperRegistryAsync; private readonly IAmAMessageTransformerFactoryAsync? _messageTransformerFactoryAsync; private readonly Func _mapRequestType; + private readonly ILoggerFactory? _loggerFactory; public ConsumerFactory( IAmACommandProcessor commandProcessor, @@ -48,7 +50,8 @@ public ConsumerFactory( IAmAMessageTransformerFactory? messageTransformerFactory, IAmARequestContextFactory requestContextFactory, IAmABrighterTracer? tracer, - InstrumentationOptions instrumentationOptions = InstrumentationOptions.All) + InstrumentationOptions instrumentationOptions = InstrumentationOptions.All, + ILoggerFactory? loggerFactory = null) { _commandProcessor = commandProcessor; _messageMapperRegistry = messageMapperRegistry; @@ -58,9 +61,10 @@ public ConsumerFactory( _requestContextFactory = requestContextFactory; _tracer = tracer; _instrumentationOptions = instrumentationOptions; + _loggerFactory = loggerFactory; _consumerName = new ConsumerName($"{_subscription.Name}-{Uuid.NewAsString()}"); } - + public ConsumerFactory( IAmACommandProcessor commandProcessor, Subscription subscription, @@ -68,7 +72,8 @@ public ConsumerFactory( IAmAMessageTransformerFactoryAsync? messageTransformerFactoryAsync, IAmARequestContextFactory requestContextFactory, IAmABrighterTracer? tracer, - InstrumentationOptions instrumentationOptions = InstrumentationOptions.All) + InstrumentationOptions instrumentationOptions = InstrumentationOptions.All, + ILoggerFactory? loggerFactory = null) { _commandProcessor = commandProcessor; _messageMapperRegistryAsync = messageMapperRegistryAsync; @@ -78,6 +83,7 @@ public ConsumerFactory( _requestContextFactory = requestContextFactory; _tracer = tracer; _instrumentationOptions = instrumentationOptions; + _loggerFactory = loggerFactory; _consumerName = new ConsumerName($"{_subscription.Name}-{Uuid.NewAsString()}"); } @@ -98,8 +104,8 @@ private Consumer CreateReactor() throw new ArgumentException("Subscription must have a Channel Factory in order to create a consumer."); var channel = _subscription.ChannelFactory.CreateSyncChannel(_subscription); - var messagePump = new Reactor(_commandProcessor, _mapRequestType, _messageMapperRegistry, - _messageTransformerFactory, _requestContextFactory, channel, _tracer, _instrumentationOptions) + var messagePump = new Reactor(_commandProcessor, _mapRequestType, _messageMapperRegistry, + _messageTransformerFactory, _requestContextFactory, channel, _tracer, _instrumentationOptions, loggerFactory: _loggerFactory) { Channel = channel, TimeOut = _subscription.TimeOut, @@ -122,8 +128,8 @@ private Consumer CreateProactor() throw new ArgumentException("Subscription must have a Channel Factory in order to create a consumer."); var channel = _subscription.ChannelFactory.CreateAsyncChannel(_subscription); - var messagePump = new Proactor(_commandProcessor, _mapRequestType, _messageMapperRegistryAsync, - _messageTransformerFactoryAsync, _requestContextFactory, channel, _tracer, _instrumentationOptions) + var messagePump = new Proactor(_commandProcessor, _mapRequestType, _messageMapperRegistryAsync, + _messageTransformerFactoryAsync, _requestContextFactory, channel, _tracer, _instrumentationOptions, loggerFactory: _loggerFactory) { Channel = channel, TimeOut = _subscription.TimeOut, diff --git a/src/Paramore.Brighter.ServiceActivator/DispatchBuilder.cs b/src/Paramore.Brighter.ServiceActivator/DispatchBuilder.cs index 74450af451..4a02733435 100644 --- a/src/Paramore.Brighter.ServiceActivator/DispatchBuilder.cs +++ b/src/Paramore.Brighter.ServiceActivator/DispatchBuilder.cs @@ -25,6 +25,8 @@ THE SOFTWARE. */ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Observability; namespace Paramore.Brighter.ServiceActivator @@ -47,6 +49,7 @@ public class DispatchBuilder : INeedACommandProcessor, INeedAChannelFactory, INe private IAmARequestContextFactory? _requestContextFactory; private IAmABrighterTracer? _tracer; private InstrumentationOptions _instrumentationOptions; + private ILoggerFactory _loggerFactory = NullLoggerFactory.Instance; private DispatchBuilder() { } @@ -134,6 +137,13 @@ public IAmADispatchBuilder NoInstrumentation() return this; } + /// + public IAmADispatchBuilder ConfigureLogging(ILoggerFactory loggerFactory) + { + _loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; + return this; + } + /// /// A list of subscriptions i.e. mappings of channels to commands or events /// @@ -160,9 +170,9 @@ public Dispatcher Build() if (_commandProcessor is null || _subscriptions is null) throw new ArgumentException("Command Processor Factory and Subscription are required."); - return new Dispatcher(_commandProcessor, _subscriptions, _messageMapperRegistry, - _messageMapperRegistryAsync, _messageTransformerFactory, _messageTransformerFactoryAsync, - _requestContextFactory, _tracer, _instrumentationOptions + return new Dispatcher(_commandProcessor, _subscriptions, _messageMapperRegistry, + _messageMapperRegistryAsync, _messageTransformerFactory, _messageTransformerFactoryAsync, + _requestContextFactory, _tracer, _instrumentationOptions, _loggerFactory ); } @@ -262,6 +272,14 @@ public interface INeedObservability /// public interface IAmADispatchBuilder { + /// + /// Supplies the used to create instance-scoped loggers for the + /// and the message pumps it constructs. If not called, a no-op logger factory is used. + /// + /// The logger factory. + /// IAmADispatchBuilder. + IAmADispatchBuilder ConfigureLogging(ILoggerFactory loggerFactory); + /// /// Builds this instance. /// diff --git a/src/Paramore.Brighter.ServiceActivator/Dispatcher.cs b/src/Paramore.Brighter.ServiceActivator/Dispatcher.cs index 5dc1185f15..e33a169587 100644 --- a/src/Paramore.Brighter.ServiceActivator/Dispatcher.cs +++ b/src/Paramore.Brighter.ServiceActivator/Dispatcher.cs @@ -30,8 +30,8 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Extensions; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; using Paramore.Brighter.ServiceActivator.Status; using BindingFlags = System.Reflection.BindingFlags; @@ -46,7 +46,8 @@ namespace Paramore.Brighter.ServiceActivator /// public partial class Dispatcher : IDispatcher { - private static readonly ILogger s_logger= ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; + private readonly ILoggerFactory? _loggerFactory; private Task? _controlTask; private readonly IAmAMessageMapperRegistry? _messageMapperRegistry; @@ -110,12 +111,15 @@ public Dispatcher( IAmAMessageMapperRegistryAsync? messageMapperRegistryAsync = null, IAmAMessageTransformerFactory? messageTransformerFactory = null, IAmAMessageTransformerFactoryAsync? messageTransformerFactoryAsync= null, - IAmARequestContextFactory? requestContextFactory = null, + IAmARequestContextFactory? requestContextFactory = null, IAmABrighterTracer? tracer = null, - InstrumentationOptions instrumentationOptions = InstrumentationOptions.All) + InstrumentationOptions instrumentationOptions = InstrumentationOptions.All, + ILoggerFactory? loggerFactory = null) { CommandProcessor = commandProcessor; - + _loggerFactory = loggerFactory; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + Subscriptions = subscriptions; _messageMapperRegistry = messageMapperRegistry; _messageMapperRegistryAsync = messageMapperRegistryAsync; @@ -148,7 +152,7 @@ public Task End() { if (State == DispatcherState.DS_RUNNING) { - Log.StoppingDispatcher(s_logger); + Log.StoppingDispatcher(_logger); Consumers.Each(consumer => consumer.Shut(consumer.Subscription.RoutingKey)); } @@ -170,7 +174,7 @@ public void Open(SubscriptionName subscriptionName) /// The subscription. public void Open(Subscription subscription) { - Log.OpeningSubscription(s_logger, subscription.Name.Value); + Log.OpeningSubscription(_logger, subscription.Name.Value); AddSubscriptionToSubscriptions(subscription); var addedConsumers = CreateConsumers([subscription]); @@ -229,7 +233,7 @@ public void Shut(Subscription subscription) { if (State == DispatcherState.DS_RUNNING) { - Log.StoppingSubscription(s_logger, subscription.Name.Value); + Log.StoppingSubscription(_logger, subscription.Name.Value); var consumersForConnection = Consumers.Where(consumer => consumer.Subscription.Name == subscription.Name).ToArray(); var noOfConsumers = consumersForConnection.Length; for (int i = 0; i < noOfConsumers; ++i) @@ -304,7 +308,7 @@ private void RunControlLoop(TaskCompletionSource startup) return; } - Log.DispatcherStarting(s_logger); + Log.DispatcherStarting(_logger); try { @@ -314,17 +318,17 @@ private void RunControlLoop(TaskCompletionSource startup) } catch (Exception ex) { - Log.ErrorOnConsumer(s_logger, ex); + Log.ErrorOnConsumer(_logger, ex); startup.TrySetException(ex); throw; } - Log.DispatcherStartingPerformers(s_logger, _tasks.Count); + Log.DispatcherStartingPerformers(_logger, _tasks.Count); WaitForPerformersToStop(); State = DispatcherState.DS_STOPPED; - Log.DispatcherStopped(s_logger); + Log.DispatcherStopped(_logger); } private void OpenConsumers() @@ -349,7 +353,7 @@ private void WaitForPerformersToStop() { ae.Handle(ex => { - Log.ErrorOnConsumer(s_logger, ex); + Log.ErrorOnConsumer(_logger, ex); return true; }); } @@ -361,7 +365,7 @@ private void HandleNextStoppedPerformer() var runningTasks = _tasks.Values.ToArray(); var index = Task.WaitAny(runningTasks); var stoppingConsumer = runningTasks[index]; - Log.PerformerStopped(s_logger, stoppingConsumer.Status); + Log.PerformerStopped(_logger, stoppingConsumer.Status); RemoveConsumerForTask(stoppingConsumer); @@ -379,7 +383,7 @@ private void RemoveConsumerForTask(Task stoppingConsumer) if (consumer is null) return; - Log.RemovingConsumer(s_logger, consumer.Name.Value); + Log.RemovingConsumer(_logger, consumer.Name.Value); if (_consumers.TryRemove(consumer.Name.Value, out consumer)) { @@ -402,15 +406,15 @@ private IEnumerable CreateConsumers(IEnumerable subscrip private Consumer CreateConsumer(Subscription subscription, int? consumerNumber) { - Log.CreatingConsumer(s_logger, consumerNumber, subscription.Name.Value); + Log.CreatingConsumer(_logger, consumerNumber, subscription.Name.Value); if (subscription.MessagePumpType == MessagePumpType.Reactor) { if (_messageMapperRegistry is null) throw new ConfigurationException("You must provide a message mapper registry for the Dispatcher to work"); - var consumerFactory = new ConsumerFactory(CommandProcessor, subscription, _messageMapperRegistry, _messageTransformerFactory, - _requestContextFactory, _tracer, _instrumentationOptions); + var consumerFactory = new ConsumerFactory(CommandProcessor, subscription, _messageMapperRegistry, _messageTransformerFactory, + _requestContextFactory, _tracer, _instrumentationOptions, _loggerFactory); return consumerFactory.Create(); } @@ -419,8 +423,8 @@ private Consumer CreateConsumer(Subscription subscription, int? consumerNumber) if (_messageMapperRegistryAsync is null) throw new ConfigurationException("You must provide a message mapper registry for the Dispatcher to work"); - var consumerFactory = new ConsumerFactory(CommandProcessor, subscription, _messageMapperRegistryAsync, _messageTransformerFactoryAsync, - _requestContextFactory, _tracer, _instrumentationOptions); + var consumerFactory = new ConsumerFactory(CommandProcessor, subscription, _messageMapperRegistryAsync, _messageTransformerFactoryAsync, + _requestContextFactory, _tracer, _instrumentationOptions, _loggerFactory); return consumerFactory.Create(); } diff --git a/src/Paramore.Brighter.ServiceActivator/MessagePump.cs b/src/Paramore.Brighter.ServiceActivator/MessagePump.cs index 9138c2e49f..fa2465e2fb 100644 --- a/src/Paramore.Brighter.ServiceActivator/MessagePump.cs +++ b/src/Paramore.Brighter.ServiceActivator/MessagePump.cs @@ -26,7 +26,7 @@ THE SOFTWARE. */ using System.Collections.Generic; using System.Reflection; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Observability; namespace Paramore.Brighter.ServiceActivator @@ -67,7 +67,7 @@ public enum MessagePumpStatus /// public abstract partial class MessagePump { - internal static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + protected readonly ILogger _logger; protected const string NoMessageReceivedDescription = "Could not receive message. Note that should return an MT_NONE from an empty queue on timeout"; @@ -154,17 +154,19 @@ public abstract partial class MessagePump /// When creating a span for operations how noisy should the attributes be /// Allows you to override the time provider, for testing purposes protected MessagePump( - IAmACommandProcessor commandProcessor, + IAmACommandProcessor commandProcessor, IAmARequestContextFactory requestContextFactory, IAmABrighterTracer? tracer, InstrumentationOptions instrumentationOptions = InstrumentationOptions.All, - TimeProvider? timeProvider = null) + TimeProvider? timeProvider = null, + ILoggerFactory? loggerFactory = null) { CommandProcessor = commandProcessor; RequestContextFactory = requestContextFactory; Tracer = tracer; InstrumentationOptions = instrumentationOptions; PumpTimeProvider = timeProvider ?? TimeProvider.System; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } @@ -192,12 +194,12 @@ protected void ValidateMessageType(MessageType messageType, IRequest request) { if (messageType == MessageType.MT_COMMAND && request is IEvent) { - Log.MessageMismatchCommand(s_logger, request.Id.Value, MessageType.MT_COMMAND); + Log.MessageMismatchCommand(_logger, request.Id.Value, MessageType.MT_COMMAND); } if (messageType == MessageType.MT_EVENT && request is ICommand) { - Log.MessageMismatchEvent(s_logger, request.Id.Value, MessageType.MT_EVENT); + Log.MessageMismatchEvent(_logger, request.Id.Value, MessageType.MT_EVENT); } } diff --git a/src/Paramore.Brighter.ServiceActivator/Ports/Handlers/ConfigurationCommandHandler.cs b/src/Paramore.Brighter.ServiceActivator/Ports/Handlers/ConfigurationCommandHandler.cs index 9f5777f385..a94c708b4d 100644 --- a/src/Paramore.Brighter.ServiceActivator/Ports/Handlers/ConfigurationCommandHandler.cs +++ b/src/Paramore.Brighter.ServiceActivator/Ports/Handlers/ConfigurationCommandHandler.cs @@ -24,7 +24,7 @@ THE SOFTWARE. */ using System; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.ServiceActivator.Ports.Commands; namespace Paramore.Brighter.ServiceActivator.Ports.Handlers @@ -34,7 +34,7 @@ namespace Paramore.Brighter.ServiceActivator.Ports.Handlers /// public partial class ConfigurationCommandHandler : RequestHandler { - private static readonly ILogger s_logger= ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private readonly IDispatcher _dispatcher; @@ -42,9 +42,11 @@ public partial class ConfigurationCommandHandler : RequestHandler class. /// /// - public ConfigurationCommandHandler(IDispatcher dispatcher) + /// The logger; falls back to a no-op logger when null. + public ConfigurationCommandHandler(IDispatcher dispatcher, ILogger? logger = null) { _dispatcher = dispatcher; + _logger = logger ?? NullLogger.Instance; } /// @@ -54,34 +56,34 @@ public ConfigurationCommandHandler(IDispatcher dispatcher) /// TRequest. public override ConfigurationCommand Handle(ConfigurationCommand configurationCommand) { - Log.HandlingConfigurationCommand(s_logger, configurationCommand.Type); + Log.HandlingConfigurationCommand(_logger, configurationCommand.Type); switch (configurationCommand.Type) { case ConfigurationCommandType.CM_STOPALL: - Log.StoppingAllConsumersBegin(s_logger, DateTime.UtcNow.ToString("o")); - Log.LogSeparator(s_logger); - Log.LogEllipsis(s_logger); + Log.StoppingAllConsumersBegin(_logger, DateTime.UtcNow.ToString("o")); + Log.LogSeparator(_logger); + Log.LogEllipsis(_logger); _dispatcher.End().Wait(); - Log.AllConsumersStopped(s_logger, DateTime.UtcNow.ToString("o")); - Log.LogSeparator(s_logger); + Log.AllConsumersStopped(_logger, DateTime.UtcNow.ToString("o")); + Log.LogSeparator(_logger); break; case ConfigurationCommandType.CM_STARTALL: - Log.LogSeparator(s_logger); - Log.StartingAllConsumersBegin(s_logger, DateTime.UtcNow.ToString("o")); - Log.LogSeparator(s_logger); + Log.LogSeparator(_logger); + Log.StartingAllConsumersBegin(_logger, DateTime.UtcNow.ToString("o")); + Log.LogSeparator(_logger); _dispatcher.Receive(); break; case ConfigurationCommandType.CM_STOPCHANNEL: - Log.LogSeparator(s_logger); - Log.StoppingChannel(s_logger, configurationCommand.SubscriptionName.Value); - Log.LogSeparator(s_logger); + Log.LogSeparator(_logger); + Log.StoppingChannel(_logger, configurationCommand.SubscriptionName.Value); + Log.LogSeparator(_logger); _dispatcher.Shut(new SubscriptionName(configurationCommand.SubscriptionName.Value)); break; case ConfigurationCommandType.CM_STARTCHANNEL: - Log.LogSeparator(s_logger); - Log.StartingChannel(s_logger, configurationCommand.SubscriptionName.Value); - Log.LogSeparator(s_logger); + Log.LogSeparator(_logger); + Log.StartingChannel(_logger, configurationCommand.SubscriptionName.Value); + Log.LogSeparator(_logger); _dispatcher.Open(new SubscriptionName(configurationCommand.SubscriptionName.Value)); break; default: diff --git a/src/Paramore.Brighter.ServiceActivator/Proactor.cs b/src/Paramore.Brighter.ServiceActivator/Proactor.cs index 6674f41c6d..299a05b5d8 100644 --- a/src/Paramore.Brighter.ServiceActivator/Proactor.cs +++ b/src/Paramore.Brighter.ServiceActivator/Proactor.cs @@ -66,11 +66,12 @@ public Proactor( IAmAChannelAsync channel, IAmABrighterTracer? tracer = null, InstrumentationOptions instrumentationOptions = InstrumentationOptions.All, - TimeProvider? timeProvider = null) - : base(commandProcessor, requestContextFactory, tracer, instrumentationOptions, timeProvider) + TimeProvider? timeProvider = null, + ILoggerFactory? loggerFactory = null) + : base(commandProcessor, requestContextFactory, tracer, instrumentationOptions, timeProvider, loggerFactory) { _mapRequestType = mapRequestType; - _transformPipelineBuilder = new TransformPipelineBuilderAsync(messageMapperRegistry, messageTransformerFactory, instrumentationOptions); + _transformPipelineBuilder = new TransformPipelineBuilderAsync(messageMapperRegistry, messageTransformerFactory, instrumentationOptions, loggerFactory); Channel = channel; } @@ -101,14 +102,14 @@ public void Run() private async Task Acknowledge(Message message) { - Log.AcknowledgeMessage(s_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.AcknowledgeMessage(_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); await Channel.AcknowledgeAsync(message); } private async Task DispatchRequest(MessageHeader messageHeader, TRequest request, RequestContext requestContext) where TRequest : class, IRequest { - Log.DispatchingMessage(s_logger, request.Id.Value, Thread.CurrentThread.ManagedThreadId, Channel.Name); + Log.DispatchingMessage(_logger, request.Id.Value, Thread.CurrentThread.ManagedThreadId, Channel.Name); requestContext.Span?.AddEvent(new ActivityEvent("Dispatch Message")); var messageType = messageHeader.MessageType; @@ -146,7 +147,7 @@ private async Task EventLoop() break; } - Log.ReceivingMessagesFromChannel(s_logger, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.ReceivingMessagesFromChannel(_logger, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); // receive span covers only the broker call so its Duration reflects broker latency, not dispatch Activity? receiveSpan = null; @@ -161,7 +162,7 @@ private async Task EventLoop() } catch (ChannelFailureException ex) when (ex.InnerException is BrokenCircuitException) { - Log.BrokenCircuitExceptionMessages(s_logger, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.BrokenCircuitExceptionMessages(_logger, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); receiveSpan?.AddException(ex); receiveSpan?.SetStatus(ActivityStatusCode.Error, ex.Message); await Task.Delay(ChannelFailureDelay); @@ -169,7 +170,7 @@ private async Task EventLoop() } catch (ChannelFailureException ex) { - Log.ChannelFailureExceptionMessages(s_logger, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.ChannelFailureExceptionMessages(_logger, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); receiveSpan?.AddException(ex); receiveSpan?.SetStatus(ActivityStatusCode.Error, ex.Message); await Task.Delay(ChannelFailureDelay); @@ -177,7 +178,7 @@ private async Task EventLoop() } catch (Exception ex) { - Log.ExceptionReceivingMessages(s_logger, ex, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.ExceptionReceivingMessages(_logger, ex, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); receiveSpan?.AddException(ex); receiveSpan?.SetStatus(ActivityStatusCode.Error, ex.Message); } @@ -200,7 +201,7 @@ private async Task EventLoop() // failed to parse a message from the incoming data if (message.Header.MessageType == MessageType.MT_UNACCEPTABLE) { - Log.FailedToParseMessage(s_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.FailedToParseMessage(_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); var description = $"MessagePump: Failed to parse a message from the incoming message with id {message.Id} from {Channel.Name} on thread # {Environment.CurrentManagedThreadId}"; receiveSpan?.SetStatus(ActivityStatusCode.Error, description); IncrementUnacceptableMessageCount(); @@ -212,7 +213,7 @@ private async Task EventLoop() // QUIT command if (message.Header.MessageType == MessageType.MT_QUIT) { - Log.QuitReceivingMessages(s_logger, Channel.Name, Environment.CurrentManagedThreadId); + Log.QuitReceivingMessages(_logger, Channel.Name, Environment.CurrentManagedThreadId); await Channel.DisposeAsync(); Status = MessagePumpStatus.MP_STOPPED; break; @@ -247,7 +248,7 @@ private async Task EventLoop() { if (exception is ConfigurationException configurationException) { - Log.StoppingReceivingMessages(s_logger, configurationException, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.StoppingReceivingMessages(_logger, configurationException, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); stop = true; Status = MessagePumpStatus.MP_ERROR; break; @@ -279,12 +280,12 @@ private async Task EventLoop() continue; } - Log.FailedToDispatchMessage(s_logger, exception, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.FailedToDispatchMessage(_logger, exception, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); } if (deferAction != null) { - Log.DeferringMessage(s_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.DeferringMessage(_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); processSpan?.SetStatus(ActivityStatusCode.Error, $"Deferring message {message.Id} for later action"); if (await RequeueMessage(message, deferAction.Delay)) continue; @@ -292,9 +293,9 @@ private async Task EventLoop() if (dontAck != null) { - Log.NotAcknowledgingMessage(s_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.NotAcknowledgingMessage(_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); if (dontAck.InnerException != null) - Log.DontAckActionInnerException(s_logger, dontAck.InnerException, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.DontAckActionInnerException(_logger, dontAck.InnerException, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); processSpan?.SetStatus(ActivityStatusCode.Error, $"Don't Ack Thrown. Not acknowledging message {message.Id}"); await Channel.NackAsync(message); IncrementUnacceptableMessageCount(); @@ -330,7 +331,7 @@ private async Task EventLoop() } catch (ConfigurationException configurationException) { - Log.StoppingReceivingMessages2(s_logger, configurationException, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.StoppingReceivingMessages2(_logger, configurationException, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); await RejectMessage(message, new MessageRejectionReason(RejectionReason.DeliveryError,$"Not processed due to configuration exception: {configurationException.Message}")); processSpan?.SetStatus(ActivityStatusCode.Error, $"MessagePump: Stopping receiving of messages from {Channel.Name} on thread # {Environment.CurrentManagedThreadId}"); await Channel.DisposeAsync(); @@ -339,7 +340,7 @@ private async Task EventLoop() } catch (DeferMessageAction deferAction) { - Log.DeferringMessage2(s_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.DeferringMessage2(_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); processSpan?.SetStatus(ActivityStatusCode.Error, $"Deferring message {message.Id} for later action"); @@ -347,9 +348,9 @@ private async Task EventLoop() } catch (DontAckAction dontAckAction) { - Log.NotAcknowledgingMessage(s_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.NotAcknowledgingMessage(_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); if (dontAckAction.InnerException != null) - Log.DontAckActionInnerException(s_logger, dontAckAction.InnerException, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.DontAckActionInnerException(_logger, dontAckAction.InnerException, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); processSpan?.SetStatus(ActivityStatusCode.Error, $"Don't Ack Thrown. Not acknowledging message {message.Id}"); await Channel.NackAsync(message); IncrementUnacceptableMessageCount(); @@ -374,7 +375,7 @@ private async Task EventLoop() catch (MessageMappingException messageMappingException) { var description = $"MessagePump: Failed to map message {message.Id} from {Channel.Name} with {Channel.RoutingKey} on thread # {Thread.CurrentThread.ManagedThreadId}"; - Log.FailedToMapMessage(s_logger, messageMappingException, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.FailedToMapMessage(_logger, messageMappingException, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); IncrementUnacceptableMessageCount(); processSpan?.SetStatus(ActivityStatusCode.Error, description); await RejectMessage(message, new MessageRejectionReason(RejectionReason.Unacceptable, description)); @@ -382,7 +383,7 @@ private async Task EventLoop() } catch (Exception e) { - Log.FailedToDispatchMessage2(s_logger, e, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.FailedToDispatchMessage2(_logger, e, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); IncrementUnacceptableMessageCount(); processSpan?.SetStatus(ActivityStatusCode.Error,$"MessagePump: Failed to dispatch message '{message.Id}' from {Channel.Name} with {Channel.RoutingKey} on thread # {Environment.CurrentManagedThreadId}"); } @@ -395,7 +396,7 @@ private async Task EventLoop() } while (true); - Log.FinishedRunningMessageLoop(s_logger, Channel.Name, Channel.RoutingKey.Value, Thread.CurrentThread.ManagedThreadId); + Log.FinishedRunningMessageLoop(_logger, Channel.Name, Channel.RoutingKey.Value, Thread.CurrentThread.ManagedThreadId); Tracer?.EndSpan(pumpSpan); } @@ -476,7 +477,7 @@ private RequestContext InitRequestContext(Activity? span, Message message) private Task RejectMessage(Message message, MessageRejectionReason reason ) { - Log.RejectingMessage(s_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.RejectingMessage(_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); message.Header.Bag[Message.RejectionReasonHeaderName] = $"Message rejected reason: {reason.RejectionReason} Description: {reason.Description}"; @@ -493,7 +494,7 @@ private Task RequeueMessage(Message message, TimeSpan? delay = null) { var originalMessageId = message.Header.Bag.TryGetValue(Message.OriginalMessageIdHeaderName, out object? value) ? value.ToString() : null; - Log.DroppingMessage(s_logger, RequeueCount, message.Id.Value, string.IsNullOrEmpty(originalMessageId) + Log.DroppingMessage(_logger, RequeueCount, message.Id.Value, string.IsNullOrEmpty(originalMessageId) ? string.Empty : $" (original message id {originalMessageId})", Channel.Name, Channel.RoutingKey.Value, Thread.CurrentThread.ManagedThreadId); @@ -505,14 +506,14 @@ private Task RequeueMessage(Message message, TimeSpan? delay = null) } } - Log.ReQueueingMessage(s_logger, message.Id.Value, Thread.CurrentThread.ManagedThreadId, Channel.Name, Channel.RoutingKey.Value); + Log.ReQueueingMessage(_logger, message.Id.Value, Thread.CurrentThread.ManagedThreadId, Channel.Name, Channel.RoutingKey.Value); return Channel.RequeueAsync(message, delay ?? RequeueDelay); } private async Task TranslateMessage(Message message, RequestContext requestContext, CancellationToken cancellationToken = default) { - Log.TranslateMessage(s_logger, message.Id.Value, Thread.CurrentThread.ManagedThreadId); + Log.TranslateMessage(_logger, message.Id.Value, Thread.CurrentThread.ManagedThreadId); requestContext.Span?.AddEvent(new ActivityEvent("Translate Message")); var requestType = _mapRequestType(message); @@ -559,7 +560,7 @@ private bool UnacceptableMessageLimitReached() if (UnacceptableMessageLimit <= 0) return false; if (UnacceptableMessageCount < UnacceptableMessageLimit) return false; - Log.UnacceptableMessageLimitReached(s_logger, UnacceptableMessageLimit, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.UnacceptableMessageLimitReached(_logger, UnacceptableMessageLimit, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); return true; } diff --git a/src/Paramore.Brighter.ServiceActivator/Reactor.cs b/src/Paramore.Brighter.ServiceActivator/Reactor.cs index 68d8e85fdc..cb7d54e69b 100644 --- a/src/Paramore.Brighter.ServiceActivator/Reactor.cs +++ b/src/Paramore.Brighter.ServiceActivator/Reactor.cs @@ -66,11 +66,12 @@ public Reactor( IAmAChannelSync channel, IAmABrighterTracer? tracer = null, InstrumentationOptions instrumentationOptions = InstrumentationOptions.All, - TimeProvider? timeProvider = null) - : base(commandProcessor, requestContextFactory, tracer, instrumentationOptions, timeProvider) + TimeProvider? timeProvider = null, + ILoggerFactory? loggerFactory = null) + : base(commandProcessor, requestContextFactory, tracer, instrumentationOptions, timeProvider, loggerFactory) { _mapRequestType = mapRequestType; - _transformPipelineBuilder = new TransformPipelineBuilder(messageMapperRegistry, messageTransformerFactory, instrumentationOptions); + _transformPipelineBuilder = new TransformPipelineBuilder(messageMapperRegistry, messageTransformerFactory, instrumentationOptions, loggerFactory); Channel = channel; } @@ -105,7 +106,7 @@ public void Run() break; } - Log.ReceivingMessages(s_logger, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.ReceivingMessages(_logger, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); // receive span covers only the broker call so its Duration reflects broker latency, not dispatch Activity? receiveSpan = null; @@ -120,7 +121,7 @@ public void Run() } catch (ChannelFailureException ex) when (ex.InnerException is BrokenCircuitException) { - Log.BrokenCircuitException(s_logger, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.BrokenCircuitException(_logger, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); receiveSpan?.AddException(ex); receiveSpan?.SetStatus(ActivityStatusCode.Error, ex.Message); Thread.Sleep(ChannelFailureDelay); //-- pause pump; blocks consuming thread on empty queue; @@ -128,7 +129,7 @@ public void Run() } catch (ChannelFailureException ex) { - Log.ChannelFailureException(s_logger, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.ChannelFailureException(_logger, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); receiveSpan?.AddException(ex); receiveSpan?.SetStatus(ActivityStatusCode.Error, ex.Message); Thread.Sleep(ChannelFailureDelay); //-- pause pump; blocks consuming thread on empty queue; @@ -136,7 +137,7 @@ public void Run() } catch (Exception ex) { - Log.ExceptionReceivingMessages(s_logger, ex, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.ExceptionReceivingMessages(_logger, ex, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); receiveSpan?.AddException(ex); receiveSpan?.SetStatus(ActivityStatusCode.Error, ex.Message); } @@ -159,7 +160,7 @@ public void Run() // failed to parse a message from the incoming data if (message.Header.MessageType == MessageType.MT_UNACCEPTABLE) { - Log.FailedToParseMessage(s_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.FailedToParseMessage(_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); var description = $"MessagePump: Failed to parse a message from the incoming message with id {message.Id} from {Channel.Name} on thread # {Environment.CurrentManagedThreadId}"; receiveSpan?.SetStatus(ActivityStatusCode.Error, description); IncrementUnacceptableMessageCount(); @@ -171,7 +172,7 @@ public void Run() // QUIT command if (message.Header.MessageType == MessageType.MT_QUIT) { - Log.QuitReceivingMessages(s_logger, Channel.Name, Environment.CurrentManagedThreadId); + Log.QuitReceivingMessages(_logger, Channel.Name, Environment.CurrentManagedThreadId); Channel.Dispose(); Status = MessagePumpStatus.MP_STOPPED; break; @@ -206,7 +207,7 @@ public void Run() { if (exception is ConfigurationException configurationException) { - Log.StoppingReceivingMessages(s_logger, configurationException, Channel.Name, + Log.StoppingReceivingMessages(_logger, configurationException, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); stop = true; rejectReason = configurationException.Message; @@ -240,13 +241,13 @@ public void Run() continue; } - Log.FailedToDispatchMessage(s_logger, exception, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, + Log.FailedToDispatchMessage(_logger, exception, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); } if (deferAction != null) { - Log.DeferringMessage(s_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value,Environment.CurrentManagedThreadId); + Log.DeferringMessage(_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value,Environment.CurrentManagedThreadId); processSpan?.SetStatus(ActivityStatusCode.Error, $"Deferring message {message.Id} for later action"); if (RequeueMessage(message, deferAction.Delay)) continue; @@ -254,9 +255,9 @@ public void Run() if (dontAck != null) { - Log.NotAcknowledgingMessage(s_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.NotAcknowledgingMessage(_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); if (dontAck.InnerException != null) - Log.DontAckActionInnerException(s_logger, dontAck.InnerException, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.DontAckActionInnerException(_logger, dontAck.InnerException, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); processSpan?.SetStatus(ActivityStatusCode.Error, $"Don't Ack Thrown. Not acknowledging message {message.Id}"); Channel.Nack(message); IncrementUnacceptableMessageCount(); @@ -295,7 +296,7 @@ public void Run() } catch (ConfigurationException configurationException) { - Log.StoppingReceivingMessages2(s_logger, configurationException, Channel.Name, Channel.RoutingKey.Value, + Log.StoppingReceivingMessages2(_logger, configurationException, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); IncrementUnacceptableMessageCount(); RejectMessage(message, new MessageRejectionReason(RejectionReason.DeliveryError, $"Not processed due to configuration exception: {configurationException.Message}")); @@ -307,15 +308,15 @@ public void Run() } catch (DeferMessageAction deferAction) { - Log.DeferringMessage2(s_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.DeferringMessage2(_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); processSpan?.SetStatus(ActivityStatusCode.Error, $"Deferring message {message.Id} for later action"); if (RequeueMessage(message, deferAction.Delay)) continue; } catch (DontAckAction dontAckAction) { - Log.NotAcknowledgingMessage(s_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.NotAcknowledgingMessage(_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); if (dontAckAction.InnerException != null) - Log.DontAckActionInnerException(s_logger, dontAckAction.InnerException, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.DontAckActionInnerException(_logger, dontAckAction.InnerException, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); processSpan?.SetStatus(ActivityStatusCode.Error, $"Don't Ack Thrown. Not acknowledging message {message.Id}"); Channel.Nack(message); IncrementUnacceptableMessageCount(); @@ -339,7 +340,7 @@ public void Run() catch (MessageMappingException messageMappingException) { var description = $"MessagePump: Failed to map message {message.Id} from {Channel.Name} with {Channel.RoutingKey} on thread # {Thread.CurrentThread.ManagedThreadId}"; - Log.FailedToMapMessage(s_logger, messageMappingException, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.FailedToMapMessage(_logger, messageMappingException, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); IncrementUnacceptableMessageCount(); processSpan?.SetStatus(ActivityStatusCode.Error, description); RejectMessage(message, new MessageRejectionReason(RejectionReason.Unacceptable, description)); @@ -347,7 +348,7 @@ public void Run() } catch (Exception e) { - Log.FailedToDispatchMessage2(s_logger, e, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.FailedToDispatchMessage2(_logger, e, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); IncrementUnacceptableMessageCount(); processSpan?.SetStatus(ActivityStatusCode.Error,$"MessagePump: Failed to dispatch message '{message.Id}' from {Channel.Name} with {Channel.RoutingKey} on thread # {Environment.CurrentManagedThreadId}"); } @@ -360,21 +361,21 @@ public void Run() } while (true); - Log.FinishedRunningMessageLoop(s_logger, Channel.Name, Channel.RoutingKey.Value, Thread.CurrentThread.ManagedThreadId); + Log.FinishedRunningMessageLoop(_logger, Channel.Name, Channel.RoutingKey.Value, Thread.CurrentThread.ManagedThreadId); Tracer?.EndSpan(pumpSpan); } private void AcknowledgeMessage(Message message) { - Log.AcknowledgeMessage(s_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.AcknowledgeMessage(_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); Channel.Acknowledge(message); } private void DispatchRequest(MessageHeader messageHeader, TRequest request, RequestContext requestContext) where TRequest : class, IRequest { - Log.DispatchingMessage(s_logger, request.Id.Value, Thread.CurrentThread.ManagedThreadId, Channel.Name); + Log.DispatchingMessage(_logger, request.Id.Value, Thread.CurrentThread.ManagedThreadId, Channel.Name); requestContext.Span?.AddEvent(new ActivityEvent("Dispatch Message")); var messageType = messageHeader.MessageType; @@ -409,7 +410,7 @@ private RequestContext InitRequestContext(Activity? span, Message message) private bool RejectMessage(Message message, MessageRejectionReason reason) { - Log.RejectingMessage(s_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.RejectingMessage(_logger, message.Id.Value, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); message.Header.Bag[Message.RejectionReasonHeaderName] = $"Message rejected reason: {reason.RejectionReason} Description: {reason.Description}"; @@ -487,7 +488,7 @@ private bool RequeueMessage(Message message, TimeSpan? delay = null) { var originalMessageId = message.Header.Bag.TryGetValue(Message.OriginalMessageIdHeaderName, out object? value) ? value.ToString() : null; - Log.DroppingMessage(s_logger, RequeueCount, message.Id.Value, string.IsNullOrEmpty(originalMessageId) + Log.DroppingMessage(_logger, RequeueCount, message.Id.Value, string.IsNullOrEmpty(originalMessageId) ? string.Empty : $" (original message id {originalMessageId})", Channel.Name, Channel.RoutingKey.Value, Thread.CurrentThread.ManagedThreadId); @@ -496,14 +497,14 @@ private bool RequeueMessage(Message message, TimeSpan? delay = null) } } - Log.RequeueingMessage(s_logger, message.Id.Value, Thread.CurrentThread.ManagedThreadId, Channel.Name, Channel.RoutingKey.Value); + Log.RequeueingMessage(_logger, message.Id.Value, Thread.CurrentThread.ManagedThreadId, Channel.Name, Channel.RoutingKey.Value); return Channel.Requeue(message, delay ?? RequeueDelay); } private IRequest TranslateMessage(Message message, RequestContext requestContext) { - Log.TranslateMessage(s_logger, message.Id.Value, Thread.CurrentThread.ManagedThreadId); + Log.TranslateMessage(_logger, message.Id.Value, Thread.CurrentThread.ManagedThreadId); requestContext.Span?.AddEvent(new ActivityEvent("Translate Message")); IRequest request; @@ -552,7 +553,7 @@ private bool UnacceptableMessageLimitReached() if (UnacceptableMessageCount >= UnacceptableMessageLimit) { - Log.UnacceptableMessageLimitReached(s_logger, UnacceptableMessageLimit, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); + Log.UnacceptableMessageLimitReached(_logger, UnacceptableMessageLimit, Channel.Name, Channel.RoutingKey.Value, Environment.CurrentManagedThreadId); return true; } diff --git a/src/Paramore.Brighter.Transformers.AWS.V4/S3LuggageStore.cs b/src/Paramore.Brighter.Transformers.AWS.V4/S3LuggageStore.cs index ef16a47e80..e994510a1e 100644 --- a/src/Paramore.Brighter.Transformers.AWS.V4/S3LuggageStore.cs +++ b/src/Paramore.Brighter.Transformers.AWS.V4/S3LuggageStore.cs @@ -35,7 +35,7 @@ THE SOFTWARE. */ using Amazon.SecurityToken; using Amazon.SecurityToken.Model; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Observability; using Paramore.Brighter.Tasks; using Paramore.Brighter.Transforms.Storage; @@ -70,7 +70,7 @@ namespace Paramore.Brighter.Transformers.AWS.V4; public partial class S3LuggageStore : IAmAStorageProvider, IAmAStorageProviderAsync { private const string ClaimCheckProvider = "aws_s3"; - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private readonly S3LuggageOptions _options; private readonly Dictionary _spanAttributes = new(); private readonly string _bucketName; @@ -81,14 +81,17 @@ public partial class S3LuggageStore : IAmAStorageProvider, IAmAStorageProviderAs /// Initializes a new instance of the class with the specified S3 luggage options. /// /// The containing the S3 client, bucket details, and other configuration. - public S3LuggageStore(S3LuggageOptions options) + /// The factory used to create the logger for this store. + public S3LuggageStore(S3LuggageOptions options, ILoggerFactory? loggerFactory = null) { _client = options.Client; _luggagePrefix = options.LuggagePrefix; _options = options; _bucketName = options.BucketName; - + _spanAttributes["claim_check.aws-s3.region"] = options.BucketRegion.Value; + + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } /// @@ -148,7 +151,7 @@ await CreateBucketAsync( } catch (Exception e) { - Log.ErrorCreatingValidatingLuggageStore(s_logger, _bucketName, _options.BucketRegion, e); + Log.ErrorCreatingValidatingLuggageStore(_logger, _bucketName, _options.BucketRegion, e); throw; } } @@ -165,7 +168,7 @@ public async Task DeleteAsync(string claimCheck, CancellationToken cancellationT if (response.HttpStatusCode != HttpStatusCode.NoContent) { - Log.CouldNotDeleteLuggage(s_logger, claimCheck, _bucketName); + Log.CouldNotDeleteLuggage(_logger, claimCheck, _bucketName); } } finally @@ -182,13 +185,13 @@ public async Task RetrieveAsync(string claimCheck, CancellationToken can { var request = new GetObjectRequest { BucketName = _bucketName, Key = claimCheck, }; - Log.Downloading(s_logger, claimCheck, _bucketName); + Log.Downloading(_logger, claimCheck, _bucketName); // Issue request and remember to dispose of the response using var response = await _client.GetObjectAsync(request, cancellationToken); if (response.HttpStatusCode != HttpStatusCode.OK) { - Log.CouldNotDownload(s_logger, claimCheck, _bucketName); + Log.CouldNotDownload(_logger, claimCheck, _bucketName); throw new InvalidOperationException($"Could not download {claimCheck} from {_bucketName}"); } @@ -206,12 +209,12 @@ public async Task RetrieveAsync(string claimCheck, CancellationToken can } catch (AmazonS3Exception) { - Log.UnableToRead(s_logger, claimCheck, _bucketName); + Log.UnableToRead(_logger, claimCheck, _bucketName); throw; } catch (Exception e) when (e is ObjectDisposedException || e is NotSupportedException) { - Log.UnableToRead(s_logger, claimCheck, _bucketName); + Log.UnableToRead(_logger, claimCheck, _bucketName); throw; } } @@ -254,7 +257,7 @@ public async Task StoreAsync(Stream stream, CancellationToken cancellati var span = Tracer?.CreateClaimCheckSpan(new ClaimCheckSpanInfo(ClaimCheckOperation.Store, ClaimCheckProvider, _bucketName, claimCheck, _spanAttributes, stream.Length)); try { - Log.Uploading(s_logger, claimCheck, _bucketName); + Log.Uploading(_logger, claimCheck, _bucketName); var transferUtility = new TransferUtility(_client); await transferUtility.UploadAsync(stream, _bucketName, claimCheck, cancellationToken); return claimCheck; diff --git a/src/Paramore.Brighter.Transformers.AWS/S3LuggageStore.cs b/src/Paramore.Brighter.Transformers.AWS/S3LuggageStore.cs index dd5c259ecc..ce6b613f0a 100644 --- a/src/Paramore.Brighter.Transformers.AWS/S3LuggageStore.cs +++ b/src/Paramore.Brighter.Transformers.AWS/S3LuggageStore.cs @@ -35,7 +35,7 @@ THE SOFTWARE. */ using Amazon.SecurityToken; using Amazon.SecurityToken.Model; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Observability; using Paramore.Brighter.Tasks; using Paramore.Brighter.Transforms.Storage; @@ -70,7 +70,7 @@ namespace Paramore.Brighter.Transformers.AWS; public partial class S3LuggageStore : IAmAStorageProvider, IAmAStorageProviderAsync { private const string ClaimCheckProvider = "aws_s3"; - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private readonly S3LuggageOptions _options; private readonly Dictionary _spanAttributes = new(); private readonly string _bucketName; @@ -81,14 +81,17 @@ public partial class S3LuggageStore : IAmAStorageProvider, IAmAStorageProviderAs /// Initializes a new instance of the class with the specified S3 luggage options. /// /// The containing the S3 client, bucket details, and other configuration. - public S3LuggageStore(S3LuggageOptions options) + /// The factory used to create the logger for this store. + public S3LuggageStore(S3LuggageOptions options, ILoggerFactory? loggerFactory = null) { _client = options.Client; _luggagePrefix = options.LuggagePrefix; _options = options; _bucketName = options.BucketName; - + _spanAttributes["claim_check.aws-s3.region"] = options.BucketRegion.Value; + + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } /// @@ -148,7 +151,7 @@ await CreateBucketAsync( } catch (Exception e) { - Log.ErrorCreatingValidatingLuggageStore(s_logger, _bucketName, _options.BucketRegion, e); + Log.ErrorCreatingValidatingLuggageStore(_logger, _bucketName, _options.BucketRegion, e); throw; } } @@ -165,7 +168,7 @@ public async Task DeleteAsync(string claimCheck, CancellationToken cancellationT if (response.HttpStatusCode != HttpStatusCode.NoContent) { - Log.CouldNotDeleteLuggage(s_logger, claimCheck, _bucketName); + Log.CouldNotDeleteLuggage(_logger, claimCheck, _bucketName); } } finally @@ -182,13 +185,13 @@ public async Task RetrieveAsync(string claimCheck, CancellationToken can { var request = new GetObjectRequest { BucketName = _bucketName, Key = claimCheck, }; - Log.Downloading(s_logger, claimCheck, _bucketName); + Log.Downloading(_logger, claimCheck, _bucketName); // Issue request and remember to dispose of the response using var response = await _client.GetObjectAsync(request, cancellationToken); if (response.HttpStatusCode != HttpStatusCode.OK) { - Log.CouldNotDownload(s_logger, claimCheck, _bucketName); + Log.CouldNotDownload(_logger, claimCheck, _bucketName); throw new InvalidOperationException($"Could not download {claimCheck} from {_bucketName}"); } @@ -206,12 +209,12 @@ public async Task RetrieveAsync(string claimCheck, CancellationToken can } catch (AmazonS3Exception) { - Log.UnableToRead(s_logger, claimCheck, _bucketName); + Log.UnableToRead(_logger, claimCheck, _bucketName); throw; } catch (Exception e) when (e is ObjectDisposedException || e is NotSupportedException) { - Log.UnableToRead(s_logger, claimCheck, _bucketName); + Log.UnableToRead(_logger, claimCheck, _bucketName); throw; } } @@ -255,7 +258,7 @@ public async Task StoreAsync(Stream stream, CancellationToken cancellati var span = Tracer?.CreateClaimCheckSpan(new ClaimCheckSpanInfo(ClaimCheckOperation.Store, ClaimCheckProvider, _bucketName, claimCheck, _spanAttributes, stream.Length)); try { - Log.Uploading(s_logger, claimCheck, _bucketName); + Log.Uploading(_logger, claimCheck, _bucketName); var transferUtility = new TransferUtility(_client); await transferUtility.UploadAsync(stream, _bucketName, claimCheck, cancellationToken); return claimCheck; diff --git a/src/Paramore.Brighter.Transformers.Gcp/GcsLuggageStore.cs b/src/Paramore.Brighter.Transformers.Gcp/GcsLuggageStore.cs index 3c726bfc9a..180941f267 100644 --- a/src/Paramore.Brighter.Transformers.Gcp/GcsLuggageStore.cs +++ b/src/Paramore.Brighter.Transformers.Gcp/GcsLuggageStore.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Google; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Observability; using Paramore.Brighter.Transforms.Storage; @@ -39,10 +39,10 @@ namespace Paramore.Brighter.Transformers.Gcp; /// Integrates with Brighter's tracing via and provides structured logging through . /// /// -public partial class GcsLuggageStore(GcsLuggageOptions options) : IAmAStorageProvider, IAmAStorageProviderAsync +public partial class GcsLuggageStore(GcsLuggageOptions options, ILoggerFactory? loggerFactory = null) : IAmAStorageProvider, IAmAStorageProviderAsync { private const string ClaimCheckProvider = "gcp_gcs"; - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); private static readonly Dictionary s_spanAttributes = new(); /// @@ -82,7 +82,7 @@ public async Task EnsureStoreExistsAsync(CancellationToken cancellationToken = d } catch(Exception e) { - Log.ErrorCreatingValidatingLuggageStore(s_logger, options.BucketName, e); + Log.ErrorCreatingValidatingLuggageStore(_logger, options.BucketName, e); throw; } } @@ -98,7 +98,7 @@ public async Task DeleteAsync(string claimCheck, CancellationToken cancellationT } catch (GoogleApiException ex) when (ex.HttpStatusCode == HttpStatusCode.NotFound) { - Log.CouldNotDeleteLuggage(s_logger, claimCheck, options.BucketName); + Log.CouldNotDeleteLuggage(_logger, claimCheck, options.BucketName); } finally { @@ -122,7 +122,7 @@ public async Task RetrieveAsync(string claimCheck, CancellationToken can } catch (Exception e) { - Log.UnableToRead(s_logger, claimCheck, options.BucketName, e); + Log.UnableToRead(_logger, claimCheck, options.BucketName, e); throw; } finally @@ -224,7 +224,7 @@ public void Delete(string claimCheck) } catch (GoogleApiException ex) when (ex.HttpStatusCode == HttpStatusCode.NotFound) { - Log.CouldNotDeleteLuggage(s_logger, claimCheck, options.BucketName); + Log.CouldNotDeleteLuggage(_logger, claimCheck, options.BucketName); } finally { @@ -248,7 +248,7 @@ public Stream Retrieve(string claimCheck) } catch (Exception e) { - Log.UnableToRead(s_logger, claimCheck, options.BucketName, e); + Log.UnableToRead(_logger, claimCheck, options.BucketName, e); throw; } finally diff --git a/src/Paramore.Brighter/CommandProcessor.cs b/src/Paramore.Brighter/CommandProcessor.cs index 06be9c002e..a62ddad0bc 100644 --- a/src/Paramore.Brighter/CommandProcessor.cs +++ b/src/Paramore.Brighter/CommandProcessor.cs @@ -34,9 +34,9 @@ THE SOFTWARE. */ using System.Threading.Tasks; using System.Transactions; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.BindingAttributes; using Paramore.Brighter.FeatureSwitch; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; using Polly; using Polly.Registry; @@ -51,7 +51,8 @@ namespace Paramore.Brighter /// public partial class CommandProcessor : IAmACommandProcessor { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; + private readonly ILoggerFactory _loggerFactory; private readonly IAmASubscriberRegistry? _subscriberRegistry; private readonly IAmAHandlerFactorySync? _handlerFactorySync; @@ -161,8 +162,12 @@ public CommandProcessor( IAmAFeatureSwitchRegistry? featureSwitchRegistry = null, InboxConfiguration? inboxConfiguration = null, IAmABrighterTracer? tracer = null, - InstrumentationOptions instrumentationOptions = InstrumentationOptions.All) + InstrumentationOptions instrumentationOptions = InstrumentationOptions.All, + ILoggerFactory? loggerFactory = null) { + _loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; + _logger = _loggerFactory.CreateLogger(); + _subscriberRegistry = subscriberRegistry; if (HandlerFactoryIsNotEitherIAmAHandlerFactorySyncOrAsync(handlerFactory)) @@ -220,9 +225,11 @@ public CommandProcessor( IEnumerable? replySubscriptions = null, IAmAChannelFactory? responseChannelFactory = null, IAmABrighterTracer? tracer = null, - InstrumentationOptions instrumentationOptions = InstrumentationOptions.All) + InstrumentationOptions instrumentationOptions = InstrumentationOptions.All, + ILoggerFactory? loggerFactory = null) : this(subscriberRegistry, handlerFactory, requestContextFactory, policyRegistry, - resilienceResiliencePipelineRegistry, requestSchedulerFactory, featureSwitchRegistry, inboxConfiguration) + resilienceResiliencePipelineRegistry, requestSchedulerFactory, featureSwitchRegistry, inboxConfiguration, + loggerFactory: loggerFactory) { _responseChannelFactory = responseChannelFactory; _tracer = tracer; @@ -260,8 +267,12 @@ public CommandProcessor( InboxConfiguration? inboxConfiguration = null, IEnumerable? replySubscriptions = null, IAmABrighterTracer? tracer = null, - InstrumentationOptions instrumentationOptions = InstrumentationOptions.All) + InstrumentationOptions instrumentationOptions = InstrumentationOptions.All, + ILoggerFactory? loggerFactory = null) { + _loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; + _logger = _loggerFactory.CreateLogger(); + _requestContextFactory = requestContextFactory; _policyRegistry = policyRegistry; _resiliencePipelineRegistry = resilienceResiliencePipelineRegistry; @@ -314,10 +325,10 @@ public void Send(T command, RequestContext? requestContext = null) where T : if (_subscriberRegistry is null) throw new ArgumentException("A subscriberRegistry must be configured."); - using var builder = new PipelineBuilder(_subscriberRegistry, _handlerFactorySync, _inboxConfiguration); + using var builder = new PipelineBuilder(_subscriberRegistry, _handlerFactorySync, _inboxConfiguration, _loggerFactory); try { - Log.BuildingSendPipelineForCommand(s_logger, command.GetType(), command.Id.Value); + Log.BuildingSendPipelineForCommand(_logger, command.GetType(), command.Id.Value); var handlerChain = builder.Build(command, context); AssertValidSendPipeline(command, handlerChain.Count()); @@ -391,10 +402,10 @@ public async Task SendAsync( if (_subscriberRegistry is null) throw new ArgumentException("A subscriberRegistry must be configured."); - using var builder = new PipelineBuilder(_subscriberRegistry, _handlerFactoryAsync, _inboxConfiguration); + using var builder = new PipelineBuilder(_subscriberRegistry, _handlerFactoryAsync, _inboxConfiguration, _loggerFactory); try { - Log.BuildingSendAsyncPipelineForCommand(s_logger, command.GetType(), command.Id.Value); + Log.BuildingSendAsyncPipelineForCommand(_logger, command.GetType(), command.Id.Value); var handlerChain = builder.BuildAsync(command, context, continueOnCapturedContext); AssertValidSendPipeline(command, handlerChain.Count()); @@ -469,13 +480,13 @@ public void Publish(T @event, RequestContext? requestContext = null) where T if (_subscriberRegistry is null) throw new ArgumentException("A subscriberRegistry must be configured."); - using var builder = new PipelineBuilder(_subscriberRegistry, _handlerFactorySync, _inboxConfiguration); - Log.BuildingSendPipelineForEvent(s_logger, @event.GetType(), @event.Id.Value); + using var builder = new PipelineBuilder(_subscriberRegistry, _handlerFactorySync, _inboxConfiguration, _loggerFactory); + Log.BuildingSendPipelineForEvent(_logger, @event.GetType(), @event.Id.Value); var handlerChain = builder.Build(@event, context); var handlerCount = handlerChain.Count(); - Log.FoundHandlerCountForEvent(s_logger, handlerCount, @event.GetType(), @event.Id.Value); + Log.FoundHandlerCountForEvent(_logger, handlerCount, @event.GetType(), @event.Id.Value); var exceptions = new ConcurrentBag(); Parallel.ForEach(handlerChain, (handleRequests) => @@ -572,16 +583,16 @@ public async Task PublishAsync( if (_subscriberRegistry is null) throw new ArgumentException("A subscriberRegistry must be configured."); - using var builder = new PipelineBuilder(_subscriberRegistry, _handlerFactoryAsync, _inboxConfiguration); + using var builder = new PipelineBuilder(_subscriberRegistry, _handlerFactoryAsync, _inboxConfiguration, _loggerFactory); var handlerSpans = new ConcurrentDictionary(); try { - Log.BuildingSendAsyncPipelineForEvent(s_logger, @event.GetType(), @event.Id.Value); + Log.BuildingSendAsyncPipelineForEvent(_logger, @event.GetType(), @event.Id.Value); var handlerChain = builder.BuildAsync(@event, context, continueOnCapturedContext); var handlerCount = handlerChain.Count(); - Log.FoundAsyncHandlerCount(s_logger, handlerCount, @event.GetType(), @event.Id.Value); + Log.FoundAsyncHandlerCount(_logger, handlerCount, @event.GetType(), @event.Id.Value); var exceptions = new ConcurrentBag(); @@ -820,7 +831,7 @@ public Id DepositPost( string? batchId = null) where TRequest : class, IRequest { - Log.SaveRequest(s_logger, request.GetType(), request.Id.Value); + Log.SaveRequest(_logger, request.GetType(), request.Id.Value); var span = _tracer?.CreateSpan(CommandProcessorSpanOperation.Deposit, request, requestContext?.Span, options: _instrumentationOptions); var context = InitRequestContext(span, requestContext); @@ -893,7 +904,7 @@ public Id[] DepositPost( Dictionary? args = null ) where TRequest : class, IRequest { - Log.SaveBulkRequestsRequest(s_logger, typeof(TRequest)); + Log.SaveBulkRequestsRequest(_logger, typeof(TRequest)); var span = _tracer?.CreateBatchSpan(requestContext?.Span, options: _instrumentationOptions); var context = InitRequestContext(span, requestContext); @@ -1062,7 +1073,7 @@ public async Task DepositPostAsync( CancellationToken cancellationToken = default, string? batchId = null) where TRequest : class, IRequest { - Log.SaveRequest(s_logger, request.GetType(), request.Id.Value); + Log.SaveRequest(_logger, request.GetType(), request.Id.Value); var span = _tracer?.CreateSpan(CommandProcessorSpanOperation.Deposit, request, requestContext?.Span, options: _instrumentationOptions); var context = InitRequestContext(span, requestContext); @@ -1439,7 +1450,7 @@ public async Task ClearOutboxAsync( subscription.RoutingKey = new RoutingKey(routingKey); using var responseChannel = _responseChannelFactory.CreateSyncChannel(subscription); - Log.CreateReplyQueueForTopic(s_logger, channelName); + Log.CreateReplyQueueForTopic(_logger, channelName); request.ReplyAddress.Topic = subscription.RoutingKey; request.ReplyAddress.CorrelationId = channelName.ToString(); @@ -1456,18 +1467,18 @@ public async Task ClearOutboxAsync( var outMessage = _mediator!.CreateMessageFromRequest(request, context); //We don't store the message, if we continue to fail further retry is left to the sender - Log.SendingRequestWithRoutingkey(s_logger, channelName); + Log.SendingRequestWithRoutingkey(_logger, channelName); _mediator.CallViaExternalBus(outMessage, requestContext); Message? responseMessage = null; //now we block on the receiver to try and get the message, until timeout. - Log.AwaitingResponseOn(s_logger, channelName); + Log.AwaitingResponseOn(_logger, channelName); ExecuteWithResiliencePipeline(() => responseMessage = responseChannel.Receive(timeOut)); if (responseMessage is not null && responseMessage.Header.MessageType != MessageType.MT_NONE) { - Log.ReplyReceivedFrom(s_logger, channelName); + Log.ReplyReceivedFrom(_logger, channelName); //map to request is map to a response, but it is a request from consumer point of view. Confusing, but... _mediator.CreateRequestFromMessage(responseMessage, context, out TResponse response); Send(response); @@ -1475,7 +1486,7 @@ public async Task ClearOutboxAsync( return response; } - Log.DeletingQueueForRoutingkey(s_logger, channelName); + Log.DeletingQueueForRoutingkey(_logger, channelName); return null; } @@ -1501,7 +1512,7 @@ public static void ClearServiceBus() private void AssertValidSendPipeline(T command, int handlerCount) where T : class, IRequest { - Log.FoundHandlerCountForCommand(s_logger, handlerCount, typeof(T), command.Id.Value); + Log.FoundHandlerCountForCommand(_logger, handlerCount, typeof(T), command.Id.Value); if (handlerCount > 1) throw new ArgumentException( @@ -1550,7 +1561,7 @@ private void ExecuteWithResiliencePipeline(Action action) } catch (Exception e) { - Log.ExceptionWhilstTryingToPublishMessage(s_logger, e); + Log.ExceptionWhilstTryingToPublishMessage(_logger, e); } } diff --git a/src/Paramore.Brighter/CommandProcessorBuilder.cs b/src/Paramore.Brighter/CommandProcessorBuilder.cs index d770b21331..abe58f0659 100644 --- a/src/Paramore.Brighter/CommandProcessorBuilder.cs +++ b/src/Paramore.Brighter/CommandProcessorBuilder.cs @@ -24,6 +24,8 @@ THE SOFTWARE. */ #endregion using System.Collections.Generic; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Extensions; using Paramore.Brighter.FeatureSwitch; using Paramore.Brighter.Observability; @@ -102,6 +104,7 @@ public class CommandProcessorBuilder : INeedAHandlers, private InstrumentationOptions? _instrumetationOptions; private IAmABrighterTracer? _tracer; private IAmARequestSchedulerFactory _requestSchedulerFactory = null!; + private ILoggerFactory _loggerFactory = NullLoggerFactory.Instance; private CommandProcessorBuilder() { @@ -276,6 +279,13 @@ public IAmACommandProcessorBuilder RequestSchedulerFactory(IAmARequestSchedulerF return this; } + /// + public IAmACommandProcessorBuilder ConfigureLogging(ILoggerFactory loggerFactory) + { + _loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; + return this; + } + /// /// Builds the from the configuration. /// @@ -310,7 +320,8 @@ public CommandProcessor Build() resilienceResiliencePipelineRegistry: _resiliencePipelineRegistry, featureSwitchRegistry: _featureSwitchRegistry, instrumentationOptions: _instrumetationOptions.Value, - requestSchedulerFactory: _requestSchedulerFactory); + requestSchedulerFactory: _requestSchedulerFactory, + loggerFactory: _loggerFactory); } if (!_useRequestReplyQueues) @@ -326,7 +337,8 @@ public CommandProcessor Build() inboxConfiguration: _inboxConfiguration, tracer: _tracer, instrumentationOptions: _instrumetationOptions.Value, - requestSchedulerFactory: _requestSchedulerFactory + requestSchedulerFactory: _requestSchedulerFactory, + loggerFactory: _loggerFactory ); if (_useRequestReplyQueues) @@ -344,7 +356,8 @@ public CommandProcessor Build() responseChannelFactory: _responseChannelFactory, tracer: _tracer, instrumentationOptions: _instrumetationOptions.Value, - requestSchedulerFactory: _requestSchedulerFactory + requestSchedulerFactory: _requestSchedulerFactory, + loggerFactory: _loggerFactory ); throw new ConfigurationException( @@ -481,6 +494,14 @@ public interface INeedARequestSchedulerFactory /// public interface IAmACommandProcessorBuilder { + /// + /// Supplies the used to create instance-scoped loggers for the + /// and the object graph it constructs. If not called, a no-op logger factory is used. + /// + /// The logger factory. + /// IAmACommandProcessorBuilder. + IAmACommandProcessorBuilder ConfigureLogging(ILoggerFactory loggerFactory); + /// /// Builds this instance. /// diff --git a/src/Paramore.Brighter/Defer/Handlers/DeferMessageOnErrorHandler.cs b/src/Paramore.Brighter/Defer/Handlers/DeferMessageOnErrorHandler.cs index f21e371875..2a575e84d0 100644 --- a/src/Paramore.Brighter/Defer/Handlers/DeferMessageOnErrorHandler.cs +++ b/src/Paramore.Brighter/Defer/Handlers/DeferMessageOnErrorHandler.cs @@ -24,8 +24,8 @@ THE SOFTWARE. */ using System; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Actions; -using Paramore.Brighter.Logging; namespace Paramore.Brighter.Defer.Handlers; @@ -41,9 +41,18 @@ namespace Paramore.Brighter.Defer.Handlers; public partial class DeferMessageOnErrorHandler : RequestHandler, IAmABackstopHandler where TRequest : class, IRequest { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger>(); + private readonly ILogger _logger; private int _delayMilliseconds; + /// + /// Initializes a new instance of the class. + /// + /// The logger; falls back to a no-op logger when null. + public DeferMessageOnErrorHandler(ILogger>? logger = null) + { + _logger = logger ?? NullLogger>.Instance; + } + /// /// Initializes from attribute parameters. /// @@ -70,7 +79,7 @@ public override TRequest Handle(TRequest request) } catch (Exception ex) { - Log.UnhandledExceptionDeferringMessage(s_logger, ex, typeof(TRequest).Name, ex.Message); + Log.UnhandledExceptionDeferringMessage(_logger, ex, typeof(TRequest).Name, ex.Message); throw new DeferMessageAction(ex.Message, ex, _delayMilliseconds); } } diff --git a/src/Paramore.Brighter/Defer/Handlers/DeferMessageOnErrorHandlerAsync.cs b/src/Paramore.Brighter/Defer/Handlers/DeferMessageOnErrorHandlerAsync.cs index 07e4739f34..4f73201ed7 100644 --- a/src/Paramore.Brighter/Defer/Handlers/DeferMessageOnErrorHandlerAsync.cs +++ b/src/Paramore.Brighter/Defer/Handlers/DeferMessageOnErrorHandlerAsync.cs @@ -26,8 +26,8 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Actions; -using Paramore.Brighter.Logging; namespace Paramore.Brighter.Defer.Handlers; @@ -44,9 +44,18 @@ namespace Paramore.Brighter.Defer.Handlers; public partial class DeferMessageOnErrorHandlerAsync : RequestHandlerAsync, IAmABackstopHandler where TRequest : class, IRequest { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger>(); + private readonly ILogger _logger; private int _delayMilliseconds; + /// + /// Initializes a new instance of the class. + /// + /// The logger; falls back to a no-op logger when null. + public DeferMessageOnErrorHandlerAsync(ILogger>? logger = null) + { + _logger = logger ?? NullLogger>.Instance; + } + /// /// Initializes from attribute parameters. /// @@ -74,7 +83,7 @@ public override async Task HandleAsync(TRequest command, CancellationT } catch (Exception ex) { - Log.UnhandledExceptionDeferringMessage(s_logger, ex, typeof(TRequest).Name, ex.Message); + Log.UnhandledExceptionDeferringMessage(_logger, ex, typeof(TRequest).Name, ex.Message); throw new DeferMessageAction(ex.Message, ex, _delayMilliseconds); } } diff --git a/src/Paramore.Brighter/HandlerLifetimeScope.cs b/src/Paramore.Brighter/HandlerLifetimeScope.cs index e009e5bc4d..6535c66dd9 100644 --- a/src/Paramore.Brighter/HandlerLifetimeScope.cs +++ b/src/Paramore.Brighter/HandlerLifetimeScope.cs @@ -25,32 +25,33 @@ THE SOFTWARE. */ using System; using System.Collections.Generic; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Extensions; -using Paramore.Brighter.Logging; namespace Paramore.Brighter { internal sealed partial class HandlerLifetimeScope : IAmALifetime { - private static readonly ILogger s_logger= ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private readonly IAmAHandlerFactorySync? _handlerFactorySync; private readonly List _trackedObjects = new List(); private readonly List _trackedAsyncObjects = new List(); private readonly IAmAHandlerFactoryAsync? _asyncHandlerFactory; - public HandlerLifetimeScope(IAmAHandlerFactorySync handlerFactorySync) - : this(handlerFactorySync, null) + public HandlerLifetimeScope(IAmAHandlerFactorySync handlerFactorySync, ILoggerFactory? loggerFactory = null) + : this(handlerFactorySync, null, loggerFactory) {} - public HandlerLifetimeScope(IAmAHandlerFactoryAsync asyncHandlerFactory) - : this(null, asyncHandlerFactory) + public HandlerLifetimeScope(IAmAHandlerFactoryAsync asyncHandlerFactory, ILoggerFactory? loggerFactory = null) + : this(null, asyncHandlerFactory, loggerFactory) {} - public HandlerLifetimeScope(IAmAHandlerFactorySync? handlerFactorySync, IAmAHandlerFactoryAsync? asyncHandlerFactory) + public HandlerLifetimeScope(IAmAHandlerFactorySync? handlerFactorySync, IAmAHandlerFactoryAsync? asyncHandlerFactory, ILoggerFactory? loggerFactory = null) { _handlerFactorySync = handlerFactorySync; _asyncHandlerFactory = asyncHandlerFactory; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } public int TrackedItemCount => _trackedObjects.Count + _trackedAsyncObjects.Count; @@ -60,7 +61,7 @@ public void Add(IHandleRequests instance) if (_handlerFactorySync == null) throw new ArgumentException("An instance of a handler can not be added without a HandlerFactory."); _trackedObjects.Add(instance); - Log.TrackingInstance(s_logger, instance.GetHashCode(), instance.GetType()); + Log.TrackingInstance(_logger, instance.GetHashCode(), instance.GetType()); } public void Add(IHandleRequestsAsync instance) @@ -68,7 +69,7 @@ public void Add(IHandleRequestsAsync instance) if (_asyncHandlerFactory == null) throw new ArgumentException("An instance of an async handler can not be added without an AsyncHandlerFactory."); _trackedAsyncObjects.Add(instance); - Log.TrackingAsyncHandlerInstance(s_logger, instance.GetHashCode(), instance.GetType()); + Log.TrackingAsyncHandlerInstance(_logger, instance.GetHashCode(), instance.GetType()); } public void Dispose() @@ -77,14 +78,14 @@ public void Dispose() { //free disposable items _handlerFactorySync?.Release(trackedItem, this); - Log.ReleasingHandlerInstance(s_logger, trackedItem.GetHashCode(), trackedItem.GetType()); + Log.ReleasingHandlerInstance(_logger, trackedItem.GetHashCode(), trackedItem.GetType()); }); _trackedAsyncObjects.Each(trackedItem => { //free disposable items _asyncHandlerFactory?.Release(trackedItem, this); - Log.ReleasingAsyncHandlerInstance(s_logger, trackedItem.GetHashCode(), trackedItem.GetType()); + Log.ReleasingAsyncHandlerInstance(_logger, trackedItem.GetHashCode(), trackedItem.GetType()); }); //clear our tracking diff --git a/src/Paramore.Brighter/InMemoryScheduler.cs b/src/Paramore.Brighter/InMemoryScheduler.cs index d7cd6639a8..f781adf610 100644 --- a/src/Paramore.Brighter/InMemoryScheduler.cs +++ b/src/Paramore.Brighter/InMemoryScheduler.cs @@ -30,8 +30,8 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Paramore.Brighter.Scheduler.Events; using Paramore.Brighter.Tasks; using InvalidOperationException = System.InvalidOperationException; @@ -51,12 +51,13 @@ public class InMemoryScheduler( TimeProvider timeProvider, Func getOrCreateRequestSchedulerId, Func getOrCreateMessageSchedulerId, - OnSchedulerConflict onConflict) + OnSchedulerConflict onConflict, + ILoggerFactory? loggerFactory = null) : IAmAMessageSchedulerSync, IAmAMessageSchedulerAsync, IAmARequestSchedulerSync, IAmARequestSchedulerAsync, IDisposable, IAsyncDisposable { private readonly ConcurrentDictionary _timers = new(); private long _generation; - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); /// public string Schedule(Message message, DateTimeOffset at) @@ -296,7 +297,7 @@ private void Execute(object? state) return; } - s_logger.LogError("Invalid input during executing scheduler {Data}", state); + _logger.LogError("Invalid input during executing scheduler {Data}", state); } /// diff --git a/src/Paramore.Brighter/Inbox/Handlers/UseInboxHandler.cs b/src/Paramore.Brighter/Inbox/Handlers/UseInboxHandler.cs index 2d13ac76fd..59ec389640 100644 --- a/src/Paramore.Brighter/Inbox/Handlers/UseInboxHandler.cs +++ b/src/Paramore.Brighter/Inbox/Handlers/UseInboxHandler.cs @@ -25,8 +25,8 @@ THE SOFTWARE. */ using System; using System.Diagnostics; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Inbox.Exceptions; -using Paramore.Brighter.Logging; namespace Paramore.Brighter.Inbox.Handlers { @@ -42,7 +42,7 @@ namespace Paramore.Brighter.Inbox.Handlers /// public partial class UseInboxHandler : RequestHandler where T: class, IRequest { - private static readonly ILogger s_logger= ApplicationLogging.CreateLogger>(); + private readonly ILogger _logger; private readonly IAmAnInboxSync _inbox; private bool _onceOnly; @@ -53,9 +53,11 @@ public partial class UseInboxHandler : RequestHandler where T: class, IReq /// Initializes a new instance of the class. /// /// The store for commands that pass into the system - public UseInboxHandler(IAmAnInboxSync inbox) + /// The logger; falls back to a no-op logger when null. + public UseInboxHandler(IAmAnInboxSync inbox, ILogger>? logger = null) { _inbox = inbox; + _logger = logger ?? NullLogger>.Instance; } public override void InitializeFromAttributeParams(params object?[] initializerList) @@ -82,26 +84,26 @@ public override T Handle(T request) if (_onceOnly) { - Log.CheckingIfCommandHasAlreadyBeenSeen(s_logger, request.Id.Value); + Log.CheckingIfCommandHasAlreadyBeenSeen(_logger, request.Id.Value); var exists = _inbox.Exists(request.Id.Value, _contextKey, requestContext); if (exists && _onceOnlyAction is OnceOnlyAction.Throw) { - Log.CommandHasAlreadyBeenSeenAsDebug(s_logger, request.Id.Value); + Log.CommandHasAlreadyBeenSeenAsDebug(_logger, request.Id.Value); throw new OnceOnlyException($"A command with id {request.Id} has already been handled"); } if (exists && _onceOnlyAction is OnceOnlyAction.Warn) { - Log.CommandHasAlreadyBeenSeenAsWarning(s_logger, request.Id.Value); + Log.CommandHasAlreadyBeenSeenAsWarning(_logger, request.Id.Value); return request; } } T handledCommand = base.Handle(request); - Log.WritingCommandToTheInbox(s_logger, request.Id.Value); + Log.WritingCommandToTheInbox(_logger, request.Id.Value); _inbox.Add(request, _contextKey, requestContext); diff --git a/src/Paramore.Brighter/Inbox/Handlers/UseInboxHandlerAsync.cs b/src/Paramore.Brighter/Inbox/Handlers/UseInboxHandlerAsync.cs index b3b489bcca..1a1174f7b1 100644 --- a/src/Paramore.Brighter/Inbox/Handlers/UseInboxHandlerAsync.cs +++ b/src/Paramore.Brighter/Inbox/Handlers/UseInboxHandlerAsync.cs @@ -27,8 +27,8 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Inbox.Exceptions; -using Paramore.Brighter.Logging; namespace Paramore.Brighter.Inbox.Handlers { @@ -43,7 +43,7 @@ namespace Paramore.Brighter.Inbox.Handlers /// public partial class UseInboxHandlerAsync : RequestHandlerAsync where T : class, IRequest { - private static readonly ILogger s_logger= ApplicationLogging.CreateLogger>(); + private readonly ILogger _logger; private readonly IAmAnInboxAsync _inbox; private bool _onceOnly; @@ -54,9 +54,11 @@ public partial class UseInboxHandlerAsync : RequestHandlerAsync where T : /// Initializes a new instance of the class. /// /// The store for commands that pass into the system - public UseInboxHandlerAsync(IAmAnInboxAsync inbox) + /// The logger; falls back to a no-op logger when null. + public UseInboxHandlerAsync(IAmAnInboxAsync inbox, ILogger>? logger = null) { _inbox = inbox; + _logger = logger ?? NullLogger>.Instance; } @@ -86,7 +88,7 @@ public override async Task HandleAsync(T command, CancellationToken cancellat if (_onceOnly) { - Log.CheckingIfCommandHasBeenSeen(s_logger, command.Id.Value); + Log.CheckingIfCommandHasBeenSeen(_logger, command.Id.Value); //TODO: We should not use an infinite timeout here - how to configure var exists = await _inbox.ExistsAsync(command.Id.Value, _contextKey, requestContext, -1, cancellationToken) @@ -94,18 +96,18 @@ await _inbox.ExistsAsync(command.Id.Value, _contextKey, requestContext, -1, c if (exists && _onceOnlyAction is OnceOnlyAction.Throw) { - Log.CommandHasBeenSeen(s_logger, command.Id.Value); + Log.CommandHasBeenSeen(_logger, command.Id.Value); throw new OnceOnlyException($"A command with id {command.Id} has already been handled"); } if (exists && _onceOnlyAction is OnceOnlyAction.Warn) { - Log.CommandHasBeenSeenWarning(s_logger, command.Id.Value); + Log.CommandHasBeenSeenWarning(_logger, command.Id.Value); return command; } } - Log.WritingCommandToInbox(s_logger, command.Id.Value); + Log.WritingCommandToInbox(_logger, command.Id.Value); T handledCommand = await base.HandleAsync(command, cancellationToken).ConfigureAwait(ContinueOnCapturedContext); diff --git a/src/Paramore.Brighter/Logging/ApplicationLogging.cs b/src/Paramore.Brighter/Logging/ApplicationLogging.cs index 7c80f1e807..8c2088c82f 100644 --- a/src/Paramore.Brighter/Logging/ApplicationLogging.cs +++ b/src/Paramore.Brighter/Logging/ApplicationLogging.cs @@ -1,10 +1,47 @@ -using Microsoft.Extensions.Logging; +using System; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Paramore.Brighter.Logging { + /// + /// Provides a process-wide for creating loggers. + /// + /// + /// This static accessor is obsolete. Brighter logging is now instance-scoped: the runtime objects Brighter + /// constructs receive an (flowed from the DI container, from + /// CommandProcessorBuilder.ConfigureLogging/DispatchBuilder.ConfigureLogging, or via the optional + /// loggerFactory constructor parameters added across the codebase). The DI extensions no longer copy the + /// container's into this static, which previously caused use-after-dispose when the + /// container was disposed and cross-talk between two Brighter instances in the same process. + /// It remains only as a crash-free, no-op fallback for external callers that still reference it, and will be + /// removed in a future release. + /// + [Obsolete("Brighter logging is now instance-scoped; inject an ILoggerFactory (via the DI extensions, " + + "CommandProcessorBuilder.ConfigureLogging/DispatchBuilder.ConfigureLogging, or the optional loggerFactory " + + "constructor parameters). This static will be removed in a future release.")] public static class ApplicationLogging { - public static ILoggerFactory LoggerFactory { get; set; } = new LoggerFactory(); - public static ILogger CreateLogger() => LoggerFactory.CreateLogger(); + /// + /// The logger factory used by this static accessor. Defaults to a no-op factory; nothing in Brighter writes + /// to it any longer. Setting it does not affect Brighter's instance-scoped logging. + /// + public static ILoggerFactory LoggerFactory { get; set; } = NullLoggerFactory.Instance; + + /// + /// Creates a logger from . Tolerates a disposed factory by returning a no-op logger + /// rather than throwing. + /// + public static ILogger CreateLogger() + { + try + { + return LoggerFactory.CreateLogger(); + } + catch (ObjectDisposedException) + { + return NullLogger.Instance; + } + } } } diff --git a/src/Paramore.Brighter/Logging/Handlers/RequestLoggingHandler.cs b/src/Paramore.Brighter/Logging/Handlers/RequestLoggingHandler.cs index 214348e8f1..0666d566ae 100644 --- a/src/Paramore.Brighter/Logging/Handlers/RequestLoggingHandler.cs +++ b/src/Paramore.Brighter/Logging/Handlers/RequestLoggingHandler.cs @@ -25,6 +25,7 @@ THE SOFTWARE. */ using System; using System.Text.Json; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.JsonConverters; using Paramore.Brighter.Policies.Attributes; using Paramore.Brighter.Policies.Handlers; @@ -40,10 +41,19 @@ namespace Paramore.Brighter.Logging.Handlers /// The type of the t request. public partial class RequestLoggingHandler : RequestHandler where TRequest : class, IRequest { - private static readonly ILogger s_logger= ApplicationLogging.CreateLogger>(); + private readonly ILogger _logger; private HandlerTiming _timing; + /// + /// Initializes a new instance of the class. + /// + /// The logger; falls back to a no-op logger when null. + public RequestLoggingHandler(ILogger>? logger = null) + { + _logger = logger ?? NullLogger>.Instance; + } + /// /// Initializes from attribute parameters. /// @@ -60,7 +70,7 @@ public override void InitializeFromAttributeParams(params object?[] initializerL /// TRequest. public override TRequest Handle(TRequest request) { - Log.LogCommand(s_logger, _timing.ToString(), typeof(TRequest), JsonSerializer.Serialize(request, JsonSerialisationOptions.Options), DateTime.UtcNow); + Log.LogCommand(_logger, _timing.ToString(), typeof(TRequest), JsonSerializer.Serialize(request, JsonSerialisationOptions.Options), DateTime.UtcNow); return base.Handle(request); } @@ -85,7 +95,7 @@ public override TRequest Handle(TRequest request) /// TRequest. public override TRequest Fallback(TRequest command) { - Log.LogFailure(s_logger, typeof(TRequest), JsonSerializer.Serialize(command, JsonSerialisationOptions.Options), DateTime.UtcNow); + Log.LogFailure(_logger, typeof(TRequest), JsonSerializer.Serialize(command, JsonSerialisationOptions.Options), DateTime.UtcNow); return base.Fallback(command); } diff --git a/src/Paramore.Brighter/Logging/Handlers/RequestLoggingHandlerAsync.cs b/src/Paramore.Brighter/Logging/Handlers/RequestLoggingHandlerAsync.cs index a7b87a1fe1..ffa78975f8 100644 --- a/src/Paramore.Brighter/Logging/Handlers/RequestLoggingHandlerAsync.cs +++ b/src/Paramore.Brighter/Logging/Handlers/RequestLoggingHandlerAsync.cs @@ -27,6 +27,7 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.JsonConverters; namespace Paramore.Brighter.Logging.Handlers @@ -39,10 +40,19 @@ namespace Paramore.Brighter.Logging.Handlers /// The type of the t request. public partial class RequestLoggingHandlerAsync : RequestHandlerAsync where TRequest : class, IRequest { - private static readonly ILogger s_logger= ApplicationLogging.CreateLogger>(); + private readonly ILogger _logger; private HandlerTiming _timing; + /// + /// Initializes a new instance of the class. + /// + /// The logger; falls back to a no-op logger when null. + public RequestLoggingHandlerAsync(ILogger>? logger = null) + { + _logger = logger ?? NullLogger>.Instance; + } + /// /// Initializes from attribute parameters. /// @@ -60,7 +70,7 @@ public override void InitializeFromAttributeParams(params object?[] initializerL /// Awaitable . public override async Task HandleAsync(TRequest command, CancellationToken cancellationToken = default) { - Log.LogCommand(s_logger, _timing.ToString(), typeof(TRequest), JsonSerializer.Serialize(command, JsonSerialisationOptions.Options), DateTime.UtcNow); + Log.LogCommand(_logger, _timing.ToString(), typeof(TRequest), JsonSerializer.Serialize(command, JsonSerialisationOptions.Options), DateTime.UtcNow); return await base.HandleAsync(command, cancellationToken).ConfigureAwait(ContinueOnCapturedContext); } @@ -86,7 +96,7 @@ public override async Task HandleAsync(TRequest command, CancellationT /// TRequest. public override async Task FallbackAsync(TRequest command, CancellationToken cancellationToken = default) { - Log.LogFailure(s_logger, typeof(TRequest), JsonSerializer.Serialize(command, JsonSerialisationOptions.Options), DateTime.UtcNow); + Log.LogFailure(_logger, typeof(TRequest), JsonSerializer.Serialize(command, JsonSerialisationOptions.Options), DateTime.UtcNow); return await base.FallbackAsync(command, cancellationToken).ConfigureAwait(ContinueOnCapturedContext); } diff --git a/src/Paramore.Brighter/NullOutboxArchiveProvider.cs b/src/Paramore.Brighter/NullOutboxArchiveProvider.cs index 4a687f73ee..2f248fcced 100644 --- a/src/Paramore.Brighter/NullOutboxArchiveProvider.cs +++ b/src/Paramore.Brighter/NullOutboxArchiveProvider.cs @@ -27,7 +27,7 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Paramore.Brighter { @@ -36,7 +36,7 @@ namespace Paramore.Brighter /// public class NullOutboxArchiveProvider : IAmAnArchiveProvider { - private readonly ILogger _logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger = NullLogger.Instance; /// /// Send a Message to the archive provider diff --git a/src/Paramore.Brighter/OutboxArchiver.cs b/src/Paramore.Brighter/OutboxArchiver.cs index fd61c5e77a..28db497aee 100644 --- a/src/Paramore.Brighter/OutboxArchiver.cs +++ b/src/Paramore.Brighter/OutboxArchiver.cs @@ -27,7 +27,7 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Observability; namespace Paramore.Brighter @@ -39,7 +39,7 @@ namespace Paramore.Brighter /// The transaction type of the Db public partial class OutboxArchiver where TMessage : Message { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger>(); + private readonly ILogger _logger; private readonly IAmARequestContextFactory _requestContextFactory; private readonly IAmAnOutboxSync? _outBox; private readonly IAmAnOutboxAsync? _asyncOutbox; @@ -59,13 +59,15 @@ public OutboxArchiver( IAmARequestContextFactory? requestContextFactory = null, int archiveBatchSize = 100, IAmABrighterTracer? tracer = null, - InstrumentationOptions instrumentationOptions = InstrumentationOptions.All) + InstrumentationOptions instrumentationOptions = InstrumentationOptions.All, + ILoggerFactory? loggerFactory = null) { _archiveProvider = archiveProvider; _archiveBatchSize = archiveBatchSize; _tracer = tracer; _instrumentationOptions = instrumentationOptions; _requestContextFactory = requestContextFactory ?? new InMemoryRequestContextFactory(); + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger>(); if (outbox is IAmAnOutboxSync syncOutbox) _outBox = syncOutbox; if (outbox is IAmAnOutboxAsync asyncOutbox) _asyncOutbox = asyncOutbox; @@ -110,7 +112,7 @@ public void Archive(TimeSpan dispatchedSince, RequestContext? requestContext = n .DispatchedMessages(dispatchedSince, requestContext, _archiveBatchSize) .ToArray(); - Log.FoundMessagesToArchive(s_logger, messages.Length, _archiveBatchSize); + Log.FoundMessagesToArchive(_logger, messages.Length, _archiveBatchSize); if (messages.Length <= 0) return; @@ -121,11 +123,11 @@ public void Archive(TimeSpan dispatchedSince, RequestContext? requestContext = n _outBox.Delete(messages.Select(e => e.Id).ToArray(), requestContext); - Log.SuccessfullyArchivedMessages(s_logger, messages.Length, _archiveBatchSize); + Log.SuccessfullyArchivedMessages(_logger, messages.Length, _archiveBatchSize); } catch (Exception e) { - Log.ErrorArchivingFromOutbox(s_logger, e); + Log.ErrorArchivingFromOutbox(_logger, e); _tracer?.AddExceptionToSpan(span, [e]); throw; } @@ -178,7 +180,7 @@ await _asyncOutbox.DeleteAsync(messages.Select(e => e.Id).ToArray(), requestCont } catch (Exception e) { - Log.ErrorArchivingFromOutbox(s_logger, e); + Log.ErrorArchivingFromOutbox(_logger, e); _tracer?.AddExceptionToSpan(span, [e]); throw; } diff --git a/src/Paramore.Brighter/OutboxProducerMediator.cs b/src/Paramore.Brighter/OutboxProducerMediator.cs index 78093f5e06..f876571d75 100644 --- a/src/Paramore.Brighter/OutboxProducerMediator.cs +++ b/src/Paramore.Brighter/OutboxProducerMediator.cs @@ -29,8 +29,8 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.CircuitBreaker; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; using Paramore.Brighter.Scheduler.Events; using Polly; @@ -49,7 +49,7 @@ public partial class OutboxProducerMediator : IAmAnOutbo IAmAnOutboxProducerMediator where TMessage : Message { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private readonly ResiliencePipelineRegistry _resiliencePipelineRegistry; private readonly TransformPipelineBuilder _transformPipelineBuilder; @@ -117,8 +117,12 @@ public OutboxProducerMediator( TimeSpan? maxOutStandingCheckInterval = null, Dictionary? outBoxBag = null, TimeProvider? timeProvider = null, - InstrumentationOptions instrumentationOptions = InstrumentationOptions.All) + InstrumentationOptions instrumentationOptions = InstrumentationOptions.All, + ILoggerFactory? loggerFactory = null) { + loggerFactory ??= NullLoggerFactory.Instance; + _logger = loggerFactory.CreateLogger(); + _producerRegistry = producerRegistry ?? throw new ConfigurationException("Missing Producer Registry for External Bus Services"); _resiliencePipelineRegistry = resiliencePipelineRegistry ?? @@ -139,9 +143,9 @@ public OutboxProducerMediator( _timeProvider = timeProvider ?? TimeProvider.System; _lastOutStandingMessageCheckAt = _timeProvider.GetUtcNow(); - _transformPipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory, instrumentationOptions); + _transformPipelineBuilder = new TransformPipelineBuilder(mapperRegistry, messageTransformerFactory, instrumentationOptions, loggerFactory); _transformPipelineBuilderAsync = - new TransformPipelineBuilderAsync(mapperRegistryAsync, messageTransformerFactoryAsync, instrumentationOptions); + new TransformPipelineBuilderAsync(mapperRegistryAsync, messageTransformerFactoryAsync, instrumentationOptions, loggerFactory); //default to in-memory; expectation for an in memory box is Message and CommittableTransaction outbox ??= new InMemoryOutbox(TimeProvider.System); @@ -316,7 +320,7 @@ public void ClearOutbox( if (messages.Length != posts.Length) { var missingMessageIds = posts.Where(id => !messages.Any(m => m.Id == id)); - Log.OutboxMessagesNotFound(s_logger, string.Join(",", missingMessageIds)); + Log.OutboxMessagesNotFound(_logger, string.Join(",", missingMessageIds)); } BrighterTracer.WriteOutboxEvent(BoxDbOperation.Get, messages, parentSpan, false, false, _instrumentationOptions); @@ -385,7 +389,7 @@ public async Task ClearOutboxAsync( if (messages.Length != postArray.Length) { var missingMessageIds = postArray.Where(id => !messages.Any(m => m.Id == id)); - Log.OutboxMessagesNotFound(s_logger, string.Join(",", missingMessageIds)); + Log.OutboxMessagesNotFound(_logger, string.Join(",", missingMessageIds)); } BrighterTracer.WriteOutboxEvent(BoxDbOperation.Get, messages, parentSpan, false, true, _instrumentationOptions); @@ -636,7 +640,7 @@ CancellationToken cancellationToken requestContext.Span = parentSpan; - Log.FoundMessagesToClear(s_logger, messages.Length, amountToClear); + Log.FoundMessagesToClear(_logger, messages.Length, amountToClear); if (useBulk) { @@ -647,11 +651,11 @@ CancellationToken cancellationToken await DispatchAsync(messages, requestContext, false, cancellationToken); } - Log.MessagesHaveBeenCleared(s_logger); + Log.MessagesHaveBeenCleared(_logger); } catch (Exception e) { - Log.ErrorWhileDispatchingFromOutbox(s_logger, e); + Log.ErrorWhileDispatchingFromOutbox(_logger, e); requestContext.Span?.SetStatus(ActivityStatusCode.Error, "Error while dispatching from outbox"); throw; } @@ -666,7 +670,7 @@ CancellationToken cancellationToken else { requestContext.Span?.SetStatus(ActivityStatusCode.Error); - Log.SkippingDispatchOfMessages(s_logger); + Log.SkippingDispatchOfMessages(_logger); } } @@ -676,7 +680,7 @@ private void CheckOutboxOutstandingLimit() if (!hasOutBox) return; - Log.OutboxOutstandingMessageCount(s_logger, _outStandingCount); + Log.OutboxOutstandingMessageCount(_logger, _outStandingCount); // Because a thread recalculates this, we may always be in a delay, so we check on entry for the next outstanding item bool exceedsOutstandingMessageLimit = _maxOutStandingMessages != -1 && _outStandingCount > _maxOutStandingMessages; @@ -692,15 +696,15 @@ private void CheckOutstandingMessages(RequestContext? requestContext) var timeSinceLastCheck = now - _lastOutStandingMessageCheckAt; - Log.TimeSinceLastCheck(s_logger, timeSinceLastCheck.TotalSeconds); + Log.TimeSinceLastCheck(_logger, timeSinceLastCheck.TotalSeconds); if (timeSinceLastCheck < _maxOutStandingCheckInterval) { - Log.CheckNotReadyToRunYet(s_logger); + Log.CheckNotReadyToRunYet(_logger); return; } - Log.RunningOutstandingMessageCheck(s_logger, now, timeSinceLastCheck.TotalSeconds); + Log.RunningOutstandingMessageCheck(_logger, now, timeSinceLastCheck.TotalSeconds); //This is expensive, so use a background thread Task.Run( () => OutstandingMessagesCheck(requestContext) @@ -742,7 +746,7 @@ private void ConfigureAsyncPublisherCallbackMaybe(IAmAMessageProducerAsync produ { if (success) { - Log.SentMessage(s_logger, id); + Log.SentMessage(_logger, id); if (_asyncOutbox != null) await ExecuteWithResiliencePipelineAsync( async ct => @@ -769,7 +773,7 @@ private bool ConfigurePublisherCallbackMaybe(IAmAMessageProducerSync producer, R { if (success) { - Log.SentMessage(s_logger, id); + Log.SentMessage(_logger, id); if (_outBox != null) ExecuteWithResiliencePipeline( @@ -819,7 +823,7 @@ private void Dispatch(IEnumerable posts, RequestContext requestContext, // Log the wire topic (Header.Topic) — where the message is going. Producer // lookup uses GetProducerLookupTopic, which may differ from Header.Topic when // a mapper overrode it (e.g. Reply messages routed to a dynamic reply address). - Log.DecoupledInvocationOfMessage(s_logger, message.Header.Topic.Value, message.Id.Value); + Log.DecoupledInvocationOfMessage(_logger, message.Header.Topic.Value, message.Id.Value); var producer = _producerRegistry.LookupBy(GetProducerLookupTopic(message), message.Header.Type, requestContext); var span = _tracer?.CreateProducerSpan(producer.Publication, message, requestContext.Span, @@ -900,7 +904,7 @@ private async Task BulkDispatchAsync( { var messages = topicBatch.ToArray(); - Log.BulkDispatchingMessages(s_logger, messages.Length, topicBatch.Key.WireTopic.Value); + Log.BulkDispatchingMessages(_logger, messages.Length, topicBatch.Key.WireTopic.Value); foreach (var batch in await bulkMessageProducer.CreateBatchesAsync(messages, cancellationToken)) { @@ -964,7 +968,7 @@ private async Task DispatchAsync( // Log the wire topic (Header.Topic) — where the message is going. Producer // lookup uses GetProducerLookupTopic, which may differ from Header.Topic when // a mapper overrode it (e.g. Reply messages routed to a dynamic reply address). - Log.DecoupledInvocationOfMessage(s_logger, message.Header.Topic.Value, message.Id.Value); + Log.DecoupledInvocationOfMessage(_logger, message.Header.Topic.Value, message.Id.Value); var producer = _producerRegistry.LookupBy(GetProducerLookupTopic(message), message.Header.Type, requestContext); var span = _tracer?.CreateProducerSpan(producer.Publication, message, parentSpan, @@ -1065,7 +1069,7 @@ private void OutstandingMessagesCheck(RequestContext? requestContext) s_checkOutstandingSemaphoreToken.Wait(); _lastOutStandingMessageCheckAt = _timeProvider.GetUtcNow(); - Log.BeginCountOfOutstandingMessages(s_logger); + Log.BeginCountOfOutstandingMessages(_logger); try { if (_outBox != null) @@ -1098,12 +1102,12 @@ private void OutstandingMessagesCheck(RequestContext? requestContext) catch (Exception ex) { //if we can't talk to the outbox, swallow the exception on this thread - Log.ErrorGettingOutstandingMessageCount(s_logger, ex); + Log.ErrorGettingOutstandingMessageCount(_logger, ex); _outStandingCount = 0; } finally { - Log.CurrentOutstandingCount(s_logger, _outStandingCount); + Log.CurrentOutstandingCount(_logger, _outStandingCount); s_checkOutstandingSemaphoreToken.Release(); } } @@ -1127,7 +1131,7 @@ private bool ExecuteWithResiliencePipeline(Action action, RequestContext? reques } catch (Exception ex) { - Log.ExceptionWhilstTryingToPublishMessage(s_logger, ex); + Log.ExceptionWhilstTryingToPublishMessage(_logger, ex); CheckOutstandingMessages(requestContext); return false; } @@ -1159,7 +1163,7 @@ await resiliencePipeline.ExecuteAsync(async ct => await send(ct), cancellationTo } catch (Exception ex) { - Log.ExceptionWhilstTryingToPublishMessage(s_logger, ex); + Log.ExceptionWhilstTryingToPublishMessage(_logger, ex); CheckOutstandingMessages(requestContext); return false; } diff --git a/src/Paramore.Brighter/PipelineBuilder.cs b/src/Paramore.Brighter/PipelineBuilder.cs index e7b9a50389..ac99751d04 100644 --- a/src/Paramore.Brighter/PipelineBuilder.cs +++ b/src/Paramore.Brighter/PipelineBuilder.cs @@ -27,9 +27,9 @@ THE SOFTWARE. */ using System.Linq; using System.Collections.Generic; using Paramore.Brighter.Extensions; -using Paramore.Brighter.Logging; using Paramore.Brighter.Validation; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Inbox.Attributes; namespace Paramore.Brighter @@ -37,7 +37,7 @@ namespace Paramore.Brighter public partial class PipelineBuilder : IAmAPipelineBuilder, IAmAnAsyncPipelineBuilder where TRequest : class, IRequest { - private static readonly ILogger s_logger= ApplicationLogging.CreateLogger>(); + private readonly ILogger _logger; private readonly IAmASubscriberRegistry? _subscriberRegistry; private readonly IAmASubscriberRegistryInspector? _subscriberRegistryInspector; @@ -59,11 +59,13 @@ public partial class PipelineBuilder : IAmAPipelineBuilder, public PipelineBuilder( IAmASubscriberRegistry subscriberRegistry, IAmAHandlerFactorySync syncHandlerFactory, - InboxConfiguration? inboxConfiguration = null) + InboxConfiguration? inboxConfiguration = null, + ILoggerFactory? loggerFactory = null) { _subscriberRegistry = subscriberRegistry; _syncHandlerFactory = syncHandlerFactory; _inboxConfiguration = inboxConfiguration; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger>(); } /// @@ -76,11 +78,13 @@ public PipelineBuilder( public PipelineBuilder( IAmASubscriberRegistry subscriberRegistry, IAmAHandlerFactoryAsync asyncHandlerFactory, - InboxConfiguration? inboxConfiguration = null) + InboxConfiguration? inboxConfiguration = null, + ILoggerFactory? loggerFactory = null) { _subscriberRegistry = subscriberRegistry; _asyncHandlerFactory = asyncHandlerFactory; _inboxConfiguration = inboxConfiguration; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger>(); } /// @@ -91,10 +95,12 @@ public PipelineBuilder( /// Optional inbox configuration for global inbox attribute detection. public PipelineBuilder( IAmASubscriberRegistryInspector subscriberRegistryInspector, - InboxConfiguration? inboxConfiguration = null) + InboxConfiguration? inboxConfiguration = null, + ILoggerFactory? loggerFactory = null) { _subscriberRegistryInspector = subscriberRegistryInspector; _inboxConfiguration = inboxConfiguration; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger>(); } /// @@ -302,7 +308,7 @@ private IHandleRequests BuildPipeline(RequestHandler implici } AppendToPipeline(postAttributes, implicitHandler, requestContext, instanceScope); - Log.NewHandlerPipelineCreated(s_logger, TracePipeline(firstInPipeline).ToString()); + Log.NewHandlerPipelineCreated(_logger, TracePipeline(firstInPipeline).ToString()); return firstInPipeline; } @@ -343,7 +349,7 @@ private IHandleRequestsAsync BuildAsyncPipeline(RequestHandlerAsync : RequestHandler, IAmABackstopHandler where TRequest : class, IRequest { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger>(); + private readonly ILogger _logger; + + /// + /// Initializes a new instance of the class. + /// + /// The logger; falls back to a no-op logger when null. + public RejectMessageOnErrorHandler(ILogger>? logger = null) + { + _logger = logger ?? NullLogger>.Instance; + } /// /// Handles the request by passing it to the next handler in the pipeline. @@ -60,7 +69,7 @@ public override TRequest Handle(TRequest request) } catch (Exception ex) { - Log.UnhandledExceptionRejectingMessage(s_logger, ex, typeof(TRequest).Name, ex.Message); + Log.UnhandledExceptionRejectingMessage(_logger, ex, typeof(TRequest).Name, ex.Message); throw new RejectMessageAction(ex.Message, ex); } } diff --git a/src/Paramore.Brighter/Reject/Handlers/RejectMessageOnErrorHandlerAsync.cs b/src/Paramore.Brighter/Reject/Handlers/RejectMessageOnErrorHandlerAsync.cs index 22addbbfcc..485b4adb84 100644 --- a/src/Paramore.Brighter/Reject/Handlers/RejectMessageOnErrorHandlerAsync.cs +++ b/src/Paramore.Brighter/Reject/Handlers/RejectMessageOnErrorHandlerAsync.cs @@ -26,8 +26,8 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Actions; -using Paramore.Brighter.Logging; namespace Paramore.Brighter.Reject.Handlers; @@ -44,7 +44,16 @@ namespace Paramore.Brighter.Reject.Handlers; public partial class RejectMessageOnErrorHandlerAsync : RequestHandlerAsync, IAmABackstopHandler where TRequest : class, IRequest { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger>(); + private readonly ILogger _logger; + + /// + /// Initializes a new instance of the class. + /// + /// The logger; falls back to a no-op logger when null. + public RejectMessageOnErrorHandlerAsync(ILogger>? logger = null) + { + _logger = logger ?? NullLogger>.Instance; + } /// /// Handles the request asynchronously by passing it to the next handler in the pipeline. @@ -64,7 +73,7 @@ public override async Task HandleAsync(TRequest command, CancellationT } catch (Exception ex) { - Log.UnhandledExceptionRejectingMessage(s_logger, ex, typeof(TRequest).Name, ex.Message); + Log.UnhandledExceptionRejectingMessage(_logger, ex, typeof(TRequest).Name, ex.Message); throw new RejectMessageAction(ex.Message, ex); } } diff --git a/src/Paramore.Brighter/RequestHandler.cs b/src/Paramore.Brighter/RequestHandler.cs index c4a1ed0804..7d1ee80953 100644 --- a/src/Paramore.Brighter/RequestHandler.cs +++ b/src/Paramore.Brighter/RequestHandler.cs @@ -26,7 +26,7 @@ THE SOFTWARE. */ using System.Linq; using System.Reflection; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Observability; using Paramore.Brighter.Policies.Attributes; using Paramore.Brighter.Policies.Handlers; @@ -49,9 +49,9 @@ namespace Paramore.Brighter /// /// The type of the t request. /// The for how deep should the instrumentation go? - public abstract partial class RequestHandler(InstrumentationOptions instrumentationOptions = InstrumentationOptions.All) : IHandleRequests where TRequest : class, IRequest + public abstract partial class RequestHandler(InstrumentationOptions instrumentationOptions = InstrumentationOptions.All, ILoggerFactory? loggerFactory = null) : IHandleRequests where TRequest : class, IRequest { - private static readonly ILogger s_logger= ApplicationLogging.CreateLogger>(); + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger>(); private IHandleRequests? _successor; @@ -110,7 +110,7 @@ public virtual TRequest Handle(TRequest request) if (_successor != null) { - Log.PassingRequestFromTo(s_logger, Name, _successor.Name); + Log.PassingRequestFromTo(_logger, Name, _successor.Name); return _successor.Handle(request); } @@ -145,7 +145,7 @@ public virtual TRequest Fallback(TRequest command) if (_successor != null) { - Log.FallingBackFromTo(s_logger, Name, _successor.Name); + Log.FallingBackFromTo(_logger, Name, _successor.Name); return _successor.Fallback(command); } diff --git a/src/Paramore.Brighter/RequestHandlerAsync.cs b/src/Paramore.Brighter/RequestHandlerAsync.cs index 4e23a909ee..6c7515a75f 100644 --- a/src/Paramore.Brighter/RequestHandlerAsync.cs +++ b/src/Paramore.Brighter/RequestHandlerAsync.cs @@ -28,7 +28,7 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Observability; using Paramore.Brighter.Policies.Attributes; using Paramore.Brighter.Policies.Handlers; @@ -37,7 +37,7 @@ THE SOFTWARE. */ namespace Paramore.Brighter { /// - /// Class RequestHandlerAsync + /// Class RequestHandlerAsync /// A target of the either as the target of the Command Dispatcher to provide the domain logic required to handle the /// or or as an orthogonal handler used as part of the Command Processor pipeline. /// We recommend deriving your concrete handler from instead of implementing the interface as it provides boilerplate @@ -51,9 +51,9 @@ namespace Paramore.Brighter /// /// The type of the t request. /// The for how deep should the instrumentation go? - public abstract partial class RequestHandlerAsync(InstrumentationOptions instrumentationOptions = InstrumentationOptions.All) : IHandleRequestsAsync where TRequest : class, IRequest + public abstract partial class RequestHandlerAsync(InstrumentationOptions instrumentationOptions = InstrumentationOptions.All, ILoggerFactory? loggerFactory = null) : IHandleRequestsAsync where TRequest : class, IRequest { - private static readonly ILogger s_logger= ApplicationLogging.CreateLogger>(); + private readonly ILogger _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger>(); private IHandleRequestsAsync? _successor; @@ -125,7 +125,7 @@ public virtual async Task HandleAsync(TRequest command, CancellationTo if (_successor != null) { - Log.PassingRequest(s_logger, Name, _successor.Name); + Log.PassingRequest(_logger, Name, _successor.Name); return await _successor.HandleAsync(command, cancellationToken).ConfigureAwait(ContinueOnCapturedContext); } @@ -161,7 +161,7 @@ public virtual async Task FallbackAsync(TRequest command, Cancellation if (_successor != null) { - Log.FallingBack(s_logger, Name, _successor.Name); + Log.FallingBack(_logger, Name, _successor.Name); return await _successor.FallbackAsync(command, cancellationToken).ConfigureAwait(ContinueOnCapturedContext); } diff --git a/src/Paramore.Brighter/TransformLifetimeScope.cs b/src/Paramore.Brighter/TransformLifetimeScope.cs index 7584dbedc0..76d24077ee 100644 --- a/src/Paramore.Brighter/TransformLifetimeScope.cs +++ b/src/Paramore.Brighter/TransformLifetimeScope.cs @@ -1,20 +1,21 @@ using System; using System.Collections.Generic; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Extensions; -using Paramore.Brighter.Logging; namespace Paramore.Brighter { public partial class TransformLifetimeScope : IAmATransformLifetime { - private static readonly ILogger s_logger= ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private readonly IAmAMessageTransformerFactory _factory; private readonly IList _trackedObjects = new List(); - public TransformLifetimeScope(IAmAMessageTransformerFactory factory) + public TransformLifetimeScope(IAmAMessageTransformerFactory factory, ILoggerFactory? loggerFactory = null) { _factory = factory; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } public void Dispose() @@ -31,7 +32,7 @@ public void Dispose() public void Add(IAmAMessageTransform instance) { _trackedObjects.Add(instance); - Log.TrackingInstance(s_logger, instance.GetHashCode(), instance.GetType()); + Log.TrackingInstance(_logger, instance.GetHashCode(), instance.GetType()); } private void ReleaseTrackedObjects() @@ -39,7 +40,7 @@ private void ReleaseTrackedObjects() _trackedObjects.Each((trackedItem) => { _factory.Release(trackedItem); - Log.ReleasingHandlerInstance(s_logger, trackedItem.GetHashCode(), trackedItem.GetType()); + Log.ReleasingHandlerInstance(_logger, trackedItem.GetHashCode(), trackedItem.GetType()); }); } diff --git a/src/Paramore.Brighter/TransformLifetimeScopeAsync.cs b/src/Paramore.Brighter/TransformLifetimeScopeAsync.cs index 7b88682da8..6acc8f6369 100644 --- a/src/Paramore.Brighter/TransformLifetimeScopeAsync.cs +++ b/src/Paramore.Brighter/TransformLifetimeScopeAsync.cs @@ -1,20 +1,21 @@ using System; using System.Collections.Generic; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Extensions; -using Paramore.Brighter.Logging; namespace Paramore.Brighter { public partial class TransformLifetimeScopeAsync : IAmATransformLifetimeAsync { - private static readonly ILogger s_logger= ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private readonly IAmAMessageTransformerFactoryAsync _factory; private readonly IList _trackedObjects = new List(); - public TransformLifetimeScopeAsync(IAmAMessageTransformerFactoryAsync factory) + public TransformLifetimeScopeAsync(IAmAMessageTransformerFactoryAsync factory, ILoggerFactory? loggerFactory = null) { _factory = factory; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } public void Dispose() @@ -31,7 +32,7 @@ public void Dispose() public void Add(IAmAMessageTransformAsync instance) { _trackedObjects.Add(instance); - Log.TrackingInstance(s_logger, instance.GetHashCode(), instance.GetType()); + Log.TrackingInstance(_logger, instance.GetHashCode(), instance.GetType()); } private void ReleaseTrackedObjects() @@ -39,7 +40,7 @@ private void ReleaseTrackedObjects() _trackedObjects.Each((trackedItem) => { _factory.Release(trackedItem); - Log.ReleasingHandlerInstance(s_logger, trackedItem.GetHashCode(), trackedItem.GetType()); + Log.ReleasingHandlerInstance(_logger, trackedItem.GetHashCode(), trackedItem.GetType()); }); } diff --git a/src/Paramore.Brighter/TransformPipelineBuilder.cs b/src/Paramore.Brighter/TransformPipelineBuilder.cs index 3329b35205..49d60b0a10 100644 --- a/src/Paramore.Brighter/TransformPipelineBuilder.cs +++ b/src/Paramore.Brighter/TransformPipelineBuilder.cs @@ -29,8 +29,8 @@ THE SOFTWARE. */ using System.Linq; using System.Reflection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Extensions; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; using Paramore.Brighter.Validation; @@ -47,7 +47,7 @@ namespace Paramore.Brighter /// public partial class TransformPipelineBuilder { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private readonly IAmAMessageMapperRegistry _mapperRegistry; private readonly IAmAMessageTransformerFactory _messageTransformerFactory; @@ -72,9 +72,10 @@ public partial class TransformPipelineBuilder /// The for how deep should the instrumentation go? /// Throws a configuration exception on a null mapperRegistry public TransformPipelineBuilder( - IAmAMessageMapperRegistry mapperRegistry, + IAmAMessageMapperRegistry mapperRegistry, IAmAMessageTransformerFactory messageTransformerFactory, - InstrumentationOptions instrumentationOptions = InstrumentationOptions.All + InstrumentationOptions instrumentationOptions = InstrumentationOptions.All, + ILoggerFactory? loggerFactory = null ) { _mapperRegistry = mapperRegistry ?? @@ -82,6 +83,7 @@ public TransformPipelineBuilder( _messageTransformerFactory = messageTransformerFactory; _instrumentationOptions = instrumentationOptions; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } /// @@ -100,12 +102,12 @@ public WrapPipeline BuildWrapPipeline() where TRequest : cla var pipeline = new WrapPipeline(messageMapper, _messageTransformerFactory, transforms, _instrumentationOptions); - Log.NewWrapPipelineCreated(s_logger, typeof(TRequest).Name, TraceWrapPipeline(pipeline)); + Log.NewWrapPipelineCreated(_logger, typeof(TRequest).Name, TraceWrapPipeline(pipeline)); var unwraps = FindUnwrapTransforms(messageMapper); if (unwraps.Any()) { - Log.UnwrapAttributesOnMapToMessageMethodIgnored(s_logger, typeof(TRequest).Name, TraceWrapPipeline(pipeline)); + Log.UnwrapAttributesOnMapToMessageMethodIgnored(_logger, typeof(TRequest).Name, TraceWrapPipeline(pipeline)); } return pipeline; @@ -132,12 +134,12 @@ public UnwrapPipeline BuildUnwrapPipeline() where TRequest : var pipeline = new UnwrapPipeline(transforms, _messageTransformerFactory, messageMapper); - Log.NewUnwrapPipelineCreated(s_logger, typeof(TRequest).Name, TraceUnwrapPipeline(pipeline)); + Log.NewUnwrapPipelineCreated(_logger, typeof(TRequest).Name, TraceUnwrapPipeline(pipeline)); var wraps = FindWrapTransforms(messageMapper); if (wraps.Any()) { - Log.WrapAttributesOnMapToRequestMethodIgnored(s_logger, typeof(TRequest).Name, TraceUnwrapPipeline(pipeline)); + Log.WrapAttributesOnMapToRequestMethodIgnored(_logger, typeof(TRequest).Name, TraceUnwrapPipeline(pipeline)); } return pipeline; @@ -163,7 +165,7 @@ private IEnumerable BuildTransformPipeline(IEnum { int i = transformAttributes.Count(); if (i > 0) - Log.NoMessageTransformerFactoryConfigured(s_logger, i); + Log.NoMessageTransformerFactoryConfigured(_logger, i); return transforms; } diff --git a/src/Paramore.Brighter/TransformPipelineBuilderAsync.cs b/src/Paramore.Brighter/TransformPipelineBuilderAsync.cs index 1934bd203e..431263a4eb 100644 --- a/src/Paramore.Brighter/TransformPipelineBuilderAsync.cs +++ b/src/Paramore.Brighter/TransformPipelineBuilderAsync.cs @@ -30,8 +30,8 @@ THE SOFTWARE. */ using System.Reflection; using System.Threading; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Extensions; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; namespace Paramore.Brighter @@ -47,7 +47,7 @@ namespace Paramore.Brighter /// public partial class TransformPipelineBuilderAsync { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private readonly IAmAMessageMapperRegistryAsync _mapperRegistryAsync; private readonly IAmAMessageTransformerFactoryAsync _messageTransformerFactoryAsync; @@ -74,7 +74,8 @@ public partial class TransformPipelineBuilderAsync public TransformPipelineBuilderAsync( IAmAMessageMapperRegistryAsync mapperRegistryAsync, IAmAMessageTransformerFactoryAsync messageTransformerFactoryAsync, - InstrumentationOptions instrumentationOptions + InstrumentationOptions instrumentationOptions, + ILoggerFactory? loggerFactory = null ) { _mapperRegistryAsync = mapperRegistryAsync ?? @@ -82,6 +83,7 @@ InstrumentationOptions instrumentationOptions "TransformPipelineBuilder expected a Message Mapper Registry but none supplied"); _messageTransformerFactoryAsync = messageTransformerFactoryAsync; _instrumentationOptions = instrumentationOptions; + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); } /// @@ -100,12 +102,12 @@ public WrapPipelineAsync BuildWrapPipeline() where TRequest var pipeline = new WrapPipelineAsync(messageMapper, _messageTransformerFactoryAsync, transforms, _instrumentationOptions); - Log.NewWrapPipelineCreated(s_logger, typeof(TRequest).Name, TraceWrapPipeline(pipeline)); + Log.NewWrapPipelineCreated(_logger, typeof(TRequest).Name, TraceWrapPipeline(pipeline)); var unwraps = FindUnwrapTransforms(messageMapper); if (unwraps.Any()) { - Log.UnwrapAttributesOnMapToMessageMethodIgnored(s_logger, typeof(TRequest).Name, TraceWrapPipeline(pipeline)); + Log.UnwrapAttributesOnMapToMessageMethodIgnored(_logger, typeof(TRequest).Name, TraceWrapPipeline(pipeline)); } return pipeline; @@ -132,12 +134,12 @@ public UnwrapPipelineAsync BuildUnwrapPipeline() where TRequ var pipeline = new UnwrapPipelineAsync(transforms, _messageTransformerFactoryAsync, messageMapper); - Log.NewUnwrapPipelineCreated(s_logger, typeof(TRequest).Name, TraceUnwrapPipeline(pipeline)); + Log.NewUnwrapPipelineCreated(_logger, typeof(TRequest).Name, TraceUnwrapPipeline(pipeline)); var wraps = FindWrapTransforms(messageMapper); if (wraps.Any()) { - Log.WrapAttributesOnMapToRequestMethodIgnored(s_logger, typeof(TRequest).Name, TraceUnwrapPipeline(pipeline)); + Log.WrapAttributesOnMapToRequestMethodIgnored(_logger, typeof(TRequest).Name, TraceUnwrapPipeline(pipeline)); } return pipeline; @@ -163,7 +165,7 @@ private IEnumerable BuildTransformPipeline( { int i = transformAttributes.Count(); if (i > 0) - Log.NoMessageTransformerFactoryConfigured(s_logger, i); + Log.NoMessageTransformerFactoryConfigured(_logger, i); return transforms; } diff --git a/src/Paramore.Brighter/Transforms/Transformers/CloudEventsTransformer.cs b/src/Paramore.Brighter/Transforms/Transformers/CloudEventsTransformer.cs index d9dab06086..2127d39255 100644 --- a/src/Paramore.Brighter/Transforms/Transformers/CloudEventsTransformer.cs +++ b/src/Paramore.Brighter/Transforms/Transformers/CloudEventsTransformer.cs @@ -6,9 +6,9 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Extensions; using Paramore.Brighter.JsonConverters; -using Paramore.Brighter.Logging; using Paramore.Brighter.Transforms.Attributes; namespace Paramore.Brighter.Transforms.Transformers; @@ -36,9 +36,18 @@ namespace Paramore.Brighter.Transforms.Transformers; /// public partial class CloudEventsTransformer : IAmAMessageTransform, IAmAMessageTransformAsync { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private readonly ILogger _logger; private static readonly Uri s_defaultSource = new(MessageHeader.DefaultSource); + /// + /// Initializes a new instance of the class. + /// + /// The factory used to create the logger; falls back to a no-op factory when null. + public CloudEventsTransformer(ILoggerFactory? loggerFactory = null) + { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger(); + } + private Uri? _source; private string? _type; private string? _specVersion; @@ -148,7 +157,7 @@ public Message Unwrap(Message message) return ReadCloudEventJsonMessage(message); } - private static Message ReadCloudEventJsonMessage(Message message) + private Message ReadCloudEventJsonMessage(Message message) { try { @@ -214,7 +223,7 @@ private static Message ReadCloudEventJsonMessage(Message message) } catch(JsonException ex) { - Log.ErrorDuringDeserializerOnUnwrap(s_logger, ex); + Log.ErrorDuringDeserializerOnUnwrap(_logger, ex); return message; } } @@ -302,7 +311,7 @@ private Message WriteJsonMessage(Message message, Publication publication) } catch (JsonException e) { - Log.ErrorDuringDeserializerAJsonOnWrap(s_logger, e); + Log.ErrorDuringDeserializerAJsonOnWrap(_logger, e); return message; } } diff --git a/src/Paramore.Brighter/WrapPipeline.cs b/src/Paramore.Brighter/WrapPipeline.cs index faf96316cd..66514eeb7b 100644 --- a/src/Paramore.Brighter/WrapPipeline.cs +++ b/src/Paramore.Brighter/WrapPipeline.cs @@ -24,8 +24,8 @@ THE SOFTWARE. */ using System.Collections.Generic; using System.Diagnostics; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Extensions; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; namespace Paramore.Brighter @@ -38,8 +38,8 @@ namespace Paramore.Brighter /// public partial class WrapPipeline : TransformPipeline where TRequest: class, IRequest { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger>(); - + private readonly ILogger _logger; + private readonly InstrumentationOptions _instrumentationOptions; /// @@ -49,13 +49,16 @@ public partial class WrapPipeline : TransformPipeline where /// Factory for transforms, required to release /// The transforms applied after the message mapper /// The for how deep should the instrumentation go? + /// The factory used to create the logger; falls back to a no-op factory when null. public WrapPipeline( - IAmAMessageMapper messageMapper, - IAmAMessageTransformerFactory? messageTransformerFactory, + IAmAMessageMapper messageMapper, + IAmAMessageTransformerFactory? messageTransformerFactory, IEnumerable transforms, - InstrumentationOptions instrumentationOptions + InstrumentationOptions instrumentationOptions, + ILoggerFactory? loggerFactory = null ) : base(messageMapper, transforms) { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger>(); _instrumentationOptions = instrumentationOptions; if (messageTransformerFactory != null) { @@ -92,7 +95,7 @@ public Message Wrap(TRequest request, RequestContext requestContext, Publication if (message.Header.Topic != publication.Topic) { - Log.DifferentPublicationAndMessageTopic(s_logger, publication.Topic?.Value ?? string.Empty, message.Header.Topic.Value); + Log.DifferentPublicationAndMessageTopic(_logger, publication.Topic?.Value ?? string.Empty, message.Header.Topic.Value); if (publication.Topic is not null) { message.Header.Bag[Message.ProducerTopicHeaderName] = publication.Topic.Value; diff --git a/src/Paramore.Brighter/WrapPipelineAsync.cs b/src/Paramore.Brighter/WrapPipelineAsync.cs index 33008cab65..46e68d09b0 100644 --- a/src/Paramore.Brighter/WrapPipelineAsync.cs +++ b/src/Paramore.Brighter/WrapPipelineAsync.cs @@ -28,8 +28,8 @@ THE SOFTWARE. */ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Paramore.Brighter.Extensions; -using Paramore.Brighter.Logging; using Paramore.Brighter.Observability; namespace Paramore.Brighter @@ -42,8 +42,8 @@ namespace Paramore.Brighter /// public partial class WrapPipelineAsync : TransformPipelineAsync where TRequest: class, IRequest { - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger>(); - + private readonly ILogger _logger; + private readonly InstrumentationOptions _instrumentationOptions; /// @@ -53,13 +53,16 @@ public partial class WrapPipelineAsync : TransformPipelineAsyncFactory for transforms, required to release /// The transforms applied after the message mapper /// The for how deep should the instrumentation go? + /// The factory used to create the logger; falls back to a no-op factory when null. public WrapPipelineAsync( - IAmAMessageMapperAsync messageMapperAsync, - IAmAMessageTransformerFactoryAsync messageTransformerFactoryAsync, + IAmAMessageMapperAsync messageMapperAsync, + IAmAMessageTransformerFactoryAsync messageTransformerFactoryAsync, IEnumerable transforms, - InstrumentationOptions instrumentationOptions + InstrumentationOptions instrumentationOptions, + ILoggerFactory? loggerFactory = null ) : base(messageMapperAsync, transforms) { + _logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger>(); _instrumentationOptions = instrumentationOptions; if (messageTransformerFactoryAsync != null) { @@ -99,7 +102,7 @@ public async Task WrapAsync(TRequest request, RequestContext requestCon if (message.Header.Topic != publication.Topic) { - Log.DifferentPublicationAndMessageTopic(s_logger, publication.Topic?.Value ?? string.Empty, message.Header.Topic.Value); + Log.DifferentPublicationAndMessageTopic(_logger, publication.Topic?.Value ?? string.Empty, message.Header.Topic.Value); if (publication.Topic is not null) { message.Header.Bag[Message.ProducerTopicHeaderName] = publication.Topic.Value; diff --git a/tests/Paramore.Brighter.Core.Tests/Initializer.cs b/tests/Paramore.Brighter.Core.Tests/Initializer.cs index ec51a7dc81..d03b1c171b 100644 --- a/tests/Paramore.Brighter.Core.Tests/Initializer.cs +++ b/tests/Paramore.Brighter.Core.Tests/Initializer.cs @@ -1,17 +1,25 @@ -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; using Microsoft.Extensions.Logging; -using Paramore.Brighter.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Serilog; namespace Paramore.Brighter.Core.Tests { - sealed class Initializer + static class Initializer { + /// + /// A Serilog-backed wired to Serilog's TestCorrelator sink. Brighter logging + /// is now instance-scoped, so tests that assert on log output must pass this factory into the Brighter + /// objects they construct (e.g. as the loggerFactory constructor argument), rather than relying on a + /// process-wide static. + /// + public static ILoggerFactory TestLoggerFactory { get; private set; } = NullLoggerFactory.Instance; + [ModuleInitializer] public static void InitializeTestLogger() { var logger = new LoggerConfiguration().WriteTo.TestCorrelator().CreateLogger(); - ApplicationLogging.LoggerFactory = new LoggerFactory().AddSerilog(logger); + TestLoggerFactory = new LoggerFactory().AddSerilog(logger); } } } diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/Proactor/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/Proactor/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs index fd42cf3c2b..f35431cf3f 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/Proactor/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/Proactor/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked_async.cs @@ -34,8 +34,9 @@ public MessagePumpCommandProcessingExceptionTestsAsync() new SimpleMessageMapperFactoryAsync(_ => new MyCommandMessageMapperAsync())); messageMapperRegistry.RegisterAsync(); - _messagePump = new ServiceActivator.Proactor(commandProcessor, (message) => typeof(MyCommand), - messageMapperRegistry, new EmptyMessageTransformerFactoryAsync(), new InMemoryRequestContextFactory(), _channel + _messagePump = new ServiceActivator.Proactor(commandProcessor, (message) => typeof(MyCommand), + messageMapperRegistry, new EmptyMessageTransformerFactoryAsync(), new InMemoryRequestContextFactory(), _channel, + loggerFactory: Initializer.TestLoggerFactory ) { Channel = _channel, TimeOut = TimeSpan.FromMilliseconds(5000), RequeueCount = _requeueCount diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/Proactor/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/Proactor/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs index a5a59d9a9b..967496e435 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/Proactor/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/Proactor/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked_async.cs @@ -36,8 +36,8 @@ public MessagePumpEventProcessingExceptionTestsAsync() new SimpleMessageMapperFactoryAsync(_ => new MyEventMessageMapperAsync())); messageMapperRegistry.RegisterAsync(); - _messagePump = new ServiceActivator.Proactor(commandProcessor, (message) => typeof(MyEvent), - messageMapperRegistry, null, new InMemoryRequestContextFactory(), _channel) + _messagePump = new ServiceActivator.Proactor(commandProcessor, (message) => typeof(MyEvent), + messageMapperRegistry, null, new InMemoryRequestContextFactory(), _channel, loggerFactory: Initializer.TestLoggerFactory) { Channel = _channel, TimeOut = TimeSpan.FromMilliseconds(5000), RequeueCount = _requeueCount }; diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/Reactor/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/Reactor/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked.cs index 59d978136c..1847eab9cd 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/Reactor/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/Reactor/When_a_command_handler_throws_unhandled_exception_Then_message_is_acked.cs @@ -31,8 +31,8 @@ public MessagePumpCommandProcessingExceptionTests() null); messageMapperRegistry.Register(); - _messagePump = new ServiceActivator.Reactor(commandProcessor, (message) => typeof(MyCommand), - messageMapperRegistry, new EmptyMessageTransformerFactory(), new InMemoryRequestContextFactory(), _channel) + _messagePump = new ServiceActivator.Reactor(commandProcessor, (message) => typeof(MyCommand), + messageMapperRegistry, new EmptyMessageTransformerFactory(), new InMemoryRequestContextFactory(), _channel, loggerFactory: Initializer.TestLoggerFactory) { Channel = _channel, TimeOut = TimeSpan.FromMilliseconds(5000), RequeueCount = _requeueCount }; diff --git a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/Reactor/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked.cs b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/Reactor/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked.cs index c06674903b..fdd4819281 100644 --- a/tests/Paramore.Brighter.Core.Tests/MessageDispatch/Reactor/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked.cs +++ b/tests/Paramore.Brighter.Core.Tests/MessageDispatch/Reactor/When_an_event_handler_throws_unhandled_exception_Then_message_is_acked.cs @@ -38,8 +38,8 @@ public MessagePumpEventProcessingExceptionTests() messageMapperRegistry.Register(); var requestContextFactory = new InMemoryRequestContextFactory(); - _messagePump = new ServiceActivator.Reactor(commandProcessor, (message) => typeof(MyEvent), - messageMapperRegistry, null, requestContextFactory, _channel) + _messagePump = new ServiceActivator.Reactor(commandProcessor, (message) => typeof(MyEvent), + messageMapperRegistry, null, requestContextFactory, _channel, loggerFactory: Initializer.TestLoggerFactory) { Channel = _channel, TimeOut = TimeSpan.FromMilliseconds(5000), RequeueCount = _requeueCount }; diff --git a/tests/Paramore.Brighter.Extensions.Tests/InstanceScopedLoggingTests.cs b/tests/Paramore.Brighter.Extensions.Tests/InstanceScopedLoggingTests.cs new file mode 100644 index 0000000000..fdd2724668 --- /dev/null +++ b/tests/Paramore.Brighter.Extensions.Tests/InstanceScopedLoggingTests.cs @@ -0,0 +1,114 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2026 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Paramore.Brighter.Extensions.DependencyInjection; +using Paramore.Brighter.Extensions.Tests.TestDoubles; +using Xunit; + +namespace Paramore.Brighter.Extensions.Tests; + +/// +/// Verifies that Brighter logging is instance-scoped: each logs through the +/// of the container that built it. This replaces the previous behaviour where the +/// container's factory was copied into the static ApplicationLogging.LoggerFactory, which caused +/// use-after-dispose (when a container was disposed) and cross-talk between two Brighter instances in one process. +/// +public class InstanceScopedLoggingTests +{ + private static ServiceProvider BuildProvider(CapturingLoggerProvider capture) + { + var services = new ServiceCollection(); + services.AddLogging(builder => + { + builder.SetMinimumLevel(LogLevel.Trace); + builder.AddProvider(capture); + }); + services.AddBrighter(); + return services.BuildServiceProvider(); + } + + private class LogProbeEvent() : Event(Guid.NewGuid()); + + [Fact] + public void CommandProcessor_LogsThroughItsOwnContainersFactory_NotAnothers() + { + var captureA = new CapturingLoggerProvider(); + var captureB = new CapturingLoggerProvider(); + + using var providerA = BuildProvider(captureA); + using var providerB = BuildProvider(captureB); + + var commandProcessorA = providerA.GetRequiredService(); + providerB.GetRequiredService(); + + // Publishing with no subscribers still emits pipeline log lines through A's logger only. + commandProcessorA.Publish(new LogProbeEvent()); + + Assert.NotEmpty(captureA.Entries); + Assert.Empty(captureB.Entries); + } + + [Fact] + public void DisposingOneContainer_DoesNotBreakLoggingInAnother() + { + var captureA = new CapturingLoggerProvider(); + var captureB = new CapturingLoggerProvider(); + + var providerA = BuildProvider(captureA); + using var providerB = BuildProvider(captureB); + + // Resolve B first, then A, so that under the previous (buggy) static behaviour the shared + // ApplicationLogging.LoggerFactory would have ended up pointing at A's factory (last writer wins). + var commandProcessorB = providerB.GetRequiredService(); + providerA.GetRequiredService(); + + // Disposing A disposes A's container (and its ILoggerFactory). B must be entirely unaffected: + // with instance-scoped logging, B logs through B's own factory and never touches A's. + providerA.Dispose(); + + var exception = Record.Exception(() => commandProcessorB.Publish(new LogProbeEvent())); + + Assert.Null(exception); + Assert.NotEmpty(captureB.Entries); + Assert.Empty(captureA.Entries); + } + + [Fact] + public void NoLoggerFactoryRegistered_FallsBackSafely_AndDoesNotThrow() + { + // No AddLogging(): the DI extension must fall back to a no-op factory, never the disposed/absent one. + var services = new ServiceCollection(); + services.AddBrighter(); + using var provider = services.BuildServiceProvider(); + + var commandProcessor = provider.GetRequiredService(); + + var exception = Record.Exception(() => commandProcessor.Publish(new LogProbeEvent())); + + Assert.Null(exception); + } +} diff --git a/tests/Paramore.Brighter.Extensions.Tests/TestDoubles/CapturingLoggerProvider.cs b/tests/Paramore.Brighter.Extensions.Tests/TestDoubles/CapturingLoggerProvider.cs new file mode 100644 index 0000000000..278ec37960 --- /dev/null +++ b/tests/Paramore.Brighter.Extensions.Tests/TestDoubles/CapturingLoggerProvider.cs @@ -0,0 +1,71 @@ +#region Licence +/* The MIT License (MIT) +Copyright © 2026 Ian Cooper + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Logging; + +namespace Paramore.Brighter.Extensions.Tests.TestDoubles; + +/// +/// An that captures log messages so a test can assert which +/// a Brighter object actually logged through. Once disposed (as the DI +/// container does when its is disposed), logging through it throws +/// — this is what surfaces a use-after-dispose if an object is +/// still holding a reference to a factory owned by a disposed container. +/// +public sealed class CapturingLoggerProvider : ILoggerProvider +{ + private readonly List _entries = new(); + private readonly object _gate = new(); + + public bool IsDisposed { get; private set; } + + public IReadOnlyList Entries + { + get { lock (_gate) { return _entries.ToList(); } } + } + + public ILogger CreateLogger(string categoryName) => new CapturingLogger(this); + + public void Dispose() => IsDisposed = true; + + private sealed class CapturingLogger(CapturingLoggerProvider provider) : ILogger + { + public IDisposable? BeginScope(TState state) where TState : notnull => null; + + public bool IsEnabled(LogLevel logLevel) => true; + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, + Func formatter) + { + if (provider.IsDisposed) + throw new ObjectDisposedException(nameof(CapturingLoggerProvider)); + + lock (provider._gate) + provider._entries.Add(formatter(state, exception)); + } + } +} diff --git a/tests/Paramore.Brighter.MQTT.Tests/MessagingGateway/Helpers/Base/MqttTestClassBase.cs b/tests/Paramore.Brighter.MQTT.Tests/MessagingGateway/Helpers/Base/MqttTestClassBase.cs index 08c80a4668..54c7963f75 100644 --- a/tests/Paramore.Brighter.MQTT.Tests/MessagingGateway/Helpers/Base/MqttTestClassBase.cs +++ b/tests/Paramore.Brighter.MQTT.Tests/MessagingGateway/Helpers/Base/MqttTestClassBase.cs @@ -3,7 +3,6 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using MQTTnet; -using Paramore.Brighter.Logging; using Paramore.Brighter.MessagingGateway.MQTT; using Paramore.Brighter.MQTT.Tests.MessagingGateway.Helpers.Server; using Paramore.Test.Helpers.Base; @@ -44,7 +43,7 @@ public abstract class MqttTestClassBase : TestClassBase protected MqttTestClassBase(string clientID, string topicPrefix, ITestOutputHelper testOutputHelper) : base(testOutputHelper) { - ApplicationLogging.LoggerFactory = LoggerFactory.Create(configure => + var loggerFactory = LoggerFactory.Create(configure => { configure.Services.AddSingleton(TestOutputHelper); configure.Services.AddSingleton(); @@ -56,7 +55,7 @@ protected MqttTestClassBase(string clientID, string topicPrefix, ITestOutputHelp IPAddress serverIPAddress = IPAddress.Any; int serverPort = MqttTestServer.GetRandomServerPort(); - MqttTestServer = MqttTestServer.CreateTestMqttServer(s_mqttFactory, true, ApplicationLogging.CreateLogger(), serverIPAddress, serverPort, null, TestDisplayName); + MqttTestServer = MqttTestServer.CreateTestMqttServer(s_mqttFactory, true, loggerFactory.CreateLogger(), serverIPAddress, serverPort, null, TestDisplayName); var mqttProducerConfig = new MqttMessagingGatewayProducerConfiguration { diff --git a/tests/Paramore.Brighter.PostgresSQL.Tests/PostgresSqlTestHelper.cs b/tests/Paramore.Brighter.PostgresSQL.Tests/PostgresSqlTestHelper.cs index f790922bd5..7d6cdb51ef 100644 --- a/tests/Paramore.Brighter.PostgresSQL.Tests/PostgresSqlTestHelper.cs +++ b/tests/Paramore.Brighter.PostgresSQL.Tests/PostgresSqlTestHelper.cs @@ -1,15 +1,15 @@ using System; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Npgsql; -using Paramore.Brighter.Logging; namespace Paramore.Brighter.PostgresSQL.Tests { internal sealed class PostgresSqlTestHelper { private readonly bool _binaryMessagePayload; - private static readonly ILogger s_logger = ApplicationLogging.CreateLogger(); + private static readonly ILogger s_logger = NullLogger.Instance; private readonly PostgreSqlSettings _postgreSqlSettings; private readonly string _tableName; private readonly object _syncObject = new(); diff --git a/tests/Paramore.Brighter.Redis.Tests/MessagingGateway/When_parsing_a_good_redis_message_to_brighter.cs b/tests/Paramore.Brighter.Redis.Tests/MessagingGateway/When_parsing_a_good_redis_message_to_brighter.cs index 9803217839..6a0ac8f012 100644 --- a/tests/Paramore.Brighter.Redis.Tests/MessagingGateway/When_parsing_a_good_redis_message_to_brighter.cs +++ b/tests/Paramore.Brighter.Redis.Tests/MessagingGateway/When_parsing_a_good_redis_message_to_brighter.cs @@ -16,7 +16,7 @@ public class RedisGoodMessageParsingTests [Fact] public void When_parsing_a_good_redis_message_to_brighter() { - Message message = RedisMessageCreator.CreateMessage(GoodMessage); + Message message = new RedisMessageCreator().CreateMessage(GoodMessage); // Assert existing properties Assert.Equal(DateTime.Parse("2018-02-07T09:38:36Z"), message.Header.TimeStamp);