33
33
from opentelemetry .trace .status import Status , StatusCode
34
34
35
35
36
- class _GuardedSpan :
37
- def __init__ (self , span ):
38
- self .span = span
39
- self .generated_span = None
40
- self ._engaged = True
41
-
42
- def __enter__ (self ):
43
- self .generated_span = self .span .__enter__ ()
44
- return self
45
-
46
- def __exit__ (self , * args , ** kwargs ):
47
- if self ._engaged :
48
- self .generated_span = None
49
- return self .span .__exit__ (* args , ** kwargs )
50
- return False
51
-
52
- def release (self ):
53
- self ._engaged = False
54
- return self .span
55
-
56
-
57
36
class _CarrierSetter (Setter ):
58
37
"""We use a custom setter in order to be able to lower case
59
38
keys as is required by grpc.
@@ -68,7 +47,7 @@ def set(self, carrier: MutableMapping[str, str], key: str, value: str):
68
47
69
48
def _make_future_done_callback (span , rpc_info ):
70
49
def callback (response_future ):
71
- with span :
50
+ with trace . use_span ( span , end_on_exit = True ) :
72
51
code = response_future .code ()
73
52
if code != grpc .StatusCode .OK :
74
53
rpc_info .error = code
@@ -85,7 +64,7 @@ class OpenTelemetryClientInterceptor(
85
64
def __init__ (self , tracer ):
86
65
self ._tracer = tracer
87
66
88
- def _start_span (self , method ):
67
+ def _start_span (self , method , ** kwargs ):
89
68
service , meth = method .lstrip ("/" ).split ("/" , 1 )
90
69
attributes = {
91
70
SpanAttributes .RPC_SYSTEM : "grpc" ,
@@ -95,16 +74,19 @@ def _start_span(self, method):
95
74
}
96
75
97
76
return self ._tracer .start_as_current_span (
98
- name = method , kind = trace .SpanKind .CLIENT , attributes = attributes
77
+ name = method ,
78
+ kind = trace .SpanKind .CLIENT ,
79
+ attributes = attributes ,
80
+ ** kwargs ,
99
81
)
100
82
101
83
# pylint:disable=no-self-use
102
- def _trace_result (self , guarded_span , rpc_info , result ):
103
- # If the RPC is called asynchronously, release the guard and add a
104
- # callback so that the span can be finished once the future is done.
84
+ def _trace_result (self , span , rpc_info , result ):
85
+ # If the RPC is called asynchronously, add a callback to end the span
86
+ # when the future is done, else end the span immediately
105
87
if isinstance (result , grpc .Future ):
106
88
result .add_done_callback (
107
- _make_future_done_callback (guarded_span . release () , rpc_info )
89
+ _make_future_done_callback (span , rpc_info )
108
90
)
109
91
return result
110
92
response = result
@@ -115,41 +97,54 @@ def _trace_result(self, guarded_span, rpc_info, result):
115
97
if isinstance (result , tuple ):
116
98
response = result [0 ]
117
99
rpc_info .response = response
118
-
100
+ span . end ()
119
101
return result
120
102
121
- def _start_guarded_span (self , * args , ** kwargs ):
122
- return _GuardedSpan (self ._start_span (* args , ** kwargs ))
123
-
124
- def intercept_unary (self , request , metadata , client_info , invoker ):
103
+ def _intercept (self , request , metadata , client_info , invoker ):
125
104
if not metadata :
126
105
mutable_metadata = OrderedDict ()
127
106
else :
128
107
mutable_metadata = OrderedDict (metadata )
129
-
130
- with self ._start_guarded_span (client_info .full_method ) as guarded_span :
131
- inject (mutable_metadata , setter = _carrier_setter )
132
- metadata = tuple (mutable_metadata .items ())
133
-
134
- rpc_info = RpcInfo (
135
- full_method = client_info .full_method ,
136
- metadata = metadata ,
137
- timeout = client_info .timeout ,
138
- request = request ,
139
- )
140
-
108
+ with self ._start_span (
109
+ client_info .full_method ,
110
+ end_on_exit = False ,
111
+ record_exception = False ,
112
+ set_status_on_exception = False ,
113
+ ) as span :
114
+ result = None
141
115
try :
142
- result = invoker (request , metadata )
143
- except grpc .RpcError as err :
144
- guarded_span .generated_span .set_status (
145
- Status (StatusCode .ERROR )
116
+ inject (mutable_metadata , setter = _carrier_setter )
117
+ metadata = tuple (mutable_metadata .items ())
118
+
119
+ rpc_info = RpcInfo (
120
+ full_method = client_info .full_method ,
121
+ metadata = metadata ,
122
+ timeout = client_info .timeout ,
123
+ request = request ,
146
124
)
147
- guarded_span .generated_span .set_attribute (
148
- SpanAttributes .RPC_GRPC_STATUS_CODE , err .code ().value [0 ]
125
+
126
+ result = invoker (request , metadata )
127
+ except Exception as exc :
128
+ if isinstance (exc , grpc .RpcError ):
129
+ span .set_attribute (
130
+ SpanAttributes .RPC_GRPC_STATUS_CODE ,
131
+ exc .code ().value [0 ],
132
+ )
133
+ span .set_status (
134
+ Status (
135
+ status_code = StatusCode .ERROR ,
136
+ description = "{}: {}" .format (type (exc ).__name__ , exc ),
137
+ )
149
138
)
150
- raise err
139
+ span .record_exception (exc )
140
+ raise exc
141
+ finally :
142
+ if not result :
143
+ span .end ()
144
+ return self ._trace_result (span , rpc_info , result )
151
145
152
- return self ._trace_result (guarded_span , rpc_info , result )
146
+ def intercept_unary (self , request , metadata , client_info , invoker ):
147
+ return self ._intercept (request , metadata , client_info , invoker )
153
148
154
149
# For RPCs that stream responses, the result can be a generator. To record
155
150
# the span across the generated responses and detect any errors, we wrap
@@ -194,32 +189,6 @@ def intercept_stream(
194
189
request_or_iterator , metadata , client_info , invoker
195
190
)
196
191
197
- if not metadata :
198
- mutable_metadata = OrderedDict ()
199
- else :
200
- mutable_metadata = OrderedDict (metadata )
201
-
202
- with self ._start_guarded_span (client_info .full_method ) as guarded_span :
203
- inject (mutable_metadata , setter = _carrier_setter )
204
- metadata = tuple (mutable_metadata .items ())
205
- rpc_info = RpcInfo (
206
- full_method = client_info .full_method ,
207
- metadata = metadata ,
208
- timeout = client_info .timeout ,
209
- request = request_or_iterator ,
210
- )
211
-
212
- rpc_info .request = request_or_iterator
213
-
214
- try :
215
- result = invoker (request_or_iterator , metadata )
216
- except grpc .RpcError as err :
217
- guarded_span .generated_span .set_status (
218
- Status (StatusCode .ERROR )
219
- )
220
- guarded_span .generated_span .set_attribute (
221
- SpanAttributes .RPC_GRPC_STATUS_CODE , err .code ().value [0 ],
222
- )
223
- raise err
224
-
225
- return self ._trace_result (guarded_span , rpc_info , result )
192
+ return self ._intercept (
193
+ request_or_iterator , metadata , client_info , invoker
194
+ )
0 commit comments