.NET Core 配置系统与日志系统整理

8 阅读8分钟

.NET Core 配置系统与日志系统整理

本文档整理了.NET Core中配置系统日志系统的核心使用方法、关键包、实现步骤及高级特性,涵盖基础操作、进阶用法和第三方组件集成,是.NET Core开发中配置与日志管理的核心参考。

一、配置系统

.NET Core配置系统摒弃了传统Web.config,支持多源配置(JSON/XML/INI、环境变量、命令行等),提供直接读取对象绑定选项方式(推荐) 三种读取方式,核心依赖Microsoft.Extensions.Configuration系列包。

1. 核心基础准备

(1)必备NuGet包
功能包名
配置系统核心Microsoft.Extensions.Configuration
读取JSON配置文件Microsoft.Extensions.Configuration.Json
配置绑定到实体类Microsoft.Extensions.Configuration.Binder
选项方式+依赖注入Microsoft.Extensions.Options + Microsoft.Extensions.DependencyInjection
(2)配置文件基础设置
  1. 项目根目录添加配置文件(如config.json/appsettings.json);
  2. 右键文件→属性复制到输出目录设置为如果较新则复制
  3. 运行时读取exe同级目录的配置文件,修改后需重新生成项目。

2. 三种读取配置的方式

(1)直接读取(IConfigurationRoot)

核心:通过ConfigurationBuilder构建配置根对象,直接通过键名/冒号分隔的分级键读取,支持数据库连接字符串专用方法。

// 1. 构建配置
var configBuilder = new ConfigurationBuilder();
configBuilder.AddJsonFile("config.json", optional: false, reloadOnChange: true);
IConfigurationRoot config = configBuilder.Build();

// 2. 读取普通配置
string name = config["name"]!;
string proxyAddr = config.GetSection("proxy:address").Value!;

// 3. 读取数据库连接字符串(专用节点ConnectionStrings)
string connStr = config.GetConnectionString("str");
  • optional: false:配置文件不存在时程序报错,便于排查路径/文件名错误;
  • reloadOnChange: true:配置文件修改后自动重新加载。
(2)绑定方式读取

核心:将配置节点自动映射到自定义实体类,避免逐个读取键值,适用于配置项较多的场景。

  1. 定义属性名与配置节点完全一致的实体类;
  2. 通过GetSection(节点名).Get<实体类>()config.Get<根实体类>()完成绑定。
// 定义实体类(属性名与配置一致,类名随意)
public class Proxy { public string? address { get; set; } public int port { get; set; } }
public class ConStr { public string? str { get; set; } }
public class Config {
    public string? name { get; set; }
    public Proxy? proxy { get; set; }
    public ConStr? ConnectionStrings { get; set; }
}

// 绑定并读取
Config c = config.Get<Config>()!;
Console.WriteLine(c.name + c.proxy?.address + c.ConnectionStrings!.str);
(3)选项方式读取(推荐)

核心:结合依赖注入(DI)配置自动刷新,是.NET Core官方推荐方式,封装了绑定方式,支持三种泛型接口处理配置生命周期。

① 三个核心泛型接口区别
接口配置刷新特性适用场景
IOptions不支持刷新,需重启程序启动后不再修改的配置(资源占用最少)
IOptionsMonitor实时刷新,同一范围可读取新值无需保证范围配置一致性的场景(易引发业务混乱)
IOptionsSnapshot范围内一致,新范围读取新值大部分业务场景(推荐,保证单次请求/方法内配置一致)

示例:单次方法中配置修改,IOptionsMonitor会导致读写用不同配置,IOptionsSnapshot则保持一致,避免业务异常。

② 实现步骤
  1. 定义配置实体类(如DBSettings/SmtpSettings),匹配appsettings.json节点;
  2. 构建配置根对象,配置DI容器,注册选项和自定义类;
  3. 通过构造函数注入泛型接口,读取配置值;
  4. 新范围读取新配置:通过CreateScope()创建新DI范围,实现配置刷新。
// 1. 定义实体类
public class DBSettings { public string? DbType { get; set; } public string? ConnectionString { get; set; } }
public class SmtpSettings { public string? Server { get; set; } public string? UserName { get; set; } public string? Password { get; set; } }

// 2. 自定义类,构造函数注入IOptionsSnapshot<T>
public class ConfigDemo {
    private readonly IOptionsSnapshot<DBSettings> _optDb;
    private readonly IOptionsSnapshot<SmtpSettings> _optSmtp;
    public ConfigDemo(IOptionsSnapshot<DBSettings> optDb, IOptionsSnapshot<SmtpSettings> optSmtp) {
        _optDb = optDb; _optSmtp = optSmtp;
    }
    public void Run() {
        Console.WriteLine(_optDb.Value.DbType + _optDb.Value.ConnectionString);
        Console.WriteLine(_optSmtp.Value.Server + _optSmtp.Value.UserName);
    }
}

