google-cloud-logging 0.20.1 → 0.21.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
  SHA1:
3
- metadata.gz: aa3ebfa38e22e40778bd4a67a5d0f6373fd094a4
4
- data.tar.gz: a792bb01d9ba78eb0c728771eb07528280170bf0
3
+ metadata.gz: 8c9421790cb4f209636fe57644a348a9076ed293
4
+ data.tar.gz: 79e077faf17e8a77e8f4a471a3421207743d826c
5
5
  SHA512:
6
- metadata.gz: 4f751572455011147d48324f1159fa9d18ce447eb2bfa69bc8a150906386eaad5f033f4b8167da7d7cf84fcc1691bd3af2f04e82fa251abb194ce18e3b94a55b
7
- data.tar.gz: e8fc4fe86dc494fc4e3f1e2ea951f14fd6941a132394b8c26a1eddf620f6946da00832e1cd7556eed81aa5fe9fbe4f1fc5ab08b8ec34dc4e2cd6c8397731a70a
6
+ metadata.gz: 6d797aa5a7888d7b6f617439f37e8b20695fd4ac8c5b2f241ac52376bfdcf5154c2faf268e3411c09697d4ee37d13f3abe2f7428b5650ce7db3cdc581fae4eaf
7
+ data.tar.gz: 9c6ec7fe172ac640ebaa6c94c0501ed0e90b3b8004ba2387de6aadbaafcb134500a7d7ebf6a9aa921afa181ff663b48aa79d6271d2da86fdac2942d2cfbe1fc9
@@ -13,8 +13,8 @@
13
13
  # limitations under the License.
14
14
 
15
15
  ##
16
- # This file is here to be autorequired by bundler, so that the .bigquery and
17
- # #bigquery methods can be available, but the library and all dependencies won't
16
+ # This file is here to be autorequired by bundler, so that the .logging and
17
+ # #logging methods can be available, but the library and all dependencies won't
18
18
  # be loaded until required and used.
19
19
 
20
20
 
@@ -38,9 +38,9 @@ module Google
38
38
  # The default scope is:
39
39
  #
40
40
  # * `https://www.googleapis.com/auth/logging.admin`
41
- # @param [Integer] retries Number of times to retry requests on server
42
- # error. The default value is `3`. Optional.
43
41
  # @param [Integer] timeout Default timeout to use in requests. Optional.
42
+ # @param [Hash] client_config A hash of values to override the default
43
+ # behavior of the API client. Optional.
44
44
  #
45
45
  # @return [Google::Cloud::Logging::Project]
46
46
  #
@@ -49,7 +49,11 @@ module Google
49
49
  #
50
50
  # gcloud = Google::Cloud.new
51
51
  # logging = gcloud.logging
52
- # # ...
52
+ #
53
+ # entries = logging.entries
54
+ # entries.each do |e|
55
+ # puts "[#{e.timestamp}] #{e.log_name} #{e.payload.inspect}"
56
+ # end
53
57
  #
54
58
  # @example The default scope can be overridden with the `scope` option:
55
59
  # require "google/cloud"
@@ -58,10 +62,10 @@ module Google
58
62
  # platform_scope = "https://www.googleapis.com/auth/cloud-platform"
59
63
  # logging = gcloud.logging scope: platform_scope
60
64
  #
61
- def logging scope: nil, retries: nil, timeout: nil
65
+ def logging scope: nil, timeout: nil, client_config: nil
62
66
  Google::Cloud.logging @project, @keyfile, scope: scope,
63
- retries: (retries || @retries),
64
- timeout: (timeout || @timeout)
67
+ timeout: (timeout || @timeout),
68
+ client_config: client_config
65
69
  end
66
70
 
67
71
  ##
@@ -72,7 +76,7 @@ module Google
72
76
  # Guide](https://googlecloudplatform.github.io/google-cloud-ruby/#/docs/guides/authentication).
73
77
  #
74
78
  # @param [String] project Project identifier for the Stackdriver Logging
