You've already forked mariadb-columnstore-engine
							
							
				mirror of
				https://github.com/mariadb-corporation/mariadb-columnstore-engine.git
				synced 2025-11-03 17:13:17 +03:00 
			
		
		
		
	Added mirroring of spans into Sentry Tracer is a facade that redirects actions to tracing backends
		
			
				
	
	
		
			156 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			156 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""This module implements a tracer facade that creates spans, injects/extracts traceparent headers,
 | 
						|
and delegates span lifecycle and enrichment to pluggable backends (e.g., Traceparent and Sentry).
 | 
						|
It uses contextvars to store the trace/span/parent_span ids and start time for each context.
 | 
						|
"""
 | 
						|
 | 
						|
import contextvars
 | 
						|
import logging
 | 
						|
import time
 | 
						|
from collections.abc import Iterator
 | 
						|
from contextlib import contextmanager
 | 
						|
from typing import Any, Dict, List, Optional, Tuple
 | 
						|
 | 
						|
from tracing.backend import TracerBackend
 | 
						|
from tracing.span import TraceSpan
 | 
						|
from tracing.utils import (
 | 
						|
    rand_16_hex,
 | 
						|
    rand_8_hex,
 | 
						|
    format_traceparent,
 | 
						|
    parse_traceparent,
 | 
						|
)
 | 
						|
 | 
						|
logger = logging.getLogger(__name__)
 | 
						|
 | 
						|
 | 
						|
# Context vars are something like thread-local storage, they are context-local variables
 | 
						|
_current_trace_id = contextvars.ContextVar[str]("trace_id", default="")
 | 
						|
_current_span_id = contextvars.ContextVar[str]("span_id", default="")
 | 
						|
_current_parent_span_id = contextvars.ContextVar[str]("parent_span_id", default="")
 | 
						|
_current_span_start_ns = contextvars.ContextVar[int]("span_start_ns", default=0)
 | 
						|
 | 
						|
 | 
						|
class Tracer:
 | 
						|
    def __init__(self) -> None:
 | 
						|
        self._backends: List[TracerBackend] = []
 | 
						|
 | 
						|
    def register_backend(self, backend: TracerBackend) -> None:
 | 
						|
        try:
 | 
						|
            self._backends.append(backend)
 | 
						|
            logger.info(
 | 
						|
                "Tracing backend registered: %s", backend.__class__.__name__
 | 
						|
            )
 | 
						|
        except Exception:
 | 
						|
            logger.exception("Failed to register tracing backend")
 | 
						|
 | 
						|
    def clear_backends(self) -> None:
 | 
						|
        self._backends.clear()
 | 
						|
 | 
						|
    @contextmanager
 | 
						|
    def start_as_current_span(self, name: str, kind: str = "INTERNAL") -> Iterator[TraceSpan]:
 | 
						|
        trace_id = _current_trace_id.get() or rand_16_hex()
 | 
						|
        parent_span = _current_span_id.get()
 | 
						|
        new_span_id = rand_8_hex()
 | 
						|
 | 
						|
        tok_tid = _current_trace_id.set(trace_id)
 | 
						|
        tok_sid = _current_span_id.set(new_span_id)
 | 
						|
        tok_pid = _current_parent_span_id.set(parent_span)
 | 
						|
        tok_start = _current_span_start_ns.set(time.time_ns())
 | 
						|
 | 
						|
        span = TraceSpan(
 | 
						|
            name=name,
 | 
						|
            kind=kind,
 | 
						|
            start_ns=_current_span_start_ns.get(),
 | 
						|
            trace_id=trace_id,
 | 
						|
            span_id=new_span_id,
 | 
						|
            parent_span_id=parent_span,
 | 
						|
            attributes={"span.kind": kind, "span.name": name},
 | 
						|
            tracer=self,
 | 
						|
        )
 | 
						|
 | 
						|
        caught_exc: Optional[BaseException] = None
 | 
						|
        try:
 | 
						|
            for backend in list(self._backends):
 | 
						|
                backend.on_span_start(span)
 | 
						|
            yield span
 | 
						|
        except BaseException as exc:
 | 
						|
            span.record_exception(exc)
 | 
						|
            span.set_status("ERROR", str(exc))
 | 
						|
            caught_exc = exc
 | 
						|
            raise
 | 
						|
        finally:
 | 
						|
            for backend in list(self._backends):
 | 
						|
                backend.on_span_end(span, caught_exc)
 | 
						|
            _current_span_start_ns.reset(tok_start)
 | 
						|
            _current_parent_span_id.reset(tok_pid)
 | 
						|
            _current_span_id.reset(tok_sid)
 | 
						|
            _current_trace_id.reset(tok_tid)
 | 
						|
 | 
						|
    def set_incoming_context(
 | 
						|
        self,
 | 
						|
        trace_id: Optional[str] = None,
 | 
						|
        parent_span_id: Optional[str] = None,
 | 
						|
    ) -> None:
 | 
						|
        if trace_id:
 | 
						|
            _current_trace_id.set(trace_id)
 | 
						|
        if parent_span_id:
 | 
						|
            _current_parent_span_id.set(parent_span_id)
 | 
						|
 | 
						|
    def current_trace_ids(self) -> Tuple[str, str, str]:
 | 
						|
        return _current_trace_id.get(), _current_span_id.get(), _current_parent_span_id.get()
 | 
						|
 | 
						|
    def inject_traceparent(self, headers: Dict[str, str]) -> None:
 | 
						|
        trace_id, span_id, _ = self.current_trace_ids()
 | 
						|
        if not trace_id or not span_id:
 | 
						|
            trace_id = trace_id or rand_16_hex()
 | 
						|
            span_id = span_id or rand_8_hex()
 | 
						|
        headers["traceparent"] = format_traceparent(trace_id, span_id)
 | 
						|
 | 
						|
    def inject_outbound_headers(self, headers: Dict[str, str]) -> None:
 | 
						|
        self.inject_traceparent(headers)
 | 
						|
        for backend in list(self._backends):
 | 
						|
            backend.on_inject_headers(headers)
 | 
						|
 | 
						|
    def notify_incoming_request(self, headers: Dict[str, str], method: str, path: str) -> None:
 | 
						|
        for backend in list(self._backends):
 | 
						|
            backend.on_incoming_request(headers, method, path)
 | 
						|
 | 
						|
    def notify_request_finished(self, status_code: Optional[int]) -> None:
 | 
						|
        for backend in list(self._backends):
 | 
						|
            backend.on_request_finished(status_code)
 | 
						|
 | 
						|
    def extract_traceparent(self, headers: Dict[str, str]) -> Tuple[str, str]:
 | 
						|
        raw_traceparent = (headers.get("traceparent")
 | 
						|
                            or headers.get("Traceparent")
 | 
						|
                            or headers.get("TRACEPARENT"))
 | 
						|
        if not raw_traceparent:
 | 
						|
            return "", ""
 | 
						|
        parsed = parse_traceparent(raw_traceparent)
 | 
						|
        if not parsed:
 | 
						|
            return "", ""
 | 
						|
        return parsed[0], parsed[1]
 | 
						|
 | 
						|
    def _notify_event(self, span: TraceSpan, name: str, attrs: Dict[str, Any]) -> None:
 | 
						|
        for backend in list(self._backends):
 | 
						|
            backend.on_span_event(span, name, attrs)
 | 
						|
 | 
						|
    def _notify_status(self, span: TraceSpan, code: str, description: str) -> None:
 | 
						|
        for backend in list(self._backends):
 | 
						|
            backend.on_span_status(span, code, description)
 | 
						|
 | 
						|
    def _notify_exception(self, span: TraceSpan, exc: BaseException) -> None:
 | 
						|
        for backend in list(self._backends):
 | 
						|
            backend.on_span_exception(span, exc)
 | 
						|
 | 
						|
    def _notify_attribute(self, span: TraceSpan, key: str, value: Any) -> None:
 | 
						|
        for backend in list(self._backends):
 | 
						|
            backend.on_span_attribute(span, key, value)
 | 
						|
 | 
						|
 | 
						|
_tracer = Tracer()
 | 
						|
 | 
						|
 | 
						|
def get_tracer() -> Tracer:
 | 
						|
    return _tracer
 | 
						|
 | 
						|
 |