// 3. 主程序配置DI并调用
var configBuilder = new ConfigurationBuilder();
configBuilder.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
IConfigurationRoot config = configBuilder.Build();

// 注册DI服务
var services = new ServiceCollection();
services.AddOptions()
        .Configure<DBSettings>(c => config.GetSection("DB").Bind(c))
        .Configure<SmtpSettings>(s => config.GetSection("Smtp").Bind(s));
services.AddScoped<ConfigDemo>();

// 创建容器,循环读取(新范围刷新配置)
using (var sp = services.BuildServiceProvider()) {
    while (true) {
        using (var scope = sp.CreateScope()) { // 新范围
            var demo = scope.ServiceProvider.GetRequiredService<ConfigDemo>();
            demo.Run();
        }
        Console.ReadKey();
    }
}

二、日志系统

日志是程序的“黑匣子”,.NET Core提供内置日志框架,支持控制台输出,可集成NLog/Log4Net/Serilog等第三方组件实现日志持久化(文件/数据库/邮件),核心依赖Microsoft.Extensions.Logging系列包。

1. 核心基础

(1)日志级别(从低到高)
级别描述适用场景
Trace最详细信息,含敏感数据开发调试(生产环境禁用)
Debug开发调试短期有用信息开发阶段排查问题
Information程序常规流程跟踪记录正常操作(如“开始连接数据库”)
Warning潜在问题,未影响运行记录重试操作(如“第一次数据库连接失败”)
Error错误,功能无法正常执行记录操作失败(如“数据库连接失败”)
Critical致命错误,系统即将崩溃记录严重故障(如“服务器磁盘满”)

使用原则:按操作结果分级,如数据库连接→3次重试:Information(开始)→Warning(重试)→Error(失败)。

(2)必备NuGet包
功能包名
日志系统核心Microsoft.Extensions.Logging
控制台输出日志Microsoft.Extensions.Logging.Console
集成NLogNLog.Extensions.Logging

2. 内置日志(控制台输出)

核心:通过DI注入日志服务,使用泛型ILogger(T为当前类,便于定位日志来源),不支持非泛型ILogger注入。

实现步骤
  1. 安装核心包,配置DI容器,注册日志服务并指定输出到控制台;
  2. 构造函数注入ILogger<T>,调用日志方法输出不同级别信息;
  3. 可选:设置日志最小级别,过滤低级别日志。
// 1. 配置DI
var services = new ServiceCollection();
services.AddLogging(logBuilder => {
    logBuilder.AddConsole(); // 输出到控制台
    logBuilder.SetMinimumLevel(LogLevel.Error); // 只输出Error及以上级别
});

// 2. 自定义类注入日志
public class TestLog {
    private readonly ILogger<TestLog> _logger;
    public TestLog(ILogger<TestLog> logger) { _logger = logger; }
    public void ConnectSql() {
        _logger.LogInformation("开始连接数据库");
        _logger.LogWarning("第一次连接失败");
        _logger.LogError("数据库连接失败");
        // 占位符+异常记录
        try { int.Parse("abc"); }
        catch (Exception ex) { _logger.LogError(ex, "类型转换失败"); }
    }
}

// 3. 注册并调用
services.AddScoped<TestLog>();
using (var sp = services.BuildServiceProvider()) {
    var testLog = sp.GetRequiredService<TestLog>();
    testLog.ConnectSql();
}

3. 第三方日志NLog(文件+控制台,推荐)

内置日志仅控制台输出,无持久化,NLog是轻量、强大的第三方日志组件,支持日志分类、文件分割、级别过滤,可输出到文件/控制台/数据库/邮件等。

核心优势
  • 日志持久化到文件,支持按日期/大小分割;
  • 细粒度日志分类(按模块/级别分文件);
  • 配置文件自动重载,无需重启程序;
  • 限制日志文件数量和大小,防止磁盘占满。

4. NLog基本使用

实现步骤
  1. 安装NuGet包:Microsoft.Extensions.Logging + NLog.Extensions.Logging
  2. 项目根目录添加nlog.config配置文件,设置复制到输出目录=如果较新则复制
  3. 配置DI容器,替换日志输出为NLog;
  4. 运行程序,日志自动输出到exe同级目录的文件和控制台。
