gapic-common 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a2024379257fb476ddc9d24c767c78625a18e00ea07ddeabafa58130d1fa2185
4
- data.tar.gz: cb1daa2c812ca07c9ba5dd8e4a8985b1f770538ef4051ef52ff2b9ce513d9527
3
+ metadata.gz: dcd83f8068cc42ed2b87bab10ef10ce8f3b3ac6cabc5980c1b52ae1279a9a7b8
4
+ data.tar.gz: 80137d53ab13ad21af6a083f3f72405952ff844b3b3a49ca71f3ee7339518587
5
5
  SHA512:
6
- metadata.gz: aaf2ce7ebb30a22733a7e75124439d3bda7c07e06865f812f69881ff90206103325ed853c6853f70befbaa89c2de288f6ad1b642ff73ac72d59fe9917cc745a9
7
- data.tar.gz: a4bf7f9d40eb38a5891701ffcb0d6779c72ca970908ad5fe3b3c312f091fd504c9ec865fdff01a81576e57a1b725197645e8a535351a17221a0e046508e8baca
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
@@ -14,6 +14,6 @@
14
14
 
15
15
  module Gapic
16
16
  module Common
17
- VERSION = "0.23.0".freeze
17
+ VERSION = "0.24.0".freeze
18
18
  end
19
19
  end
@@ -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
- rpc_call = RpcCall.new @grpc_stub.method method_name
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, endpoint:, credentials:, channel_args: nil, interceptors: nil, config: nil
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 response and RPC operation using a block:
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 |response, operation|
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
- 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
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
- 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
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
- Time.now + options.timeout
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 > Time.now
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
- rpc_call = RpcCall.new @grpc_stub.method method_name
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, uri:, body:, params:, options:, is_server_streaming: false, &block
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
- base_make_http_request(verb: verb, uri: uri, body: body,
166
- params: params, metadata: options.metadata,
167
- timeout: next_timeout,
168
- is_server_streaming: is_server_streaming,
169
- &block)
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.23.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-10-23 00:00:00.000000000 Z
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.11'
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.11'
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.65'
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.65'
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.21
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