带自定义属性的自定义异常日志序列化失败问题求助
解决自定义可序列化异常的GetObjectData未调用问题
首先,咱们先把自定义可序列化异常的正确实现姿势理清楚,这是解决问题的核心基础:
1. 正确编写带自定义属性的可序列化异常
你的异常类必须满足几个硬性要求,少一个都可能导致序列化逻辑不触发:
using System; using System.Runtime.Serialization; [Serializable] // 必须添加这个特性!这是.NET识别可序列化类型的标记 public class SerializableExceptionWithCustomProperties : Exception { // 自定义属性 public string CustomProperty { get; } // 常规构造函数 public SerializableExceptionWithCustomProperties(string message, string customProperty) : base(message) { CustomProperty = customProperty; } // 序列化构造函数:必须是protected(或public),签名严格匹配 protected SerializableExceptionWithCustomProperties(SerializationInfo info, StreamingContext context) : base(info, context) { // 从序列化信息中读取自定义属性 CustomProperty = info.GetString(nameof(CustomProperty)); } // 必须重写GetObjectData,且务必调用基类方法 public override void GetObjectData(SerializationInfo info, StreamingContext context) { if (info == null) throw new ArgumentNullException(nameof(info)); // 先写入自定义属性到序列化信息 info.AddValue(nameof(CustomProperty), CustomProperty); // 调用基类方法,确保异常的基础信息(Message、StackTrace等)被序列化 base.GetObjectData(info, context); } // 可选:重写ToString让自定义属性直接显示在日志文本中(这就是你之前看到的触发效果) public override string ToString() { return $"{base.ToString()}{Environment.NewLine}CustomProperty: {CustomProperty}"; } }
2. 为什么GetObjectData和序列化构造函数没被调用?
最常见的原因有两个:
原因一:日志框架默认不触发异常序列化逻辑
很多主流日志库(比如Serilog、NLog、log4net)默认只会捕获异常的Message、StackTrace、InnerException这些基础属性,不会主动调用GetObjectData来序列化自定义属性。
解决办法分两种:
- 手动在日志中附加自定义属性:记录日志时显式把异常的自定义字段加进去,比如用Serilog的写法:
try { throw new SerializableExceptionWithCustomProperties("操作失败", "自定义标识123"); } catch (SerializableExceptionWithCustomProperties ex) { Log.Error(ex, "发生业务异常,自定义属性:{CustomProperty}", ex.CustomProperty); } - 配置日志框架深度解构异常:以Serilog为例,通过配置让框架自动解析异常的自定义属性:
Log.Logger = new LoggerConfiguration() .Destructure.ByTransforming<SerializableExceptionWithCustomProperties>(ex => new { ex.Message, ex.StackTrace, ex.CustomProperty }) .WriteTo.Console() .CreateLogger();
原因二:异常类的序列化实现不规范
比如:
- 遗漏了
[Serializable]特性:没有这个标记,.NET序列化框架完全不会识别这个类是可序列化的。 - 序列化构造函数访问修饰符错误:必须是
protected(方便子类继承调用)或public,如果设为private,序列化框架无法调用它。 - 未调用
base.GetObjectData(info, context):基类方法负责处理异常基础信息的序列化,也是触发整个序列化流程的关键步骤。
3. 区分ToString()和真正的序列化
你之前添加ToString()能看到自定义属性,本质是日志框架输出异常时调用了这个方法做字符串格式化,这不是真正的序列化。如果需要把异常序列化后存储(比如存数据库、缓存)或跨进程传输,得用.NET的序列化机制,比如用Newtonsoft.Json来序列化:
// 先安装Newtonsoft.Json包 var ex = new SerializableExceptionWithCustomProperties("操作失败", "自定义标识123"); var json = JsonConvert.SerializeObject(ex, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, Formatting = Formatting.Indented }); Console.WriteLine(json);
这样序列化后的JSON会包含你的自定义属性,因为Newtonsoft.Json会反射异常的所有公开属性,同时也会触发必要的序列化逻辑。
内容的提问来源于stack exchange,提问作者LP13
相关产品推荐
相关产品推荐

