Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

trace_id_helper.py 2.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import contextlib
  2. import re
  3. from collections.abc import Mapping
  4. from typing import Any, Optional
  5. def is_valid_trace_id(trace_id: str) -> bool:
  6. """
  7. Check if the trace_id is valid.
  8. Requirements: 1-128 characters, only letters, numbers, '-', and '_'.
  9. """
  10. return bool(re.match(r"^[a-zA-Z0-9\-_]{1,128}$", trace_id))
  11. def get_external_trace_id(request: Any) -> Optional[str]:
  12. """
  13. Retrieve the trace_id from the request.
  14. Priority:
  15. 1. header ('X-Trace-Id')
  16. 2. parameters
  17. 3. JSON body
  18. 4. Current OpenTelemetry context (if enabled)
  19. 5. OpenTelemetry traceparent header (if present and valid)
  20. Returns None if no valid trace_id is provided.
  21. """
  22. trace_id = request.headers.get("X-Trace-Id")
  23. if not trace_id:
  24. trace_id = request.args.get("trace_id")
  25. if not trace_id and getattr(request, "is_json", False):
  26. json_data = getattr(request, "json", None)
  27. if json_data:
  28. trace_id = json_data.get("trace_id")
  29. if not trace_id:
  30. trace_id = get_trace_id_from_otel_context()
  31. if not trace_id:
  32. traceparent = request.headers.get("traceparent")
  33. if traceparent:
  34. trace_id = parse_traceparent_header(traceparent)
  35. if isinstance(trace_id, str) and is_valid_trace_id(trace_id):
  36. return trace_id
  37. return None
  38. def extract_external_trace_id_from_args(args: Mapping[str, Any]):
  39. """
  40. Extract 'external_trace_id' from args.
  41. Returns a dict suitable for use in extras. Returns an empty dict if not found.
  42. """
  43. trace_id = args.get("external_trace_id")
  44. if trace_id:
  45. return {"external_trace_id": trace_id}
  46. return {}
  47. def get_trace_id_from_otel_context() -> Optional[str]:
  48. """
  49. Retrieve the current trace ID from the active OpenTelemetry trace context.
  50. Returns None if:
  51. 1. OpenTelemetry SDK is not installed or enabled.
  52. 2. There is no active span or trace context.
  53. """
  54. try:
  55. from opentelemetry.trace import SpanContext, get_current_span
  56. from opentelemetry.trace.span import INVALID_TRACE_ID
  57. span = get_current_span()
  58. if not span:
  59. return None
  60. span_context: SpanContext = span.get_span_context()
  61. if not span_context or span_context.trace_id == INVALID_TRACE_ID:
  62. return None
  63. trace_id_hex = f"{span_context.trace_id:032x}"
  64. return trace_id_hex
  65. except Exception:
  66. return None
  67. def parse_traceparent_header(traceparent: str) -> Optional[str]:
  68. """
  69. Parse the `traceparent` header to extract the trace_id.
  70. Expected format:
  71. 'version-trace_id-span_id-flags'
  72. Reference:
  73. W3C Trace Context Specification: https://www.w3.org/TR/trace-context/
  74. """
  75. with contextlib.suppress(Exception):
  76. parts = traceparent.split("-")
  77. if len(parts) == 4 and len(parts[1]) == 32:
  78. return parts[1]
  79. return None