SafeSerializer.cs 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. using System;
  2. using System.Collections;
  3. using Newtonsoft.Json;
  4. namespace Aivfo.OperationLog
  5. {
  6. /// <summary>
  7. /// 安全序列化 input/output(14§7 序列化兜底):
  8. /// - 全程 try,绝不抛异常拖垮业务;
  9. /// - 循环引用忽略;
  10. /// - 大对象 / 字节数组 / 流 只记类型名+长度,不记内容。
  11. /// </summary>
  12. internal static class SafeSerializer
  13. {
  14. private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
  15. {
  16. ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
  17. NullValueHandling = NullValueHandling.Ignore,
  18. MaxDepth = 8
  19. };
  20. // 超过此长度的字符串只保留摘要,避免大对象冲垮日志。
  21. private const int MaxJsonLength = 4000;
  22. public static string Serialize(object value)
  23. {
  24. if (value == null) return null;
  25. try
  26. {
  27. // 字符串直接用(但截断)。
  28. if (value is string s) return Cap(s);
  29. // 大对象 / 二进制:只记类型名 + 长度,不记内容。
  30. if (value is byte[] bytes)
  31. return $"{{\"_type\":\"byte[]\",\"length\":{bytes.Length}}}";
  32. if (value is System.IO.Stream stream)
  33. return $"{{\"_type\":\"Stream\",\"canRead\":{stream.CanRead.ToString().ToLowerInvariant()}}}";
  34. var json = JsonConvert.SerializeObject(value, Settings);
  35. return Cap(json);
  36. }
  37. catch (Exception ex)
  38. {
  39. // 序列化失败:退而记类型名 + 异常,绝不抛出。
  40. try
  41. {
  42. var typeName = value.GetType().FullName;
  43. if (value is ICollection col)
  44. return $"{{\"_type\":\"{typeName}\",\"count\":{col.Count},\"_serErr\":\"{Escape(ex.Message)}\"}}";
  45. return $"{{\"_type\":\"{typeName}\",\"_serErr\":\"{Escape(ex.Message)}\"}}";
  46. }
  47. catch
  48. {
  49. return "{\"_serErr\":\"unserializable\"}";
  50. }
  51. }
  52. }
  53. private static string Cap(string json)
  54. {
  55. if (string.IsNullOrEmpty(json) || json.Length <= MaxJsonLength) return json;
  56. return json.Substring(0, MaxJsonLength) + $"...(truncated,len={json.Length})";
  57. }
  58. private static string Escape(string s)
  59. {
  60. return (s ?? string.Empty).Replace("\\", "\\\\").Replace("\"", "\\\"");
  61. }
  62. }
  63. }