|
1 | 1 | import re |
| 2 | +import traceback |
2 | 3 | from contextlib import contextmanager |
3 | 4 | from typing import Any |
4 | 5 |
|
|
8 | 9 | DB_QUERY_PARAMETER_TEMPLATE, |
9 | 10 | DB_USER, |
10 | 11 | ) |
| 12 | +from opentelemetry.semconv.attributes.code_attributes import ( |
| 13 | + CODE_COLUMN_NUMBER, |
| 14 | + CODE_FILE_PATH, |
| 15 | + CODE_FUNCTION_NAME, |
| 16 | + CODE_LINE_NUMBER, |
| 17 | + CODE_STACKTRACE, |
| 18 | +) |
11 | 19 | from opentelemetry.semconv.attributes.db_attributes import ( |
12 | 20 | DB_COLLECTION_NAME, |
13 | 21 | DB_NAMESPACE, |
@@ -126,6 +134,8 @@ def db_span(db, sql: Any, *, many: bool = False, params=None): |
126 | 134 | DB_OPERATION_NAME: operation, |
127 | 135 | } |
128 | 136 |
|
| 137 | + attrs.update(_get_code_attributes()) |
| 138 | + |
129 | 139 | # Add collection name if detected |
130 | 140 | if collection_name: |
131 | 141 | attrs[DB_COLLECTION_NAME] = collection_name |
@@ -173,3 +183,43 @@ def suppress_db_tracing(): |
173 | 183 | yield |
174 | 184 | finally: |
175 | 185 | otel_context.detach(token) |
| 186 | + |
| 187 | + |
| 188 | +def _get_code_attributes(): |
| 189 | + """Extract code context attributes for the current database query. |
| 190 | +
|
| 191 | + Returns a dict of OpenTelemetry code attributes. |
| 192 | + """ |
| 193 | + stack = traceback.extract_stack() |
| 194 | + |
| 195 | + # Find the user code frame |
| 196 | + for frame in reversed(stack): |
| 197 | + filepath = frame.filename |
| 198 | + if not filepath: |
| 199 | + continue |
| 200 | + |
| 201 | + if "/plain/models/" in filepath: |
| 202 | + continue |
| 203 | + |
| 204 | + if filepath.endswith("contextlib.py"): |
| 205 | + continue |
| 206 | + |
| 207 | + # Found user code - build attributes dict |
| 208 | + attrs = {} |
| 209 | + |
| 210 | + if filepath: |
| 211 | + attrs[CODE_FILE_PATH] = filepath |
| 212 | + if frame.lineno: |
| 213 | + attrs[CODE_LINE_NUMBER] = frame.lineno |
| 214 | + if frame.name: |
| 215 | + attrs[CODE_FUNCTION_NAME] = frame.name |
| 216 | + if frame.colno: |
| 217 | + attrs[CODE_COLUMN_NUMBER] = frame.colno |
| 218 | + |
| 219 | + # Add full stack trace only in DEBUG mode (expensive) |
| 220 | + if settings.DEBUG: |
| 221 | + attrs[CODE_STACKTRACE] = "".join(traceback.format_stack()) |
| 222 | + |
| 223 | + return attrs |
| 224 | + |
| 225 | + return {} |
0 commit comments