重构.NET Framework应用:如何为.NET Core日志框架新增日志级别?
Hey Julian, great question! I’ve dealt with this exact scenario when migrating legacy .NET Framework apps to use the modern Microsoft.Extensions.Logging stack, so let’s break down how to add your custom log levels without ditching the framework’s built-in functionality.
Step 1: Define Your Custom Log Level Enumeration
First, create an enumeration that includes the default .NET log levels plus your custom ones. This makes it easier to reference levels throughout your code instead of using magic numbers:
public enum CustomLogLevel { // Match default Microsoft.Extensions.Logging.LogLevel values Trace = 0, Debug = 1, Information = 2, Warning = 3, Error = 4, Critical = 5, // Add your custom levels here Verbose = 6, Audit = 7 }
Step 2: Extend ILogger for Custom Level Methods
To make logging with your custom levels as intuitive as the default ones, add extension methods to ILogger:
public static class CustomLoggerExtensions { // Basic logging methods public static void Verbose(this ILogger logger, string message, params object[] args) { logger.Log((LogLevel)(int)CustomLogLevel.Verbose, message, args); } public static void Audit(this ILogger logger, string message, params object[] args) { logger.Log((LogLevel)(int)CustomLogLevel.Audit, message, args); } // Overloads for logging exceptions public static void Verbose(this ILogger logger, Exception exception, string message, params object[] args) { logger.Log((LogLevel)(int)CustomLogLevel.Verbose, exception, message, args); } public static void Audit(this ILogger logger, Exception exception, string message, params object[] args) { logger.Log((LogLevel)(int)CustomLogLevel.Audit, exception, message, args); } }
Now you can call logger.Verbose("Detailed trace message") just like you’d use logger.LogInformation(...).
Step 3: Handle Custom Levels in Log Formatting
Default log providers (like the Console provider) won’t recognize your custom level names out of the box. To display friendly names instead of numeric values, create a custom formatter:
public class CustomConsoleFormatter : ConsoleFormatter { public CustomConsoleFormatter() : base("CustomFormatter") { } public override void Write<TState>(in LogEntry<TState> logEntry, IExternalScopeProvider scopeProvider, TextWriter textWriter) { // Map the numeric LogLevel to your custom enum name var levelName = ((CustomLogLevel)(int)logEntry.LogLevel).ToString(); var timestamp = DateTimeOffset.Now.ToString("yyyy-MM-dd HH:mm:ss"); // Build your log line (customize this to match your legacy format if needed) textWriter.WriteLine($"[{timestamp}] [{levelName}] {logEntry.Message}"); // Include exception details if present if (logEntry.Exception != null) { textWriter.WriteLine(logEntry.Exception.ToString()); } } }
Then register this formatter when setting up your logging:
var serviceCollection = new ServiceCollection(); serviceCollection.AddLogging(builder => { builder.AddConsole(options => { // Use your custom formatter options.FormatterName = "CustomFormatter"; }) .AddConsoleFormatter<CustomConsoleFormatter>(); });
Step 4: Configure Log Filtering for Custom Levels
When setting up log filters (either via code or config), you can reference your custom levels by their numeric value or map them in your config. For example, in appsettings.json:
{ "Logging": { "LogLevel": { "Default": "Information", "YourAppNamespace": "6" // Allow Verbose level logs from your namespace }, "Console": { "LogLevel": { "YourAppNamespace.Audit": "7" // Only show Audit logs from this sub-namespace } } } }
If you prefer using names in config, you can add a custom configuration provider or map the names to numeric values in code during setup.
Step 5: Support Custom Levels in Your Custom Log Provider
Since you’re building a custom log provider for multi-target output, make sure it checks for your custom levels. For example, in your provider’s log processing logic:
public void Log<TState>(LogEntry<TState> logEntry) { var customLevel = (CustomLogLevel)(int)logEntry.LogLevel; switch (customLevel) { case CustomLogLevel.Verbose: // Handle Verbose logs (e.g., write to a detailed trace file) break; case CustomLogLevel.Audit: // Handle Audit logs (e.g., write to a dedicated audit database) break; // Handle default levels default: // Process Trace/Debug/Info etc. break; } }
Key Notes for .NET Framework
- Make sure you’ve installed the necessary NuGet packages:
Microsoft.Extensions.Logging,Microsoft.Extensions.Logging.Console, andMicrosoft.Extensions.DependencyInjection(for dependency injection setup). - If your legacy app doesn’t use DI, you can still create a logger factory manually instead of using service collection.
This approach lets you leverage the full power of Microsoft.Extensions.Logging while preserving all the custom log levels from your original system. Test each level thoroughly to ensure logs are routed correctly to your multiple output targets!
内容的提问来源于stack exchange,提问作者Julian

