1313# limitations under the License.
1414
1515require "gapic/call_options"
16+ require "gapic/logging_concerns"
1617require "grpc/errors"
1718
1819module Gapic
@@ -32,8 +33,11 @@ class RpcCall
3233 #
3334 # @param stub_method [Proc] Used to make a bare rpc call.
3435 #
35- def initialize stub_method
36+ def initialize stub_method , stub_logger : nil , method_name : nil
3637 @stub_method = stub_method
38+ @stub_logger = stub_logger
39+ @method_name = method_name
40+ @request_id = LoggingConcerns . random_uuid4
3741 end
3842
3943 ##
@@ -44,7 +48,8 @@ def initialize stub_method
4448 # customize the options object, using keys that match the arguments for {Gapic::CallOptions.new}. This object
4549 # should only be used once.
4650 #
47- # @yield [response, operation] Access the response along with the RPC operation.
51+ # @yield [response, operation] Access the response along with the RPC operation. Additionally, throwing
52+ # `:response, obj` within the block will change the return value to `obj`.
4853 # @yieldparam response [Object] The response object.
4954 # @yieldparam operation [::GRPC::ActiveCall::Operation] The RPC operation for the response.
5055 #
@@ -91,7 +96,7 @@ def initialize stub_method
9196 # )
9297 # response = echo_call.call request, options: options
9398 #
94- # @example Accessing the response and RPC operation using a block:
99+ # @example Accessing the RPC operation using a block:
95100 # require "google/showcase/v1beta1/echo_pb"
96101 # require "google/showcase/v1beta1/echo_services_pb"
97102 # require "gapic"
@@ -107,8 +112,8 @@ def initialize stub_method
107112 # echo_call = Gapic::ServiceStub::RpcCall.new echo_stub.method :echo
108113 #
109114 # request = Google::Showcase::V1beta1::EchoRequest.new
110- # echo_call.call request do |response , operation|
111- # operation.trailing_metadata
115+ # metadata = echo_call.call request do |_response , operation|
116+ # throw :response, operation.trailing_metadata
112117 # end
113118 #
114119 def call request , options : nil
@@ -117,21 +122,27 @@ def call request, options: nil
117122 deadline = calculate_deadline options
118123 metadata = options . metadata
119124
125+ try_number = 1
120126 retried_exception = nil
121127 begin
128+ request = log_request request , metadata , try_number
122129 operation = stub_method . call request , deadline : deadline , metadata : metadata , return_op : true
123130 response = operation . execute
124- yield response , operation if block_given?
125- response
131+ catch :response do
132+ response = log_response response , try_number
133+ yield response , operation if block_given?
134+ response
135+ end
126136 rescue ::GRPC ::DeadlineExceeded => e
137+ log_response e , try_number
127138 raise Gapic ::GRPC ::DeadlineExceededError . new e . message , root_cause : retried_exception
128139 rescue StandardError => e
129- if e . is_a? ( ::GRPC ::Unavailable ) && /Signet::AuthorizationError/ =~ e . message
130- e = Gapic ::GRPC ::AuthorizationError . new e . message . gsub ( %r{^\d +:} , "" )
131- end
140+ e = normalize_exception e
141+ log_response e , try_number
132142
133143 if check_retry? ( deadline ) && options . retry_policy . call ( e )
134144 retried_exception = e
145+ try_number += 1
135146 retry
136147 end
137148
@@ -163,6 +174,93 @@ def current_time
163174 nsecs_part = nanos % 1_000_000_000
164175 Time . at secs_part , nsecs_part , :nanosecond
165176 end
177+
178+ def normalize_exception exception
179+ if exception . is_a? ( ::GRPC ::Unavailable ) && /Signet::AuthorizationError/ =~ exception . message
180+ exception = Gapic ::GRPC ::AuthorizationError . new exception . message . gsub ( %r{^\d +:} , "" )
181+ end
182+ exception
183+ end
184+
185+ def log_request request , metadata , try_number
186+ return request unless @stub_logger
187+ @stub_logger . info do |entry |
188+ entry . set_system_name
189+ entry . set_service
190+ entry . set "rpcName" , @method_name
191+ entry . set "retryAttempt" , try_number
192+ entry . set "requestId" , @request_id
193+ entry . message =
194+ if request . is_a? Enumerable
195+ "Sending stream to #{ entry . service } .#{ @method_name } (try #{ try_number } )"
196+ else
197+ "Sending request to #{ entry . service } .#{ @method_name } (try #{ try_number } )"
198+ end
199+ end
200+ loggable_metadata = metadata . to_h rescue { }
201+ if request . is_a? Enumerable
202+ request . lazy . map do |req |
203+ log_single_request req , loggable_metadata
204+ end
205+ else
206+ log_single_request request , loggable_metadata
207+ end
208+ end
209+
210+ def log_single_request request , metadata
211+ request_content = request . respond_to? ( :to_h ) ? ( request . to_h rescue { } ) : request . to_s
212+ if !request_content . empty? || !metadata . empty?
213+ @stub_logger . debug do |entry |
214+ entry . set "requestId" , @request_id
215+ entry . set "request" , request_content
216+ entry . set "headers" , metadata
217+ entry . message = "(request payload as #{ request . class } )"
218+ end
219+ end
220+ request
221+ end
222+
223+ def log_response response , try_number
224+ return response unless @stub_logger
225+ @stub_logger . info do |entry |
226+ entry . set_system_name
227+ entry . set_service
228+ entry . set "rpcName" , @method_name
229+ entry . set "retryAttempt" , try_number
230+ entry . set "requestId" , @request_id
231+ case response
232+ when StandardError
233+ entry . set "exception" , response . to_s
234+ entry . message = "Received error for #{ entry . service } .#{ @method_name } (try #{ try_number } ): #{ response } "
235+ when Enumerable
236+ entry . message = "Receiving stream for #{ entry . service } .#{ @method_name } (try #{ try_number } )"
237+ else
238+ entry . message = "Received response for #{ entry . service } .#{ @method_name } (try #{ try_number } )"
239+ end
240+ end
241+ case response
242+ when StandardError
243+ response
244+ when Enumerable
245+ response . lazy . map do |resp |
246+ log_single_response resp
247+ end
248+ else
249+ log_single_response response
250+ end
251+ end
252+
253+ def log_single_response response
254+ response_content = response . respond_to? ( :to_h ) ? ( response . to_h rescue { } ) : response . to_s
255+ unless response_content . empty?
256+ @stub_logger . debug do |entry |
257+ entry . set "requestId" , @request_id
258+ entry . set "response" , response_content
259+ entry . message = "(response payload as #{ response . class } )"
260+ end
261+ end
262+ response
263+ end
166264 end
167265 end
168266end
0 commit comments