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 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