Upload
This commit is contained in:
Executable
+3
@@ -0,0 +1,3 @@
|
|||||||
|
<Solution>
|
||||||
|
<Project Path="NETLogger/NETLogger.csproj" />
|
||||||
|
</Solution>
|
||||||
Executable
+123
@@ -0,0 +1,123 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace NETLogger.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A service that allows for the registration of loggers and controls their enabled state.
|
||||||
|
/// </summary>
|
||||||
|
public interface ILoggerControlService
|
||||||
|
{
|
||||||
|
bool IsEnabled { get; set; }
|
||||||
|
|
||||||
|
IDisposable Register(string name, Action<bool> setEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A service that allows for the registration of loggers and controls their enabled state.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class LoggerControlService : ILoggerControlService
|
||||||
|
{
|
||||||
|
private readonly object _syncRoot = new object();
|
||||||
|
private readonly Dictionary<Guid, Registration> _registrations = new Dictionary<Guid, Registration>();
|
||||||
|
|
||||||
|
private bool _isEnabled = true;
|
||||||
|
|
||||||
|
public bool IsEnabled
|
||||||
|
{
|
||||||
|
get { lock (_syncRoot) return _isEnabled; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Action<bool>[] setters;
|
||||||
|
|
||||||
|
lock (_syncRoot)
|
||||||
|
{
|
||||||
|
if (_isEnabled == value) return;
|
||||||
|
|
||||||
|
_isEnabled = value;
|
||||||
|
setters = _registrations.Values.Select(x => x.SetEnabled).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Action<bool> setter in setters) setter(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a logger with the control service, allowing it to be enabled or disabled.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <param name="setEnabled"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentException"></exception>
|
||||||
|
/// <exception cref="ArgumentNullException"></exception>
|
||||||
|
public IDisposable Register(string name, Action<bool> setEnabled)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Logger name is required.", nameof(name));
|
||||||
|
if (setEnabled == null) throw new ArgumentNullException(nameof(setEnabled));
|
||||||
|
|
||||||
|
var id = Guid.NewGuid();
|
||||||
|
|
||||||
|
lock (_syncRoot)
|
||||||
|
{
|
||||||
|
_registrations.Add(id, new Registration(name, setEnabled));
|
||||||
|
setEnabled(_isEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new UnregisterAction(() =>
|
||||||
|
{
|
||||||
|
lock (_syncRoot) _registrations.Remove(id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class Registration
|
||||||
|
{
|
||||||
|
public Registration(string name, Action<bool> setEnabled)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
SetEnabled = setEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
public Action<bool> SetEnabled { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class UnregisterAction : IDisposable
|
||||||
|
{
|
||||||
|
private Action _unregister;
|
||||||
|
|
||||||
|
public UnregisterAction(Action unregister) => _unregister = unregister;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
var unregister = _unregister;
|
||||||
|
_unregister = null;
|
||||||
|
unregister?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates and registers a logger of type T with the provided ILoggerControlService.
|
||||||
|
/// The logger's enabled state will be controlled by the ILoggerControlService.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="loggerControlService"></param>
|
||||||
|
/// <param name="registration"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentNullException"></exception>
|
||||||
|
public static ILogger<T> CreateAndRegisterNETLogger<T>(ILoggerControlService loggerControlService, out IDisposable registration)
|
||||||
|
{
|
||||||
|
if (loggerControlService == null) throw new ArgumentNullException(nameof(loggerControlService));
|
||||||
|
|
||||||
|
var logger = new Logger<T>();
|
||||||
|
|
||||||
|
registration = loggerControlService.Register(typeof(T).FullName, isEnabled =>
|
||||||
|
{
|
||||||
|
if (isEnabled) logger.Enable();
|
||||||
|
else logger.Disable();
|
||||||
|
});
|
||||||
|
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+223
@@ -0,0 +1,223 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NETLogger
|
||||||
|
{
|
||||||
|
public enum LogLevel
|
||||||
|
{
|
||||||
|
Trace = 0,
|
||||||
|
Debug = 1,
|
||||||
|
Info = 2,
|
||||||
|
Warn = 3,
|
||||||
|
Error = 4,
|
||||||
|
Fatal = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ILogger
|
||||||
|
{
|
||||||
|
void Log(LogLevel level, string message, Exception ex = null);
|
||||||
|
void Trace(string message);
|
||||||
|
void Debug(string message);
|
||||||
|
void Info(string message);
|
||||||
|
void Warn(string message, Exception ex = null);
|
||||||
|
void Error(string message, Exception ex = null);
|
||||||
|
void Fatal(string message, Exception ex = null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ILogger<T> : ILogger { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configuration for all Logger Instances.
|
||||||
|
/// This class is static and should be configured at application startup.
|
||||||
|
/// </summary>
|
||||||
|
public static class LogConfiguration
|
||||||
|
{
|
||||||
|
public static LogLevel MinimumLevel { get; set; } = LogLevel.Debug;
|
||||||
|
public static bool EnableDebugView { get; set; } = true;
|
||||||
|
public static bool EnableConsole { get; set; } = false;
|
||||||
|
|
||||||
|
public static string LogFilePath { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a new log file path based on the current timestamp in the specified directory.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="directory"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetNewLogFilePath(string directory)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(directory)) return null;
|
||||||
|
|
||||||
|
var fileName = $"log_{DateTime.Now:yyyyMMdd_HHmmss_fff}.log";
|
||||||
|
return Path.Combine(directory, fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the log file path. If the directory does not exist, it will be created.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path"></param>
|
||||||
|
public static void SetLogFile(string path)
|
||||||
|
{
|
||||||
|
LogFilePath = path;
|
||||||
|
if (!string.IsNullOrEmpty(path))
|
||||||
|
{
|
||||||
|
var dir = Path.GetDirectoryName(path);
|
||||||
|
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) Directory.CreateDirectory(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convenience method to get a new log file path and set it as the current log file
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="directory"></param>
|
||||||
|
public static void GetAndSetNewLogFile(string directory) => SetLogFile(GetNewLogFilePath(directory));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a default logging path in the user's application data folder for the specified application name.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="appName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentException"></exception>
|
||||||
|
public static string CreateAndGetDefaultLoggingPath(string appName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(appName)) throw new ArgumentException("Application name is required.", nameof(appName));
|
||||||
|
var dir = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), appName, "Logs");
|
||||||
|
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RemoveLoggingPath()
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(LogFilePath))
|
||||||
|
{
|
||||||
|
try { File.Delete(LogFilePath); }
|
||||||
|
catch { /* silently ignore */ }
|
||||||
|
LogFilePath = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class LogWriter
|
||||||
|
{
|
||||||
|
private static readonly object _fileLock = new object();
|
||||||
|
private static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||||
|
|
||||||
|
// P/Invoke for Windows OutputDebugString (DbgView64)
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, EntryPoint = "OutputDebugStringW")]
|
||||||
|
private static extern void OutputDebugString(string lpOutputString);
|
||||||
|
|
||||||
|
internal static void Write(LogLevel level, string category, string message, Exception ex)
|
||||||
|
{
|
||||||
|
if (level < LogConfiguration.MinimumLevel) return;
|
||||||
|
|
||||||
|
string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
|
||||||
|
string levelStr = level.ToString().ToUpperInvariant();
|
||||||
|
string fullMessage = ex == null ? $"[{timestamp}] [{levelStr}] [{category}] {message}" : $"[{timestamp}] [{levelStr}] [{category}] {message} --> Exception: {ex}";
|
||||||
|
|
||||||
|
if (LogConfiguration.EnableDebugView && IsWindows) OutputDebugString(fullMessage + Environment.NewLine);
|
||||||
|
if (LogConfiguration.EnableConsole) Console.WriteLine(fullMessage);
|
||||||
|
if (!string.IsNullOrEmpty(LogConfiguration.LogFilePath))
|
||||||
|
{
|
||||||
|
lock (_fileLock)
|
||||||
|
{
|
||||||
|
try { File.AppendAllText(LogConfiguration.LogFilePath, fullMessage + Environment.NewLine, Encoding.UTF8); }
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// logging should under no circumstances crash the application, so we silently ignore any exceptions here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Utility class for clearing log files in the log directory.
|
||||||
|
/// </summary>
|
||||||
|
internal class LogClearer
|
||||||
|
{
|
||||||
|
public static string DirectoryPath => Directory.GetParent(LogConfiguration.LogFilePath).FullName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears all log files in the log directory.
|
||||||
|
/// </summary>
|
||||||
|
public static void ClearDirectory()
|
||||||
|
{
|
||||||
|
var dir = DirectoryPath;
|
||||||
|
if (string.IsNullOrEmpty(dir) || !Directory.Exists(dir)) return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (var file in Directory.GetFiles(dir))
|
||||||
|
{
|
||||||
|
try { File.Delete(file); }
|
||||||
|
catch { /* silently ignore */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { /* silently ignore */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears old log files in the log directory, keeping only the specified number of most recent logs.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logsToKeep"></param>
|
||||||
|
public static void ClearOldLogs(int logsToKeep = 30)
|
||||||
|
{
|
||||||
|
var dir = DirectoryPath;
|
||||||
|
if (string.IsNullOrEmpty(dir) || !Directory.Exists(dir)) return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var logFiles = new DirectoryInfo(dir).GetFiles("log_*.log");
|
||||||
|
Array.Sort(logFiles, (x, y) => y.CreationTime.CompareTo(x.CreationTime));
|
||||||
|
|
||||||
|
for (int i = logsToKeep; i < logFiles.Length; i++)
|
||||||
|
{
|
||||||
|
try { logFiles[i].Delete(); }
|
||||||
|
catch { /* silently ignore */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{ /* silently ignore */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// typed generic logger class that implements ILogger<T> interface
|
||||||
|
/// </summary>
|
||||||
|
public class Logger<T> : ILogger<T>
|
||||||
|
{
|
||||||
|
private readonly string _categoryName;
|
||||||
|
|
||||||
|
public bool Enabled { get; private set; } = true;
|
||||||
|
|
||||||
|
public Logger() => _categoryName = typeof(T).Name;
|
||||||
|
public Logger(string customCategory) => _categoryName = customCategory;
|
||||||
|
|
||||||
|
public void Enable() => Enabled = true;
|
||||||
|
public void Disable() => Enabled = false;
|
||||||
|
|
||||||
|
public void Log(LogLevel level, string message, Exception ex = null)
|
||||||
|
{
|
||||||
|
if (!Enabled) return;
|
||||||
|
LogWriter.Write(level, _categoryName, message, ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Trace(string message) => Log(LogLevel.Trace, message);
|
||||||
|
public void Debug(string message) => Log(LogLevel.Debug, message);
|
||||||
|
public void Info(string message) => Log(LogLevel.Info, message);
|
||||||
|
public void Warn(string message, Exception ex = null) => Log(LogLevel.Warn, message, ex);
|
||||||
|
public void Error(string message, Exception ex = null) => Log(LogLevel.Error, message, ex);
|
||||||
|
public void Fatal(string message, Exception ex = null) => Log(LogLevel.Fatal, message, ex);
|
||||||
|
|
||||||
|
public void ClearLogs()
|
||||||
|
{
|
||||||
|
LogClearer.ClearDirectory();
|
||||||
|
Info("Clearing all log files in the log directory.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearOldLogs(int logsToKeep = 30)
|
||||||
|
{
|
||||||
|
LogClearer.ClearOldLogs(logsToKeep);
|
||||||
|
Info($"Clearing old log files in the log directory, keeping only the {logsToKeep} most recent logs.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+12
@@ -0,0 +1,12 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<Version>1.1.0</Version>
|
||||||
|
<Authors>Timo Niemann</Authors>
|
||||||
|
<Company>Timo Niemann</Company>
|
||||||
|
<Description>NETLogger is a logging library for .NET applications. With support for multiple logging providers and easy configuration, it simplifies the process of adding robust logging to your app.</Description>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
# NETLogger
|
||||||
|
|
||||||
|
NETLogger is a small logging library for .NET applications targeting .NET Standard 2.0.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Add `NETLogger/NETLogger.csproj` as a project reference or install the NuGet package generated during the build.
|
||||||
|
|
||||||
|
## Configure the application
|
||||||
|
|
||||||
|
Configure the global logger settings once during application startup. The following example stores log files in the current user's application data directory:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using NETLogger;
|
||||||
|
|
||||||
|
LogConfiguration.MinimumLevel = LogLevel.Info;
|
||||||
|
LogConfiguration.EnableConsole = true;
|
||||||
|
LogConfiguration.EnableDebugView = true; // Windows only
|
||||||
|
|
||||||
|
string logDirectory = LogConfiguration.CreateAndGetDefaultLoggingPath("MyApplication");
|
||||||
|
LogConfiguration.GetAndSetNewLogFile(logDirectory);
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, use `LogConfiguration.SetLogFile(path)` to specify a complete file path. Without a configured file path, NETLogger writes only to the enabled console and Windows debug outputs.
|
||||||
|
|
||||||
|
## Use a logger directly
|
||||||
|
|
||||||
|
For an independent class, create a typed `Logger<T>`. The type name is automatically included in each log entry as its category:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class Service
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger = new Logger<Service>();
|
||||||
|
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
_logger.Info("Service started.");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Application code
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.Error("Service failed.", exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A directly created `Logger<T>` can be disabled and enabled independently with `Disable()` and `Enable()`. `ClearOldLogs(logsToKeep)` removes old log files; without an argument, it keeps the 30 most recent files. `ClearLogs()` removes all log files from the configured directory.
|
||||||
|
|
||||||
|
## Control multiple loggers centrally
|
||||||
|
|
||||||
|
Use a shared `LoggerControlService` when multiple loggers need to be enabled or disabled together. An owning class creates the service and registers its logger:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using NETLogger;
|
||||||
|
using NETLogger.Services;
|
||||||
|
|
||||||
|
public sealed class ApplicationController : IDisposable
|
||||||
|
{
|
||||||
|
private readonly ILoggerControlService _loggerControlService;
|
||||||
|
private readonly ILogger<ApplicationController> _logger;
|
||||||
|
private readonly IDisposable _loggerRegistration;
|
||||||
|
|
||||||
|
public ILoggerControlService LoggerControlService => _loggerControlService;
|
||||||
|
|
||||||
|
public ApplicationController() : this(new LoggerControlService()) { }
|
||||||
|
|
||||||
|
public ApplicationController(ILoggerControlService loggerControlService)
|
||||||
|
{
|
||||||
|
_loggerControlService = loggerControlService
|
||||||
|
?? throw new ArgumentNullException(nameof(loggerControlService));
|
||||||
|
|
||||||
|
_logger = NETLogger.Services.LoggerControlService.CreateAndRegisterNETLogger<ApplicationController>(
|
||||||
|
_loggerControlService,
|
||||||
|
out _loggerRegistration);
|
||||||
|
|
||||||
|
_logger.Info("Application controller initialized.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_loggerRegistration.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Dependent classes receive the same service and register their own typed logger. Dispose the `IDisposable` registration when the owning instance is disposed:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public sealed class BackgroundService : IDisposable
|
||||||
|
{
|
||||||
|
private readonly ILogger<BackgroundService> _logger;
|
||||||
|
private readonly IDisposable _loggerRegistration;
|
||||||
|
|
||||||
|
public BackgroundService(ILoggerControlService loggerControlService)
|
||||||
|
{
|
||||||
|
_logger = LoggerControlService.CreateAndRegisterNETLogger<BackgroundService>(
|
||||||
|
loggerControlService,
|
||||||
|
out _loggerRegistration);
|
||||||
|
|
||||||
|
_logger.Info("Background service initialized.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_loggerRegistration.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
All registered loggers can be toggled together:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
loggerControlService.IsEnabled = false; // Disable all registered loggers
|
||||||
|
loggerControlService.IsEnabled = true; // Enable all registered loggers
|
||||||
|
```
|
||||||
|
|
||||||
|
## Initialize loggers for static members
|
||||||
|
|
||||||
|
Classes with static logging members can use explicit initialization to participate in the shared `LoggerControlService`:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public sealed class ConfigurationCache
|
||||||
|
{
|
||||||
|
private static ILogger<ConfigurationCache> _logger;
|
||||||
|
private static IDisposable _loggerRegistration;
|
||||||
|
|
||||||
|
private ConfigurationCache() { }
|
||||||
|
|
||||||
|
public static void InitializeLogger(ILoggerControlService loggerControlService)
|
||||||
|
{
|
||||||
|
if (loggerControlService == null)
|
||||||
|
throw new ArgumentNullException(nameof(loggerControlService));
|
||||||
|
|
||||||
|
_loggerRegistration?.Dispose();
|
||||||
|
_logger = LoggerControlService.CreateAndRegisterNETLogger<ConfigurationCache>(
|
||||||
|
loggerControlService,
|
||||||
|
out _loggerRegistration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The owning class initializes the logger with its shared service:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
ConfigurationCache.InitializeLogger(_loggerControlService);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Log levels and outputs
|
||||||
|
|
||||||
|
The available levels are `Trace`, `Debug`, `Info`, `Warn`, `Error`, and `Fatal`. Messages below `LogConfiguration.MinimumLevel` are ignored. `Warn`, `Error`, and `Fatal` optionally accept an exception:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Application code
|
||||||
|
}
|
||||||
|
catch (Exception exception)
|
||||||
|
{
|
||||||
|
_logger.Error("Operation failed.", exception);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
NETLogger can write to the Windows debug output, the console, and a UTF-8 log file at the same time.
|
||||||
Reference in New Issue
Block a user