如何避免重复编写ILogger消息并实现消息复用?
避免日志与通知重复构建消息的方案
要解决重复编写消息模板和参数的问题,同时保留结构化日志的优势(即日志系统能识别占位符对应的参数,而非仅记录拼接后的字符串),可以用以下几种实用方案:
1. 封装通用通知服务
创建一个统一的服务类,整合日志记录和弹窗通知逻辑,只需传入一次模板和参数即可完成多渠道推送:
public class NotificationService { private readonly ILogger<NotificationService> _logger; public NotificationService(ILogger<NotificationService> logger) { _logger = logger; } // 通用方法:记录结构化日志 + 显示成功弹窗 public async Task LogAndShowSuccessAlert(string messageTemplate, params object[] args) { // 保留结构化日志特性:直接传入模板和参数 _logger.LogInformation(messageTemplate, args); // 渲染成用户可见的字符串(复用模板和参数) var userFriendlyMessage = string.Format(messageTemplate, args); await App.Show.AlertSuccess(userFriendlyMessage); } // 可扩展:添加其他类型的通知(如错误弹窗、进度报告等) public async Task LogAndShowErrorAlert(string messageTemplate, params object[] args) { _logger.LogError(messageTemplate, args); var userFriendlyMessage = string.Format(messageTemplate, args); await App.Show.AlertError(userFriendlyMessage); } }
使用方式:
// 注入NotificationService后调用 var productContracts = await context.ProductContracts.ToListAsync(); var contractListSummary = productContracts.ValuesToStringNewLines(c => c.GetDescription()); await _notificationService.LogAndShowSuccessAlert( "All contracts are up to date:\r\n{ContractListSummary}", contractListSummary );
优势:集中管理所有通知逻辑,后续扩展新渠道(如邮件、进度条)只需在服务中添加方法,代码复用性最高。
2. 扩展Logger的链式方法
通过扩展方法实现类似你想要的链式调用风格,同时保留结构化日志:
public static class LoggerNotificationExtensions { // 扩展ILogger,添加日志+弹窗的链式方法 public static async Task ThenShowSuccessAlert(this ILogger logger, string messageTemplate, params object[] args) { logger.LogInformation(messageTemplate, args); var userMessage = string.Format(messageTemplate, args); await App.Show.AlertSuccess(userMessage); } // 若想先执行日志再自定义后续操作,可返回包含模板和参数的对象 public static LoggedMessage LogInfoWithPayload(this ILogger logger, string messageTemplate, params object[] args) { logger.LogInformation(messageTemplate, args); return new LoggedMessage(messageTemplate, args); } } // 用于承载日志模板和参数的结构体 public struct LoggedMessage { public string Template { get; } public object[] Args { get; } public LoggedMessage(string template, object[] args) { Template = template; Args = args; } public async Task ShowSuccessAlert() { var message = string.Format(Template, Args); await App.Show.AlertSuccess(message); } // 可扩展其他通知方法 public async Task ShowProgressReport() { var message = string.Format(Template, Args); await App.Show.UpdateProgress(message); } }
使用方式:
// 直接链式调用 await logger.ThenShowSuccessAlert( "All contracts are up to date:\r\n{ContractListSummary}", contractListSummary ); // 或先记录日志,再灵活选择后续操作 logger.LogInfoWithPayload( "All contracts are up to date:\r\n{ContractListSummary}", contractListSummary ).ShowSuccessAlert();
优势:写法简洁直观,符合你期望的链式调用习惯,无需额外注入服务。
3. 复用消息模板常量
如果只是想避免重复写相同的模板字符串,可以把常用模板定义为常量:
public static class MessageTemplates { public const string ContractsUpToDate = "All contracts are up to date:\r\n{ContractListSummary}"; public const string DataSavedSuccess = "Data {EntityName} saved successfully (ID: {EntityId})"; }
使用方式:
var contractListSummary = productContracts.ValuesToStringNewLines(c => c.GetDescription()); // 日志和弹窗复用同一个模板 logger.LogInformation(MessageTemplates.ContractsUpToDate, contractListSummary); await App.Show.AlertSuccess(string.Format(MessageTemplates.ContractsUpToDate, contractListSummary));
优势:改动最小,适合模板数量不多的场景,避免拼写错误。
关键注意点
不要直接使用拼接后的字符串(如$"All contracts...")同时传给日志和弹窗,因为这样会丢失结构化日志的核心价值:日志系统无法识别占位符对应的参数,只能记录完整字符串,后续无法通过参数值搜索、过滤日志。
内容的提问来源于stack exchange,提问作者Sean
相关产品推荐
相关产品推荐

