You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在运行时切换Serilog的WriteTo与AuditTo日志写入方式?

解决方案思路与实现

1. 核心思路:自定义动态切换Sink包装器

Serilog的AuditTo和WriteTo本质对应不同的Sink执行逻辑——前者同步写入、失败抛异常,后者异步批量、静默容错。要实现运行时切换,需要一个中间包装Sink,内部维护两种模式的Sink实例,通过开关动态路由日志写入逻辑。

2. 实现动态切换Sink

public class DynamicSwitchSink : ILogEventSink
{
    private readonly ILogEventSink _auditSink;
    private readonly ILogEventSink _writeToSink;
    private bool _useAuditMode;
    private readonly object _lockObj = new();

    public DynamicSwitchSink(ILogEventSink auditSink, ILogEventSink writeToSink, bool initialUseAuditMode = true)
    {
        _auditSink = auditSink ?? throw new ArgumentNullException(nameof(auditSink));
        _writeToSink = writeToSink ?? throw new ArgumentNullException(nameof(writeToSink));
        _useAuditMode = initialUseAuditMode;
    }

    // 线程安全的模式切换方法
    public void ToggleAuditMode(bool useAuditMode)
    {
        lock (_lockObj)
        {
            _useAuditMode = useAuditMode;
        }
    }

    public void Emit(LogEvent logEvent)
    {
        lock (_lockObj)
        {
            if (_useAuditMode)
            {
                // 严格遵循AuditTo逻辑:同步写入,失败直接抛出异常
                _auditSink.Emit(logEvent);
            }
            else
            {
                // 遵循WriteTo逻辑:异步批量写入,不抛出异常
                _writeToSink.Emit(logEvent);
            }
        }
    }
}

3. 实现SQLite的Audit模式Sink

由于官方SQLite Sink不支持AuditTo,需要自定义一个同步写入、失败抛异常的Audit版Sink:

public class SqliteAuditSink : ILogEventSink
{
    private readonly string _connectionString;
    private readonly IFormatProvider _formatProvider;

    public SqliteAuditSink(string connectionString, IFormatProvider formatProvider)
    {
        _connectionString = connectionString;
        _formatProvider = formatProvider;
        EnsureTableExists();
    }

    private void EnsureTableExists()
    {
        using var conn = new SQLiteConnection(_connectionString);
        conn.Open();
        conn.Execute(@"
            CREATE TABLE IF NOT EXISTS AuditLogs (
                Id INTEGER PRIMARY KEY AUTOINCREMENT,
                Timestamp DATETIME NOT NULL,
                Level TEXT NOT NULL,
                Message TEXT NOT NULL,
                MessageTemplate TEXT,
                Properties TEXT,
                Exception TEXT
            )");
    }

    public void Emit(LogEvent logEvent)
    {
        // 同步写入,失败直接抛出SQLite异常,符合AuditTo的可靠性要求
        using var conn = new SQLiteConnection(_connectionString);
        conn.Open();
        var props = JsonConvert.SerializeObject(logEvent.Properties);
        conn.Execute(@"
            INSERT INTO AuditLogs (Timestamp, Level, Message, MessageTemplate, Properties, Exception)
            VALUES (@Timestamp, @Level, @Message, @MessageTemplate, @Properties, @Exception)",
            new
            {
                Timestamp = logEvent.Timestamp,
                Level = logEvent.Level.ToString(),
                Message = logEvent.RenderMessage(_formatProvider),
                MessageTemplate = logEvent.MessageTemplate.Text,
                Properties = props,
                Exception = logEvent.Exception?.ToString()
            });
    }
}

4. 集成Serilog并实现运行时切换

初始化Serilog时注册自定义Sink,保留切换开关的引用即可在运行时随时切换模式:

// 初始化两种模式的SQLite Sink
var sqliteAuditSink = new SqliteAuditSink("Data Source=audit.db", CultureInfo.InvariantCulture);
var sqliteWriteToSink = new AsyncSink(
    new SqliteSink("Data Source=audit.db", new SqliteSinkOptions()),
    new AsyncSinkOptions { QueueLimit = 1000, BatchSizeLimit = 100 });

// 创建动态切换Sink
var dynamicSink = new DynamicSwitchSink(sqliteAuditSink, sqliteWriteToSink);

// 配置Serilog
Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Information()
    .WriteTo.Sink(dynamicSink)
    .CreateLogger();

// 运行时切换示例(可通过API、配置中心等触发)
// 切换到WriteTo模式(异步容错)
dynamicSink.ToggleAuditMode(false);
// 切回Audit模式(同步强一致)
dynamicSink.ToggleAuditMode(true);

5. 关键注意事项

  • 线程安全:切换逻辑和日志写入逻辑加锁,避免并发切换时出现数据不一致
  • 异常处理:Audit模式下写入失败会抛出异常,业务代码需按需捕获处理(符合审计数据不丢的设计目标)
  • 性能平衡:WriteTo模式用AsyncSink包装,保证异步批量写入不阻塞业务线程
  • 配置持久化:可将当前模式存入配置中心,应用启动时读取初始值,运行时监听配置变化自动切换

内容的提问来源于stack exchange,提问作者kliszaq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 00:35:17