75
- # service.
79
+ # service you are connecting to.
76
80
  # @param [String, Hash] keyfile Keyfile downloaded from Google Cloud. If
77
81
  # file path the file must be readable.
78
82
  # @param [String, Array<String>] scope The OAuth 2.0 scopes controlling the
@@ -83,37 +87,28 @@ module Google
83
87
  # The default scope is:
84
88
  #
85
89
  # * `https://www.googleapis.com/auth/logging.admin`
86
- # @param [Integer] retries Number of times to retry requests on server
87
- # error. The default value is `3`. Optional.
88
90
  # @param [Integer] timeout Default timeout to use in requests. Optional.
91
+ # @param [Hash] client_config A hash of values to override the default
92
+ # behavior of the API client. Optional.
89
93
  #
90
94
  # @return [Google::Cloud::Logging::Project]
91
95
  #
92
96
  # @example
93
- # require "google/cloud/logging"
97
+ # require "google/cloud"
94
98
  #
95
- # gcloud = Google::Cloud.new
96
- # logging = gcloud.logging
97
- # # ...
99
+ # logging = Google::Cloud.logging
98
100
  #
99
- def self.logging project = nil, keyfile = nil, scope: nil, retries: nil,
100
- timeout: nil
101
+ # entries = logging.entries
102
+ # entries.each do |e|
103
+ # puts "[#{e.timestamp}] #{e.log_name} #{e.payload.inspect}"
104
+ # end
105
+ #
106
+ def self.logging project = nil, keyfile = nil, scope: nil, timeout: nil,
107
+ client_config: nil
101
108
  require "google/cloud/logging"
102
- project ||= Google::Cloud::Logging::Project.default_project
103
- project = project.to_s # Always cast to a string
104
- fail ArgumentError, "project is missing" if project.empty?
105
-
106
- if keyfile.nil?
107
- credentials = Google::Cloud::Logging::Credentials.default(
108
- scope: scope)
109
- else
110
- credentials = Google::Cloud::Logging::Credentials.new(
111
- keyfile, scope: scope)
112
- end
113
-
114
- Google::Cloud::Logging::Project.new(
115
- Google::Cloud::Logging::Service.new(
116
- project, credentials, retries: retries, timeout: timeout))
109
+ Google::Cloud::Logging.new project: project, keyfile: keyfile,
110
+ scope: scope, timeout: timeout,
111
+ client_config: client_config
117
112
  end
118
113
  end
119
114
  end
@@ -44,6 +44,11 @@ module Google
44
44
  # about the options for connecting in the [Authentication
45
45
  # Guide](https://googlecloudplatform.github.io/google-cloud-ruby/#/docs/guides/authentication).
46
46
  #
47
+ # If you just want to write your application's logs to the Stackdriver
48
+ # Logging service, you may find it easiest to use the [Ruby Logger
49
+ # implementation](#creating-a-ruby-logger-implementation) provided by this
50
+ # library. Otherwise, read on to learn more about the Logging API.
51
+ #
47
52
  # ## Listing log entries
48
53
  #
49
54
  # Stackdriver Logging gathers log entries from many services, including
@@ -55,10 +60,9 @@ module Google
55
60
  # {Google::Cloud::Logging::Entry} records belonging to your project:
56
61
  #
57
62
  # ```ruby
58
- # require "google/cloud"
63
+ # require "google/cloud/logging"
59
64
  #
60
- # gcloud = Google::Cloud.new
61
- # logging = gcloud.logging
65
+ # logging = Google::Cloud::Logging.new
62
66
  # entries = logging.entries
63
67
  # entries.each do |e|
64
68
  # puts "[#{e.timestamp}] #{e.log_name} #{e.payload.inspect}"
@@ -75,10 +79,9 @@ module Google
75
79
  # Stackdriver Logging API.
76
80
  #
77
81
  # ```ruby
78
- # require "google/cloud"
82
+ # require "google/cloud/logging"
79
83
  #
80
- # gcloud = Google::Cloud.new
81
- # logging = gcloud.logging
84
+ # logging = Google::Cloud::Logging.new
82
85
  # entries = logging.entries filter: "log:syslog"
