如何捕获ASP.NET Core中Serilog MQTT Sink抛出的异常?
解决Serilog MQTT Sink异常捕获问题
方案1:包装MQTT Sink做异常隔离
自定义一个Sink包装类,把原MQTT Sink包裹起来,在内部捕获异常,避免异常扩散到应用层级:
public class SafeMqttSinkWrapper : ILogEventSink { private readonly ILogEventSink _innerMqttSink; public SafeMqttSinkWrapper(ILogEventSink innerSink) { _innerMqttSink = innerSink; } public void Emit(LogEvent logEvent) { try { _innerMqttSink.Emit(logEvent); } catch (Exception ex) { // 在这里处理异常,比如写入本地文件、输出控制台,注意别调用Serilog日志避免循环 Console.WriteLine($"MQTT日志发送失败: {ex.Message}"); // 可选:添加重试逻辑或标记Broker不可用状态 } } }
修改Serilog配置,用包装类替换原MQTT Sink:
builder.Host.UseSerilog((ctx, lc) => lc .WriteTo.Console() .WriteTo.Sink(new SafeMqttSinkWrapper( new MqttSink(publisherOptions, "top/log", new RenderedCompactJsonFormatter()))));
方案2:修复原Sink的async void反模式
原Sink的Emit用了async void,这会导致异步异常无法被上层捕获。可以重写Sink的异步逻辑,改用async Task并内部处理异常:
public class AsyncSafeMqttSink : ILogEventSink { private readonly MqttPublisherOptions _publisherOptions; private readonly string _topic; private readonly ITextFormatter _formatter; private readonly IMqttClient _mqttClient; // 复制原MQTT Sink的构造逻辑,初始化_mqttClient等依赖 public AsyncSafeMqttSink(MqttPublisherOptions options, string topic, ITextFormatter formatter) { _publisherOptions = options; _topic = topic; _formatter = formatter; // 这里初始化_mqttClient,和原Sink逻辑一致 } private async Task EmitLogEventAsync(LogEvent logEvent) { try { var stringWriter = new StringWriter(); _formatter.Format(logEvent, stringWriter); var applicationMessage = new MqttApplicationMessageBuilder() .WithTopic(_topic) .WithPayload(stringWriter.ToString()) .Build(); await _mqttClient.PublishAsync(applicationMessage); } catch (Exception ex) { Console.WriteLine($"MQTT发布异常: {ex}"); // 可添加重试、Broker状态标记等逻辑 } } // 实现ILogEventSink的同步方法,转调异步逻辑 public void Emit(LogEvent logEvent) { _ = EmitLogEventAsync(logEvent); // 用弃元操作避免编译器警告 } }
配置时直接使用这个自定义Sink:
builder.Host.UseSerilog((ctx, lc) => lc .WriteTo.Console() .WriteTo.Sink(new AsyncSafeMqttSink(publisherOptions, "top/log", new RenderedCompactJsonFormatter())));
方案3:用Serilog全局SelfLog捕获内部错误
如果不想修改Sink代码,可以启用Serilog的SelfLog,它会捕获Serilog自身处理日志时的所有异常:
// 在Program.cs启动初期配置 Serilog.Debugging.SelfLog.Enable(msg => { Console.WriteLine($"Serilog内部错误: {msg}"); // 也可以写入本地文件 // File.AppendAllText("serilog-error.log", $"{DateTime.Now}: {msg}\n"); });
这种方式快速简单,但只能记录异常信息,无法做重试等复杂处理。
内容的提问来源于stack exchange,提问作者pyrogen
相关产品推荐
相关产品推荐

