.NET 6 WebApi中如何运行时动态增减Serilog日志级别覆盖?
在.NET 6 WebApi中用Serilog实现运行时动态调整日志级别覆盖
可行,以下是具体实现指导:
核心思路
Serilog本身无内置动态配置能力,但可通过自定义LoggingLevelSwitch结合可修改的级别规则集合,配合Serilog过滤器实现运行时添加/移除命名空间的日志级别覆盖。
具体实现步骤
1. 准备依赖与基础配置
确保项目安装必要的Serilog包:
Install-Package Serilog.AspNetCore Install-Package Serilog.Settings.Configuration Install-Package Serilog.Expressions
在Program.cs中初始化Serilog时,引入可动态修改的级别开关集合:
// 线程安全集合,存储命名空间与对应级别开关 var dynamicLevelSwitches = new ConcurrentDictionary<string, LoggingLevelSwitch>(); var configuration = new ConfigurationBuilder() .AddJsonFile("appsettings.json") .Build(); // 初始化Serilog,添加动态过滤规则 Log.Logger = new LoggerConfiguration() .ReadFrom.Configuration(configuration) .Filter.ByIncludingOnly(logEvent => { var sourceContext = logEvent.Properties.TryGetValue("SourceContext", out var value) ? value.ToString().Trim('"') : string.Empty; // 匹配动态规则中的命名空间,应用对应级别 foreach (var (ns, switcher) in dynamicLevelSwitches) { if (sourceContext.StartsWith(ns)) { return logEvent.Level >= switcher.MinimumLevel; } } // 无匹配规则时使用默认配置 return true; }) .CreateLogger(); var builder = WebApplication.CreateBuilder(args); builder.Host.UseSerilog();
2. 实现动态调整的API接口
添加控制器提供外部调用入口,用于管理日志级别规则:
[ApiController] [Route("api/logging")] public class LoggingController : ControllerBase { private readonly ConcurrentDictionary<string, LoggingLevelSwitch> _dynamicLevelSwitches; private readonly ILogger<LoggingController> _logger; public LoggingController(ConcurrentDictionary<string, LoggingLevelSwitch> dynamicLevelSwitches, ILogger<LoggingController> logger) { _dynamicLevelSwitches = dynamicLevelSwitches; _logger = logger; } // 添加/更新命名空间日志级别 [HttpPost("set-level")] public IActionResult SetNamespaceLevel([FromBody] LevelConfig config) { if (string.IsNullOrWhiteSpace(config.Namespace)) return BadRequest("命名空间不能为空"); if (!Enum.TryParse<LogEventLevel>(config.Level, true, out var targetLevel)) return BadRequest("无效的日志级别"); _dynamicLevelSwitches.AddOrUpdate( config.Namespace, _ => new LoggingLevelSwitch(targetLevel), (_, switcher) => { switcher.MinimumLevel = targetLevel; return switcher; } ); _logger.Information("命名空间 {Namespace} 日志级别已设为 {Level}", config.Namespace, targetLevel); return Ok(); } // 移除命名空间的级别覆盖 [HttpDelete("remove-level/{namespace}")] public IActionResult RemoveNamespaceLevel(string @namespace) { if (_dynamicLevelSwitches.TryRemove(@namespace, out _)) { _logger.Information("已移除命名空间 {Namespace} 的日志级别覆盖", @namespace); return Ok(); } return NotFound("未找到对应命名空间的级别规则"); } // 查询当前所有动态级别规则 [HttpGet("current-levels")] public IActionResult GetCurrentLevels() { var result = _dynamicLevelSwitches.ToDictionary( kv => kv.Key, kv => kv.Value.MinimumLevel.ToString() ); return Ok(result); } } // 请求模型 public class LevelConfig { public string Namespace { get; set; } public string Level { get; set; } // 可选值:Debug/Information/Warning/Error/Fatal }
3. 可选优化:规则持久化
若需重启后保留动态规则,可将配置存储到数据库或分布式配置中心,在项目启动时加载:
// Program.cs中启动时加载持久化规则 var persistedRules = /* 从数据库/配置中心读取规则 */; foreach (var rule in persistedRules) { if (Enum.TryParse<LogEventLevel>(rule.Level, true, out var level)) { dynamicLevelSwitches.TryAdd(rule.Namespace, new LoggingLevelSwitch(level)); } }
4. 注意事项
- 权限控制:动态调整API必须添加身份验证(如JWT),避免未授权访问
- 性能:若规则数量较多,可自定义
ILogEventFilter实现更高效的匹配逻辑 - 线程安全:必须使用线程安全集合(如
ConcurrentDictionary)维护规则
内容的提问来源于stack exchange,提问作者askids
相关产品推荐
相关产品推荐