83
86
  # entries.each do |e|
84
87
  # puts "[#{e.timestamp}] #{e.payload.inspect}"
@@ -88,10 +91,9 @@ module Google
88
91
  # You can also order the log entries by `timestamp`.
89
92
  #
90
93
  # ```ruby
91
- # require "google/cloud"
94
+ # require "google/cloud/logging"
92
95
  #
93
- # gcloud = Google::Cloud.new
94
- # logging = gcloud.logging
96
+ # logging = Google::Cloud::Logging.new
95
97
  # entries = logging.entries order: "timestamp desc"
96
98
  # entries.each do |e|
97
99
  # puts "[#{e.timestamp}] #{e.log_name} #{e.payload.inspect}"
@@ -121,11 +123,9 @@ module Google
121
123
  # logs](https://cloud.google.com/logging/docs/export/configure_export#setting_product_name_short_permissions_for_writing_exported_logs).
122
124
  #
123
125
  # ```ruby
124
- # require "google/cloud"
126
+ # require "google/cloud/logging"
125
127
  #
126
- # gcloud = Google::Cloud.new
127
- # logging = gcloud.logging
128
- # storage = gcloud.storage
128
+ # storage = Google::Cloud::Storage.new
129
129
  #
130
130
  # bucket = storage.create_bucket "my-logs-bucket"
131
131
  #
@@ -133,6 +133,10 @@ module Google
133
133
  # email = "cloud-logs@google.com"
134
134
  # bucket.acl.add_owner "group-#{email}"
135
135
  #
136
+ # require "google/cloud/logging"
137
+ #
138
+ # logging = Google::Cloud::Logging.new
139
+ #
136
140
  # sink = logging.create_sink "my-sink",
137
141
  # "storage.googleapis.com/#{bucket.id}"
138
142
  # ```
@@ -147,10 +151,9 @@ module Google
147
151
  # {Google::Cloud::Logging::Project#sinks}.
148
152
  #
149
153
  # ```ruby
150
- # require "google/cloud"
154
+ # require "google/cloud/logging"
151
155
  #
152
- # gcloud = Google::Cloud.new
153
- # logging = gcloud.logging
156
+ # logging = Google::Cloud::Logging.new
154
157
  # sinks = logging.sinks
155
158
  # sinks.each do |s|
156
159
  # puts "#{s.name}: #{s.filter} -> #{s.destination}"
@@ -172,10 +175,9 @@ module Google
172
175
  # filter](https://cloud.google.com/logging/docs/view/advanced_filters).
173
176
  #
174
177
  # ```ruby
175
- # require "google/cloud"
178
+ # require "google/cloud/logging"
176
179
  #
177
- # gcloud = Google::Cloud.new
178
- # logging = gcloud.logging
180
+ # logging = Google::Cloud::Logging.new
179
181
  # metric = logging.create_metric "errors", "severity>=ERROR"
180
182
  # ```
181
183
  #
@@ -185,10 +187,9 @@ module Google
185
187
  # {Google::Cloud::Logging::Project#metrics}.
186
188
  #
187
189
  # ```ruby
188
- # require "google/cloud"
190
+ # require "google/cloud/logging"
189
191
  #
190
- # gcloud = Google::Cloud.new
191
- # logging = gcloud.logging
192
+ # logging = Google::Cloud::Logging.new
192
193
  # metrics = logging.metrics
193
194
  # metrics.each do |m|
194
195
  # puts "#{m.name}: #{m.filter}"
@@ -205,10 +206,9 @@ module Google
205
206
  # contain a log name and a resource.
206
207
  #
207
208
  # ```ruby
208
- # require "google/cloud"
209
+ # require "google/cloud/logging"
209
210
  #
210
- # gcloud = Google::Cloud.new
211
- # logging = gcloud.logging
211
+ # logging = Google::Cloud::Logging.new
212
212
  #
213
213
  # entry = logging.entry
214
214
  # entry.payload = "Job started."
@@ -225,10 +225,9 @@ module Google
225
225
  # these values from the individual entries.
