功能场景:
随着系统的功能越发增多,需要将系统的各个功能进行拆分服务(比如 Web API,文件上传,Websocket 等),但是不同服务的日志默认下都是生成 log文件,记录在当前服务下,无法统一查看,不方便管理和监控
那么,我们索性直接将日志服务也独立出来(日志方案基于Nlog),并直接收集其他服务的日志,进行统一记录和监控,方便管理
(其实不分离服务也可以对日志进行统一记录的,因为 Nlog 可以配置生成日志的位置,只是我为了各个服务职责单一,并且将日志服务做成一个日志中心(可监控,可分析,可搜索),所以在其他服务中,直接将日志记录功能取消,而是由 日志服务 进行统一处理)
日志中心效果如图:
实践:
原理很简单,基于 Redis List (消息队列)来进行数据收集,也就是说
1.假若有 A B C 三个服务,我们直接在原本 记录错误日志的中间件 或 接口请求日志记录的拦截器(ActionFilter)中,将他们的日志消息做好标识(比如服务名称,日志消息生成时间等),直接添加到 Redis List 进行存储
(在这里,我使用三个级别的日志等级,Error记录系统错误,Debug记录认为手动需要记录的日志,Trace为请求日志,也就是说,我这里会有三个 list)
//ErrorHandlingMiddleware public async Task Invoke(HttpContext context) { try { if (!context.Response.HasStarted) { await _next(context); } } catch (Exception e) { await HandleExceptionAsync(context, e.Message); RedisHelper.LPush("LogErrorList", $"{PROJECT_NAME}-{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")} |{context.Response.StatusCode}|{context.Request.Method} {context.Request.Host}{ context.Request.Path } \nMessage:{e.Message} \nInnerException:{e.InnerException} \nStackTrace:{e.StackTrace}"); } }
2.然后在独立部署出来的 日志服务 中,通过 BackgroundService 后台任务,使用 BRPop List 将数据弹出消费,进行本地化存储(Nlog),这样一来,就达成我们的统一存储记录日志的需求
//LogErrorJob public class LogErrorJob : BackgroundService { private static Logger logger; protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { var log = RedisHelper.BRPop(1, "LogErrorList"); if (!string.IsNullOrEmpty(log)) { string logName = log.Split('-').First(); string msg = log.Replace($"{logName}-", ""); logger = LogManager.GetLogger(logName); logger.Error($"{msg}"); //error级别日志,发送邮件提示 EmailServer.SendEmail(log); } //添加等待时间,避免cpu过高,这里等待时间为10ms await Task.Delay(10, stoppingToken); } } }
3.再写一个 SMTP邮件服务,如果发现有 Error 级别的日志,进行邮件发送提醒
4.最后,使用 LogDashboard 来对日志进行可视化和可搜素处理(其日志路径和日志格式被 LogDashboard 指定,一些其他细节处理可看官方文档)
Github:待定