核心nlog.config配置(基础版)
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xsi:schemaLocation="NLog NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true" <!-- 配置修改自动重载 -->
      internalLogLevel="off" <!-- 关闭NLog自身日志 -->
      internalLogFile="nlog-internal.log">

  <!-- 日志输出目标:文件+控制台 -->
  <targets>
    <!-- 文件输出:按日期命名,指定日志格式 -->
    <target xsi:type="File" name="logfile" 
            fileName="logs/log-${shortdate}.log"
            layout="${longdate}|${level}|${message}|${exception:format=tostring}" />
    <!-- 控制台输出 -->
    <target xsi:type="Console" name="logconsole" 
            layout="${longdate}|${level}|${message}|${exception:format=tostring}" />
  </targets>

  <!-- 日志输出规则:所有日志输出到文件+控制台,最低级别Trace -->
  <rules>
    <logger name="*" minlevel="Trace" writeTo="logfile,logconsole" />
  </rules>
</nlog>
主程序代码
var services = new ServiceCollection();
services.AddLogging(logBuilder => logBuilder.AddNLog()); // 替换为NLog
services.AddScoped<TestLog>();
using (var sp = services.BuildServiceProvider()) {
    var testLog = sp.GetRequiredService<TestLog>();
    testLog.ConnectSql();
}

5. NLog高级特性(分类+过滤+文件限制)

(1)核心特性
  • 日志分类:按命名空间/模块分文件(如登录模块、文件模块各一个日志文件);
  • 日志过滤:按级别/模块设置输出规则,支持final=true(匹配后不再执行后续规则);
  • 文件限制archiveAboveSize(单个文件最大字节)、maxArchiveFiles(最大文件数),自动删除旧文件。
(2)高级配置示例(nlog.config)
<nlog autoReload="true" internalLogLevel="off">
  <targets>
    <!-- 通用日志:TestLog类(ConsoleApp命名空间),按日期分文件 -->
    <target xsi:type="File" name="logfile" 
            fileName="logs/log-${shortdate}.log"
            layout="${longdate}|${level}|${message}|${exception}" />
    <!-- 系统文件模块日志:SystemFile命名空间,限制单个10KB,最多3个文件 -->
    <target xsi:type="File" name="sysFileLog" 
            fileName="logs/sysFile-${shortdate}.log"
            archiveAboveSize="10240" <!-- 10KB -->
            maxArchiveFiles="3" <!-- 最多3个 -->
            layout="${longdate}|${level}|${message}|${exception}" />
    <!-- 控制台输出:只显示Warn及以上级别 -->
    <target xsi:type="Console" name="logconsole" layout="${longdate}|${level}|${message}" />
  </targets>

  <rules>
    <!-- 规则1:所有模块,Warn-Fatal级别输出到控制台 -->
    <logger name="*" minlevel="Warn" maxlevel="Fatal" writeTo="logconsole" />
    <!-- 规则2:SystemFile命名空间,所有级别输出到sysFileLog,final=true不再匹配后续 -->
    <logger name="SystemFile.*" minlevel="Trace" writeTo="sysFileLog" final="true" />
    <!-- 规则3:其他所有模块,所有级别输出到logfile -->
    <logger name="*" minlevel="Trace" writeTo="logfile" />
  </rules>
</nlog>
(3)使用注意
  • 若使用NLog的分级/过滤,注释掉.NET内置的SetMinimumLevel,避免冲突;
  • 命名空间作为分类标识,不同模块放在不同命名空间,便于日志隔离;
  • 日志文件路径为exe同级目录,可通过绝对路径指定自定义目录。

6. NLog扩展

NLog支持更多输出目标,可通过官方配置文档扩展:

  • 输出到数据库(SQL Server/MySQL);
  • 输出到邮件(异常时发送告警邮件);
  • 输出到日志服务器(如ELK);
  • 自定义日志格式(添加自定义字段、请求ID等)。 NLog官方配置文档nlog-project.org/config/

三、核心总结

1. 配置系统

  1. 推荐使用选项方式(IOptionsSnapshot),结合DI和配置自动刷新,适配大部分业务场景;
  2. 配置文件必须设置复制到输出目录,运行时读取exe同级目录文件;
  3. 数据库连接字符串统一放在ConnectionStrings节点,通过GetConnectionString读取。

2. 日志系统

  1. 开发阶段用内置日志控制台输出,生产环境用NLog实现持久化;
  2. 日志必须按级别输出,泛型ILogger<T>便于定位日志来源;
  3. NLog核心配置:targets(输出目标)+rules(输出规则),支持按模块/级别分文件,限制文件大小和数量。

3. 通用原则

  • 所有配置和日志服务均通过依赖注入注册和使用,符合.NET Core设计思想;
  • 第三方组件(NLog)的配置文件需设置自动重载复制到输出目录
  • 生产环境禁用Trace/Debug级别日志,减少磁盘占用和敏感信息泄露。