google-cloud-logging 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +18 -0
  3. data/AUTHENTICATION.md +178 -0
  4. data/CHANGELOG.md +407 -0
  5. data/CODE_OF_CONDUCT.md +40 -0
  6. data/CONTRIBUTING.md +188 -0
  7. data/INSTRUMENTATION.md +71 -0
  8. data/LICENSE +201 -0
  9. data/LOGGING.md +32 -0
  10. data/OVERVIEW.md +321 -0
  11. data/TROUBLESHOOTING.md +31 -0
  12. data/lib/google-cloud-logging.rb +161 -0
  13. data/lib/google/cloud/logging.rb +188 -0
  14. data/lib/google/cloud/logging/async_writer.rb +513 -0
  15. data/lib/google/cloud/logging/convert.rb +70 -0
  16. data/lib/google/cloud/logging/credentials.rb +44 -0
  17. data/lib/google/cloud/logging/entry.rb +528 -0
  18. data/lib/google/cloud/logging/entry/http_request.rb +167 -0
  19. data/lib/google/cloud/logging/entry/list.rb +178 -0
  20. data/lib/google/cloud/logging/entry/operation.rb +91 -0
  21. data/lib/google/cloud/logging/entry/source_location.rb +85 -0
  22. data/lib/google/cloud/logging/errors.rb +101 -0
  23. data/lib/google/cloud/logging/log/list.rb +156 -0
  24. data/lib/google/cloud/logging/logger.rb +633 -0
  25. data/lib/google/cloud/logging/metric.rb +168 -0
  26. data/lib/google/cloud/logging/metric/list.rb +170 -0
  27. data/lib/google/cloud/logging/middleware.rb +307 -0
  28. data/lib/google/cloud/logging/project.rb +838 -0
  29. data/lib/google/cloud/logging/rails.rb +232 -0
  30. data/lib/google/cloud/logging/resource.rb +85 -0
  31. data/lib/google/cloud/logging/resource_descriptor.rb +137 -0
  32. data/lib/google/cloud/logging/resource_descriptor/list.rb +175 -0
  33. data/lib/google/cloud/logging/service.rb +239 -0
  34. data/lib/google/cloud/logging/sink.rb +315 -0
  35. data/lib/google/cloud/logging/sink/list.rb +168 -0
  36. data/lib/google/cloud/logging/version.rb +22 -0
  37. metadata +304 -0
