gapic-common 0.23.0 → 0.24.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/lib/gapic/common/version.rb +1 -1
- data/lib/gapic/grpc/service_stub/channel.rb +4 -2
- data/lib/gapic/grpc/service_stub/channel_pool.rb +9 -2
- data/lib/gapic/grpc/service_stub/rpc_call.rb +120 -12
- data/lib/gapic/grpc/service_stub.rb +15 -3
- data/lib/gapic/headers.rb +0 -1
- data/lib/gapic/logging_concerns.rb +210 -0
- data/lib/gapic/rest/client_stub.rb +91 -18
- metadata +36 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dcd83f8068cc42ed2b87bab10ef10ce8f3b3ac6cabc5980c1b52ae1279a9a7b8
|
4
|
+
data.tar.gz: 80137d53ab13ad21af6a083f3f72405952ff844b3b3a49ca71f3ee7339518587
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ff6fac4cf4699bfc105c27a179c3d733c657ea555ed5e35213f34da1a4ea20dd069b947f017b0d3da52f491f55d7a9433cd00e33fe81bb659f7b2015a77cbd9
|
7
|
+
data.tar.gz: 047d76f0a9627aedcc4cfe5658b21fbf9ba7371ea9fb18ec39af4bc84e1fc3e4252853ca5108850dcda681f0a4fb66aafe5cd240f527e72afde6f1709795d45f
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### 0.24.0 (2024-12-05)
|
4
|
+
|
5
|
+
#### Features
|
6
|
+
|
7
|
+
* Log requests and responses ([#1112](https://github.com/googleapis/gapic-generator-ruby/issues/1112))
|
8
|
+
#### Bug Fixes
|
9
|
+
|
10
|
+
* Hardened deadline determination against Time.now hacking ([#942](https://github.com/googleapis/gapic-generator-ruby/issues/942))
|
11
|
+
|
3
12
|
### 0.23.0 (2024-10-15)
|
4
13
|
|
5
14
|
#### Features
|
data/lib/gapic/common/version.rb
CHANGED
@@ -32,12 +32,13 @@ module Gapic
|
|
32
32
|
# Creates a new Channel instance
|
33
33
|
#
|
34
34
|
def initialize grpc_stub_class, endpoint:, credentials:, channel_args: nil, interceptors: nil,
|
35
|
-
on_channel_create: nil
|
35
|
+
on_channel_create: nil, stub_logger: nil
|
36
36
|
@grpc_stub_class = grpc_stub_class
|
37
37
|
@endpoint = endpoint
|
38
38
|
@credentials = credentials
|
39
39
|
@channel_args = Hash channel_args
|
40
40
|
@interceptors = Array interceptors
|
41
|
+
@stub_logger = stub_logger
|
41
42
|
@concurrent_streams = 0
|
42
43
|
@mutex = Mutex.new
|
43
44
|
setup_grpc_stub
|
@@ -88,7 +89,8 @@ module Gapic
|
|
88
89
|
def call_rpc method_name, request, options: nil, &block
|
89
90
|
@mutex.synchronize { @concurrent_streams += 1 }
|
90
91
|
begin
|
91
|
-
|
92
|
+
meth = @grpc_stub.method method_name
|
93
|
+
rpc_call = RpcCall.new meth, stub_logger: @stub_logger, method_name: method_name
|
92
94
|
response = rpc_call.call request, options: options, &block
|
93
95
|
response
|
94
96
|
ensure
|
@@ -30,7 +30,12 @@ module Gapic
|
|
30
30
|
##
|
31
31
|
# Initialize an instance of ServiceStub::ChannelPool
|
32
32
|
#
|
33
|
-
def initialize grpc_stub_class,
|
33
|
+
def initialize grpc_stub_class,
|
34
|
+
endpoint:, credentials:,
|
35
|
+
channel_args: nil,
|
36
|
+
interceptors: nil,
|
37
|
+
config: nil,
|
38
|
+
stub_logger: nil
|
34
39
|
if credentials.is_a? ::GRPC::Core::Channel
|
35
40
|
raise ArgumentError, "Can't create a channel pool with GRPC::Core::Channel as credentials"
|
36
41
|
end
|
@@ -41,6 +46,7 @@ module Gapic
|
|
41
46
|
@channel_args = channel_args
|
42
47
|
@interceptors = interceptors
|
43
48
|
@config = config || Configuration.new
|
49
|
+
@stub_logger = stub_logger
|
44
50
|
|
45
51
|
@channels = (1..@config.channel_count).map { create_channel }
|
46
52
|
end
|
@@ -49,7 +55,8 @@ module Gapic
|
|
49
55
|
# Creates a new channel.
|
50
56
|
def create_channel
|
51
57
|
Channel.new @grpc_stub_class, endpoint: @endpoint, credentials: @credentials, channel_args: @channel_args,
|
52
|
-
interceptors: @interceptors, on_channel_create: @config.on_channel_create
|
58
|
+
interceptors: @interceptors, on_channel_create: @config.on_channel_create,
|
59
|
+
stub_logger: @stub_logger
|
53
60
|
end
|
54
61
|
|
55
62
|
##
|
@@ -13,6 +13,7 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
require "gapic/call_options"
|
16
|
+
require "gapic/logging_concerns"
|
16
17
|
require "grpc/errors"
|
17
18
|
|
18
19
|
module Gapic
|
@@ -32,8 +33,11 @@ module Gapic
|
|
32
33
|
#
|
33
34
|
# @param stub_method [Proc] Used to make a bare rpc call.
|
34
35
|
#
|
35
|
-
def initialize stub_method
|
36
|
+
def initialize stub_method, stub_logger: nil, method_name: nil
|
36
37
|
@stub_method = stub_method
|
38
|
+
@stub_logger = stub_logger
|
39
|
+
@method_name = method_name
|
40
|
+
@request_id = LoggingConcerns.random_uuid4
|
37
41
|
end
|
38
42
|
|
39
43
|
##
|
@@ -44,7 +48,8 @@ module Gapic
|
|
44
48
|
# customize the options object, using keys that match the arguments for {Gapic::CallOptions.new}. This object
|
45
49
|
# should only be used once.
|
46
50
|
#
|
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`.
|
48
53
|
# @yieldparam response [Object] The response object.
|
49
54
|
# @yieldparam operation [::GRPC::ActiveCall::Operation] The RPC operation for the response.
|
50
55
|
#
|
@@ -91,7 +96,7 @@ module Gapic
|
|
91
96
|
# )
|
92
97
|
# response = echo_call.call request, options: options
|
93
98
|
#
|
94
|
-
# @example Accessing the
|
99
|
+
# @example Accessing the RPC operation using a block:
|
95
100
|
# require "google/showcase/v1beta1/echo_pb"
|
96
101
|
# require "google/showcase/v1beta1/echo_services_pb"
|
97
102
|
# require "gapic"
|
@@ -107,8 +112,8 @@ module Gapic
|
|
107
112
|
# echo_call = Gapic::ServiceStub::RpcCall.new echo_stub.method :echo
|
108
113
|
#
|
109
114
|
# request = Google::Showcase::V1beta1::EchoRequest.new
|
110
|
-
# echo_call.call request do |
|
111
|
-
# operation.trailing_metadata
|
115
|
+
# metadata = echo_call.call request do |_response, operation|
|
116
|
+
# throw :response, operation.trailing_metadata
|
112
117
|
# end
|
113
118
|
#
|
114
119
|
def call request, options: nil
|
@@ -117,21 +122,27 @@ module Gapic
|
|
117
122
|
deadline = calculate_deadline options
|
118
123
|
metadata = options.metadata
|
119
124
|
|
125
|
+
try_number = 1
|
120
126
|
retried_exception = nil
|
121
127
|
begin
|
128
|
+
request = log_request request, metadata, try_number
|
122
129
|
operation = stub_method.call request, deadline: deadline, metadata: metadata, return_op: true
|
123
130
|
response = operation.execute
|
124
|
-
|
125
|
-
|
131
|
+
catch :response do
|
132
|
+
response = log_response response, try_number
|
133
|
+
yield response, operation if block_given?
|
134
|
+
response
|
135
|
+
end
|
126
136
|
rescue ::GRPC::DeadlineExceeded => e
|
137
|
+
log_response e, try_number
|
127
138
|
raise Gapic::GRPC::DeadlineExceededError.new e.message, root_cause: retried_exception
|
128
139
|
rescue StandardError => e
|
129
|
-
|
130
|
-
|
131
|
-
end
|
140
|
+
e = normalize_exception e
|
141
|
+
log_response e, try_number
|
132
142
|
|
133
143
|
if check_retry?(deadline) && options.retry_policy.call(e)
|
134
144
|
retried_exception = e
|
145
|
+
try_number += 1
|
135
146
|
retry
|
136
147
|
end
|
137
148
|
|
@@ -145,13 +156,110 @@ module Gapic
|
|
145
156
|
return if options.timeout.nil?
|
146
157
|
return if options.timeout.negative?
|
147
158
|
|
148
|
-
|
159
|
+
current_time + options.timeout
|
149
160
|
end
|
150
161
|
|
151
162
|
def check_retry? deadline
|
152
163
|
return true if deadline.nil?
|
153
164
|
|
154
|
-
deadline >
|
165
|
+
deadline > current_time
|
166
|
+
end
|
167
|
+
|
168
|
+
def current_time
|
169
|
+
# An alternative way of saying Time.now without actually calling
|
170
|
+
# Time.now. This allows clients that replace Time.now (e.g. via the
|
171
|
+
# timecop gem) to do so without interfering with the deadline.
|
172
|
+
nanos = Process.clock_gettime Process::CLOCK_REALTIME, :nanosecond
|
173
|
+
secs_part = nanos / 1_000_000_000
|
174
|
+
nsecs_part = nanos % 1_000_000_000
|
175
|
+
Time.at secs_part, nsecs_part, :nanosecond
|
176
|
+
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
|
155
263
|
end
|
156
264
|
end
|
157
265
|
end
|
@@ -17,6 +17,7 @@ require "googleauth"
|
|
17
17
|
require "gapic/grpc/service_stub/rpc_call"
|
18
18
|
require "gapic/grpc/service_stub/channel"
|
19
19
|
require "gapic/grpc/service_stub/channel_pool"
|
20
|
+
require "gapic/logging_concerns"
|
20
21
|
require "gapic/universe_domain_concerns"
|
21
22
|
|
22
23
|
module Gapic
|
@@ -32,6 +33,7 @@ module Gapic
|
|
32
33
|
#
|
33
34
|
class ServiceStub
|
34
35
|
include UniverseDomainConcerns
|
36
|
+
include LoggingConcerns
|
35
37
|
|
36
38
|
attr_reader :grpc_stub
|
37
39
|
attr_reader :channel_pool
|
@@ -64,6 +66,9 @@ module Gapic
|
|
64
66
|
# be used for intercepting calls before they are executed Interceptors are an EXPERIMENTAL API.
|
65
67
|
# @param channel_pool_config [::Gapic::ServiceStub:ChannelPool::Configuration] The configuration for channel
|
66
68
|
# pool. This argument will raise error when `credentials` is provided as a `::GRPC::Core::Channel`.
|
69
|
+
# @param logger [Logger,:default,nil] An explicit logger to use, or one
|
70
|
+
# of the values `:default` (the default) to construct a default logger,
|
71
|
+
# or `nil` to disable logging explicitly.
|
67
72
|
#
|
68
73
|
def initialize grpc_stub_class,
|
69
74
|
credentials:,
|
@@ -72,13 +77,19 @@ module Gapic
|
|
72
77
|
universe_domain: nil,
|
73
78
|
channel_args: nil,
|
74
79
|
interceptors: nil,
|
75
|
-
channel_pool_config: nil
|
80
|
+
channel_pool_config: nil,
|
81
|
+
logger: :default
|
76
82
|
raise ArgumentError, "grpc_stub_class is required" if grpc_stub_class.nil?
|
77
83
|
|
78
84
|
setup_universe_domain universe_domain: universe_domain,
|
79
85
|
endpoint: endpoint,
|
80
86
|
endpoint_template: endpoint_template,
|
81
87
|
credentials: credentials
|
88
|
+
setup_logging logger: logger,
|
89
|
+
system_name: grpc_stub_class,
|
90
|
+
service: grpc_stub_class,
|
91
|
+
endpoint: self.endpoint,
|
92
|
+
client_id: object_id
|
82
93
|
|
83
94
|
@channel_pool = nil
|
84
95
|
@grpc_stub = nil
|
@@ -102,7 +113,7 @@ module Gapic
|
|
102
113
|
end
|
103
114
|
@channel_pool = ChannelPool.new grpc_stub_class, endpoint: endpoint, credentials: credentials,
|
104
115
|
channel_args: channel_args, interceptors: interceptors,
|
105
|
-
config: channel_pool_config
|
116
|
+
config: channel_pool_config, stub_logger: stub_logger
|
106
117
|
end
|
107
118
|
|
108
119
|
def create_grpc_stub grpc_stub_class, endpoint:, credentials:, channel_args: nil, interceptors: nil
|
@@ -201,7 +212,8 @@ module Gapic
|
|
201
212
|
#
|
202
213
|
def call_rpc method_name, request, options: nil, &block
|
203
214
|
if @channel_pool.nil?
|
204
|
-
|
215
|
+
meth = @grpc_stub.method method_name
|
216
|
+
rpc_call = RpcCall.new meth, stub_logger: stub_logger, method_name: method_name
|
205
217
|
rpc_call.call request, options: options, &block
|
206
218
|
else
|
207
219
|
@channel_pool.call_rpc method_name, request, options: options, &block
|
data/lib/gapic/headers.rb
CHANGED
@@ -38,7 +38,6 @@ module Gapic
|
|
38
38
|
def self.x_goog_api_client ruby_version: nil, lib_name: nil, lib_version: nil, gax_version: nil,
|
39
39
|
gapic_version: nil, grpc_version: nil, rest_version: nil, protobuf_version: nil,
|
40
40
|
transports_version_send: [:grpc]
|
41
|
-
|
42
41
|
ruby_version ||= ::RUBY_VERSION
|
43
42
|
gax_version ||= ::Gapic::Common::VERSION
|
44
43
|
grpc_version ||= ::GRPC::VERSION if defined? ::GRPC::VERSION
|
@@ -0,0 +1,210 @@
|
|
1
|
+
# Copyright 2024 Google LLC
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require "google/cloud/env"
|
16
|
+
require "google/logging/message"
|
17
|
+
require "google/logging/structured_formatter"
|
18
|
+
|
19
|
+
module Gapic
|
20
|
+
##
|
21
|
+
# A mixin module that handles logging setup for a stub.
|
22
|
+
#
|
23
|
+
module LoggingConcerns
|
24
|
+
##
|
25
|
+
# The logger for this object.
|
26
|
+
#
|
27
|
+
# @return [Logger]
|
28
|
+
#
|
29
|
+
attr_reader :logger
|
30
|
+
|
31
|
+
##
|
32
|
+
# @private
|
33
|
+
# A convenience object used by stub-based logging.
|
34
|
+
#
|
35
|
+
class StubLogger
|
36
|
+
OMIT_FILES = [
|
37
|
+
/^#{Regexp.escape __dir__}/
|
38
|
+
].freeze
|
39
|
+
|
40
|
+
def initialize logger: nil, **kwargs
|
41
|
+
@logger = logger
|
42
|
+
@kwargs = kwargs
|
43
|
+
end
|
44
|
+
|
45
|
+
def log severity
|
46
|
+
return unless @logger
|
47
|
+
locations = caller_locations
|
48
|
+
@logger.add severity do
|
49
|
+
builder = LogEntryBuilder.new(**@kwargs)
|
50
|
+
builder.set_source_location_from locations
|
51
|
+
yield builder
|
52
|
+
builder.build
|
53
|
+
rescue StandardError
|
54
|
+
# Do nothing
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def info &block
|
59
|
+
log Logger::INFO, &block
|
60
|
+
end
|
61
|
+
|
62
|
+
def debug &block
|
63
|
+
log Logger::DEBUG, &block
|
64
|
+
end
|
65
|
+
|
66
|
+
##
|
67
|
+
# @private
|
68
|
+
# Builder for a log entry, passed to {StubLogger#log}.
|
69
|
+
#
|
70
|
+
class LogEntryBuilder
|
71
|
+
def initialize system_name: nil,
|
72
|
+
service: nil,
|
73
|
+
endpoint: nil,
|
74
|
+
client_id: nil
|
75
|
+
@system_name = system_name
|
76
|
+
@service = service
|
77
|
+
@endpoint = endpoint
|
78
|
+
@client_id = client_id
|
79
|
+
@message = nil
|
80
|
+
@caller_locations = caller_locations
|
81
|
+
@fields = { "clientId" => @client_id }
|
82
|
+
end
|
83
|
+
|
84
|
+
attr_reader :system_name
|
85
|
+
|
86
|
+
attr_reader :service
|
87
|
+
|
88
|
+
attr_reader :endpoint
|
89
|
+
|
90
|
+
attr_accessor :message
|
91
|
+
|
92
|
+
attr_writer :source_location
|
93
|
+
|
94
|
+
attr_reader :fields
|
95
|
+
|
96
|
+
def set name, value
|
97
|
+
fields[name] = value
|
98
|
+
end
|
99
|
+
|
100
|
+
def set_system_name
|
101
|
+
set "system", system_name
|
102
|
+
end
|
103
|
+
|
104
|
+
def set_service
|
105
|
+
set "serviceName", service
|
106
|
+
end
|
107
|
+
|
108
|
+
def set_credentials_fields creds
|
109
|
+
creds = creds.client if creds.respond_to? :client
|
110
|
+
set "credentialsId", creds.object_id
|
111
|
+
set "credentialsType", creds.class.name
|
112
|
+
set "credentialsScope", creds.scope if creds.respond_to? :scope
|
113
|
+
set "useSelfSignedJWT", creds.enable_self_signed_jwt? if creds.respond_to? :enable_self_signed_jwt?
|
114
|
+
set "universeDomain", creds.universe_domain if creds.respond_to? :universe_domain
|
115
|
+
end
|
116
|
+
|
117
|
+
def source_location
|
118
|
+
@source_location ||= Google::Logging::SourceLocation.for_caller omit_files: OMIT_FILES,
|
119
|
+
locations: @caller_locations
|
120
|
+
end
|
121
|
+
|
122
|
+
def set_source_location_from locations
|
123
|
+
@caller_locations = locations
|
124
|
+
@source_location = nil
|
125
|
+
end
|
126
|
+
|
127
|
+
def build
|
128
|
+
Google::Logging::Message.from message: message, source_location: source_location, fields: fields
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
##
|
134
|
+
# @private
|
135
|
+
# Initialize logging concerns.
|
136
|
+
#
|
137
|
+
def setup_logging logger: :default,
|
138
|
+
stream: nil,
|
139
|
+
formatter: nil,
|
140
|
+
level: nil,
|
141
|
+
system_name: nil,
|
142
|
+
service: nil,
|
143
|
+
endpoint: nil,
|
144
|
+
client_id: nil
|
145
|
+
service = LoggingConcerns.normalize_service service
|
146
|
+
system_name = LoggingConcerns.normalize_system_name system_name
|
147
|
+
logging_env = ENV["GOOGLE_SDK_RUBY_LOGGING_GEMS"].to_s.downcase
|
148
|
+
logger = nil if ["false", "none"].include? logging_env
|
149
|
+
if logger == :default
|
150
|
+
logger = nil
|
151
|
+
if ["true", "all"].include?(logging_env) || logging_env.split(",").include?(system_name)
|
152
|
+
stream ||= $stderr
|
153
|
+
level ||= "DEBUG"
|
154
|
+
formatter ||= Google::Logging::StructuredFormatter.new if Google::Cloud::Env.get.logging_agent_expected?
|
155
|
+
logger = Logger.new stream, progname: system_name, level: level, formatter: formatter
|
156
|
+
end
|
157
|
+
end
|
158
|
+
@logger = logger
|
159
|
+
@stub_logger = StubLogger.new logger: logger,
|
160
|
+
system_name: system_name,
|
161
|
+
service: service,
|
162
|
+
endpoint: endpoint,
|
163
|
+
client_id: client_id
|
164
|
+
end
|
165
|
+
|
166
|
+
# @private
|
167
|
+
attr_reader :stub_logger
|
168
|
+
|
169
|
+
class << self
|
170
|
+
# @private
|
171
|
+
def random_uuid4
|
172
|
+
ary = Random.bytes 16
|
173
|
+
ary.setbyte 6, ((ary.getbyte(6) & 0x0f) | 0x40)
|
174
|
+
ary.setbyte 8, ((ary.getbyte(8) & 0x3f) | 0x80)
|
175
|
+
ary.unpack("H8H4H4H4H12").join "-"
|
176
|
+
end
|
177
|
+
|
178
|
+
# @private
|
179
|
+
def normalize_system_name input
|
180
|
+
case input
|
181
|
+
when String
|
182
|
+
input
|
183
|
+
when Class
|
184
|
+
input.name.split("::")[..-3]
|
185
|
+
.map { |elem| elem.scan(/[A-Z][A-Z]*(?=[A-Z][a-z0-9]|$)|[A-Z][a-z0-9]+/).map(&:downcase).join("_") }
|
186
|
+
.join("-")
|
187
|
+
else
|
188
|
+
"googleapis"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# @private
|
193
|
+
def normalize_service input
|
194
|
+
case input
|
195
|
+
when String
|
196
|
+
input
|
197
|
+
when Class
|
198
|
+
mod = input.name.split("::")[..-2].inject(Object) { |m, n| m.const_get n }
|
199
|
+
if mod.const_defined? "Service"
|
200
|
+
mod.const_get("Service").service_name
|
201
|
+
else
|
202
|
+
name_segments = input.name.split("::")[..-3]
|
203
|
+
mod = name_segments.inject(Object) { |m, n| m.const_get n }
|
204
|
+
name_segments.join "." if mod.const_defined? "Rest"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
@@ -13,6 +13,7 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
require "googleauth"
|
16
|
+
require "gapic/logging_concerns"
|
16
17
|
require "gapic/rest/faraday_middleware"
|
17
18
|
require "gapic/universe_domain_concerns"
|
18
19
|
require "faraday/retry"
|
@@ -28,9 +29,11 @@ module Gapic
|
|
28
29
|
#
|
29
30
|
class ClientStub
|
30
31
|
include UniverseDomainConcerns
|
32
|
+
include LoggingConcerns
|
31
33
|
|
32
34
|
##
|
33
35
|
# Initializes with an endpoint and credentials
|
36
|
+
#
|
34
37
|
# @param endpoint [String] The endpoint of the API. Overrides any endpoint_template.
|
35
38
|
# @param endpoint_template [String] The endpoint of the API, where the
|
36
39
|
# universe domain component of the hostname is marked by the string in
|
@@ -41,12 +44,14 @@ module Gapic
|
|
41
44
|
# Credentials to send with calls in form of a googleauth credentials object.
|
42
45
|
# (see the [googleauth docs](https://googleapis.dev/ruby/googleauth/latest/index.html))
|
43
46
|
# @param numeric_enums [Boolean] Whether to signal the server to JSON-encode enums as ints
|
44
|
-
#
|
45
47
|
# @param raise_faraday_errors [Boolean]
|
46
48
|
# Whether to raise Faraday errors instead of wrapping them in `Gapic::Rest::Error`
|
47
49
|
# Added for backwards compatibility.
|
48
50
|
# Default is `true`. All REST clients (except for old versions of `google-cloud-compute-v1`)
|
49
51
|
# should explicitly set this parameter to `false`.
|
52
|
+
# @param logger [Logger,:default,nil] An explicit logger to use, or one
|
53
|
+
# of the values `:default` (the default) to construct a default logger,
|
54
|
+
# or `nil` to disable logging explicitly.
|
50
55
|
#
|
51
56
|
# @yield [Faraday::Connection]
|
52
57
|
#
|
@@ -55,7 +60,10 @@ module Gapic
|
|
55
60
|
endpoint_template: nil,
|
56
61
|
universe_domain: nil,
|
57
62
|
numeric_enums: false,
|
58
|
-
raise_faraday_errors: true
|
63
|
+
raise_faraday_errors: true,
|
64
|
+
logging_system: nil,
|
65
|
+
service_name: nil,
|
66
|
+
logger: :default
|
59
67
|
setup_universe_domain universe_domain: universe_domain,
|
60
68
|
endpoint: endpoint,
|
61
69
|
endpoint_template: endpoint_template,
|
@@ -65,6 +73,12 @@ module Gapic
|
|
65
73
|
endpoint_url = "https://#{endpoint_url}" unless /^https?:/.match? endpoint_url
|
66
74
|
endpoint_url = endpoint_url.sub %r{/$}, ""
|
67
75
|
|
76
|
+
setup_logging logger: logger,
|
77
|
+
system_name: logging_system,
|
78
|
+
service: service_name,
|
79
|
+
endpoint: endpoint_url,
|
80
|
+
client_id: object_id
|
81
|
+
|
68
82
|
@numeric_enums = numeric_enums
|
69
83
|
|
70
84
|
@raise_faraday_errors = raise_faraday_errors
|
@@ -88,8 +102,8 @@ module Gapic
|
|
88
102
|
# @param options [::Gapic::CallOptions,Hash] gapic options to be applied
|
89
103
|
# to the REST call. Currently only timeout and headers are supported.
|
90
104
|
# @return [Faraday::Response]
|
91
|
-
def make_get_request uri:, params: {}, options: {}
|
92
|
-
make_http_request :get, uri: uri, body: nil, params: params, options: options
|
105
|
+
def make_get_request uri:, params: {}, options: {}, method_name: nil
|
106
|
+
make_http_request :get, uri: uri, body: nil, params: params, options: options, method_name: method_name
|
93
107
|
end
|
94
108
|
|
95
109
|
##
|
@@ -100,8 +114,8 @@ module Gapic
|
|
100
114
|
# @param options [::Gapic::CallOptions,Hash] gapic options to be applied
|
101
115
|
# to the REST call. Currently only timeout and headers are supported.
|
102
116
|
# @return [Faraday::Response]
|
103
|
-
def make_delete_request uri:, params: {}, options: {}
|
104
|
-
make_http_request :delete, uri: uri, body: nil, params: params, options: options
|
117
|
+
def make_delete_request uri:, params: {}, options: {}, method_name: nil
|
118
|
+
make_http_request :delete, uri: uri, body: nil, params: params, options: options, method_name: method_name
|
105
119
|
end
|
106
120
|
|
107
121
|
##
|
@@ -113,8 +127,8 @@ module Gapic
|
|
113
127
|
# @param options [::Gapic::CallOptions,Hash] gapic options to be applied
|
114
128
|
# to the REST call. Currently only timeout and headers are supported.
|
115
129
|
# @return [Faraday::Response]
|
116
|
-
def make_patch_request uri:, body:, params: {}, options: {}
|
117
|
-
make_http_request :patch, uri: uri, body: body, params: params, options: options
|
130
|
+
def make_patch_request uri:, body:, params: {}, options: {}, method_name: nil
|
131
|
+
make_http_request :patch, uri: uri, body: body, params: params, options: options, method_name: method_name
|
118
132
|
end
|
119
133
|
|
120
134
|
##
|
@@ -126,8 +140,8 @@ module Gapic
|
|
126
140
|
# @param options [::Gapic::CallOptions,Hash] gapic options to be applied
|
127
141
|
# to the REST call. Currently only timeout and headers are supported.
|
128
142
|
# @return [Faraday::Response]
|
129
|
-
def make_post_request uri:, body: nil, params: {}, options: {}
|
130
|
-
make_http_request :post, uri: uri, body: body, params: params, options: options
|
143
|
+
def make_post_request uri:, body: nil, params: {}, options: {}, method_name: nil
|
144
|
+
make_http_request :post, uri: uri, body: body, params: params, options: options, method_name: method_name
|
131
145
|
end
|
132
146
|
|
133
147
|
##
|
@@ -139,8 +153,8 @@ module Gapic
|
|
139
153
|
# @param options [::Gapic::CallOptions,Hash] gapic options to be applied
|
140
154
|
# to the REST call. Currently only timeout and headers are supported.
|
141
155
|
# @return [Faraday::Response]
|
142
|
-
def make_put_request uri:, body: nil, params: {}, options: {}
|
143
|
-
make_http_request :put, uri: uri, body: body, params: params, options: options
|
156
|
+
def make_put_request uri:, body: nil, params: {}, options: {}, method_name: nil
|
157
|
+
make_http_request :put, uri: uri, body: body, params: params, options: options, method_name: method_name
|
144
158
|
end
|
145
159
|
|
146
160
|
##
|
@@ -154,27 +168,38 @@ module Gapic
|
|
154
168
|
# @param is_server_streaming [Boolean] flag if method is streaming
|
155
169
|
# @yieldparam chunk [String] The chunk of data received during server streaming.
|
156
170
|
# @return [Faraday::Response]
|
157
|
-
def make_http_request verb,
|
171
|
+
def make_http_request verb,
|
172
|
+
uri:, body:, params:, options:,
|
173
|
+
is_server_streaming: false, method_name: nil,
|
174
|
+
&block
|
158
175
|
# Converts hash and nil to an options object
|
159
176
|
options = ::Gapic::CallOptions.new(**options.to_h) unless options.is_a? ::Gapic::CallOptions
|
160
177
|
deadline = calculate_deadline options
|
161
178
|
retried_exception = nil
|
162
179
|
next_timeout = get_timeout deadline
|
180
|
+
request_id = LoggingConcerns.random_uuid4
|
181
|
+
try_number = 1
|
163
182
|
|
164
183
|
begin
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
184
|
+
log_request method_name, request_id, try_number, body, options.metadata
|
185
|
+
response = base_make_http_request verb: verb, uri: uri, body: body,
|
186
|
+
params: params, metadata: options.metadata,
|
187
|
+
timeout: next_timeout,
|
188
|
+
is_server_streaming: is_server_streaming,
|
189
|
+
&block
|
190
|
+
log_response method_name, request_id, try_number, response, is_server_streaming
|
191
|
+
response
|
170
192
|
rescue ::Faraday::TimeoutError => e
|
193
|
+
log_response method_name, request_id, try_number, e, is_server_streaming
|
171
194
|
raise if @raise_faraday_errors
|
172
195
|
raise Gapic::Rest::DeadlineExceededError.wrap_faraday_error e, root_cause: retried_exception
|
173
196
|
rescue ::Faraday::Error => e
|
197
|
+
log_response method_name, request_id, try_number, e, is_server_streaming
|
174
198
|
next_timeout = get_timeout deadline
|
175
199
|
|
176
200
|
if check_retry?(next_timeout) && options.retry_policy.call(e)
|
177
201
|
retried_exception = e
|
202
|
+
try_number += 1
|
178
203
|
retry
|
179
204
|
end
|
180
205
|
|
@@ -251,6 +276,54 @@ module Gapic
|
|
251
276
|
|
252
277
|
timeout.positive?
|
253
278
|
end
|
279
|
+
|
280
|
+
def log_request method_name, request_id, try_number, body, metadata
|
281
|
+
return unless stub_logger
|
282
|
+
stub_logger.info do |entry|
|
283
|
+
entry.set_system_name
|
284
|
+
entry.set_service
|
285
|
+
entry.set "rpcName", method_name
|
286
|
+
entry.set "retryAttempt", try_number
|
287
|
+
entry.set "requestId", request_id
|
288
|
+
entry.message = "Sending request to #{entry.service}.#{method_name} (try #{try_number})"
|
289
|
+
end
|
290
|
+
body = body.to_s
|
291
|
+
metadata = metadata.to_h rescue {}
|
292
|
+
return if body.empty? && metadata.empty?
|
293
|
+
stub_logger.debug do |entry|
|
294
|
+
entry.set "requestId", request_id
|
295
|
+
entry.set "request", body
|
296
|
+
entry.set "headers", metadata
|
297
|
+
entry.message = "(request payload as JSON)"
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
def log_response method_name, request_id, try_number, response, is_server_streaming
|
302
|
+
return unless stub_logger
|
303
|
+
stub_logger.info do |entry|
|
304
|
+
entry.set_system_name
|
305
|
+
entry.set_service
|
306
|
+
entry.set "rpcName", method_name
|
307
|
+
entry.set "retryAttempt", try_number
|
308
|
+
entry.set "requestId", request_id
|
309
|
+
if response.is_a? StandardError
|
310
|
+
entry.set "exception", response.to_s
|
311
|
+
entry.message = "Received error for #{entry.service}.#{method_name} (try #{try_number}): #{response}"
|
312
|
+
elsif is_server_streaming
|
313
|
+
entry.message = "Receiving stream for #{entry.service}.#{method_name} (try #{try_number})"
|
314
|
+
else
|
315
|
+
entry.message = "Received response for #{entry.service}.#{method_name} (try #{try_number})"
|
316
|
+
end
|
317
|
+
end
|
318
|
+
return if is_server_streaming || !response.respond_to?(:body)
|
319
|
+
body = response.body.to_s
|
320
|
+
return if body.empty?
|
321
|
+
stub_logger.debug do |entry|
|
322
|
+
entry.set "requestId", request_id
|
323
|
+
entry.set "response", body
|
324
|
+
entry.message = "(response payload as JSON)"
|
325
|
+
end
|
326
|
+
end
|
254
327
|
end
|
255
328
|
end
|
256
329
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gapic-common
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.24.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Google API Authors
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-12-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -84,14 +84,42 @@ dependencies:
|
|
84
84
|
requirements:
|
85
85
|
- - "~>"
|
86
86
|
- !ruby/object:Gem::Version
|
87
|
-
version: '1.
|
87
|
+
version: '1.12'
|
88
88
|
type: :runtime
|
89
89
|
prerelease: false
|
90
90
|
version_requirements: !ruby/object:Gem::Requirement
|
91
91
|
requirements:
|
92
92
|
- - "~>"
|
93
93
|
- !ruby/object:Gem::Version
|
94
|
-
version: '1.
|
94
|
+
version: '1.12'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: google-cloud-env
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - "~>"
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '2.2'
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - "~>"
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '2.2'
|
109
|
+
- !ruby/object:Gem::Dependency
|
110
|
+
name: google-logging-utils
|
111
|
+
requirement: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - "~>"
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0.1'
|
116
|
+
type: :runtime
|
117
|
+
prerelease: false
|
118
|
+
version_requirements: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - "~>"
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0.1'
|
95
123
|
- !ruby/object:Gem::Dependency
|
96
124
|
name: google-protobuf
|
97
125
|
requirement: !ruby/object:Gem::Requirement
|
@@ -118,14 +146,14 @@ dependencies:
|
|
118
146
|
requirements:
|
119
147
|
- - "~>"
|
120
148
|
- !ruby/object:Gem::Version
|
121
|
-
version: '1.
|
149
|
+
version: '1.66'
|
122
150
|
type: :runtime
|
123
151
|
prerelease: false
|
124
152
|
version_requirements: !ruby/object:Gem::Requirement
|
125
153
|
requirements:
|
126
154
|
- - "~>"
|
127
155
|
- !ruby/object:Gem::Version
|
128
|
-
version: '1.
|
156
|
+
version: '1.66'
|
129
157
|
description:
|
130
158
|
email:
|
131
159
|
- googleapis-packages@google.com
|
@@ -159,6 +187,7 @@ files:
|
|
159
187
|
- lib/gapic/grpc/service_stub/rpc_call.rb
|
160
188
|
- lib/gapic/grpc/status_details.rb
|
161
189
|
- lib/gapic/headers.rb
|
190
|
+
- lib/gapic/logging_concerns.rb
|
162
191
|
- lib/gapic/lru_hash.rb
|
163
192
|
- lib/gapic/operation.rb
|
164
193
|
- lib/gapic/operation/retry_policy.rb
|
@@ -196,7 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
196
225
|
- !ruby/object:Gem::Version
|
197
226
|
version: '0'
|
198
227
|
requirements: []
|
199
|
-
rubygems_version: 3.5.
|
228
|
+
rubygems_version: 3.5.23
|
200
229
|
signing_key:
|
201
230
|
specification_version: 4
|
202
231
|
summary: Common code for GAPIC-generated API clients
|