diff --git a/Shared.Logger.Sample/NLog.config b/Shared.Logger.Sample/NLog.config
new file mode 100644
index 0000000..31b7d0c
--- /dev/null
+++ b/Shared.Logger.Sample/NLog.config
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Shared.Logger.Sample/Program.cs b/Shared.Logger.Sample/Program.cs
new file mode 100644
index 0000000..330496d
--- /dev/null
+++ b/Shared.Logger.Sample/Program.cs
@@ -0,0 +1,63 @@
+using NLog;
+using Shared.Logger.TennantContext;
+using StaticLogger = Shared.Logger.Logger;
+
+Boolean generateHighVolumeLogs = true;
+const Int32 HighVolumeLogEventCount = 1000;
+
+LogManager.Setup().LoadConfigurationFromFile("NLog.config");
+
+NLog.Logger nlogLogger = LogManager.GetLogger("Shared.Logger.Sample");
+StaticLogger.Initialise(nlogLogger, "shared-logger-sample");
+
+// Simulate the tenant context normally populated by the service middleware.
+TenantContext tenantContext = new();
+tenantContext.Initialise(
+ new TenantIdentifiers(
+ EstateId: Guid.Parse("1e543b2a-d100-4ec1-9735-8590aab2c819"),
+ MerchantId: Guid.Parse("579267d9-46a6-4057-8c66-0b34d7494a28")),
+ perTenantLogsEnabled: false);
+tenantContext.SetCorrelationId(CorrelationId.From(Guid.Parse("b0354c7e-b6de-4a27-9a54-715dc3e56566")));
+TenantContext.CurrentTenant = tenantContext;
+
+Guid transactionId = Guid.NewGuid();
+StaticLogger.LogTrace("Starting sample run {runId}", Guid.NewGuid());
+StaticLogger.LogDebug("Loaded {recordCount} records for {tenantName}", 12, "Example Estate");
+StaticLogger.LogInformation("Processed transaction {transactionId} for {amount} {currency}", transactionId, 125.50m, "GBP");
+StaticLogger.LogWarning("Transaction {transactionId} required a retry after {attemptCount} attempts", transactionId, 2);
+
+try
+{
+ throw new InvalidOperationException("Example failure for the structured exception log.");
+}
+catch (InvalidOperationException exception)
+{
+ StaticLogger.LogError(exception, "Could not process transaction {transactionId}", transactionId);
+}
+
+StaticLogger.LogCritical("Sample critical event for {component}", "payment processor");
+
+if (generateHighVolumeLogs)
+{
+ for (Int32 eventNumber = 1; eventNumber <= HighVolumeLogEventCount; eventNumber++)
+ {
+ TenantContext volumeTenantContext = new();
+ volumeTenantContext.Initialise(
+ new TenantIdentifiers(EstateId: Guid.NewGuid(), MerchantId: Guid.NewGuid()),
+ perTenantLogsEnabled: true);
+ volumeTenantContext.SetCorrelationId(CorrelationId.From(Guid.NewGuid()));
+ TenantContext.CurrentTenant = volumeTenantContext;
+
+ StaticLogger.LogInformation(
+ "Volume sample event {eventNumber} of {totalEvents} for transaction {transactionId} with amount {amount}",
+ eventNumber,
+ HighVolumeLogEventCount,
+ Guid.NewGuid(),
+ (eventNumber % 500) + 0.25m);
+ }
+}
+
+TenantContext.CurrentTenant = new TenantContext();
+LogManager.Shutdown();
+
+Console.WriteLine($"Structured log entries written under: {Path.Combine(AppContext.BaseDirectory, "logs")}");
diff --git a/Shared.Logger.Sample/README.md b/Shared.Logger.Sample/README.md
new file mode 100644
index 0000000..5a449a0
--- /dev/null
+++ b/Shared.Logger.Sample/README.md
@@ -0,0 +1,13 @@
+# Shared.Logger sample
+
+This console app demonstrates structured message-template calls through `Shared.Logger` and NLog. NLog reads [NLog.config](NLog.config) and writes JSON lines with event properties included, so template values and tenant/correlation metadata are available as separate fields. The tenant scope controls file routing: the tenant-scoped event goes into its tenant-specific subfolder, while the regular event goes into `logs/shared`.
+
+Run it from the repository root:
+
+```powershell
+dotnet run --project Shared.Logger.Sample
+```
+
+The sample writes under `logs` in the app's output directory. With per-tenant logging enabled, each logger call produces the regular event and the tenant-scoped event, which this config routes separately.
+
+To generate additional volume, set `generateHighVolumeLogs` to `true` near the top of `Program.cs`. It then writes 1,000 more structured events, each with a fresh correlation ID, estate ID, and merchant ID (and each routed to the regular and tenant-specific files); change `HighVolumeLogEventCount` to adjust the batch size.
diff --git a/Shared.Logger.Sample/Shared.Logger.Sample.csproj b/Shared.Logger.Sample/Shared.Logger.Sample.csproj
new file mode 100644
index 0000000..27f5c36
--- /dev/null
+++ b/Shared.Logger.Sample/Shared.Logger.Sample.csproj
@@ -0,0 +1,17 @@
+
+
+ Exe
+ net10.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Shared.Logger/ILogger.cs b/Shared.Logger/ILogger.cs
index 5594c48..91d3fb4 100644
--- a/Shared.Logger/ILogger.cs
+++ b/Shared.Logger/ILogger.cs
@@ -1,6 +1,10 @@
namespace Shared.Logger;
using System;
+using System.Collections;
+using System.Globalization;
+using System.Text;
+using System.Text.Json;
///
///
@@ -20,13 +24,32 @@ public interface ILogger
void LogCritical(Exception exception);
+#pragma warning disable S2190 // This overload delegates to the required string-only overload; it does not call itself.
+ void LogCritical(String messageTemplate,
+ params Object?[] args) => this.LogCritical(FormatMessageTemplate(messageTemplate, args));
+#pragma warning restore S2190
+
+ void LogCritical(Exception exception,
+ String messageTemplate,
+ params Object?[] args) => this.LogCritical(FormatMessageTemplate(messageTemplate, args), exception);
+
void LogCritical(String message,
Exception exception);
void LogDebug(String message);
+ void LogDebug(String messageTemplate,
+ params Object?[] args) => this.LogDebug(FormatMessageTemplate(messageTemplate, args));
+
void LogError(Exception exception);
+ void LogError(String messageTemplate,
+ params Object?[] args) => this.LogError(FormatMessageTemplate(messageTemplate, args));
+
+ void LogError(Exception exception,
+ String messageTemplate,
+ params Object?[] args) => this.LogError(FormatMessageTemplate(messageTemplate, args), exception);
+
void LogError(String message);
void LogError(String message,
@@ -34,8 +57,64 @@ void LogError(String message,
void LogInformation(String message);
+ void LogInformation(String messageTemplate,
+ params Object?[] args) => this.LogInformation(FormatMessageTemplate(messageTemplate, args));
+
void LogTrace(String message);
+ void LogTrace(String messageTemplate,
+ params Object?[] args) => this.LogTrace(FormatMessageTemplate(messageTemplate, args));
+
void LogWarning(String message);
-}
\ No newline at end of file
+ void LogWarning(String messageTemplate,
+ params Object?[] args) => this.LogWarning(FormatMessageTemplate(messageTemplate, args));
+
+ private static String FormatMessageTemplate(String messageTemplate, Object?[] args)
+ {
+ if (args == null || args.Length == 0) return messageTemplate;
+ StringBuilder result = new(messageTemplate.Length + args.Length * 8);
+ Int32 argumentIndex = 0;
+ Int32 index = 0;
+ while (index < messageTemplate.Length)
+ {
+ if (messageTemplate[index] == '{' && index + 1 < messageTemplate.Length && messageTemplate[index + 1] == '{')
+ {
+ result.Append("{{"); index += 2; continue;
+ }
+ if (messageTemplate[index] == '{')
+ {
+ Int32 end = messageTemplate.IndexOf('}', index + 1);
+ if (end >= 0 && argumentIndex < args.Length)
+ {
+ result.Append(FormatArgument(args[argumentIndex++]));
+ index = end + 1;
+ continue;
+ }
+ }
+ result.Append(messageTemplate[index++]);
+ }
+ return result.ToString();
+ }
+
+ private static String FormatArgument(Object? argument)
+ {
+ if (argument is IEnumerable && argument is not String)
+ {
+ try
+ {
+ return JsonSerializer.Serialize(argument, argument.GetType());
+ }
+ catch (NotSupportedException)
+ {
+ // Unsupported collection types use the invariant scalar fallback below.
+ }
+ catch (JsonException)
+ {
+ // Collections that cannot be serialized also use the invariant scalar fallback below.
+ }
+ }
+ return Convert.ToString(argument, CultureInfo.InvariantCulture) ?? String.Empty;
+ }
+
+}
diff --git a/Shared.Logger/Logger.cs b/Shared.Logger/Logger.cs
index 0816eae..08e8e58 100644
--- a/Shared.Logger/Logger.cs
+++ b/Shared.Logger/Logger.cs
@@ -1,216 +1,190 @@
-using Shared.Logger.TennantContext;
-
namespace Shared.Logger;
-using NLog;
using System;
using System.Diagnostics.CodeAnalysis;
+using NLog;
+using Shared.Logger.TennantContext;
[ExcludeFromCodeCoverage]
-public static class Logger {
- private static ILogger LoggerObject;
+public static class Logger
+{
+ private static ILogger? LoggerObject;
public static Boolean IsInitialised { get; set; }
public static void Initialise(NLog.Logger loggerObject,
- String fileName) {
+ String fileName)
+ {
NlogLogger logger = new();
logger.Initialise(loggerObject, fileName);
Logger.Initialise(logger);
}
- public static void Initialise(Microsoft.Extensions.Logging.ILogger loggerObject) {
- MicrosoftLogger logger = new();
- logger.Initialise(loggerObject);
- Logger.Initialise(logger);
- }
-
- public static void Initialise(ILogger loggerObject) {
+ public static void Initialise(ILogger loggerObject)
+ {
Logger.LoggerObject = loggerObject ?? throw new ArgumentNullException(nameof(loggerObject));
-
Logger.IsInitialised = true;
}
- private const String CorrelationIdPropertyName = "correlationId";
- private const String TenantIdPropertyName = "tenantId";
-
- public static void LogCritical(Exception exception) {
- ValidateLoggerObject();
-
- TenantContext tenantContext = TenantContext.CurrentTenant;
+ public static void LogTrace(String messageTemplate,
+ params Object?[] args)
+ {
+ Logger.ValidateLoggerObject();
+ Logger.Write(logger => logger.LogTrace(messageTemplate, args), true);
+ }
- if (tenantContext == null) {
- LoggerObject.LogCritical(exception);
- return;
- }
- using (ScopeContext.PushProperty(CorrelationIdPropertyName, $"Correlation ID: {tenantContext.CorrelationId.ToString()}")) {
- // Write to the normal log
- LoggerObject.LogCritical(exception);
-
- if (tenantContext.PerTenantLogsEnabled && tenantContext.EstateId != Guid.Empty) {
- // Write to the tenant log
- using (ScopeContext.PushProperty(TenantIdPropertyName, $"_{tenantContext.EstateId.ToString()}")) {
- LoggerObject.LogCritical(exception);
- }
- }
- }
-
+ public static void LogDebug(String messageTemplate,
+ params Object?[] args)
+ {
+ Logger.ValidateLoggerObject();
+ Logger.Write(logger => logger.LogDebug(messageTemplate, args), true);
}
- public static void LogDebug(String message) {
- ValidateLoggerObject();
- TenantContext tenantContext = TenantContext.CurrentTenant;
+ public static void LogInformation(String messageTemplate,
+ params Object?[] args)
+ {
+ Logger.ValidateLoggerObject();
+ Logger.Write(logger => logger.LogInformation(messageTemplate, args), true);
+ }
- if (tenantContext == null) {
- LoggerObject.LogDebug(message);
- return;
- }
+ public static void LogWarning(String messageTemplate,
+ params Object?[] args)
+ {
+ Logger.ValidateLoggerObject();
+ Logger.Write(logger => logger.LogWarning(messageTemplate, args), true);
+ }
- using (ScopeContext.PushProperty(CorrelationIdPropertyName, $"Correlation ID: {tenantContext.CorrelationId.ToString()}")) {
- // Write to the normal log
- LoggerObject.LogDebug(message);
+ public static void LogError(String messageTemplate,
+ params Object?[] args)
+ {
+ Logger.ValidateLoggerObject();
+ Logger.Write(logger => logger.LogError(messageTemplate, args), true);
+ }
- if (tenantContext.PerTenantLogsEnabled && tenantContext.EstateId != Guid.Empty) {
- // Write to the tenant log
- using (ScopeContext.PushProperty(TenantIdPropertyName, $"_{tenantContext.EstateId.ToString()}")) {
- LoggerObject.LogDebug(message);
- }
- }
- }
+ public static void LogError(Exception exception,
+ String messageTemplate,
+ params Object?[] args)
+ {
+ Logger.ValidateLoggerObject();
+ Logger.Write(logger => logger.LogError(exception, messageTemplate, args), true);
}
- public static void LogError(Exception exception) {
- ValidateLoggerObject();
+ public static void LogCritical(String messageTemplate,
+ params Object?[] args)
+ {
+ Logger.ValidateLoggerObject();
+ Logger.Write(logger => logger.LogCritical(messageTemplate, args), true);
+ }
- TenantContext tenantContext = TenantContext.CurrentTenant;
+ public static void LogCritical(Exception exception,
+ String messageTemplate,
+ params Object?[] args)
+ {
+ Logger.ValidateLoggerObject();
+ Logger.Write(logger => logger.LogCritical(exception, messageTemplate, args), true);
+ }
- if (tenantContext == null) {
- LoggerObject.LogError(exception);
- return;
- }
+ // Compatibility overloads below remain available for existing callers and can be removed only in a breaking release.
+#pragma warning disable S1133 // These deprecated APIs are retained for source compatibility until a breaking release.
+ [Obsolete("Use LogCritical(String messageTemplate, params Object?[] args) or the exception-aware structured overload.")] // NOSONAR - retained for source compatibility until a breaking release.
+ public static void LogCritical(Exception exception)
+ {
+ Logger.ValidateLoggerObject();
+ Logger.Write(logger => logger.LogCritical(exception));
+ }
- using (ScopeContext.PushProperty(CorrelationIdPropertyName, $"Correlation ID: {tenantContext.CorrelationId.ToString()}")) {
- // Write to the normal log
- LoggerObject.LogError(exception);
+ [Obsolete("Use LogCritical(String messageTemplate, params Object?[] args) or the exception-aware structured overload.")] // NOSONAR - retained for source compatibility until a breaking release.
+ public static void LogCritical(String message,
+ Exception exception)
+ {
+ Logger.ValidateLoggerObject();
+ Logger.Write(logger => logger.LogCritical(message, exception));
+ }
- if (tenantContext.PerTenantLogsEnabled && tenantContext.EstateId != Guid.Empty) {
- // Write to the tenant log
- using (ScopeContext.PushProperty(TenantIdPropertyName, $"_{tenantContext.EstateId.ToString()}")) {
- LoggerObject.LogError(exception);
- }
- }
- }
+ [Obsolete("Use LogDebug(String messageTemplate, params Object?[] args).")] // NOSONAR - retained for source compatibility until a breaking release.
+ public static void LogDebug(String message)
+ {
+ Logger.ValidateLoggerObject();
+ Logger.Write(logger => logger.LogDebug(message));
}
- public static void LogError(String message) {
- ValidateLoggerObject();
+ [Obsolete("Use LogError(Exception exception, String messageTemplate, params Object?[] args).")] // NOSONAR - retained for source compatibility until a breaking release.
+ public static void LogError(Exception exception)
+ {
+ Logger.ValidateLoggerObject();
+ Logger.Write(logger => logger.LogError(exception));
+ }
- TenantContext tenantContext = TenantContext.CurrentTenant;
- if (tenantContext == null) {
- LoggerObject.LogError(message);
- return;
- }
- using (ScopeContext.PushProperty(CorrelationIdPropertyName, $"Correlation ID: {tenantContext.CorrelationId.ToString()}")) {
- // Write to the normal log
- LoggerObject.LogError(message);
-
- if (tenantContext.PerTenantLogsEnabled && tenantContext.EstateId != Guid.Empty) {
- // Write to the tenant log
- using (ScopeContext.PushProperty(TenantIdPropertyName, $"_{tenantContext.EstateId.ToString()}")) {
- LoggerObject.LogError(message);
- }
- }
- }
+ [Obsolete("Use LogError(String messageTemplate, params Object?[] args).")] // NOSONAR - retained for source compatibility until a breaking release.
+ public static void LogError(String message)
+ {
+ Logger.ValidateLoggerObject();
+ Logger.Write(logger => logger.LogError(message));
}
+ [Obsolete("Use LogError(Exception exception, String messageTemplate, params Object?[] args).")] // NOSONAR - retained for source compatibility until a breaking release.
public static void LogError(String message,
- Exception exception) {
- ValidateLoggerObject();
-
- TenantContext tenantContext = TenantContext.CurrentTenant;
- if (tenantContext == null) {
- LoggerObject.LogError(message, exception);
- return;
- }
- using (ScopeContext.PushProperty(CorrelationIdPropertyName, $"Correlation ID: {tenantContext.CorrelationId.ToString()}")) {
- // Write to the normal log
- LoggerObject.LogError(message, exception);
-
- if (tenantContext.PerTenantLogsEnabled && tenantContext.EstateId != Guid.Empty) {
- // Write to the tenant log
- using (ScopeContext.PushProperty(TenantIdPropertyName, $"_{tenantContext.EstateId.ToString()}")) {
- LoggerObject.LogError(message, exception);
- }
- }
- }
+ Exception exception)
+ {
+ Logger.ValidateLoggerObject();
+ Logger.Write(logger => logger.LogError(message, exception));
}
- public static void LogInformation(String message) {
- ValidateLoggerObject();
+ [Obsolete("Use LogInformation(String messageTemplate, params Object?[] args).")] // NOSONAR - retained for source compatibility until a breaking release.
+ public static void LogInformation(String message)
+ {
+ Logger.ValidateLoggerObject();
+ Logger.Write(logger => logger.LogInformation(message));
+ }
- TenantContext tenantContext = TenantContext.CurrentTenant;
- if (tenantContext == null) {
- Logger.LoggerObject.LogInformation(message);
- return;
- }
- using (ScopeContext.PushProperty(CorrelationIdPropertyName, $"Correlation ID: {tenantContext.CorrelationId.ToString()}")) {
- // Write to the normal log
- Logger.LoggerObject.LogInformation(message);
-
- if (tenantContext.PerTenantLogsEnabled && tenantContext.EstateId != Guid.Empty) {
- // Write to the tenant log
- using (ScopeContext.PushProperty(TenantIdPropertyName, $"_{tenantContext.EstateId.ToString()}")) {
- Logger.LoggerObject.LogInformation(message);
- }
- }
- }
+ [Obsolete("Use LogTrace(String messageTemplate, params Object?[] args).")] // NOSONAR - retained for source compatibility until a breaking release.
+ public static void LogTrace(String message)
+ {
+ Logger.ValidateLoggerObject();
+ Logger.Write(logger => logger.LogTrace(message));
}
- public static void LogTrace(String message) {
- ValidateLoggerObject();
+ [Obsolete("Use LogWarning(String messageTemplate, params Object?[] args).")] // NOSONAR - retained for source compatibility until a breaking release.
+ public static void LogWarning(String message)
+ {
+ Logger.ValidateLoggerObject();
+ Logger.Write(logger => logger.LogWarning(message));
+ }
+#pragma warning restore S1133
- TenantContext tenantContext = TenantContext.CurrentTenant;
- if (tenantContext == null) {
- Logger.LoggerObject.LogTrace(message);
- return;
- }
- using (ScopeContext.PushProperty(CorrelationIdPropertyName, $"Correlation ID: {tenantContext.CorrelationId.ToString()}")) {
- // Write to the normal log
- Logger.LoggerObject.LogTrace(message);
-
- if (tenantContext.PerTenantLogsEnabled && tenantContext.EstateId != Guid.Empty) {
- // Write to the tenant log
- using (ScopeContext.PushProperty(TenantIdPropertyName, $"_{tenantContext.EstateId.ToString()}")) {
- Logger.LoggerObject.LogTrace(message);
- }
- }
+ [MemberNotNull(nameof(LoggerObject))]
+ private static void ValidateLoggerObject()
+ {
+ if (Logger.LoggerObject == null)
+ {
+ throw new InvalidOperationException("Logger has not been initialised");
}
}
- public static void LogWarning(String message) {
- ValidateLoggerObject();
-
+ private static void Write(Action write, Boolean isStructured=false)
+ {
TenantContext tenantContext = TenantContext.CurrentTenant;
- if (tenantContext == null) {
- Logger.LoggerObject.LogWarning(message);
+ if (tenantContext == null)
+ {
+ write(Logger.LoggerObject!);
return;
}
- using (ScopeContext.PushProperty(CorrelationIdPropertyName, $"Correlation ID: {tenantContext.CorrelationId.ToString()}")) {
- // Write to the normal log
- Logger.LoggerObject.LogWarning(message);
-
- if (tenantContext.PerTenantLogsEnabled && tenantContext.EstateId != Guid.Empty) {
- // Write to the tenant log
- using (ScopeContext.PushProperty(TenantIdPropertyName, $"_{tenantContext.EstateId.ToString()}")) {
- Logger.LoggerObject.LogWarning(message);
+
+ String correlationId = isStructured switch {
+ true => tenantContext.CorrelationId.ToString(),
+ _ => $"Correlation ID: {tenantContext.CorrelationId}"
+ };
+
+ using (ScopeContext.PushProperty(TenantContext.KeyNameCorrelationId, correlationId))
+ {
+ write(Logger.LoggerObject!);
+ if (tenantContext.PerTenantLogsEnabled && tenantContext.EstateId != Guid.Empty)
+ {
+ using (ScopeContext.PushProperty("tenantId", $"_{tenantContext.EstateId}"))
+ {
+ write(Logger.LoggerObject!);
}
}
}
}
-
- private static void ValidateLoggerObject() {
- if (Logger.LoggerObject == null) {
- throw new InvalidOperationException("Logger has not been initialised");
- }
- }
-}
\ No newline at end of file
+}
diff --git a/Shared.Logger/LoggerEventProperties.cs b/Shared.Logger/LoggerEventProperties.cs
new file mode 100644
index 0000000..ae8b2b8
--- /dev/null
+++ b/Shared.Logger/LoggerEventProperties.cs
@@ -0,0 +1,57 @@
+namespace Shared.Logger;
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Reflection;
+using Shared.Logger.TennantContext;
+
+internal static class LoggerEventProperties
+{
+ public static Dictionary Create(String severity)
+ {
+ Dictionary properties = new(StringComparer.OrdinalIgnoreCase)
+ {
+ ["eventId"] = Guid.NewGuid(),
+ ["timestampUtc"] = DateTimeOffset.UtcNow,
+ ["serviceName"] = Assembly.GetEntryAssembly()?.GetName().Name ?? "UnknownService",
+ ["severity"] = severity
+ };
+
+ TenantContext tenantContext = TenantContext.CurrentTenant;
+ if (tenantContext != null)
+ {
+ Guid correlationId = tenantContext.CorrelationId.Value;
+ if (correlationId != Guid.Empty)
+ {
+ properties["correlationId"] = correlationId;
+ }
+
+ if (tenantContext.EstateId != Guid.Empty)
+ {
+ properties["tenantId"] = tenantContext.EstateId;
+ }
+
+ if (tenantContext.MerchantId != Guid.Empty)
+ {
+ properties["merchantId"] = tenantContext.MerchantId;
+ }
+ }
+
+ Activity? activity = Activity.Current;
+ if (activity != null)
+ {
+ if (activity.TraceId != default)
+ {
+ properties["traceId"] = activity.TraceId.ToString();
+ }
+
+ if (activity.SpanId != default)
+ {
+ properties["spanId"] = activity.SpanId.ToString();
+ }
+ }
+
+ return properties;
+ }
+}
diff --git a/Shared.Logger/MicrosoftLogger.cs b/Shared.Logger/MicrosoftLogger.cs
deleted file mode 100644
index 51d2cdc..0000000
--- a/Shared.Logger/MicrosoftLogger.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-namespace Shared.Logger;
-
-using System;
-using System.Diagnostics.CodeAnalysis;
-using Microsoft.Extensions.Logging;
-
-[ExcludeFromCodeCoverage]
-public class MicrosoftLogger : ILogger
-{
-
- private Microsoft.Extensions.Logging.ILogger LoggerObject;
-
-
-
- public Boolean IsInitialised { get; set; }
-
-
-
- public void Initialise(Microsoft.Extensions.Logging.ILogger loggerObject) {
- this.LoggerObject = loggerObject;
- this.IsInitialised = true;
- }
-
- public void LogCritical(Exception exception) {
- this.LogMessage(LogLevel.Critical, exception.Message, exception);
- }
-
- public void LogCritical(String message,
- Exception exception) {
- this.LogMessage(LogLevel.Critical, message, exception);
- }
-
- public void LogDebug(String message) {
- this.LogMessage(LogLevel.Debug, message);
- }
-
- public void LogError(Exception exception) {
- this.LogMessage(LogLevel.Error, exception.Message, exception);
- }
-
- public void LogError(String message) {
- this.LogMessage(LogLevel.Error, message);
- }
-
- public void LogError(String message,
- Exception exception) {
- this.LogMessage(LogLevel.Error, message, exception);
- }
-
- public void LogInformation(String message) {
- this.LogMessage(LogLevel.Information, message);
- }
-
- public void LogTrace(String message) {
- this.LogMessage(LogLevel.Trace, message);
- }
-
- public void LogWarning(String message) {
- this.LogMessage(LogLevel.Warning, message);
- }
-
- private void LogMessage(LogLevel logLevel,
- String message,
- Exception exception = null) {
- if (this.LoggerObject != null) {
- this.LoggerObject.Log(logLevel,
- new EventId(),
- message,
- exception,
- (state,
- ex) => message);
- }
- else {
- throw new InvalidOperationException("LoggerObject has not been set");
- }
- }
-
-}
\ No newline at end of file
diff --git a/Shared.Logger/NlogLogger.cs b/Shared.Logger/NlogLogger.cs
index fc2a0b6..864ede6 100644
--- a/Shared.Logger/NlogLogger.cs
+++ b/Shared.Logger/NlogLogger.cs
@@ -5,81 +5,115 @@ namespace Shared.Logger;
using NLog;
///
-///
+/// Writes structured events through NLog.
///
-///
[ExcludeFromCodeCoverage]
public class NlogLogger : ILogger
{
+ private String? FileName;
- private String FileName;
-
- private NLog.Logger LoggerObject;
-
-
+ private NLog.Logger? LoggerObject;
public Boolean IsInitialised { get; set; }
-
-
public void Initialise(NLog.Logger loggerObject,
- String fileName) {
- this.LoggerObject = loggerObject;
+ String fileName)
+ {
+ this.LoggerObject = loggerObject ?? throw new ArgumentNullException(nameof(loggerObject));
this.FileName = fileName;
this.IsInitialised = true;
}
- public void LogCritical(Exception exception) {
- this.LogMessage(LogLevel.Fatal, exception.Message, exception);
- }
+ public void LogCritical(Exception exception) =>
+ this.LogMessage(LogLevel.Fatal, exception?.Message ?? String.Empty, exception, Array.Empty