using Serilog; using Serilog.Core; using Serilog.Events; // alias to prevent conflict own Log method using SerilogLoggingInstance = Serilog.Log; namespace Common.Logging; /// /// Implements a Logger that uses the Serilog package to create log entries and distributes them into different sinks. /// Implemented Sinks: File (rolling file), Debug (). /// public class SerilogLogger : ILogger { private readonly LoggingLevelSwitch loggingLevelSwitch; public event EventHandler? NewLogEntry; public LogLevel CurrentMinimumLogLevel { get; private set; } = LogLevel.Undefined; /// /// Constructor of a Logger that uses the Serilog package to create log entries and distributes them into different sinks. /// /// /// path where the logfile shall be created. /// rolling file is created with daily interval. date is automatically added to file name. /// Example: "C:/log/log-.txt" /// /// /// if true the log entries are written to . /// Minimum level is ignored for this log entries, therefore every log entry will be logged. /// public SerilogLogger(string logFilePath, bool logToDebug) { // create logging level switch and initialized with lowest level this.loggingLevelSwitch = new LoggingLevelSwitch() { MinimumLevel = Serilog.Events.LogEventLevel.Verbose }; // create logger instance if (logToDebug) { SerilogLoggingInstance.Logger = new LoggerConfiguration() .WriteTo.Debug() .WriteTo.File(logFilePath, rollingInterval: RollingInterval.Day, levelSwitch: loggingLevelSwitch, outputTemplate: "{Message:lj}") // outputTemplate = plain message, formatting is done inside log entry .CreateLogger(); } else { SerilogLoggingInstance.Logger = new LoggerConfiguration() .WriteTo.File(logFilePath, rollingInterval: RollingInterval.Day, levelSwitch: loggingLevelSwitch, outputTemplate: "{Message:lj}") // outputTemplate = plain message, formatting is done inside log entry .CreateLogger(); } } public void Initialize() { this.Initialize(LogLevel.Trace); } public void Initialize(LogLevel minimumLogLevel) { this.SetMinimumLogLevel(minimumLogLevel); // Nothing else to do for this logger } public void SetMinimumLogLevel(LogLevel newMinimumLogLevel) { this.loggingLevelSwitch.MinimumLevel = this.ConvertGenericToSerilogLogLevel(newMinimumLogLevel); this.CurrentMinimumLogLevel = newMinimumLogLevel; } private LogEventLevel ConvertGenericToSerilogLogLevel(LogLevel newMinimumLogLevel) => newMinimumLogLevel switch { LogLevel.Undefined => LogEventLevel.Verbose, LogLevel.Trace => LogEventLevel.Verbose, LogLevel.Debug => LogEventLevel.Debug, LogLevel.Info => LogEventLevel.Information, LogLevel.Warn => LogEventLevel.Warning, LogLevel.Error => LogEventLevel.Error, _ => throw new NotImplementedException($"'{nameof(ConvertGenericToSerilogLogLevel)}()' does not contain an entry for {nameof(LogLevel)} {newMinimumLogLevel}"), }; public void StopLogging() { SerilogLoggingInstance.CloseAndFlush(); } #region Logging methods public void Log(LogEntry logEntry) { var serilogEventLevel = this.ConvertGenericToSerilogLogLevel(logEntry.LogLevel); // formatting is done inside logEntry.ToString() method => therefore log plain message text with right category switch (serilogEventLevel) { case LogEventLevel.Verbose: SerilogLoggingInstance.Verbose(logEntry.ToString()); break; case LogEventLevel.Debug: SerilogLoggingInstance.Debug(logEntry.ToString()); break; case LogEventLevel.Information: SerilogLoggingInstance.Information(logEntry.ToString()); break; case LogEventLevel.Warning: SerilogLoggingInstance.Warning(logEntry.ToString()); break; case LogEventLevel.Error: SerilogLoggingInstance.Error(logEntry.ToString()); break; case LogEventLevel.Fatal: SerilogLoggingInstance.Error(logEntry.ToString()); break; default: throw new NotImplementedException($"'{nameof(Log)}()' does not contain an implementation for {nameof(LogEventLevel)} {serilogEventLevel}."); } // Raise event (only if the log is wanted due to the configured log level) if (logEntry.LogLevel <= this.CurrentMinimumLogLevel) { this.NewLogEntry?.Invoke(this, new NewLogEntryEventArgs(logEntry)); } } public void LogTrace(string message, string category = "") { this.Log(new LogEntry { Category = category, LogLevel = LogLevel.Trace, Message = message }); } public void LogDebug(string message, string category = "") { this.Log(new LogEntry { Category = category, LogLevel = LogLevel.Debug, Message = message }); } public void LogInfo(string message, string category = "") { this.Log(new LogEntry { Category = category, LogLevel = LogLevel.Info, Message = message }); } public void LogWarn(string message, string category = "") { this.Log(new LogEntry { Category = category, LogLevel = LogLevel.Warn, Message = message }); } public void LogError(string message, string category = "") { this.Log(new LogEntry { Category = category, LogLevel = LogLevel.Error, Message = message }); } public void LogException(Exception exception, string category = "") { this.Log(new LogEntry { Category = category, LogLevel = LogLevel.Error, Exception = exception }); } public void LogException(Exception exception, string message, string category = "") { this.Log( new LogEntry { Category = category, LogLevel = LogLevel.Error, Message = message, Exception = exception }); } #endregion }