Skip to content
This repository was archived by the owner on Sep 17, 2025. It is now read-only.

Commit d358613

Browse files
Fix #967 by adding http.xxx attributes on grpc server_interceptor (#971)
1 parent 3a2d8df commit d358613

File tree

2 files changed

+113
-14
lines changed

2 files changed

+113
-14
lines changed

contrib/opencensus-ext-grpc/opencensus/ext/grpc/server_interceptor.py

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,40 @@
2525
from opencensus.trace import tracer as tracer_module
2626
from opencensus.trace.propagation import binary_format
2727

28-
ATTRIBUTE_COMPONENT = 'COMPONENT'
29-
ATTRIBUTE_ERROR_NAME = 'ERROR_NAME'
30-
ATTRIBUTE_ERROR_MESSAGE = 'ERROR_MESSAGE'
28+
COMPONENT = attributes_helper.COMMON_ATTRIBUTES['COMPONENT']
29+
ERROR_NAME = attributes_helper.COMMON_ATTRIBUTES['ERROR_NAME']
30+
ERROR_MESSAGE = attributes_helper.COMMON_ATTRIBUTES['ERROR_MESSAGE']
31+
32+
HTTP_HOST = attributes_helper.COMMON_ATTRIBUTES['HTTP_HOST']
33+
HTTP_METHOD = attributes_helper.COMMON_ATTRIBUTES['HTTP_METHOD']
34+
HTTP_PATH = attributes_helper.COMMON_ATTRIBUTES['HTTP_PATH']
35+
HTTP_ROUTE = attributes_helper.COMMON_ATTRIBUTES['HTTP_ROUTE']
36+
HTTP_URL = attributes_helper.COMMON_ATTRIBUTES['HTTP_URL']
37+
HTTP_STATUS_CODE = attributes_helper.COMMON_ATTRIBUTES['HTTP_STATUS_CODE']
38+
GRPC_METHOD = attributes_helper.GRPC_ATTRIBUTES['GRPC_METHOD']
39+
3140
RECV_PREFIX = 'Recv'
3241

42+
GRPC_HTTP_STATUS_MAPPING = {
43+
grpc.StatusCode.OK: 200,
44+
grpc.StatusCode.FAILED_PRECONDITION: 400,
45+
grpc.StatusCode.INVALID_ARGUMENT: 400,
46+
grpc.StatusCode.OUT_OF_RANGE: 400,
47+
grpc.StatusCode.UNAUTHENTICATED: 401,
48+
grpc.StatusCode.PERMISSION_DENIED: 403,
49+
grpc.StatusCode.NOT_FOUND: 404,
50+
grpc.StatusCode.ABORTED: 409,
51+
grpc.StatusCode.ALREADY_EXISTS: 409,
52+
grpc.StatusCode.RESOURCE_EXHAUSTED: 429,
53+
grpc.StatusCode.CANCELLED: 499,
54+
grpc.StatusCode.UNKNOWN: 500,
55+
grpc.StatusCode.INTERNAL: 500,
56+
grpc.StatusCode.DATA_LOSS: 500,
57+
grpc.StatusCode.UNIMPLEMENTED: 501,
58+
grpc.StatusCode.UNAVAILABLE: 503,
59+
grpc.StatusCode.DEADLINE_EXCEEDED: 504
60+
}
61+
3362

3463
class OpenCensusServerInterceptor(grpc.ServerInterceptor):
3564
def __init__(self, sampler=None, exporter=None):
@@ -56,6 +85,12 @@ def new_behavior(request_or_iterator, servicer_context):
5685
# invoke the original rpc behavior
5786
response_or_iterator = behavior(request_or_iterator,
5887
servicer_context)
88+
89+
http_status_code = _convert_grpc_code_to_http_status_code(
90+
servicer_context._state.code
91+
)
92+
span.add_attribute(HTTP_STATUS_CODE, http_status_code)
93+
5994
if response_streaming:
6095
response_or_iterator = grpc_utils.wrap_iter_with_message_events( # noqa: E501
6196
request_or_response_iter=response_or_iterator,
@@ -107,28 +142,60 @@ def _start_server_span(self, servicer_context):
107142
)
108143

109144
span.span_kind = span_module.SpanKind.SERVER
145+
146+
grpc_call_details = servicer_context._rpc_event.call_details
147+
grpc_host = grpc_call_details.host.decode('utf-8')
148+
grpc_method = grpc_call_details.method.decode('utf-8')
149+
150+
tracer.add_attribute_to_current_span(
151+
COMPONENT, 'grpc'
152+
)
153+
tracer.add_attribute_to_current_span(
154+
GRPC_METHOD, grpc_method
155+
)
156+
157+
tracer.add_attribute_to_current_span(
158+
HTTP_HOST, grpc_host
159+
)
160+
tracer.add_attribute_to_current_span(
161+
HTTP_METHOD, 'POST'
162+
)
163+
tracer.add_attribute_to_current_span(
164+
HTTP_ROUTE, grpc_method
165+
)
166+
tracer.add_attribute_to_current_span(
167+
HTTP_PATH, grpc_method
168+
)
110169
tracer.add_attribute_to_current_span(
111-
attribute_key=attributes_helper.COMMON_ATTRIBUTES.get(
112-
ATTRIBUTE_COMPONENT),
113-
attribute_value='grpc')
170+
HTTP_URL, 'grpc://' + grpc_host + grpc_method
171+
)
114172

115173
execution_context.set_opencensus_tracer(tracer)
116174
execution_context.set_current_span(span)
117175
return span
118176

