如何获取ILogger.Log格式化输出以用于其他目标场景?
如何在遵循ILogger格式化规范的同时,直接获取格式化后的日志消息用于UI展示?
业界普遍推荐让ILogger自行格式化日志消息,而非传入预格式化字符串,CA2254规则会对这种不当用法提出警告。但在UI应用中,经常需要同时把日志消息输出到日志系统和UI(比如错误弹窗),这时开发者很容易写出违反规范的代码:
string message = $"Exception while doing a thing. Code={ex.Code}, {ex.Message}"; _logger.LogError(message); Dialog.Open(Icon.Error, message);
除了重复编写格式化逻辑的方式外,有没有简洁的方法,既能以符合ILogger要求的方式定义消息,又能在调用日志方法的同一上下文直接获取格式化后的结果?注意:这里不是指通过监听日志流再导入UI列表的方式,比如能否实现类似以下代码的效果——既保持日志的正常效率,又能直接拿到格式化后的内容:
LogMessage message = new(LogLevel.Error, "Exception while doing a thing. Code={exCode}, {exMessage}", ex.Code, ex.Message); _logger.Log(message); Dialog.Open(Icon.Error, message.ToString());
方法1:自定义LogMessage类并复用ILogger格式化逻辑
自己实现一个LogMessage类,内部复用.NET官方的日志格式化逻辑,既符合ILogger规范,又能直接获取格式化后的字符串:
public class LogMessage { public LogLevel Level { get; } public string MessageTemplate { get; } public object[] Args { get; } public LogMessage(LogLevel level, string messageTemplate, params object[] args) { Level = level; MessageTemplate = messageTemplate; Args = args; } public override string ToString() { // 复用官方格式化逻辑,保证和ILogger输出完全一致 return LoggerMessageFormatter.Format(MessageTemplate, Args); } } // 扩展ILogger,支持直接传入LogMessage public static class LoggerExtensions { public static void Log(this ILogger logger, LogMessage logMessage) { logger.Log(logMessage.Level, logMessage.MessageTemplate, logMessage.Args); } }
使用方式完全符合你的预期:
var message = new LogMessage(LogLevel.Error, "Exception while doing a thing. Code={exCode}, {exMessage}", ex.Code, ex.Message); _logger.Log(message); Dialog.Open(Icon.Error, message.ToString());
方法2:用扩展方法同时完成日志记录和返回格式化消息
如果不想额外定义类,可以写一个轻量的扩展方法,在记录日志的同时直接返回格式化后的内容:
public static class LoggerUiExtensions { public static string LogAndGetFormattedMessage(this ILogger logger, LogLevel level, string messageTemplate, params object[] args) { var formattedMessage = LoggerMessageFormatter.Format(messageTemplate, args); logger.Log(level, messageTemplate, args); return formattedMessage; } }
使用时更简洁:
var formattedMsg = _logger.LogAndGetFormattedMessage(LogLevel.Error, "Exception while doing a thing. Code={exCode}, {exMessage}", ex.Code, ex.Message); Dialog.Open(Icon.Error, formattedMsg);
方法3:强类型委托实现(极致性能)
如果追求最高性能,可以使用LoggerMessage.Define预编译日志委托,同时封装格式化逻辑:
public static class ErrorLogMessages { private static readonly Action<ILogger, int, string, Exception> _operationFailed = LoggerMessage.Define<int, string>(LogLevel.Error, new EventId(1, "OperationFailed"), "Exception while doing a thing. Code={ExCode}, {ExMessage}"); public static string LogOperationFailed(ILogger logger, int exCode, string exMessage, Exception ex) { _operationFailed(logger, exCode, exMessage, ex); return LoggerMessageFormatter.Format("Exception while doing a thing. Code={ExCode}, {ExMessage}", exCode, exMessage); } }
使用方式:
var formattedMsg = ErrorLogMessages.LogOperationFailed(_logger, ex.Code, ex.Message, ex); Dialog.Open(Icon.Error, formattedMsg);
关键注意事项
- 所有方法都复用了官方的
LoggerMessageFormatter.Format,确保格式化结果和ILogger输出完全一致,避免自定义格式化带来的差异。 - 绝对不要手动实现格式化逻辑,否则会违反CA2254规则的初衷——ILogger需要原始模板和参数来生成结构化日志,预格式化会丢失这些元数据。
内容的提问来源于stack exchange,提问作者Hamish
相关产品推荐
相关产品推荐