226
226
  #
227
227
  # ```ruby
228
- # require "google/cloud"
228
+ # require "google/cloud/logging"
229
229
  #
230
- # gcloud = Google::Cloud.new
231
- # logging = gcloud.logging
230
+ # logging = Google::Cloud::Logging.new
232
231
  #
233
232
  # entry1 = logging.entry
234
233
  # entry1.payload = "Job started."
@@ -246,6 +245,36 @@ module Google
246
245
  # labels: labels
247
246
  # ```
248
247
  #
248
+ # Normally, writing log entries is done synchronously; the call to
249
+ # {Google::Cloud::Logging::Project#write_entries} will block until it has
250
+ # either completed transmitting the data or encountered an error. To "fire
251
+ # and forget" without blocking, use {Google::Cloud::Logging::AsyncWriter};
252
+ # it spins up a background thread that writes log entries in batches. Calls
253
+ # to {Google::Cloud::Logging::AsyncWriter#write_entries} simply add entries
254
+ # to its work queue and return immediately.
255
+ #
256
+ # ```ruby
257
+ # require "google/cloud/logging"
258
+ #
259
+ # logging = Google::Cloud::Logging.new
260
+ # async = logging.async_writer
261
+ #
262
+ # entry1 = logging.entry
263
+ # entry1.payload = "Job started."
264
+ # entry2 = logging.entry
265
+ # entry2.payload = "Job completed."
266
+ # labels = { job_size: "large", job_code: "red" }
267
+ #
268
+ # resource = logging.resource "gae_app",
269
+ # "module_id" => "1",
270
+ # "version_id" => "20150925t173233"
271
+ #
272
+ # async.write_entries [entry1, entry2],
273
+ # log_name: "my_app_log",
274
+ # resource: resource,
275
+ # labels: labels
276
+ # ```
277
+ #
249
278
  # ### Creating a Ruby Logger implementation
250
279
  #
251
280
  # If your environment requires a logger instance that is API-compatible with
@@ -254,10 +283,9 @@ module Google
254
283
  # {Google::Cloud::Logging::Project#logger} to create one.
255
284
  #
256
285
  # ```ruby
257
- # require "google/cloud"
286
+ # require "google/cloud/logging"
258
287
  #
259
- # gcloud = Google::Cloud.new
260
- # logging = gcloud.logging
288
+ # logging = Google::Cloud::Logging.new
261
289
  #
262
290
  # resource = logging.resource "gae_app",
263
291
  # module_id: "1",
@@ -267,27 +295,89 @@ module Google
267
295
  # logger.info "Job started."
268
296
  # ```
269
297
  #
270
- # ## Configuring retries and timeout
298
+ # By default, the logger instance writes log entries asynchronously in a
299
+ # background thread using an {Google::Cloud::Logging::AsyncWriter}. If you
300
+ # want to customize or disable asynchronous writing, you may call the
301
+ # Logger constructor directly.
271
302
  #
272
- # You can configure how many times API requests may be automatically
273
- # retried. When an API request fails, the response will be inspected to see
274
- # if the request meets criteria indicating that it may succeed on retry,
275
- # such as `500` and `503` status codes or a specific internal error code
276
- # such as `rateLimitExceeded`. If it meets the criteria, the request will be
277
- # retried after a delay. If another error occurs, the delay will be
278
- # increased before a subsequent attempt, until the `retries` limit is
279
- # reached.
303
+ # ```ruby
304
+ # require "google/cloud/logging"
280
305
  #
281
- # You can also set the request `timeout` value in seconds.
306
+ # logging = Google::Cloud::Logging.new
307
+ #
308
+ # resource = logging.resource "gae_app",
309
+ # module_id: "1",
310
+ # version_id: "20150925t173233"
311
+ #
312
+ # logger = Google::Cloud::Logging::Logger.new logging,
313
+ # "my_app_log",
314
+ # resource,
315
+ # {env: :production}
316
+ # logger.info "Log entry written synchronously."
317
+ # ```
318
+ #
319
+ # ## Configuring timeout
320
+ #
321
+ # You can configure the request `timeout` value in seconds.
282
322
  #
