trace_id.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. """
  2. Trace ID 生成和解析工具
  3. 提供 Trace ID 的生成、解析等功能。
  4. Trace ID 格式:
  5. - 主 Trace: {uuid} (标准 UUID)
  6. - Sub-Trace: {parent_id}@{mode}-{timestamp}-{seq}
  7. 例如: 2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001
  8. """
  9. import uuid
  10. from datetime import datetime
  11. from threading import Lock
  12. from typing import Dict, Optional
  13. # 全局计数器(线程安全)
  14. _seq_lock = Lock()
  15. _seq_counter: Dict[str, int] = {} # key: "{parent_id}@{mode}-{timestamp}"
  16. def generate_trace_id() -> str:
  17. """
  18. 生成主 Trace ID
  19. Returns:
  20. 标准 UUID 字符串
  21. """
  22. return str(uuid.uuid4())
  23. def generate_sub_trace_id(parent_id: str, mode: str) -> str:
  24. """
  25. 生成 Sub-Trace ID
  26. 格式: {parent_id}@{mode}-{timestamp}-{seq}
  27. 使用完整的 parent_id(不截断),避免 ID 冲突风险。
  28. 同一秒内多次调用会递增序号。
  29. Args:
  30. parent_id: 父 Trace ID(完整 UUID)
  31. mode: 运行模式(explore, delegate, compaction 等)
  32. Returns:
  33. Sub-Trace ID
  34. Examples:
  35. >>> generate_sub_trace_id("2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d", "explore")
  36. '2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001'
  37. >>> generate_sub_trace_id("2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d", "delegate")
  38. '2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@delegate-20260204220030-001'
  39. """
  40. # 直接使用完整 UUID,不截断
  41. timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
  42. # 生成序号(同一秒内递增)
  43. prefix = f"{parent_id}@{mode}-{timestamp}"
  44. with _seq_lock:
  45. seq = _seq_counter.get(prefix, 0) + 1
  46. _seq_counter[prefix] = seq
  47. return f"{prefix}-{seq:03d}"
  48. def parse_parent_trace_id(trace_id: str) -> Optional[str]:
  49. """
  50. 从 trace_id 解析出 parent_trace_id
  51. Args:
  52. trace_id: Trace ID
  53. Returns:
  54. 父 Trace ID,如果是主 Trace 则返回 None
  55. Examples:
  56. >>> parse_parent_trace_id("2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001")
  57. '2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d'
  58. >>> parse_parent_trace_id("2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d")
  59. None
  60. """
  61. if '@' in trace_id:
  62. return trace_id.split('@')[0]
  63. return None
  64. def is_sub_trace(trace_id: str) -> bool:
  65. """
  66. 判断是否为 Sub-Trace
  67. Args:
  68. trace_id: Trace ID
  69. Returns:
  70. True 表示是 Sub-Trace,False 表示是主 Trace
  71. Examples:
  72. >>> is_sub_trace("2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001")
  73. True
  74. >>> is_sub_trace("2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d")
  75. False
  76. """
  77. return '@' in trace_id
  78. def extract_mode(trace_id: str) -> Optional[str]:
  79. """
  80. 从 Sub-Trace ID 中提取运行模式
  81. Args:
  82. trace_id: Trace ID
  83. Returns:
  84. 运行模式(explore, delegate 等),如果是主 Trace 则返回 None
  85. Examples:
  86. >>> extract_mode("2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001")
  87. 'explore'
  88. >>> extract_mode("2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@delegate-20260204220030-001")
  89. 'delegate'
  90. >>> extract_mode("2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d")
  91. None
  92. """
  93. if '@' not in trace_id:
  94. return None
  95. # 格式: parent@mode-timestamp-seq
  96. parts = trace_id.split('@')[1] # "mode-timestamp-seq"
  97. mode = parts.split('-')[0]
  98. return mode
  99. def reset_seq_counter():
  100. """
  101. 重置序号计数器
  102. 主要用于测试,生产环境不应调用此函数。
  103. """
  104. global _seq_counter
  105. with _seq_lock:
  106. _seq_counter.clear()