This repository was archived by the owner on Jul 31, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathgrpc_server_interceptor.rb
226 lines (207 loc) · 6.82 KB
/
grpc_server_interceptor.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
require "opencensus"
module OpenCensus
module Trace
module Integrations
##
# # gRPC interceptor
#
# This is a interceptor for gRPC:
#
# * It wraps all incoming requests in a root span
# * It exports the captured spans at the end of the request.
#
# Example:
#
# require "opencensus/trace/integrations/grpc_server_interceptor"
#
# server = GRPC::RpcServer.new(
# interceptors: [
# OpenCensus::Trace::Integrations::GrpcServerInterceptor.new,
# ]
# )
#
class GrpcServerInterceptor
##
# A key we use to read the parent span context.
#
# @private
#
OPENCENSUS_TRACE_BIN_KEY = "grpc-trace-bin".freeze
##
# @param [#export] exporter The exported used to export captured spans
# at the end of the request. Optional: If omitted, uses the exporter
# in the current config.
# @param [#call] span_modifier Modify span if necessary. It takes span,
# request, call, method as its parameters.
#
def initialize exporter: nil, span_modifier: nil
@exporter = exporter || OpenCensus::Trace.config.exporter
@span_modifier = span_modifier
@formatter = Formatters::Binary.new
end
##
# Intercept a unary request response call.
#
# @param [Object] request
# @param [GRPC::ActiveCall::SingleReqView] call
# @param [Method] method
#
def request_response request:, call:, method:, &block
context_bin = call.metadata[OPENCENSUS_TRACE_BIN_KEY]
context = context_bin ? deserialize(context_bin) : nil
Trace.start_request_trace \
trace_context: context,
same_process_as_parent: false do |span_context|
begin
yield_with_trace(request, call, method, &block)
ensure
@exporter.export span_context.build_contained_spans
end
end
end
# NOTE: For now, we don't support server_streamer, client_streamer and
# bidi_streamer
private
##
# @param [String] context_bin OpenCensus span context in binary format
# @return [OpenCensus::Trace::TraceContextData, nil]
#
def deserialize context_bin
@formatter.deserialize(context_bin)
end
##
# @param [Object] request
# @param [GRPC::ActiveCall::SingleReqView] call
# @param [Method] method
#
def yield_with_trace request, call, method
Trace.in_span get_name(method) do |span|
modify_span span, request, call, method
start_request span, call, method
begin
grpc_ex = GRPC::Ok.new
yield request: request, call: call, method: method
rescue StandardError => e
grpc_ex = to_grpc_ex(e)
raise e
ensure
finish_request span, grpc_ex
end
end
end
##
# Span name is represented as $package.$service/$method
# cf. https://github.com/census-instrumentation/opencensus-specs/blob/master/trace/gRPC.md#spans
#
# @param [Method] method
# @return [String]
#
def get_name method
"#{method.owner.service_name}/#{camelize(method.name.to_s)}"
end
##
# @param [Method] method
# @return [String]
#
def get_path method
"/" + get_name(method)
end
##
# @param [String] term
# @return [String]
#
def camelize term
term.split("_").map(&:capitalize).join
end
##
# Modify span by custom span modifier
#
# @param [OpenCensus::Trace::SpanBuilder] span
# @param [Object] request
# @param [GRPC::ActiveCall::SingleReqView] call
# @param [Method] method
#
def modify_span span, request, call, method
@span_modifier.call(span, request, call, method) if @span_modifier
end
##
# @param [OpenCensus::Trace::SpanBuilder] span
# @param [GRPC::ActiveCall::SingleReqView] call
# @param [Method] method
#
def start_request span, call, method
span.kind = SpanBuilder::SERVER
span.put_attribute "http.path", get_path(method)
span.put_attribute "http.method", "POST" # gRPC always uses "POST"
if call.metadata["user-agent"]
span.put_attribute "http.user_agent", call.metadata["user-agent"]
end
end
##
# @param [OpenCensus::Trace::SpanBuilder] span
# @param [GRPC::BadStatus] exception
#
def finish_request span, exception
# Set gRPC server status
# https://github.com/census-instrumentation/opencensus-specs/blob/master/trace/gRPC.md#spans
span.set_status exception.code
span.put_attribute "http.status_code", to_http_status(exception)
end
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/CyclomaticComplexity
##
# cf. https://github.com/census-instrumentation/opencensus-specs/blob/master/trace/HTTP.md#mapping-from-http-status-codes-to-trace-status-codes
#
# @param [GRPC::BadStatus] exception
# @return [Integer]
#
def to_http_status exception
case exception
when GRPC::Ok
200
when GRPC::InvalidArgument
400
when GRPC::DeadlineExceeded
504
when GRPC::NotFound
404
when GRPC::PermissionDenied
403
when GRPC::Unauthenticated
401
when GRPC::Aborted
# For GRPC::Aborted, grpc-gateway uses 409. We do the same.
# cf. https://github.com/grpc-ecosystem/grpc-gateway/blob/e8db07a3923d3f5c77dbcea96656afe43a2757a8/runtime/errors.go#L17-L58
409
when GRPC::ResourceExhausted
429
when GRPC::Unimplemented
501
when GRPC::Unavailable
503
when GRPC::Unknown
# NOTE: This is not same with the correct mapping
500
else
# NOTE: Here, we use 500 temporarily.
500
end
end
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/CyclomaticComplexity
##
# @param [Exception] exception
# @return [GRPC::BadStatus]
#
def to_grpc_ex exception
case exception
when GRPC::BadStatus
exception
else
GRPC::Unknown.new(exception.message)
end
end
end
end
end
end