11from sqlalchemy .event import listen
2- from sqlalchemy .engine import Connectable
3-
4- import opentracing
52
63g_tracer = None
74
8- def set_tracer (tracer ):
5+ def init_tracing (tracer ):
96 '''
107 Set our global tracer.
118 Tracer objects from our pyramid/flask/django libraries
@@ -17,42 +14,77 @@ def set_tracer(tracer):
1714 else :
1815 g_tracer = tracer
1916
20- def set_parent_span (stmt , parent_span ):
17+ def set_parent_span (stmt_obj , parent_span ):
2118 '''
2219 Start tracing a given statement under
2320 a specific span.
2421 '''
25- stmt ._parent_span = parent_span
22+ stmt_obj ._parent_span = parent_span
2623
27- def has_parent_span (stmt ):
24+ def has_parent_span (stmt_obj ):
2825 '''
2926 Get whether or not the statement has
3027 a parent span.
3128 '''
32- return hasattr (stmt , '_parent_span' )
29+ return hasattr (stmt_obj , '_parent_span' )
3330
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 )
3736
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 )
3946
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 :
4277 return
4378
44- span = clauseelement ._span
4579 span .finish ()
4680
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
5487
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 ()
5890
0 commit comments