283
323
  # ```ruby
284
- # require "google/cloud"
324
+ # require "google/cloud/logging"
285
325
  #
286
- # gcloud = Google::Cloud.new
287
- # logging = gcloud.logging retries: 10, timeout: 120
326
+ # logging = Google::Cloud::Logging.new timeout: 120
288
327
  # ```
289
328
  #
290
329
  module Logging
330
+ ##
331
+ # Creates a new object for connecting to the Stackdriver Logging service.
332
+ # Each call creates a new connection.
333
+ #
334
+ # For more information on connecting to Google Cloud see the
335
+ # [Authentication
336
+ # Guide](https://googlecloudplatform.github.io/google-cloud-ruby/#/docs/guides/authentication).
337
+ #
338
+ # @param [String] project Project identifier for the Stackdriver Logging
339
+ # service.
340
+ # @param [String, Hash] keyfile Keyfile downloaded from Google Cloud. If
341
+ # file path the file must be readable.
342
+ # @param [String, Array<String>] scope The OAuth 2.0 scopes controlling
343
+ # the set of resources and operations that the connection can access.
344
+ # See [Using OAuth 2.0 to Access Google
345
+ # APIs](https://developers.google.com/identity/protocols/OAuth2).
346
+ #
347
+ # The default scope is:
348
+ #
349
+ # * `https://www.googleapis.com/auth/logging.admin`
350
+ # @param [Integer] timeout Default timeout to use in requests. Optional.
351
+ # @param [Hash] client_config A hash of values to override the default
352
+ # behavior of the API client. Optional.
353
+ #
354
+ # @return [Google::Cloud::Logging::Project]
355
+ #
356
+ # @example
357
+ # require "google/cloud/logging"
358
+ #
359
+ # logging = Google::Cloud::Logging.new
360
+ #
361
+ # entries = logging.entries
362
+ # entries.each do |e|
363
+ # puts "[#{e.timestamp}] #{e.log_name} #{e.payload.inspect}"
364
+ # end
365
+ #
366
+ def self.new project: nil, keyfile: nil, scope: nil, timeout: nil,
367
+ client_config: nil
368
+ project ||= Google::Cloud::Logging::Project.default_project
369
+ project = project.to_s # Always cast to a string
370
+ fail ArgumentError, "project is missing" if project.empty?
371
+
372
+ credentials =
373
+ Google::Cloud::Logging::Credentials.credentials_with_scope keyfile,
374
+ scope
375
+
376
+ Google::Cloud::Logging::Project.new(
377
+ Google::Cloud::Logging::Service.new(
378
+ project, credentials, timeout: timeout,
379
+ client_config: client_config))
380
+ end
291
381
  end
292
382
  end
293
383
  end