119177

178+
def _convert_grpc_code_to_http_status_code(grpc_state_code):
179+
"""
180+
Converts a gRPC state code into the corresponding HTTP response status.
181+
See:
182+
https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
183+
"""
184+
if grpc_state_code is None:
185+
return 200
186+
else:
187+
return GRPC_HTTP_STATUS_MAPPING.get(grpc_state_code, 500)
188+
189+
120190
def _add_exc_info(span):
121191
exc_type, exc_value, tb = sys.exc_info()
122-
span.add_attribute(
123-
attributes_helper.COMMON_ATTRIBUTES.get(
124-
ATTRIBUTE_ERROR_MESSAGE),
125-
str(exc_value)
126-
)
192+
span.add_attribute(ERROR_MESSAGE, str(exc_value))
127193
span.stack_trace = stack_trace.StackTrace.from_traceback(tb)
128194
span.status = status.Status(
129195
code=code_pb2.UNKNOWN,
130196
message=str(exc_value)
131197
)
198+
span.add_attribute(HTTP_STATUS_CODE, 500)
132199

133200

134201
def _wrap_rpc_behavior(handler, fn):

contrib/opencensus-ext-grpc/tests/test_server_interceptor.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import unittest
1616

17+
import grpc
1718
import mock
1819
from google.rpc import code_pb2
1920

@@ -22,6 +23,9 @@
2223
from opencensus.trace import execution_context
2324
from opencensus.trace import span as span_module
2425

26+
MOCK_HOST = b'localhost:5000'
27+
MOCK_METHOD = b'/helloworld.Greeter/SayHello'
28+
2529

2630
class TestOpenCensusServerInterceptor(unittest.TestCase):
2731
def test_constructor(self):
@@ -38,7 +42,10 @@ def test_intercept_service_no_metadata(self):
3842
'.tracer_module.Tracer', MockTracer)
3943
mock_context = mock.Mock()
4044
mock_context.invocation_metadata = mock.Mock(return_value=None)
41-
mock_context._rpc_event.call_details.method = 'hello'
45+
46+
mock_context._rpc_event.call_details.host = MOCK_HOST
47+
mock_context._rpc_event.call_details.method = MOCK_METHOD
48+
mock_context._state.code = grpc.StatusCode.OK
4249
interceptor = server_interceptor.OpenCensusServerInterceptor(
4350
None, None)
4451
mock_handler = mock.Mock()
@@ -53,6 +60,13 @@ def test_intercept_service_no_metadata(self):
5360

5461
expected_attributes = {
5562
'component': 'grpc',
63+
'grpc.method': '/helloworld.Greeter/SayHello',
64+
'http.host': 'localhost:5000',
65+
'http.method': 'POST',
66+
'http.route': '/helloworld.Greeter/SayHello',
67+
'http.path': '/helloworld.Greeter/SayHello',
68+
'http.url': 'grpc://localhost:5000/helloworld.Greeter/SayHello',
69+
'http.status_code': 200
5670
}
5771

5872
self.assertEqual(
@@ -78,7 +92,9 @@ def test_intercept_service(self):
7892
mock_handler.response_streaming = rsp_streaming
7993
mock_continuation = mock.Mock(return_value=mock_handler)
8094

81-
mock_context._rpc_event.call_details.method = 'hello'
95+
mock_context._rpc_event.call_details.host = MOCK_HOST
96+
mock_context._rpc_event.call_details.method = MOCK_METHOD
97+
mock_context._state.code = grpc.StatusCode.OK
8298
interceptor = server_interceptor.OpenCensusServerInterceptor(
8399
None, None)
84100

@@ -89,6 +105,13 @@ def test_intercept_service(self):
89105

90106
expected_attributes = {
91107
'component': 'grpc',
108+
'grpc.method': '/helloworld.Greeter/SayHello',
109+
'http.host': 'localhost:5000',
110+
'http.method': 'POST',
111+
'http.route': '/helloworld.Greeter/SayHello',
112+
'http.path': '/helloworld.Greeter/SayHello',
113+
'http.url': 'grpc://localhost:5000/helloworld.Greeter/SayHello', # noqa: E501
114+
'http.status_code': 200
92115
}
93116

94117
self.assertEqual(
@@ -110,7 +133,9 @@ def test_intercept_handler_exception(self):
110133
None, None)
111134
mock_context = mock.Mock()
112135
mock_context.invocation_metadata = mock.Mock(return_value=None)
113-
mock_context._rpc_event.call_details.method = 'hello'
136+
137+
mock_context._rpc_event.call_details.host = MOCK_HOST
138+
mock_context._rpc_event.call_details.method = MOCK_METHOD
114139
mock_handler = mock.Mock()
115140
mock_handler.request_streaming = req_streaming
116141
mock_handler.response_streaming = rsp_streaming
@@ -128,6 +153,13 @@ def test_intercept_handler_exception(self):
128153

129154
expected_attributes = {
130155
'component': 'grpc',
156+
'grpc.method': '/helloworld.Greeter/SayHello',
157+
'http.host': 'localhost:5000',
158+
'http.method': 'POST',
159+
'http.route': '/helloworld.Greeter/SayHello',
160+
'http.path': '/helloworld.Greeter/SayHello',
161+
'http.url': 'grpc://localhost:5000/helloworld.Greeter/SayHello', # noqa: E501
162+
'http.status_code': 500,
131163
'error.message': 'Test'
132164
}
133165

0 commit comments

Comments
 (0)