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 +4 -4
- data/lib/google-cloud-logging.rb +27 -32
- data/lib/google/cloud/logging.rb +134 -44
- data/lib/google/cloud/logging/async_writer.rb +399 -0
- data/lib/google/cloud/logging/credentials.rb +10 -0
- data/lib/google/cloud/logging/entry.rb +22 -32
- data/lib/google/cloud/logging/entry/list.rb +10 -15
- data/lib/google/cloud/logging/logger.rb +93 -18
- data/lib/google/cloud/logging/metric.rb +8 -12
- data/lib/google/cloud/logging/metric/list.rb +10 -15
- data/lib/google/cloud/logging/middleware.rb +135 -0
- data/lib/google/cloud/logging/project.rb +138 -76
- data/lib/google/cloud/logging/rails.rb +121 -0
- data/lib/google/cloud/logging/resource.rb +2 -3
- data/lib/google/cloud/logging/resource_descriptor.rb +4 -6
- data/lib/google/cloud/logging/resource_descriptor/list.rb +10 -15
- data/lib/google/cloud/logging/service.rb +146 -141
- data/lib/google/cloud/logging/sink.rb +9 -11
- data/lib/google/cloud/logging/sink/list.rb +10 -15
- data/lib/google/cloud/logging/v2/config_service_v2_api.rb +93 -27
- data/lib/google/cloud/logging/v2/logging_service_v2_api.rb +118 -29
- data/lib/google/cloud/logging/v2/metrics_service_v2_api.rb +78 -10
- data/lib/google/cloud/logging/version.rb +1 -1
- data/lib/google/logging/v2/logging_config_pb.rb +1 -0
- data/lib/google/logging/v2/logging_config_services_pb.rb +1 -1
- data/lib/google/logging/v2/logging_metrics_pb.rb +6 -0
- data/lib/google/logging/v2/logging_pb.rb +1 -0
- data/lib/google/logging/v2/logging_services_pb.rb +0 -3
- metadata +84 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c9421790cb4f209636fe57644a348a9076ed293
|
4
|
+
data.tar.gz: 79e077faf17e8a77e8f4a471a3421207743d826c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d797aa5a7888d7b6f617439f37e8b20695fd4ac8c5b2f241ac52376bfdcf5154c2faf268e3411c09697d4ee37d13f3abe2f7428b5650ce7db3cdc581fae4eaf
|
7
|
+
data.tar.gz: 9c6ec7fe172ac640ebaa6c94c0501ed0e90b3b8004ba2387de6aadbaafcb134500a7d7ebf6a9aa921afa181ff663b48aa79d6271d2da86fdac2942d2cfbe1fc9
|
data/lib/google-cloud-logging.rb
CHANGED
@@ -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 .
|
17
|
-
# #
|
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,
|
65
|
+
def logging scope: nil, timeout: nil, client_config: nil
|
62
66
|
Google::Cloud.logging @project, @keyfile, scope: scope,
|
63
|
-
|
64
|
-
|
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
|
97
|
+
# require "google/cloud"
|
94
98
|
#
|
95
|
-
#
|
96
|
-
# logging = gcloud.logging
|
97
|
-
# # ...
|
99
|
+
# logging = Google::Cloud.logging
|
98
100
|
#
|
99
|
-
|
100
|
-
|
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
|
-
|
103
|
-
|
104
|
-
|
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
|
data/lib/google/cloud/logging.rb
CHANGED
@@ -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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
273
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|