@@ -0,0 +1,399 @@
1
+ # Copyright 2016 Google Inc. All rights reserved.
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
+ # http://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
+ module Google
17
+ module Cloud
18
+ module Logging
19
+ ##
20
+ # # AsyncWriter
21
+ #
22
+ # An object that batches and transmits log entries asynchronously.
23
+ #
24
+ # Use this object to transmit log entries efficiently. It keeps a queue
25
+ # of log entries, and runs a background thread that transmits them to
26
+ # the logging service in batches. Generally, adding to the queue will
27
+ # not block.
28
+ #
29
+ # This object is thread-safe; it may accept write requests from
30
+ # multiple threads simultaneously, and will serialize them when
31
+ # executing in the background thread.
32
+ #
33
+ # @example
34
+ # require "google/cloud/logging"
35
+ #
36
+ # logging = Google::Cloud::Logging.new
37
+ #
38
+ # async = logging.async_writer
39
+ #
40
+ # entry1 = logging.entry payload: "Job started."
41
+ # entry2 = logging.entry payload: "Job completed."
42
+ #
43
+ # labels = { job_size: "large", job_code: "red" }
44
+ # resource = logging.resource "gae_app",
45
+ # "module_id" => "1",
46
+ # "version_id" => "20150925t173233"
47
+ #
48
+ # async.write_entries [entry1, entry2],
49
+ # log_name: "my_app_log",
50
+ # resource: resource,
51
+ # labels: labels
52
+ #
53
+ class AsyncWriter
54
+ DEFAULT_MAX_QUEUE_SIZE = 10000
55
+
56
+ ##
57
+ # @private Item in the log entries queue.
58
+ QueueItem = Struct.new(:entries, :log_name, :resource, :labels) do
59
+ def try_combine next_item
60
+ if log_name == next_item.log_name &&
61
+ resource == next_item.resource &&
62
+ labels == next_item.labels
63
+ entries.concat(next_item.entries)
64
+ true
65
+ else
66
+ false
67
+ end
68
+ end
69
+ end
70
+
71
+ ##
72
+ # @private The logging object.
73
+ attr_accessor :logging
74
+
75
+ ##
76
+ # @private The maximum size of the entries queue, or nil if not set.
77
+ attr_accessor :max_queue_size
78
+
79
+ ##
80
+ # The current state. Either :running, :suspended, :stopping, or :stopped
81
+ attr_reader :state
82
+
83
+ ##
84
+ # The last exception thrown by the background thread, or nil if nothing
85
+ # has been thrown.
86
+ attr_reader :last_exception
87
+
88
+ ##
89
+ # @private Creates a new AsyncWriter instance.
90
+ def initialize logging, max_queue_size = DEFAULT_MAX_QUEUE_SIZE
91
+ @logging = logging
92
+ @max_queue_size = max_queue_size
93
+ @startup_lock = Mutex.new
94
+ @thread = nil
95
+ @state = :running
96
+ end
97
+
98
+ ##
99
+ # Asynchronously write one or more log entries to the Stackdriver
100
+ # Logging service.
101
+ #
102
+ # Unlike the main write_entries method, this method usually does not
103
+ # block. The actual write RPCs will happen in the background, and may
104
+ # be batched with related calls. However, if the queue is full, this
105
+ # method will block until enough space has cleared out.
106
+ #
107
+ # @param [Google::Cloud::Logging::Entry,
108
+ # Array<Google::Cloud::Logging::Entry>] entries One or more entry
109
+ # objects to write. The log entries must have values for all required
110
+ # fields.
111
+ # @param [String] log_name A default log ID for those log entries in
112
+ # `entries` that do not specify their own `log_name`. See also
113
+ # {Entry#log_name=}.
114
+ # @param [Resource] resource A default monitored resource for those log
115
+ # entries in entries that do not specify their own resource. See also
116
+ # {Entry#resource}.
117
+ # @param [Hash{Symbol,String => String}] labels User-defined `key:value`
118
+ # items that are added to the `labels` field of each log entry in
119
+ # `entries`, except when a log entry specifies its own `key:value`
120
+ # item with the same key. See also {Entry#labels=}.
121
+ #
122
+ # @return [Google::Cloud::Logging::AsyncWriter] Returns self.
123
+ #
124
+ # @example
125
+ # require "google/cloud/logging"
126
+ #
127
+ # logging = Google::Cloud::Logging.new
128
+ # async = logging.async_writer
129
+ #
130
+ # entry = logging.entry payload: "Job started.",
131
+ # log_name: "my_app_log"
132
+ # entry.resource.type = "gae_app"
133
+ # entry.resource.labels[:module_id] = "1"
134
+ # entry.resource.labels[:version_id] = "20150925t173233"
135
+ #
136
+ # async.write_entries entry
137
+ #
138
+ def write_entries entries, log_name: nil, resource: nil, labels: nil
139
+ ensure_thread
140
+ entries = Array(entries)
141
+ @lock.synchronize do
142
+ fail "AsyncWriter has been stopped" unless writable?
143
+ queue_item = QueueItem.new entries, log_name, resource, labels
144
+ if @queue.empty? || !@queue.last.try_combine(queue_item)
145
+ @queue.push queue_item
146
+ end
147
+ @queue_size += entries.size
148
+ @lock_cond.broadcast
149
+ while @max_queue_size && @queue_size > @max_queue_size
150
+ @lock_cond.wait
151
+ end
152
+ end
153
+ self
154
+ end
155
+
156
+ ##
157
+ # Creates a logger instance that is API-compatible with Ruby's standard
158
+ # library [Logger](http://ruby-doc.org/stdlib/libdoc/logger/rdoc).
159
+ #
160
+ # The logger will use AsyncWriter to transmit log entries on a
161
+ # background thread.
162
+ #
163
+ # @param [String] log_name A log resource name to be associated with the
164
+ # written log entries.
165
+ # @param [Google::Cloud::Logging::Resource] resource The monitored
166
+ # resource to be associated with written log entries.
167
+ # @param [Hash] labels A set of user-defined data to be associated with
168
+ # written log entries.
169
+ #
170
+ # @return [Google::Cloud::Logging::Logger] a Logger object that can be
171
+ # used in place of a ruby standard library logger object.
172
+ #
173
+ # @example
174
+ # require "google/cloud/logging"
175
+ #
176
+ # logging = Google::Cloud::Logging.new
177
+ #
178
+ # resource = logging.resource "gae_app",
179
+ # module_id: "1",
180
+ # version_id: "20150925t173233"
181
+ #
182
+ # async = logging.async_writer
183
+ # logger = async.logger "my_app_log", resource, env: :production
184
+ # logger.info "Job started."
185
+ #
186
+ def logger log_name, resource, labels = {}
187
+ Logger.new self, log_name, resource, labels
188
+ end
189
+
190
+ ##
191
+ # Stops this asynchronous writer.
192
+ #
193
+ # After this call succeeds, the state will change to :stopping, and
194
+ # you may not issue any additional write_entries calls. Any previously
195
+ # issued writes will complete. Once any existing backlog has been
196
+ # cleared, the state will change to :stopped.
197
+ #
198
+ # @return [Boolean] Returns true if the writer was running, or false
199
+ # if the writer had already been stopped.
200
+ #
201
+ def stop
202
+ ensure_thread
203
+ @lock.synchronize do
204
+ if state != :stopped
205
+ @state = :stopping
206
+ @lock_cond.broadcast
207
+ true
208
+ else
209
+ false
210
+ end
211
+ end
212
+ end
213
+
214
+ ##
215
+ # Suspends this asynchronous writer.
216
+ #
217
+ # After this call succeeds, the state will change to :suspended, and
218
+ # the writer will stop sending RPCs until resumed.
219
+ #
220
+ # @return [Boolean] Returns true if the writer had been running and was
221
+ # suspended, otherwise false.
222
+ #
223
+ def suspend
224
+ ensure_thread
225
+ @lock.synchronize do
226
+ if state == :running
227
+ @state = :suspended
228
+ @lock_cond.broadcast
229
+ true
230
+ else
231
+ false
232
+ end
233
+ end
234
+ end
235
+
236
+ ##
237
+ # Resumes this suspended asynchronous writer.
238
+ #
239
+ # After this call succeeds, the state will change to :running, and
240
+ # the writer will resume sending RPCs.
241
+ #
242
+ # @return [Boolean] Returns true if the writer had been suspended and
243
+ # is now running, otherwise false.
244
+ #
245
+ def resume
246
+ ensure_thread
247
+ @lock.synchronize do
248
+ if state == :suspended
249
+ @state = :running
250
+ @lock_cond.broadcast
251
+ true
252
+ else
253
+ false
254
+ end
255
+ end
256
+ end
257
+
258
+ ##
259
+ # Returns true if this writer is running.
260
+ #
261
+ # @return [Boolean] Returns true if the writer is currently running.
262
+ #
263
+ def running?
264
+ ensure_thread
265
+ @lock.synchronize do
266
+ state == :running
267
+ end
268
+ end
269
+
270
+ ##
271
+ # Returns true if this writer is suspended.
272
+ #
273
+ # @return [Boolean] Returns true if the writer is currently suspended.
274
+ #
275
+ def suspended?
276
+ ensure_thread
277
+ @lock.synchronize do
278
+ state == :suspended
279
+ end
280
+ end
281
+
282
+ ##
283
+ # Returns true if this writer is still accepting writes. This means
284
+ # it is either running or suspended.
285
+ #
286
+ # @return [Boolean] Returns true if the writer is accepting writes.
287
+ #
288
+ def writable?
289
+ ensure_thread
290
+ @lock.synchronize do
291
+ state == :suspended || state == :running
292
+ end
293
+ end
294
+
295
+ ##
296
+ # Returns true if this writer is fully stopped.
297
+ #
298
+ # @return [Boolean] Returns true if the writer is fully stopped.
299
+ #
300
+ def stopped?
301
+ ensure_thread
302
+ @lock.synchronize do
303
+ state == :stopped
304
+ end
305
+ end
306
+
307
+ ##
308
+ # Blocks until this asynchronous writer has been stopped, or the given
309
+ # timeout (if present) has elapsed.
310
+ #
311
+ # @param [Number] timeout Timeout in seconds, or nil for no timeout.
312
+ #
313
+ # @return [Boolean] Returns true if the writer is stopped, or false
314
+ # if the timeout expired.
315
+ #
316
+ def wait_until_stopped timeout = nil
317
+ ensure_thread
318
+ deadline = timeout ? ::Time.new.to_f + timeout : nil
319
+ @lock.synchronize do
320
+ until state == :stopped
321
+ cur_time = ::Time.new.to_f
322
+ return false if deadline && cur_time >= deadline
323
+ @lock_cond.wait(deadline ? deadline - cur_time : nil)
324
+ end
325
+ end
326
+ true
327
+ end
328
+
329
+ protected
330
+
331
+ ##
332
+ # @private Ensures the background thread is running. This is called
333
+ # at the start of all public methods, and kicks off the thread lazily.
334
+ # It also ensures the thread gets restarted (with an empty queue) in
335
+ # case this object is forked into a child process.
336
+ #
337
+ def ensure_thread
338
+ @startup_lock.synchronize do
339
+ if (@thread.nil? || !@thread.alive?) && @state != :stopped
340
+ @queue_size = 0
341
+ @queue = []
342
+ @lock = Monitor.new
343
+ @lock_cond = @lock.new_cond
344
+ @thread = Thread.new { run_backgrounder }
345
+ end
346
+ end
347
+ end
348
+
349
+ ##
350
+ # @private The background thread implementation, which continuously
351
+ # waits for performs work, and returns only when fully stopped.
352
+ #
353
+ def run_backgrounder
354
+ loop do
355
+ queue_item = wait_next_item
356
+ return unless queue_item
357
+ begin
358
+ logging.write_entries(
359
+ queue_item.entries,
360
+ log_name: queue_item.log_name,
361
+ resource: queue_item.resource,
362
+ labels: queue_item.labels
363
+ )
364
+ rescue => e
365
+ # Ignore any exceptions thrown from the background thread, but
366
+ # keep running to ensure its state behavior remains consistent.
367
+ @last_exception = e
368
+ end
369
+ end
370
+ end
371
+
372
+ ##
373
+ # @private Wait for and dequeue the next set of log entries to transmit.
374
+ #
375
+ # @return [QueueItem,NilClass] Returns the next set of entries. If
376
+ # the writer has been stopped and no more entries are left in the
377
+ # queue, returns nil.
378
+ #
379
+ def wait_next_item
380
+ @lock.synchronize do
381
+ while state == :suspended ||
382
+ (state == :running && @queue.empty?)
383
+ @lock_cond.wait
384
+ end
385
+ if @queue.empty?
386
+ @state = :stopped
387
+ nil
388
+ else
389
+ queue_item = @queue.shift
390
+ @queue_size -= queue_item.entries.size
391
+ @lock_cond.broadcast
392
+ queue_item
393
+ end
394
+ end
395
+ end
396
+ end
397
+ end
398
+ end
399
+ end