@@ -0,0 +1,188 @@
1
+ # Copyright 2016 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
+
16
+ require "google-cloud-logging"
17
+ require "google/cloud/logging/project"
18
+ require "google/cloud/config"
19
+ require "google/cloud/env"
20
+ require "stackdriver/core"
21
+
22
+ module Google
23
+ module Cloud
24
+ ##
25
+ # # Stackdriver Logging
26
+ #
27
+ # The Stackdriver Logging service collects and stores logs from applications
28
+ # and services on the Google Cloud Platform, giving you fine-grained,
29
+ # programmatic control over your projects' logs. You can use the Stackdriver
30
+ # Logging API to:
31
+ #
32
+ # * Read and filter log entries
33
+ # * Export your log entries to Cloud Storage, BigQuery, or Cloud Pub/Sub
34
+ # * Create logs-based metrics for use in Cloud Monitoring
35
+ # * Write log entries
36
+ #
37
+ # For general information about Stackdriver Logging, read [Stackdriver
38
+ # Logging Documentation](https://cloud.google.com/logging/docs/).
39
+ #
40
+ # See {file:OVERVIEW.md Stackdriver Logging Overview}.
41
+ #
42
+ module Logging
43
+ ##
44
+ # Creates a new object for connecting to the Stackdriver Logging service.
45
+ # Each call creates a new connection.
46
+ #
47
+ # For more information on connecting to Google Cloud see the
48
+ # {file:AUTHENTICATION.md Authentication Guide}.
49
+ #
50
+ # @param [String] project_id Project identifier for the Stackdriver
51
+ # Logging service you are connecting to. If not present, the default
52
+ # project for the credentials is used.
53
+ # @param [String, Hash, Google::Auth::Credentials] credentials The path to
54
+ # the keyfile as a String, the contents of the keyfile as a Hash, or a
55
+ # Google::Auth::Credentials object. (See {Logging::Credentials})
56
+ # @param [String, Array<String>] scope The OAuth 2.0 scopes controlling
57
+ # the set of resources and operations that the connection can access.
58
+ # See [Using OAuth 2.0 to Access Google
59
+ # APIs](https://developers.google.com/identity/protocols/OAuth2).
60
+ #
61
+ # The default scope is:
62
+ #
63
+ # * `https://www.googleapis.com/auth/logging.admin`
64
+ #
65
+ # @param [Integer] timeout Default timeout to use in requests. Optional.
66
+ # @param [String] endpoint Override of the endpoint host name. Optional.
67
+ # If the param is nil, uses the default endpoint.
68
+ # @param [String] project Alias for the `project_id` argument. Deprecated.
69
+ # @param [String] keyfile Alias for the `credentials` argument.
70
+ # Deprecated.
71
+ #
72
+ # @return [Google::Cloud::Logging::Project]
73
+ #
74
+ # @example
75
+ # require "google/cloud/logging"
76
+ #
77
+ # logging = Google::Cloud::Logging.new
78
+ #
79
+ # entries = logging.entries
80
+ # entries.each do |e|
81
+ # puts "[#{e.timestamp}] #{e.log_name} #{e.payload.inspect}"
82
+ # end
83
+ #
84
+ def self.new project_id: nil,
85
+ credentials: nil,
86
+ scope: nil,
87
+ timeout: nil,
88
+ endpoint: nil,
89
+ project: nil,
90
+ keyfile: nil
91
+ project_id ||= (project || default_project_id)
92
+ scope ||= configure.scope
93
+ timeout ||= configure.timeout
94
+ endpoint ||= configure.endpoint
95
+ credentials ||= (keyfile || default_credentials(scope: scope))
96
+
97
+ unless credentials.is_a? Google::Auth::Credentials
98
+ credentials = Logging::Credentials.new credentials, scope: scope
99
+ end
100
+
101
+ if credentials.respond_to? :project_id
102
+ project_id ||= credentials.project_id
103
+ end
104
+ project_id = project_id.to_s # Always cast to a string
105
+ raise ArgumentError, "project_id is missing" if project_id.empty?
106
+
107
+ service = Logging::Service.new project_id, credentials, host: endpoint, timeout: timeout
108
+ Logging::Project.new service
109
+ end
110
+
111
+ ##
112
+ # Configure the Google::Cloud::Logging::Middleware when used in a
113
+ # Rack-based application.
114
+ #
115
+ # The following Stackdriver Logging configuration parameters are
116
+ # supported:
117
+ #
118
+ # * `project_id` - (String) Project identifier for the Stackdriver
119
+ # Logging service you are connecting to. (The parameter `project` is
120
+ # considered deprecated, but may also be used.)
121
+ # * `credentials` - (String, Hash, Google::Auth::Credentials) The path to
122
+ # the keyfile as a String, the contents of the keyfile as a Hash, or a
123
+ # Google::Auth::Credentials object. (See {Logging::Credentials}) (The
124
+ # parameter `keyfile` is considered deprecated, but may also be used.)
125
+ # * `scope` - (String, Array<String>) The OAuth 2.0 scopes controlling
126
+ # the set of resources and operations that the connection can access.
127
+ # * `timeout` - (Integer) Default timeout to use in requests.
128
+ # * `endpoint` - (String) Override of the endpoint host name, or `nil`
129
+ # to use the default endpoint.
130
+ # * `log_name` - (String) Name of the application log file. Default:
131
+ # `"ruby_app_log"`
132
+ # * `log_name_map` - (Hash) Map specific request routes to other log.
133
+ # Default: `{ "/_ah/health" => "ruby_health_check_log" }`
134
+ # * `monitored_resource.type` (String) Resource type name. See [full
135
+ # list](https://cloud.google.com/logging/docs/api/v2/resource-list).
136
+ # Self discovered on GCP.
137
+ # * `monitored_resource.labels` -(Hash) Resource labels. See [full
138
+ # list](https://cloud.google.com/logging/docs/api/v2/resource-list).
139
+ # Self discovered on GCP.
140
+ # * `labels` - (Hash) User defined labels. A `Hash` of label names to
141
+ # string label values or callables/`Proc` which are functions of the
142
+ # Rack environment.
143
+ # * `set_default_logger_on_rails_init` - (Boolean) Whether Google Cloud
144
+ # Logging Logger should be allowed to start background threads and open
145
+ # gRPC connections during Rails initialization. This should only be used
146
+ # with a non-forking web server. Web servers such as Puma and Unicorn
147
+ # should not set this, and instead set the Rails logger to a Google
148
+ # Cloud Logging Logger object on the worker process by calling
149
+ # {Railtie.set_default_logger} at the appropriate time, such as a
150
+ # post-fork hook.
151
+ # * `on_error` - (Proc) A Proc to be run when an error is encountered
152
+ # on a background thread. The Proc must take the error object as the
153
+ # single argument. (See {AsyncWriter.on_error}.)
154
+ #
155
+ # See the [Configuration
156
+ # Guide](https://googleapis.dev/ruby/stackdriver/latest/file.INSTRUMENTATION_CONFIGURATION.html)
157
+ # for full configuration parameters.
158
+ #
159
+ # @return [Google::Cloud::Config] The configuration object
160
+ # the Google::Cloud::Logging module uses.
161
+ #
162
+ def self.configure
163
+ yield Google::Cloud.configure.logging if block_given?
164
+
165
+ Google::Cloud.configure.logging
166
+ end
167
+
168
+ ##
169
+ # @private Default project.
170
+ def self.default_project_id
171
+ Google::Cloud.configure.logging.project_id ||
172
+ Google::Cloud.configure.project_id ||
173
+ Google::Cloud.env.project_id
174
+ end
175
+
176
+ ##
177
+ # @private Default credentials.
178
+ def self.default_credentials scope: nil
179
+ Google::Cloud.configure.logging.credentials ||
180
+ Google::Cloud.configure.credentials ||
181
+ Logging::Credentials.default(scope: scope)
182
+ end
183
+ end
184
+ end
185
+
186
+ # @private
187
+ Logging = Cloud::Logging unless const_defined? :Logging
188
+ end
@@ -0,0 +1,513 @@
1
+ # Copyright 2016 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
+
16
+ require "monitor"
17
+ require "concurrent"
18
+ require "google/cloud/logging/errors"
19
+
20
+ module Google
21
+ module Cloud
22
+ module Logging
23
+ ##
24
+ # # AsyncWriter
25
+ #
26
+ # AsyncWriter buffers, batches, and transmits log entries efficiently.
27
+ # Writing log entries is asynchronous and will not block.
28
+ #
29
+ # Batches that cannot be delivered immediately are queued. When the queue
30
+ # is full new batch requests will raise errors that can be consumed using
31
+ # the {#on_error} callback. This provides back pressure in case the writer
32
+ # cannot keep up with requests.
33
+ #
34
+ # This object is thread-safe; it may accept write requests from
35
+ # multiple threads simultaneously, and will serialize them when
36
+ # executing in the background thread.
37
+ #
38
+ # @example
39
+ # require "google/cloud/logging"
40
+ #
41
+ # logging = Google::Cloud::Logging.new
42
+ #
43
+ # async = logging.async_writer
44
+ #
45
+ # entry1 = logging.entry payload: "Job started."
46
+ # entry2 = logging.entry payload: "Job completed."
47
+ #
48
+ # labels = { job_size: "large", job_code: "red" }
49
+ # resource = logging.resource "gae_app",
50
+ # "module_id" => "1",
51
+ # "version_id" => "20150925t173233"
52
+ #
53
+ # async.write_entries [entry1, entry2],
54
+ # log_name: "my_app_log",
55
+ # resource: resource,
56
+ # labels: labels
57
+ #
58
+ class AsyncWriter
59
+ include MonitorMixin
60
+
61
+ ##
62
+ # @private Implementation accessors
63
+ attr_reader :logging, :max_bytes, :max_count, :interval,
64
+ :threads, :max_queue, :partial_success
65
+
66
+ ##
67
+ # @private Creates a new AsyncWriter instance.
68
+ def initialize logging, max_count: 10000, max_bytes: 10000000,
69
+ max_queue: 100, interval: 5, threads: 10,
70
+ partial_success: false
71
+ # init MonitorMixin
72
+ super()
73
+
74
+ @logging = logging
75
+
76
+ @max_count = max_count
77
+ @max_bytes = max_bytes
78
+ @max_queue = max_queue
79
+ @interval = interval
80
+ @threads = threads
81
+
82
+ @partial_success = partial_success
83
+
84
+ @error_callbacks = []
85
+
86
+ @cond = new_cond
87
+
88
+ # Make sure all buffered messages are sent when process exits.
89
+ at_exit { stop }
90
+ end
91
+
92
+ ##
93
+ # Asynchronously write one or more log entries to the Stackdriver
94
+ # Logging service.
95
+ #
96
+ # Unlike the main write_entries method, this method usually does not
97
+ # block. The actual write RPCs will happen in the background, and may
98
+ # be batched with related calls. However, if the queue is full, this
99
+ # method will block until enough space has cleared out.
100
+ #
101
+ # @param [Google::Cloud::Logging::Entry,
102
+ # Array<Google::Cloud::Logging::Entry>] entries One or more entry
103
+ # objects to write. The log entries must have values for all required
104
+ # fields.
105
+ # @param [String] log_name A default log ID for those log entries in
106
+ # `entries` that do not specify their own `log_name`. See also
107
+ # {Entry#log_name=}.
108
+ # @param [Resource] resource A default monitored resource for those log
109
+ # entries in entries that do not specify their own resource. See also
110
+ # {Entry#resource}.
111
+ # @param [Hash{Symbol,String => String}] labels User-defined `key:value`
112
+ # items that are added to the `labels` field of each log entry in
113
+ # `entries`, except when a log entry specifies its own `key:value`
114
+ # item with the same key. See also {Entry#labels=}.
115
+ #
116
+ # @return [Google::Cloud::Logging::AsyncWriter] Returns self.
117
+ #
118
+ # @example
119
+ # require "google/cloud/logging"
120
+ #
121
+ # logging = Google::Cloud::Logging.new
122
+ # async = logging.async_writer
123
+ #
124
+ # entry = logging.entry payload: "Job started.",
125
+ # log_name: "my_app_log"
126
+ # entry.resource.type = "gae_app"
127
+ # entry.resource.labels[:module_id] = "1"
128
+ # entry.resource.labels[:version_id] = "20150925t173233"
129
+ #
130
+ # async.write_entries entry
131
+ #
132
+ def write_entries entries, log_name: nil, resource: nil, labels: nil
133
+ synchronize do
134
+ raise "AsyncWriter has been stopped" if @stopped
135
+
136
+ Array(entries).each do |entry|
137
+ # Update the entry to have all the data directly on it
138
+ entry.log_name ||= log_name
139
+ if entry.resource.nil? || entry.resource.empty?
140
+ entry.resource = resource
141
+ end
142
+ entry.labels = labels if entry.labels.nil? || entry.labels.empty?
143
+
144
+ # Add the entry to the batch
145
+ @batch ||= Batch.new self
146
+ next if @batch.try_add entry
147
+
148
+ # If we can't add to the batch, publish and create a new batch
149
+ publish_batch!
150
+ @batch = Batch.new self
151
+ @batch.add entry
152
+ end
153
+
154
+ @thread_pool ||= Concurrent::ThreadPoolExecutor.new \
155
+ max_threads: @threads, max_queue: @max_queue
156
+ @thread ||= Thread.new { run_background }
157
+
158
+ publish_batch! if @batch&.ready?
159
+
160
+ @cond.broadcast
161
+ end
162
+ self
163
+ end
164
+
165
+ ##
166
+ # Creates a logger instance that is API-compatible with Ruby's standard
167
+ # library [Logger](http://ruby-doc.org/stdlib/libdoc/logger/rdoc).
168
+ #
169
+ # The logger will use AsyncWriter to transmit log entries on a
170
+ # background thread.
171
+ #
172
+ # @param [String] log_name A log resource name to be associated with the
173
+ # written log entries.
174
+ # @param [Google::Cloud::Logging::Resource] resource The monitored
175
+ # resource to be associated with written log entries.
176
+ # @param [Hash] labels A set of user-defined data to be associated with
177
+ # written log entries.
178
+ #
179
+ # @return [Google::Cloud::Logging::Logger] a Logger object that can be
180
+ # used in place of a ruby standard library logger object.
181
+ #
182
+ # @example
183
+ # require "google/cloud/logging"
184
+ #
185
+ # logging = Google::Cloud::Logging.new
186
+ #
187
+ # resource = logging.resource "gae_app",
188
+ # module_id: "1",
189
+ # version_id: "20150925t173233"
190
+ #
191
+ # async = logging.async_writer
192
+ # logger = async.logger "my_app_log", resource, env: :production
193
+ # logger.info "Job started."
194
+ #
195
+ def logger log_name, resource, labels = {}
196
+ Logger.new self, log_name, resource, labels
197
+ end
198
+
199
+ ##
200
+ # Begins the process of stopping the writer. Entries already in the
201
+ # queue will be published, but no new entries can be added. Use {#wait!}
202
+ # to block until the writer is fully stopped and all pending entries
203
+ # have been published.
204
+ #
205
+ # @return [AsyncWriter] returns self so calls can be chained.
206
+ def stop
207
+ synchronize do
208
+ break if @stopped
209
+
210
+ @stopped = true
211
+ publish_batch!
212
+ @cond.broadcast
213
+ @thread_pool.shutdown if @thread_pool
214
+ end
215
+
216
+ self
217
+ end
218
+
219
+ ##
220
+ # Blocks until the writer is fully stopped, all pending entries have
221
+ # been published, and all callbacks have completed. Does not stop the
222
+ # writer. To stop the writer, first call {#stop} and then call {#wait!}
223
+ # to block until the writer is stopped.
224
+ #
225
+ # @param [Number, nil] timeout The maximum number of seconds to wait for
226
+ # shutdown to complete. Will wait forever when the value is `nil`. The
227
+ # default value is `nil`.
228
+ #
229
+ # @return [AsyncWriter] returns self so calls can be chained.
230
+ def wait! timeout = nil
231
+ synchronize do
232
+ if @thread_pool
233
+ @thread_pool.shutdown
234
+ @thread_pool.wait_for_termination timeout
235
+ end
236
+ end
237
+
238
+ self
239
+ end
240
+
241
+ ##
242
+ # Stop this asynchronous writer and block until it has been stopped.
243
+ #
244
+ # @param [Number, nil] timeout The maximum number of seconds to wait for
245
+ # shutdown to complete. Will wait forever when the value is `nil`. The
246
+ # default value is `nil`.
247
+ # @param [Boolean] force If set to true, and the writer hasn't stopped
248
+ # within the given timeout, kill it forcibly by terminating the
249
+ # thread. This should be used with extreme caution, as it can
250
+ # leave RPCs unfinished. Default is false.
251
+ #
252
+ # @return [Symbol] Returns `:new` if {#write_entries} has never been
253
+ # called on the AsyncWriter, `:stopped` if it was already stopped
254
+ # at the time of invocation, `:waited` if it stopped during the
255
+ # timeout period, `:timeout` if it is still running after the
256
+ # timeout, or `:forced` if it was forcibly killed.
257
+ #
258
+ def stop! timeout = nil, force: nil
259
+ return :new unless @thread_pool
260
+ return :stopped if stopped?
261
+
262
+ stop
263
+ wait! timeout
264
+
265
+ if synchronize { @thread_pool.shutdown? }
266
+ return :waited if timeout
267
+ elsif force
268
+ @thread_pool.kill
269
+ return :forced
270
+ end
271
+ :timeout
272
+ end
273
+ alias async_stop! stop!
274
+
275
+ ##
276
+ # Forces all entries in the current batch to be published
277
+ # immediately.
278
+ #
279
+ # @return [AsyncWriter] returns self so calls can be chained.
280
+ def flush
281
+ synchronize do
282
+ publish_batch!
283
+ @cond.broadcast
284
+ end
285
+
286
+ self
287
+ end
288
+
289
+ ##
290
+ # Whether the writer has been started.
291
+ #
292
+ # @return [boolean] `true` when started, `false` otherwise.
293
+ def started?
294
+ !stopped?
295
+ end
296
+
297
+ ##
298
+ # Whether the writer has been stopped.
299
+ #
300
+ # @return [boolean] `true` when stopped, `false` otherwise.
301
+ def stopped?
302
+ synchronize { @stopped }
303
+ end
304
+
305
+ ##
306
+ # Register to be notified of errors when raised.
307
+ #
308
+ # If an unhandled error has occurred the writer will attempt to
309
+ # recover from the error and resume buffering, batching, and
310
+ # transmitting log entries
311
+ #
312
+ # Multiple error handlers can be added.
313
+ #
314
+ # @yield [callback] The block to be called when an error is raised.
315
+ # @yieldparam [Exception] error The error raised.
316
+ #
317
+ # @example
318
+ # require "google/cloud/logging"
319
+ # require "google/cloud/error_reporting"
320
+ #
321
+ # logging = Google::Cloud::Logging.new
322
+ #
323
+ # resource = logging.resource "gae_app",
324
+ # module_id: "1",
325
+ # version_id: "20150925t173233"
326
+ #
327
+ # async = logging.async_writer
328
+ #
329
+ # # Register to be notified when unhandled errors occur.
330
+ # async.on_error do |error|
331
+ # # error can be a AsyncWriterError or AsyncWriteEntriesError
332
+ # Google::Cloud::ErrorReporting.report error
333
+ # end
334
+ #
335
+ # logger = async.logger "my_app_log", resource, env: :production
336
+ # logger.info "Job started."
337
+ #
338
+ def on_error &block
339
+ synchronize do
340
+ @error_callbacks << block
341
+ end
342
+ end
343
+
344
+ ##
345
+ # The most recent unhandled error to occur while transmitting log
346
+ # entries.
347
+ #
348
+ # If an unhandled error has occurred the subscriber will attempt to
349
+ # recover from the error and resume buffering, batching, and
350
+ # transmitting log entries.
351
+ #
352
+ # @return [Exception, nil] error The most recent error raised.
353
+ #
354
+ # @example
355
+ # require "google/cloud/logging"
356
+ #
357
+ # logging = Google::Cloud::Logging.new
358
+ #
359
+ # resource = logging.resource "gae_app",
360
+ # module_id: "1",
361
+ # version_id: "20150925t173233"
362
+ #
363
+ # async = logging.async_writer
364
+ #
365
+ # logger = async.logger "my_app_log", resource, env: :production
366
+ # logger.info "Job started."
367
+ #
368
+ # # If an error was raised, it can be retrieved here:
369
+ # async.last_error #=> nil
370
+ #
371
+ def last_error
372
+ synchronize { @last_error }
373
+ end
374
+ alias last_exception last_error
375
+
376
+ protected
377
+
378
+ def run_background
379
+ synchronize do
380
+ until @stopped
381
+ if @batch.nil?
382
+ @cond.wait
383
+ next
384
+ end
385
+
386
+ if @batch.ready?
387
+ # interval met, publish the batch...
388
+ publish_batch!
389
+ @cond.wait
390
+ else
391
+ # still waiting for the interval to publish the batch...
392
+ @cond.wait @batch.publish_wait
393
+ end
394
+ end
395
+ end
396
+ end
397
+
398
+ def publish_batch!
399
+ return unless @batch
400
+
401
+ batch_to_be_published = @batch
402
+ @batch = nil
403
+ publish_batch_async batch_to_be_published
404
+ end
405
+
406
+ # Sets the last_error and calls all error callbacks.
407
+ def error! error
408
+ error_callbacks = synchronize do
409
+ @last_error = error
410
+ @error_callbacks
411
+ end
412
+ error_callbacks = default_error_callbacks if error_callbacks.empty?
413
+ error_callbacks.each { |error_callback| error_callback.call error }
414
+ end
415
+
416
+ def default_error_callbacks
417
+ # This is memoized to reduce calls to the configuration.
418
+ @default_error_callbacks ||= begin
419
+ error_callback = Google::Cloud::Logging.configure.on_error
420
+ error_callback ||= Google::Cloud.configure.on_error
421
+ if error_callback
422
+ [error_callback]
423
+ else
424
+ []
425
+ end
426
+ end
427
+ end
428
+
429
+ def publish_batch_async batch
430
+ Concurrent::Promises.future_on(
431
+ @thread_pool, batch.entries
432
+ ) do |entries|
433
+ write_entries_with entries
434
+ end
435
+ rescue Concurrent::RejectedExecutionError => e
436
+ async_error = AsyncWriterError.new(
437
+ "Error writing entries: #{e.message}",
438
+ batch.entries
439
+ )
440
+ # Manually set backtrace so we don't have to raise
441
+ async_error.set_backtrace caller
442
+ error! async_error
443
+ end
444
+
445
+ def write_entries_with entries
446
+ logging.write_entries entries, partial_success: partial_success
447
+ rescue StandardError => e
448
+ write_error = AsyncWriteEntriesError.new(
449
+ "Error writing entries: #{e.message}",
450
+ entries
451
+ )
452
+ # Manually set backtrace so we don't have to raise
453
+ write_error.set_backtrace caller
454
+ error! write_error
455
+ end
456
+
457
+ ##
458
+ # @private
459
+ class Batch
460
+ attr_reader :created_at, :entries
461
+
462
+ def initialize writer
463
+ @writer = writer
464
+ @entries = []
465
+ @entries_bytes = 2 # initial size w/ partial_success
466
+ @created_at = nil
467
+ end
468
+
469
+ def add entry, addl_bytes: nil
470
+ addl_bytes ||= addl_bytes_for entry
471
+ @entries << entry
472
+ @entries_bytes += addl_bytes
473
+ @created_at ||= Time.now
474
+ nil
475
+ end
476
+
477
+ def try_add entry
478
+ addl_bytes = addl_bytes_for entry
479
+ new_message_count = @entries.count + 1
480
+ new_message_bytes = @entries_bytes + addl_bytes
481
+ if new_message_count > @writer.max_count ||
482
+ new_message_bytes >= @writer.max_bytes
483
+ return false
484
+ end
485
+ add entry, addl_bytes: addl_bytes
486
+ true
487
+ end
488
+
489
+ def ready?
490
+ @entries.count >= @writer.max_count ||
491
+ @entries_bytes >= @writer.max_bytes ||
492
+ (@created_at.nil? || (publish_at < Time.now))
493
+ end
494
+
495
+ def publish_at
496
+ return nil if @created_at.nil?
497
+ @created_at + @writer.interval
498
+ end
499
+
500
+ def publish_wait
501
+ publish_wait = publish_at - Time.now
502
+ return 0 if publish_wait < 0
503
+ publish_wait
504
+ end
505
+
506
+ def addl_bytes_for entry
507
+ entry.to_grpc.to_proto.bytesize + 2
508
+ end
509
+ end
510
+ end
511
+ end
512
+ end
513
+ end