tracecontext.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. # Copyright The OpenTelemetry Authors
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. #
  15. import re
  16. import typing
  17. from opentelemetry import trace
  18. from opentelemetry.context.context import Context
  19. from opentelemetry.propagators import textmap
  20. from opentelemetry.trace import format_span_id, format_trace_id
  21. from opentelemetry.trace.span import TraceState
  22. class TraceContextTextMapPropagator(textmap.TextMapPropagator):
  23. """Extracts and injects using w3c TraceContext's headers."""
  24. _TRACEPARENT_HEADER_NAME = "traceparent"
  25. _TRACESTATE_HEADER_NAME = "tracestate"
  26. _TRACEPARENT_HEADER_FORMAT = (
  27. "^[ \t]*([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})"
  28. + "(-.*)?[ \t]*$"
  29. )
  30. _TRACEPARENT_HEADER_FORMAT_RE = re.compile(_TRACEPARENT_HEADER_FORMAT)
  31. def extract(
  32. self,
  33. carrier: textmap.CarrierT,
  34. context: typing.Optional[Context] = None,
  35. getter: textmap.Getter[textmap.CarrierT] = textmap.default_getter,
  36. ) -> Context:
  37. """Extracts SpanContext from the carrier.
  38. See `opentelemetry.propagators.textmap.TextMapPropagator.extract`
  39. """
  40. if context is None:
  41. context = Context()
  42. header = getter.get(carrier, self._TRACEPARENT_HEADER_NAME)
  43. if not header:
  44. return context
  45. match = re.search(self._TRACEPARENT_HEADER_FORMAT_RE, header[0])
  46. if not match:
  47. return context
  48. version: str = match.group(1)
  49. trace_id: str = match.group(2)
  50. span_id: str = match.group(3)
  51. trace_flags: str = match.group(4)
  52. if trace_id == "0" * 32 or span_id == "0" * 16:
  53. return context
  54. if version == "00":
  55. if match.group(5): # type: ignore
  56. return context
  57. if version == "ff":
  58. return context
  59. tracestate_headers = getter.get(carrier, self._TRACESTATE_HEADER_NAME)
  60. if tracestate_headers is None:
  61. tracestate = None
  62. else:
  63. tracestate = TraceState.from_header(tracestate_headers)
  64. span_context = trace.SpanContext(
  65. trace_id=int(trace_id, 16),
  66. span_id=int(span_id, 16),
  67. is_remote=True,
  68. trace_flags=trace.TraceFlags(int(trace_flags, 16)),
  69. trace_state=tracestate,
  70. )
  71. return trace.set_span_in_context(
  72. trace.NonRecordingSpan(span_context), context
  73. )
  74. def inject(
  75. self,
  76. carrier: textmap.CarrierT,
  77. context: typing.Optional[Context] = None,
  78. setter: textmap.Setter[textmap.CarrierT] = textmap.default_setter,
  79. ) -> None:
  80. """Injects SpanContext into the carrier.
  81. See `opentelemetry.propagators.textmap.TextMapPropagator.inject`
  82. """
  83. span = trace.get_current_span(context)
  84. span_context = span.get_span_context()
  85. if span_context == trace.INVALID_SPAN_CONTEXT:
  86. return
  87. traceparent_string = f"00-{format_trace_id(span_context.trace_id)}-{format_span_id(span_context.span_id)}-{span_context.trace_flags:02x}"
  88. setter.set(carrier, self._TRACEPARENT_HEADER_NAME, traceparent_string)
  89. if span_context.trace_state:
  90. tracestate_string = span_context.trace_state.to_header()
  91. setter.set(
  92. carrier, self._TRACESTATE_HEADER_NAME, tracestate_string
  93. )
  94. @property
  95. def fields(self) -> typing.Set[str]:
  96. """Returns a set with the fields set in `inject`.
  97. See
  98. `opentelemetry.propagators.textmap.TextMapPropagator.fields`
  99. """
  100. return {self._TRACEPARENT_HEADER_NAME, self._TRACESTATE_HEADER_NAME}