1. 先看本质:日志记录器到底在记录什么
1.1 一条好日志必备的字段
后端服务里最容易被人忽略但又最关键的东西,往往是日志。平时一切正常时没人看它,一旦出问题,所有人都在问你“日志里有什么”。我维护过好几个 ASP.NET Core 服务,对这一点深有体会。今天想分享的这个日志记录器 WatchDog.NET,是我最近在 .NET 10 预演项目里用得比较顺手的一个:它把 HTTP 请求、异常、自定义业务日志统一收进来,再通过一个自带的面板做实时查询,MIT 开源、商业友好,接入成本和替换成本都比想象中低。
先聊一个基础问题:一条日志到底应该记录什么?很多团队写的日志就是一句话,“登录成功”“订单创建成功”,这放在排查问题时基本没用。真正可用的日志至少应该包含时间戳、日志级别、消息模板、来源组件、请求路径、状态码、耗时、TraceId,如果是异常还要有完整堆栈。WatchDog.NET 在存储层把 HTTP 请求、异常、自定义日志三类分开,每类都有固定的字段模型。比如 HTTP 请求日志会记录 Method、Path、QueryString、RequestBody、ResponseBody、客户端 IP、StatusCode、ResponseTime,异常日志则记录 Source、StackTrace、Message 和触发位置。这些字段看起来琐碎,但线上出问题时,缺了任何一个都可能要多花几个小时定位。
我自己有个习惯:判断一套日志方案好不好用,先不看它支持多少种存储,而是看它能不能在一条日志里把“谁在什么时间、调用了什么接口、拿到了什么结果、花了多久”完整还原出来。很多传统方案不是做不到,而是要么需要额外开发中间件,要么需要把文件日志手动翻出来再拼上下文。WatchDog.NET 在这方面做得比较省事,它直接以中间件的形式挂到请求管道里,请求一进来就开始记录,响应结束后落库,你不需要自己拼字段。
1.2 日志级别与筛选原则
日志级别是很容易被忽略但影响很大的设计。Debug 用于开发调试,Trace 更细,Information 记录常规业务操作,Warning 表示潜在问题但不影响主流程,Error 是已经发生的异常,Critical 是可能导致服务不可用的致命错误。日常排障时,如果全项目都只写 Information 和 Error,一旦出问题,你面对的就是大量无关噪音,真正有用的错误信息被淹没。
WatchDog.NET 的面板里可以对日志级别做筛选,不同级别还有不同的颜色标识,这一点看起来只是界面细节,实际用起来特别舒服。有一次我在现场排查一个偶发 500,打开面板直接把日志过滤到 Error,再把时间范围缩到精确的几分钟内,问题堆栈一下就出来了。如果日志全部混在一起,根本没有这个效率。
我建议大家在项目启动时就把日志级别规范定下来:外部调用入口记录 Information,业务规则校验失败记录 Warning,捕获到异常记录 Error,只有服务起停、配置加载这类事件才用 Critical。Debug 日志不是不能写,而是要用严格的条件控制,避免在生产环境刷屏。WatchDog.NET 对级别的处理比较直观,你在代码里用标准ILogger输出,它就会按级别收进对应的标签页,不需要额外做映射。
1.3 传统日志方案的不足
传统方案不是不能用,而是各有各的别扭。内置ILogger本身只是一个抽象接口,默认 Provider 只把日志写到控制台、事件源或文件,团队小的时候还能接受,服务一多就开始头疼。Serilog 非常强大,结构化日志支持也很好,但要用得舒服,通常还要配套搭建一个日志采集和查询系统,这一套下来至少多维护两个组件。NLog 胜在配置灵活、路由规则丰富,但它的强项是文件日志和格式化输出,查看体验依然停留在“打开文件、手动搜索”的层面。
不是说这些方案不好,而是它们解决的重点可能和你的需求不完全匹配。对于大多数 ASP.NET Core 10 单体服务或中小型系统,你真正需要的可能不是再引入一套重量级日志平台,而是一个开箱即用、能看能查、不用维护太多额外组件的日志记录器。WatchDog.NET 正好卡在这个位置上,这也是我一开始会持续关注它的原因。
2. 为什么我会盯上 WatchDog.NET
2.1 接入成本是真的低
我有过为了看日志搭了一套完整采集链路的经历,那套方案在大型项目里很合理,但对一个团队只有几个人的模块来说,运维压力反而超过了日志本身带来的价值。所以看到 WatchDog.NET 的接入方式时,我第一反应是“这玩意儿居然这么轻”。
它就是一个 .NET 的日志组件,通过 NuGet 包引用进项目,然后注册相关服务,再在请求管道里挂上两个中间件,就完成了最基本的接入。不需要单独部署数据库服务,默认采用本地存储方案,也不需要配置文件采集器,更不需要维护额外的日志面板站点。对单体应用和微服务中的单个服务来说,这是在“没有任何日志可视化”到“有面板能实时查日志”之间,成本最低的一条路。
我实测过的最小接入代码大概只需要改动Program.cs里的几行,再加上一个appsettings.json里的连接配置。从新建项目到面板出现日志,十分钟内可以走完。对比我之前搭那套采集链路时花的两三天,这个差距非常明显。
2.2 自带面板解决“看日志难”问题
日志组件最容易被低估的能力是“查看”。很多方案负责把日志写下来,但你怎么看、怎么看快,是完全另一回事。容器化部署之后,我见过不少同事遇到问题第一反应是docker logs,然后在一大堆无结构输出里艰难找线索。如果服务还扩容了多副本,想看某一次请求的完整上下文几乎要靠运气。
WatchDog.NET 自带一个管理面板,服务启动后通过浏览器访问固定地址就能进入。面板里可以按时间范围、日志级别、关键字、状态码等条件过滤日志,HTTP 请求、异常、自定义日志分开展示。对于开发环境联调和线上问题回溯,这个“自带 UI”的价值非常大,相当于省掉了一套独立的日志查询平台。
我自己的经验是,日志工具一定要能让“发现问题的人”直接上手看,而不是每次都要叫运维帮忙导文件。WatchDog.NET 的面板在团队里推广起来几乎没有学习成本,打开、登录、筛选,三步就完事。这一点比很多功能华丽但配置复杂的方案实在得多。
2.3 MIT 许可证带来的安全感
再聊一下标题里那个很显眼的“MIT 开源商业友好”。作为开发人员,我们选第三方组件时不能只看功能,还要看许可证。WatchDog.NET 走的是 MIT 许可证,这意味着你可以自由使用、复制、修改、合并、发布、分发、再许可和销售副本,唯一的主要义务是保留原版权声明和免责声明。
对于商业项目来说,MIT 许可证意味着你可以放心把它集成到自己的产品中,不需要担心开源传染性,也不需要因为用了这个组件就去公开整个项目的源代码。对于内部系统、外包项目或者商业化软件,这种商业友好非常重要。我选组件时一般会优先看许可证,如果可选方案许可证很严格,哪怕功能再合适,我也会尽量绕开。WatchDog.NET 在这点上几乎没有额外的法务负担。
3. 核心功能拆解:它到底能做什么
3.1 HTTP 请求与响应记录
WatchDog.NET 最核心的能力之一,就是把经过应用的 HTTP 请求自动记录下来。你在浏览器里点击一次页面,或者调用一个 API,中间件会记录请求的 Method、路径、查询字符串、请求体、响应体、状态码、耗时、来源 IP 等信息,并全部写入存储。面板里可以按路径、状态码、耗时区间做筛选,定位慢接口和异常状态码非常高效。
这里有一个操作禁忌:记录请求体和响应体虽然是好功能,但也意味着所有经过的数据都可能被持久化。比如登录接口的密码字段、支付接口的卡号信息,如果原样记录,不仅可能违反数据保护要求,还会成为黑客攻击后的敏感数据泄露点。我建议要么只针对部分接口记录 Body,要么在记录之前做脱敏处理,要么干脆关闭请求体和响应体的记录,只保留状态码、耗时和路径。WatchDog.NET 的相关配置项是有的,务必要看一眼。
另外,接口耗时这个字段对性能排查特别有用。我处理过一次“某些用户反馈页面很慢”的问题,通过面板按耗时排序,很快就发现是某个导出接口在数据量大时超过 10 秒。如果只靠代码走查,这个接口混在一堆正常接口里,根本不知道从哪下手。
3.2 异常捕获与告警
WatchDog.NET 提供了异常日志中间件,可以捕获应用处理过程中没有被处理的异常,并记录完整堆栈。配合面板里的异常标签页,你能看到异常发生的时间、类型、消息、来源源码位置以及调用链信息。对于定位偶发 500 这类问题,这个功能比从控制台翻日志靠谱得多。
有一点要注意:它捕获的主要是未被上层 try-catch 处理的异常。如果你在代码里习惯性地把所有异常用try-catch包住,然后只是Console.WriteLine一下,那么这些异常并不会自动进入面板。正确的做法是捕获后通过ILogger.LogError明确记录,或者把异常重新抛出交给全局处理。我自己踩过这个坑,之前有一个内部接口的异常被吞掉,日志面板里一直是空的,后来排查才发现异常在某个工具类里被 catch 后只记到了系统日志,没有走统一日志管道。
如果你需要主动告警能力,可以配合面板轮询或者外部监控来消费异常记录。WatchDog.NET 本身更偏重记录和查询,不擅长复杂告警,但是作为异常事件的可靠来源,已经足够支撑后续告警体系。
3.3 自定义业务日志与 ILogger 集成
除了 HTTP 请求和异常,WatchDog.NET 还实现了自定义日志的收集,接入了标准ILogger接口。你在业务代码里写的_logger.LogInformation、_logger.LogError等调用,会统一进入面板的自定义日志页签。这样业务日志、异常日志、请求日志可以在同一个界面上交叉查看,排查上下文时不用来回切换系统。
我个人比较习惯在关键业务节点打日志时带上业务编号,比如订单号、用户 ID。WatchDog.NET 支持结构化日志模板,也就是说可以用LogInformation("处理订单 {OrderNo}", orderNo)这种写法,面板里搜索订单号时就能快速定位到对应的日志。这个习惯一旦养成,线上排查效率会明显提升。
举个例子,在一个支付回调场景里,我只在回调入口、验签成功、更新订单状态、发送通知这四个节点各写一行日志,并把订单号带进去。回调出问题时,在面板里搜订单号,整个调用链一目了然,几乎不需要再去翻代码看逻辑。
3.4 内置面板的查询与过滤能力
内置面板不只是展示,还支持按时间范围、日志级别、关键字、状态码、接口路径这些维度过滤。开发环境里我习惯按当前正在调试的接口路径筛选日志,只关注那一条请求链;线上环境则按错误码或异常类型过滤,快速缩小范围到可疑点。相比用 grep 翻文件日志,这种过滤体验接近专用日志查询系统。
面板的自动刷新能力也很实用。前端联调时,你不需要来回刷新页面,日志会实时滚动,接口请求响应后马上就能看到记录。对开发人员来说,这种反馈速度会直接影响调试效率。
4. 把它装进 ASP.NET Core 10 项目的完整步骤
4.1 环境准备与包引用
在动手之前先准备好环境。这里讨论的场景是 ASP.NET Core 10,如果你还在使用更早的 .NET 8 或 .NET 9,接入逻辑也基本一致,只是项目模板和部分 API 名称可能有差异。我建议直接用一个最小 Web 项目来做实验,避免被已有项目的复杂配置干扰。
创建项目可以选择命令行或集成开发环境,命令大致是:
dotnet new web -n DemoWatchDog cd DemoWatchDog然后通过 NuGet 添加 WatchDog.NET 包:
dotnet add package WatchDog.NET不建议在没有任何概念的情况下直接拉最新预览版,稳定版本就好。引用包之后,Program.cs顶部需要包含 WatchDog 的命名空间,通常能看到using WatchDog;这样的代码。
4.2 最小配置:Program.cs 需要改哪几行
个典型的最小配置可以长这样:
using WatchDog; var builder = WebApplication.CreateBuilder(args); builder.Services.AddWatchDogServices(config => { config.IsAutoClear = true; config.ClearTimeSchedule = "0 */6 * * *"; config.WatchDogUsername = "admin"; config.WatchDogPassword = "your-strong-password"; }); var app = builder.Build(); app.UseWatchDogExceptionLogger(); app.UseWatchDog(); app.MapGet("/", () => "Hello WatchDog.NET"); app.Run();这段代码做了三件事:注册 WatchDog.NET 服务、开启异常日志中间件、开启 HTTP 记录和面板。AddWatchDogServices里的配置项里,WatchDogUsername和WatchDogPassword用于登录面板,建议务必显式配置,不要依赖默认值;IsAutoClear表示按计划自动清理过期的日志,避免存储膨胀;ClearTimeSchedule用 cron 表达式控制清理频率。
我之前见过有人把这段配置写到一半,账号密码用了弱口令,结果面板被同事传得到处都是。不管项目是不是跑在公网,面板登录凭证都不建议用默认密码或者太简单的组合。
4.3 配置外部数据库持久化
WatchDog.NET 默认采用本地嵌入式存储,开发环境完全够用。但如果你希望日志存储到共享数据库,或者服务有多个实例,我建议在注册服务时指定数据库连接。类似这样的逻辑:
var connectionString = builder.Configuration.GetConnectionString("WatchDogConnection"); builder.Services.AddWatchDogServices(config => { config.SetExternalDbConnectionString(connectionString); // 其他配置省略 });在appsettings.json里,需要补一个连接字符串:
{ "ConnectionStrings": { "WatchDogConnection": "Server=.;Database=WatchDog;User Id=sa;Password=your-password;TrustServerCertificate=True;" } }连接字符串的写法和你选择的数据库有关,这里只是示例。配置外部库以后,多个实例可以共享一份日志数据,面板查到的内容不再是单机视角。不过要提醒一句:此时数据库的备份、权限和连接数都变成你需要承担的责任,别把日志库当成不需要维护的附属设施。
4.4 面板访问与登录认证
程序跑起来后,正常情况下面板地址是/watchdog,具体以你实际版本为准,启动日志通常会有提示。浏览器访问该地址,输入配置的账号密码,就能看到面板。如果访问遇到 404,先检查中间件是否注册成功,再确认路由前缀是否被项目自己的约定覆盖。
面板本质上是应用内部的一个 UI,如果服务跑在公网环境,我建议用网关层做一层访问限制,至少加 IP 白名单或反向代理认证,不要直接把面板完全裸露。日志包含的信息量很大,虽然不是绝对敏感,但权限控制做好能避免很多不必要的风险。这是实操中很容易被忽略的一步。
4.5 日志清理与保留策略
再展开说一下日志清理。日志组件接入后最容易犯的错误是“只记录,不清理”,存储里的日志越来越多,最后面板查询变得非常慢。IsAutoClear和ClearTimeSchedule这两个配置就是用来控制这个问题的。
ClearTimeSchedule支持 cron 表达式,比如"0 0 * * *"表示每天零点清理一次,"0 */6 * * *"表示每六小时清理一次。我一般建议开发环境保留最近 3 到 7 天的日志,生产环境根据项目要求保留 30 天左右。清理太频繁会把排障时需要的历史证据删掉,清理太慢则影响面板性能。这个节奏没有绝对标准,建议按项目实际流量调整。
config.IsAutoClear = true; config.ClearTimeSchedule = "0 0 * * *";这里的 cron 表达式是标准的五段格式,如果你不熟悉,可以先用在线工具验证再填进去。
5. 和其他日志方案放在一起比
5.1 各自擅长什么
没有万能的日志方案,只有适不适合。内置ILogger是所有 ASP.NET Core 应用自带的基础设施,优点是零成本,缺点是默认输出能力有限。Serilog 是结构化日志里的老牌选手,Sink 生态非常丰富,Kafka、Elasticsearch、文件、数据库都能接。NLog 的特点是配置灵活、性能稳定,老项目里出现频率很高。WatchDog.NET 的特点则是轻量、自带面板、HTTP 请求和异常日志开箱即用。
我整理了下面这张对比表,算是个人使用感受:
| 对比维度 | 内置 ILogger | Serilog | NLog | WatchDog.NET |
|---|---|---|---|---|
| 接入成本 | 最低 | 中等 | 中等 | 低 |
| 结构化日志 | 基础支持 | 非常强 | 较强 | 支持标准模板 |
| 可视化面板 | 无 | 需配套组件 | 需配套组件 | 自带 |
| HTTP 请求日志 | 需手写中间件 | 需第三方实现 | 需自己开发 | 自动记录 |
| 异常日志 | 需手写全局处理 | 需配置 Sink | 需配置 | 自动捕获 |
| 外部存储扩展 | 自己实现 | 丰富 | 丰富 | 可选外部数据库 |
| 许可证 | 内置 | 宽松 | BSD | MIT |
5.2 什么时候可以替换,什么时候要共存
如果你所在的项目已经有一套完整的日志平台,比如日志都统一写到 Elasticsearch,由 Kibana 或 Grafana 做可视化,那么 WatchDog.NET 不是必需的,硬加进去反而会造成日志多头管理。但如果你只是几个服务,不想为了看日志就引入一套采集和分析链路,那么 WatchDog.NET 可以在几分钟内给你一个靠谱的查看界面。
还有一种组合方式:保留 Serilog 作为主力日志管道,负责把日志采集到文件或 Kafka,同时利用 WatchDog.NET 的面板做开发联调时的实时查看。这个场景下两者可以共存,不冲突。不过我一般不推荐在生产环境同时把两份日志都完整落库,那会带来双倍的存储和写入开销。
5.3 什么时候不建议使用
有几种情况不建议用 WatchDog.NET。一是你已经有了严格的日志权限体系,要求每个角色只能看指定范围的日志,而你的部署规模又很大,这时候可能还是上专业日志平台更合适。二是你只是想把日志写进文件,完全不需要 UI,那多带一个面板反而多一个暴露面。三是微服务实例特别多时,每个实例各带一个面板,统一查询体验会很差,建议要嘛共享数据库,要嘛在更上层做日志聚合。
选型不是越强越好,而是越匹配越好。WatchDog.NET 在单体应用、小型系统、内部工具、开发联调环境这些场景下性价比很高,但把它硬塞进一个已经成型的大型日志架构中,意义并不大。
6. 实战中容易踩的坑和排查实录
6.1 中间件顺序不对导致日志缺失
中间件注册顺序直接影响日志捕获效果。UseWatchDogExceptionLogger()要尽早注册,最好放在其它自定义中间件之前,这样未被处理的异常才能被它截获。如果项目里还有自己的全局异常处理中间件,要注意两个中间件的先后顺序,否则异常可能在到达 WatchDog 之前就被转成了普通响应,面板里自然看不到。
我遇到过一种情况:某个服务把全局异常处理写在最前面,异常被它处理之后直接返回了友好提示,WatchDog 的异常中间件完全没机会看到原始异常。后来调整了顺序,让 WatchDog 异常中间件先执行,才看到真实堆栈。这里的关键是理解异常日志中间件的职责是“观察并记录”,而不是“处理并响应”。
6.2 面板打不开、404 或白屏
面板访问不了时,先确认应用是否处于运行状态,再检查app.UseWatchDog()是否真的执行到了。如果项目里配置了 URL 前缀、虚拟目录或者反向代理重写规则,面板的默认路径可能被改变。启动日志和控制台输出通常会有提示,顺着这个信息找最快。
白屏问题很多时候和静态资源路径有关。我的排查步骤一般是:先直接访问面板 URL 看返回什么状态码,再用浏览器开发者工具看是否有资源加载失败,最后确认 Web 项目是否启用了静态文件支持。UseStaticFiles()如果被项目特意关闭或顺序不对,面板的样式和脚本资源可能加载不出来。
6.3 数据库连接字符串写错
配置外部数据库时,连接字符串写错是一个典型问题。服务可能正常启动,HTTP 接口也正常,但你访问面板时一直转圈或报数据库连接失败。原因是日志组件往往是延迟写库的,配置错误不会在启动时立刻暴露。
我的建议是切换数据库之前,先用一个最小连接测试确认连接信息可用。另外在开发环境不要一上来就用外部库,先用默认嵌入模式跑通功能,再切换到外部存储,这样能把问题维度拆开。连接串里的账号权限也要注意,只给它日志库的读写权限就可以,不要给过高权限。
6.4 日志量过大导致接口变慢
记录请求体和响应体听起来很方便,但流量大的时候,每一个请求的参数和响应内容都要在内存里读取、序列化、再写入存储,性能开销肉眼可见。如果某个接口是文件上传或大报表导出,记录 Body 甚至会直接拖慢接口响应。
我在实际项目中就遇到过,一个日志量很大的服务接入前面板之后,接口平均耗时涨了不少。排查下来发现是记录了所有的上传请求体,几 MB 的文件内容被反复处理。后来的处理方式是关闭这些接口的 Body 记录,让中间件只记录路径、状态码和耗时,性能恢复到了正常水平。生产环境建议慎重开启 Body 记录,优先保证业务链路的性能。
6.5 敏感信息记录与脱敏
这一点单独拿出来说,是因为很容易被低估。如果你开着 RequestBody 和 ResponseBody 记录,登录密码、Token、身份证号等字段都有机会落到数据库。一旦日志库被未授权访问,后果比日志本身要严重得多。
我的处理原则是:默认不开 Body 记录,确有必要时只针对特定路径开启,并且在进入日志组件之前对字段做脱敏。脱敏逻辑可以放在业务层,也可以在你自己的中间件里提前把敏感字段替换掉。哪怕内部系统,也不建议原样保存明文密码和令牌。这个习惯养成之后,无论用哪一款日志组件,都不会踩到数据记录的坑。
6.6 多实例部署时日志分散
多个实例同时跑的情况下,如果每个实例都用本地默认存储,你会在两个不同的地方看到两份日志,排查问题时要记住自己在看哪一台机器。把存储切到外部共享数据库后,日志就能在同一个面板里出现。但是要注意,面板仍然部署在每个实例上,只是数据源统一了。如果实例多了,更合理的方式是在网关层统一入口,或者在日志记录的同时主动推送到集中式日志系统。
我们当时为了解决多实例查看问题,短期方案是把连接串改成共享数据库,长期方案还是把关键业务日志往统一的日志通道同步。这两个阶段并不冲突,先解决当下能不能看到日志,再考虑要不要建设更完整的链路。
6.7 注意包版本和 API 变化
开源组件迭代快,你参考的配置写法可能会因为版本变化而略有差异。我第一次接入时,部分配置字段名就和当时网上看到的不太一样。遇到这种情况不要慌,看一下包自带的文档和智能提示,基本上都能找到对应的新字段。这个问题在 .NET 生态里很常见,不能因为编译报错就断定组件不行。
7. 个人经验和使用建议
说了这么多,最后分享一点我个人的体会。日志组件选型不用追求大而全,但要把“看得见”放在第一位。很多团队花了不少精力在日志的采集和存储上,最后发现排查问题时最难的不是日志没存下来,而是根本看不到,或者看到了找不到重点。WatchDog.NET 对很多 ASP.NET Core 10 项目来说,正好补上了“看得见”这一块。它自带面板、MIT 开源、商业友好,用来作为项目的日志记录器是省心省事的选择。
再分享一个小技巧:新项目接入时,我一般会先把它当作唯一日志面板用几天,确认 HTTP 请求日志、异常日志和自定义日志能覆盖日常排查的主要场景,再决定要不要接外部日志平台。很多项目其实到了这一步已经够用了,不需要为了“显得专业”就去引入一套重平台。等你真的遇到跨服务链路追踪、海量日志分析这类问题,再建设集中式日志体系也不迟。先把每天的日志看得清清楚楚,比什么都重要。