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

带自定义属性的自定义异常日志序列化失败问题求助

解决自定义可序列化异常的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:58:35