Skip to content

Commit 3f9d2b7

Browse files
committed
function decorator for adding code level attributes to span
1 parent 83edee5 commit 3f9d2b7

File tree

3 files changed

+30
-32
lines changed

3 files changed

+30
-32
lines changed

aws-opentelemetry-distro/src/amazon/opentelemetry/distro/code_correlation/__init__.py

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,13 @@
77
This module provides functionality for correlating code execution with telemetry data.
88
"""
99

10-
__version__ = "1.0.0"
11-
12-
13-
"""
14-
Utility functions for adding code information to OpenTelemetry spans.
15-
"""
16-
17-
from typing import Any, Callable
1810
from functools import wraps
11+
from typing import Any, Callable
12+
1913
from opentelemetry import trace
2014

15+
__version__ = "1.0.0"
16+
2117

2218
# Code correlation attribute constants
2319
CODE_FUNCTION_NAME = "code.function.name"
@@ -28,25 +24,25 @@
2824
def _add_code_attributes_to_span(span, func: Callable[..., Any]) -> None:
2925
"""
3026
Add code-related attributes to a span based on a Python function.
31-
27+
3228
This utility method extracts function metadata and adds the following
3329
span attributes:
3430
- CODE_FUNCTION_NAME: The name of the function
3531
- CODE_FILE_PATH: The file path where the function is defined
3632
- CODE_LINE_NUMBER: The line number where the function is defined
37-
33+
3834
Args:
3935
span: The OpenTelemetry span to add attributes to
4036
func: The Python function to extract metadata from
4137
"""
4238
if not span.is_recording():
4339
return
44-
40+
4541
try:
4642
# Get function name
4743
function_name = getattr(func, '__name__', str(func))
4844
span.set_attribute(CODE_FUNCTION_NAME, function_name)
49-
45+
5046
# Get function source file from code object
5147
try:
5248
if hasattr(func, '__code__'):
@@ -56,7 +52,7 @@ def _add_code_attributes_to_span(span, func: Callable[..., Any]) -> None:
5652
# Handle cases where code object is not available
5753
# (e.g., built-in functions, C extensions)
5854
pass
59-
55+
6056
# Get function line number from code object
6157
try:
6258
if hasattr(func, '__code__'):
@@ -65,7 +61,7 @@ def _add_code_attributes_to_span(span, func: Callable[..., Any]) -> None:
6561
except (AttributeError, TypeError):
6662
# Handle cases where code object is not available
6763
pass
68-
64+
6965
except Exception:
7066
# Silently handle any unexpected errors to avoid breaking
7167
# the instrumentation flow
@@ -75,37 +71,37 @@ def _add_code_attributes_to_span(span, func: Callable[..., Any]) -> None:
7571
def add_code_attributes_to_span(func: Callable[..., Any]) -> Callable[..., Any]:
7672
"""
7773
Decorator to automatically add code attributes to the current OpenTelemetry span.
78-
74+
7975
This decorator extracts metadata from the decorated function and adds it as
8076
attributes to the current active span. The attributes added are:
8177
- code.function.name: The name of the function
8278
- code.file.path: The file path where the function is defined
8379
- code.line.number: The line number where the function is defined
84-
80+
8581
This decorator supports both synchronous and asynchronous functions.
86-
82+
8783
Usage:
8884
@add_code_attributes_to_span
8985
def my_sync_function():
9086
# Sync function implementation
9187
pass
92-
88+
9389
@add_code_attributes_to_span
9490
async def my_async_function():
9591
# Async function implementation
9692
pass
97-
93+
9894
Args:
9995
func: The function to be decorated
100-
96+
10197
Returns:
10298
The wrapped function with current span code attributes tracing
10399
"""
104100
# Detect async functions: check function code object flags or special attributes
105101
# CO_ITERABLE_COROUTINE = 0x80, async functions will have this flag set
106-
is_async = (hasattr(func, '__code__') and
102+
is_async = (hasattr(func, '__code__') and
107103
func.__code__.co_flags & 0x80) or hasattr(func, '_is_coroutine')
108-
104+
109105
if is_async:
110106
# Async function wrapper
111107
@wraps(func)
@@ -118,10 +114,10 @@ async def async_wrapper(*args, **kwargs):
118114
except Exception:
119115
# Silently handle any unexpected errors
120116
pass
121-
117+
122118
# Call and await the original async function
123119
return await func(*args, **kwargs)
124-
120+
125121
return async_wrapper
126122
else:
127123
# Sync function wrapper
@@ -135,8 +131,8 @@ def sync_wrapper(*args, **kwargs):
135131
except Exception:
136132
# Silently handle any unexpected errors
137133
pass
138-
134+
139135
# Call the original sync function
140136
return func(*args, **kwargs)
141-
137+
142138
return sync_wrapper
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0

aws-opentelemetry-distro/tests/amazon/opentelemetry/distro/test_code_correlation.py renamed to aws-opentelemetry-distro/tests/amazon/opentelemetry/distro/code_correlation/test_code_correlation.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ def test_function():
4242

4343
# Verify function name attribute is set
4444
self.mock_span.set_attribute.assert_any_call(CODE_FUNCTION_NAME, "test_function")
45-
45+
4646
# Verify file path attribute is set
4747
expected_file_path = test_function.__code__.co_filename
4848
self.mock_span.set_attribute.assert_any_call(CODE_FILE_PATH, expected_file_path)
49-
49+
5050
# Verify line number attribute is set
5151
expected_line_number = test_function.__code__.co_firstlineno
5252
self.mock_span.set_attribute.assert_any_call(CODE_LINE_NUMBER, expected_line_number)
@@ -136,7 +136,7 @@ def test_add_code_attributes_co_filename_exception(self):
136136
mock_func.__name__ = "test_func"
137137
mock_code = MagicMock()
138138
mock_code.co_firstlineno = 10
139-
139+
140140
# Make co_filename raise AttributeError
141141
type(mock_code).co_filename = PropertyMock(side_effect=AttributeError("Test exception"))
142142
mock_func.__code__ = mock_code
@@ -158,7 +158,7 @@ def test_add_code_attributes_co_firstlineno_exception(self):
158158
mock_func.__name__ = "test_func"
159159
mock_code = MagicMock()
160160
mock_code.co_filename = "/test/file.py"
161-
161+
162162
# Make co_firstlineno raise TypeError
163163
type(mock_code).co_firstlineno = PropertyMock(side_effect=TypeError("Test exception"))
164164
mock_func.__code__ = mock_code
@@ -180,7 +180,7 @@ def test_add_code_attributes_co_filename_type_error(self):
180180
mock_func.__name__ = "test_func"
181181
mock_code = MagicMock()
182182
mock_code.co_firstlineno = 10
183-
183+
184184
# Make co_filename raise TypeError
185185
type(mock_code).co_filename = PropertyMock(side_effect=TypeError("Test exception"))
186186
mock_func.__code__ = mock_code
@@ -364,7 +364,7 @@ def test_function():
364364
# Verify the function still works correctly despite internal exception
365365
self.assertEqual(result, "test result")
366366

367-
@patch('amazon.opentelemetry.distro.code_correlation._add_code_attributes_to_span')
367+
@patch('amazon.opentelemetry.distro.code_correlation._add_code_attributes_to_span')
368368
@patch('amazon.opentelemetry.distro.code_correlation.trace.get_current_span')
369369
def test_decorator_internal_exception_handling_async(self, mock_get_current_span, mock_add_attributes):
370370
"""Test that decorator handles internal exceptions gracefully in async function."""

0 commit comments

Comments
 (0)