构造函数注入 ilogger 是唯一可靠方式,必须通过 di 容器提供,禁用 new 实例;结构化日志需用 {fieldname} 占位符传参,禁止字符串拼接;生产环境须确保 addconsole() 或第三方框架(如 serilog)正确注册。

别自己 new ILogger,也别在静态上下文里硬造实例——90% 的日志失效、请求 ID 丢失、级别不生效问题都源于此。
构造函数注入 ILogger 是唯一可靠方式
ASP.NET Core 的 ILogger 必须通过依赖注入容器提供,不是靠 new 出来的。手动调用 new LoggerFactory().CreateLogger<t>()</t> 会绕过所有配置:日志级别从 appsettings.json 失效、RequestId 和 TraceId 不自动注入、不同服务无法统一调级。
- 控制器中直接写:
public HomeController(ILogger<homecontroller> logger)</homecontroller> - 普通服务类(如
OrderService)注册进 DI 后,同样可接收ILogger<orderservice></orderservice> - 若服务未注册(比如裸类),先加
services.AddScoped<orderservice>()</orderservice>,否则构造函数参数根本不会被解析
LogInformation("xxx", arg) 不是字符串拼接,{FieldName} 才是结构化关键
占位符 {UserId} 不是语法糖,而是日志引擎识别结构化字段的标记。传参顺序必须与占位符一一对应,值会被单独提取为 JSON 字段,支持 Kibana/Seq 按字段过滤。
- 错误写法:
logger.LogInformation("User " + userId + " failed login")→ 纯文本,userId不可索引 - 正确写法:
logger.LogInformation("Login failed for user {UserId}", userId) - 多个参数:
logger.LogWarning("Order {OrderId} failed after {RetryCount} retries", orderId, retryCount) - 禁止在占位符里写表达式:
{user.Name}不会求值,应拆成{UserName}并传user.Name
本地能看到日志,但 IIS/Linux 容器里“消失”?大概率输出目标错了
默认只启用 Console 和 Debug 提供程序,而 IIS 会截断 Console 输出,Linux 容器中 stdout 才是日志采集入口(fluentd/loki 依赖它),写文件路径在容器里基本等于丢日志。
- 生产环境必须确保
builder.Logging.AddConsole()被调用(哪怕只是保留它) - 若要用文件,路径不能硬编码:
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "logs"),且提前Directory.CreateDirectory() - Docker 部署时,检查是否漏了
builder.Host.UseNLog()或builder.Host.UseSerilog()—— 第三方日志框架若没在Use...()中注册,会静默 fallback 到空配置 - 捕获
UnauthorizedAccessException并 fallback 到Console.WriteLine,比静默丢日志强
日志级别没生效?先查配置作用域和加载时机
appsettings.json 中的 "Logging": { "LogLevel": { ... } } 只对 DI 注入的 ILogger 生效,且匹配规则是「前缀最长优先」。比如配置了 "MyApp.Services.OrderService",那 ILogger<orderservice></orderservice>(全名是 MyApp.Services.OrderService)才受控;而 ILogger<homecontroller></homecontroller> 若没单独配,就走 "Default" 级别。
- 配置必须在
builder.Services.AddLogging()之后、builder.Build()之前完成 - 第三方日志(如 Serilog/NLog)若覆盖了内置配置,
LogLevel设置可能被忽略——确认是否调用了builder.Host.UseSerilog()而非仅AddSerilog() - 调试时可用
logger.IsEnabled(LogLevel.Debug)直接验证当前级别是否开启
最常被忽略的一点:日志提供程序的初始化顺序比内容更重要。没在 Use...() 中注册,或配置文件没设「始终复制」,都会导致日志静默失败——它不报错,只是不输出。











