|
6 | 6 |
|
7 | 7 | This module provides functionality for correlating code execution with telemetry data. |
8 | 8 | """ |
9 | | - |
10 | | -import inspect |
11 | | -from functools import wraps |
12 | | -from typing import Any, Callable |
13 | | - |
14 | | -from opentelemetry import trace |
15 | | - |
| 9 | +# Import code attributes span processor |
| 10 | +from .code_attributes_span_processor import CodeAttributesSpanProcessor |
| 11 | + |
| 12 | +# Import main utilities to maintain API compatibility |
| 13 | +from .utils import ( |
| 14 | + add_code_attributes_to_span, |
| 15 | + add_code_attributes_to_span_from_frame, |
| 16 | + get_callable_fullname, |
| 17 | + get_function_fullname_from_frame, |
| 18 | + record_code_attributes, |
| 19 | +) |
| 20 | + |
| 21 | +# Version information |
16 | 22 | __version__ = "1.0.0" |
17 | 23 |
|
18 | | - |
19 | | -# Code correlation attribute constants |
20 | | -CODE_FUNCTION_NAME = "code.function.name" |
21 | | -CODE_FILE_PATH = "code.file.path" |
22 | | -CODE_LINE_NUMBER = "code.line.number" |
23 | | - |
24 | | - |
25 | | -def add_code_attributes_to_span(span, func_or_class: Callable[..., Any]) -> None: |
26 | | - """ |
27 | | - Add code-related attributes to a span based on a Python function. |
28 | | -
|
29 | | - This utility method extracts function metadata and adds the following |
30 | | - span attributes: |
31 | | - - CODE_FUNCTION_NAME: The name of the function |
32 | | - - CODE_FILE_PATH: The file path where the function is defined |
33 | | - - CODE_LINE_NUMBER: The line number where the function is defined |
34 | | -
|
35 | | - Args: |
36 | | - span: The OpenTelemetry span to add attributes to |
37 | | - func: The Python function to extract metadata from |
38 | | - """ |
39 | | - if not span.is_recording(): |
40 | | - return |
41 | | - |
42 | | - try: |
43 | | - # Check if it's a class first, with proper exception handling |
44 | | - try: |
45 | | - is_class = inspect.isclass(func_or_class) |
46 | | - except Exception: # pylint: disable=broad-exception-caught |
47 | | - # If inspect.isclass fails, we can't safely determine the type, so return early |
48 | | - return |
49 | | - |
50 | | - if is_class: |
51 | | - span.set_attribute(CODE_FUNCTION_NAME, f"{func_or_class.__module__}.{func_or_class.__qualname__}") |
52 | | - span.set_attribute(CODE_FILE_PATH, inspect.getfile(func_or_class)) |
53 | | - else: |
54 | | - code = getattr(func_or_class, "__code__", None) |
55 | | - if code: |
56 | | - span.set_attribute(CODE_FUNCTION_NAME, f"{func_or_class.__module__}.{func_or_class.__qualname__}") |
57 | | - span.set_attribute(CODE_FILE_PATH, code.co_filename) |
58 | | - span.set_attribute(CODE_LINE_NUMBER, code.co_firstlineno) |
59 | | - except Exception: # pylint: disable=broad-exception-caught |
60 | | - pass |
61 | | - |
62 | | - |
63 | | -def record_code_attributes(func: Callable[..., Any]) -> Callable[..., Any]: |
64 | | - """ |
65 | | - Decorator to automatically add code attributes to the current OpenTelemetry span. |
66 | | -
|
67 | | - This decorator extracts metadata from the decorated function and adds it as |
68 | | - attributes to the current active span. The attributes added are: |
69 | | - - code.function.name: The name of the function |
70 | | - - code.file.path: The file path where the function is defined |
71 | | - - code.line.number: The line number where the function is defined |
72 | | -
|
73 | | - This decorator supports both synchronous and asynchronous functions. |
74 | | -
|
75 | | - Usage: |
76 | | - @record_code_attributes |
77 | | - def my_sync_function(): |
78 | | - # Sync function implementation |
79 | | - pass |
80 | | -
|
81 | | - @record_code_attributes |
82 | | - async def my_async_function(): |
83 | | - # Async function implementation |
84 | | - pass |
85 | | -
|
86 | | - Args: |
87 | | - func: The function to be decorated |
88 | | -
|
89 | | - Returns: |
90 | | - The wrapped function with current span code attributes tracing |
91 | | - """ |
92 | | - # Detect async functions |
93 | | - is_async = inspect.iscoroutinefunction(func) |
94 | | - |
95 | | - if is_async: |
96 | | - # Async function wrapper |
97 | | - @wraps(func) |
98 | | - async def async_wrapper(*args, **kwargs): |
99 | | - # Add code attributes to current span |
100 | | - try: |
101 | | - current_span = trace.get_current_span() |
102 | | - if current_span: |
103 | | - add_code_attributes_to_span(current_span, func) |
104 | | - except Exception: # pylint: disable=broad-exception-caught |
105 | | - # Silently handle any unexpected errors |
106 | | - pass |
107 | | - |
108 | | - # Call and await the original async function |
109 | | - return await func(*args, **kwargs) |
110 | | - |
111 | | - return async_wrapper |
112 | | - |
113 | | - # Sync function wrapper |
114 | | - @wraps(func) |
115 | | - def sync_wrapper(*args, **kwargs): |
116 | | - # Add code attributes to current span |
117 | | - try: |
118 | | - current_span = trace.get_current_span() |
119 | | - if current_span: |
120 | | - add_code_attributes_to_span(current_span, func) |
121 | | - except Exception: # pylint: disable=broad-exception-caught |
122 | | - # Silently handle any unexpected errors |
123 | | - pass |
124 | | - |
125 | | - # Call the original sync function |
126 | | - return func(*args, **kwargs) |
127 | | - |
128 | | - return sync_wrapper |
| 24 | +# Define public API |
| 25 | +__all__ = [ |
| 26 | + # Functions |
| 27 | + "add_code_attributes_to_span", |
| 28 | + "add_code_attributes_to_span_from_frame", |
| 29 | + "get_callable_fullname", |
| 30 | + "get_function_fullname_from_frame", |
| 31 | + "record_code_attributes", |
| 32 | + # Classes |
| 33 | + "CodeAttributesSpanProcessor", |
| 34 | + # Version |
| 35 | + "__version__", |
| 36 | +] |
0 commit comments