1
1
from sqlalchemy .event import listen
2
- from sqlalchemy .engine import Connectable
3
-
4
- import opentracing
5
2
6
3
g_tracer = None
7
4
8
- def set_tracer (tracer ):
5
+ def init_tracing (tracer ):
9
6
'''
10
7
Set our global tracer.
11
8
Tracer objects from our pyramid/flask/django libraries
@@ -17,42 +14,77 @@ def set_tracer(tracer):
17
14
else :
18
15
g_tracer = tracer
19
16
20
- def set_parent_span (stmt , parent_span ):
17
+ def set_parent_span (stmt_obj , parent_span ):
21
18
'''
22
19
Start tracing a given statement under
23
20
a specific span.
24
21
'''
25
- stmt ._parent_span = parent_span
22
+ stmt_obj ._parent_span = parent_span
26
23
27
- def has_parent_span (stmt ):
24
+ def has_parent_span (stmt_obj ):
28
25
'''
29
26
Get whether or not the statement has
30
27
a parent span.
31
28
'''
32
- return hasattr (stmt , '_parent_span' )
29
+ return hasattr (stmt_obj , '_parent_span' )
33
30
34
- def _before_handler (conn , clauseelement , multiparams , params ):
35
- parent_span = getattr (clauseelement , '_parent_span' , None )
36
- span = tracer .start_span (operation_name = 'sql?' , child_of = parent_span ) # (xxx) operation name
31
+ def get_span (stmt_obj ):
32
+ '''
33
+ Get the span of a statement object, if any.
34
+ '''
35
+ return getattr (stmt_obj , '_span' , None )
37
36
38
- clauseelement ._span = span
37
+ def register_connectable (obj ):
38
+ '''
39
+ Register an object to have its events be traced.
40
+ Any Connectable object is accepted, which
41
+ includes Connection and Engine.
42
+ '''
43
+ listen (obj , 'before_cursor_execute' , _before_cursor_handler )
44
+ listen (obj , 'after_cursor_execute' , _after_cursor_handler )
45
+ listen (obj , 'handle_error' , _error_handler )
39
46
40
- def _after_handler (conn , clauseelement , multiparams , params ):
41
- if getattr (clauseelement , '_span' , None ) is None :
47
+ def _get_operation_name (stmt_obj ):
48
+ return stmt_obj .__visit_name__
49
+
50
+ def _normalize_stmt (statement ):
51
+ return statement .strip ().replace ('\n ' , '' ).replace ('\t ' , '' )
52
+
53
+ def _before_cursor_handler (conn , cursor , statement , parameters , context , executemany ):
54
+ if context .compiled is None : # PRAGMA
55
+ return
56
+
57
+ stmt_obj = context .compiled .statement
58
+ parent_span = getattr (stmt_obj , '_parent_span' , None )
59
+ operation_name = _get_operation_name (stmt_obj )
60
+
61
+ # Start a new span for this query.
62
+ span = g_tracer .start_span (operation_name = operation_name , child_of = parent_span )
63
+
64
+ span .set_tag ('component' , 'sqlalchemy' )
65
+ span .set_tag ('db.type' , 'sql' )
66
+ span .set_tag ('db.statement' , _normalize_stmt (statement ))
67
+
68
+ stmt_obj ._span = span
69
+
70
+ def _after_cursor_handler (conn , cursor , statement , parameters , context , executemany ):
71
+ if context .compiled is None : # PRAGMA
72
+ return
73
+
74
+ stmt_obj = context .compiled .statement
75
+ span = get_span (stmt_obj )
76
+ if span is None :
42
77
return
43
78
44
- span = clauseelement ._span
45
79
span .finish ()
46
80
47
- def register_tracing (obj ):
48
- '''
49
- Register an object to have its events be traced.
50
- '''
51
- if isinstance (obj , Connectable ): # Engine or Connection instance.
52
- listen (obj , 'before_cursor_execute' , _before_cursor_handler )
53
- listen (obj , 'after_cursor_execute' , _after_cursor_handler )
81
+ def _error_handler (exception_context ):
82
+ execution_context = exception_context .execution_context
83
+ stmt_obj = execution_context .compiled .statement
84
+ span = get_span (stmt_obj )
85
+ if span is None :
86
+ return
54
87
55
- #elif isinstance(obj, MetaData): # Schema changes
56
- # listen(obj, "before_create", _schema_before_handler)
57
- # listen(obj, "after_create", _schema_after_handler)
88
+ span .set_tag ('error' , 'true' )
89
+ span .finish ()
58
90
0 commit comments