google-cloud-logging 1.5.7 → 1.6.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/CHANGELOG.md +40 -0
- data/CONTRIBUTING.md +1 -1
- data/lib/google-cloud-logging.rb +7 -5
- data/lib/google/cloud/logging.rb +22 -8
- data/lib/google/cloud/logging/async_writer.rb +308 -187
- data/lib/google/cloud/logging/convert.rb +15 -9
- data/lib/google/cloud/logging/entry.rb +43 -13
- data/lib/google/cloud/logging/entry/source_location.rb +3 -3
- data/lib/google/cloud/logging/errors.rb +101 -0
- data/lib/google/cloud/logging/log/list.rb +1 -1
- data/lib/google/cloud/logging/logger.rb +6 -4
- data/lib/google/cloud/logging/middleware.rb +24 -11
- data/lib/google/cloud/logging/project.rb +38 -15
- data/lib/google/cloud/logging/rails.rb +56 -5
- data/lib/google/cloud/logging/resource.rb +1 -1
- data/lib/google/cloud/logging/service.rb +42 -32
- data/lib/google/cloud/logging/v2/config_service_v2_client.rb +1 -1
- data/lib/google/cloud/logging/v2/credentials.rb +1 -1
- data/lib/google/cloud/logging/v2/doc/google/api/distribution.rb +1 -37
- data/lib/google/cloud/logging/v2/doc/google/api/label.rb +1 -1
- data/lib/google/cloud/logging/v2/doc/google/api/metric.rb +1 -13
- data/lib/google/cloud/logging/v2/doc/google/api/monitored_resource.rb +1 -1
- data/lib/google/cloud/logging/v2/doc/google/logging/type/http_request.rb +1 -1
- data/lib/google/cloud/logging/v2/doc/google/logging/v2/log_entry.rb +1 -1
- data/lib/google/cloud/logging/v2/doc/google/logging/v2/logging.rb +1 -12
- data/lib/google/cloud/logging/v2/doc/google/logging/v2/logging_config.rb +1 -1
- data/lib/google/cloud/logging/v2/doc/google/logging/v2/logging_metrics.rb +1 -1
- data/lib/google/cloud/logging/v2/doc/google/protobuf/any.rb +1 -1
- data/lib/google/cloud/logging/v2/doc/google/protobuf/duration.rb +1 -1
- data/lib/google/cloud/logging/v2/doc/google/protobuf/empty.rb +1 -1
- data/lib/google/cloud/logging/v2/doc/google/protobuf/field_mask.rb +1 -1
- data/lib/google/cloud/logging/v2/doc/google/protobuf/struct.rb +1 -1
- data/lib/google/cloud/logging/v2/doc/google/protobuf/timestamp.rb +1 -1
- data/lib/google/cloud/logging/v2/logging_service_v2_client.rb +1 -1
- data/lib/google/cloud/logging/v2/logging_service_v2_client_config.json +1 -1
- data/lib/google/cloud/logging/v2/metrics_service_v2_client.rb +1 -1
- data/lib/google/cloud/logging/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0666c5ecdf0723fa5c0457bf6fd39c46a23e854204f6b3d3c88c0147c76a8ff
|
4
|
+
data.tar.gz: 4ffc01aa4b54a78d1af59681c832a972d5bb65c35d5dc59da3b5366a5e08248e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02a939f01d6ccf2c220a175732fda505140981feeb8fdc249df25831d6efa747b7aa355f7448f56a2b955b0c17e0c05310bf92e105c60c1f4bccd4ff9a9f3223
|
7
|
+
data.tar.gz: 1b94165958561e0f4f19afdde06471c88037ac828ecb97639a2a497eba30a77c9430599b33201495e9d467868df317386e6c2dbfc61e4061d8ace47751972e2b
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,45 @@
|
|
1
1
|
# Release History
|
2
2
|
|
3
|
+
### 1.6.0 / 2019-01-22
|
4
|
+
|
5
|
+
* AsyncWriter buffer entries and make batch API calls
|
6
|
+
* Update AsyncWriter to buffer log entries and batch API calls.
|
7
|
+
* Maintain backwards compatibility with the previous AsyncWriter's public API,
|
8
|
+
although the implementation is changed.
|
9
|
+
* Back pressure is applied by limiting the number of queued API calls.
|
10
|
+
Errors will now be raised when there are not enough resources.
|
11
|
+
* Errors are reported using the AsyncWriter#on_error callback.
|
12
|
+
* Pending log entries are sent before the process closes using at_exit.
|
13
|
+
* Add Logging on_error configuration.
|
14
|
+
* Add default insert_id value for Entry
|
15
|
+
* Add Entry.insert_id
|
16
|
+
* Add default insert_id value for Entry
|
17
|
+
An Entry object is assigned an insert_id when created so that if the
|
18
|
+
Entry object gets persisted multiple times it would know its insert_id
|
19
|
+
value and not attempt to generate a new one for each persist attempt.
|
20
|
+
An Entry object will still be considered empty if the only value it has
|
21
|
+
is the insert_id.
|
22
|
+
* (This change does not use SecureRandom for performance reasons.)
|
23
|
+
* Add Logging trace_sampled
|
24
|
+
* Add Entry#trace_sampled attribute
|
25
|
+
* Add trace_sampled to Logger::RequestInfo
|
26
|
+
* Changes to Rails default Logger
|
27
|
+
* Delay updating the Rails default logger until the first web request.
|
28
|
+
* This will avoid issues with forking processes and gRPC resources.
|
29
|
+
* This is accomplished by adding the on_init argument to middleware.
|
30
|
+
* Add Railtie.set_default_logger
|
31
|
+
* This method can be called post-fork to update the Rails default logger.
|
32
|
+
* Make use of Credentials#project_id
|
33
|
+
* Use Credentials#project_id
|
34
|
+
If a project_id is not provided, use the value on the Credentials object.
|
35
|
+
This value was added in googleauth 0.7.0.
|
36
|
+
* Loosen googleauth dependency
|
37
|
+
Allow for new releases up to 0.10.
|
38
|
+
The googleauth devs have committed to maintanining the current API
|
39
|
+
and will not make backwards compatible changes before 0.10.
|
40
|
+
* Direct logs for "/healthz" requests to the health check log.
|
41
|
+
* Update documentation.
|
42
|
+
|
3
43
|
### 1.5.7 / 2018-11-15
|
4
44
|
|
5
45
|
* Add Google::Logging::V2::LogEntry#trace_sampled.
|
data/CONTRIBUTING.md
CHANGED
data/lib/google-cloud-logging.rb
CHANGED
@@ -67,8 +67,9 @@ module Google
|
|
67
67
|
# logging = gcloud.logging scope: platform_scope
|
68
68
|
#
|
69
69
|
def logging scope: nil, timeout: nil, client_config: nil
|
70
|
-
|
71
|
-
|
70
|
+
timeout ||= @timeout
|
71
|
+
Google::Cloud.logging @project, @keyfile, scope: scope,
|
72
|
+
timeout: timeout,
|
72
73
|
client_config: client_config
|
73
74
|
end
|
74
75
|
|
@@ -134,16 +135,15 @@ Google::Cloud.configure.add_config! :logging do |config|
|
|
134
135
|
ENV["LOGGING_PROJECT"]
|
135
136
|
end
|
136
137
|
default_creds = Google::Cloud::Config.deferred do
|
137
|
-
Google::Cloud::Config.credentials_from_env
|
138
|
+
Google::Cloud::Config.credentials_from_env \
|
138
139
|
"LOGGING_CREDENTIALS", "LOGGING_CREDENTIALS_JSON",
|
139
140
|
"LOGGING_KEYFILE", "LOGGING_KEYFILE_JSON"
|
140
|
-
)
|
141
141
|
end
|
142
142
|
|
143
143
|
config.add_field! :project_id, default_project, match: String, allow_nil: true
|
144
144
|
config.add_alias! :project, :project_id
|
145
145
|
config.add_field! :credentials, default_creds,
|
146
|
-
match:
|
146
|
+
match: [String, Hash, Google::Auth::Credentials],
|
147
147
|
allow_nil: true
|
148
148
|
config.add_alias! :keyfile, :credentials
|
149
149
|
config.add_field! :scope, nil, match: [String, Array]
|
@@ -156,4 +156,6 @@ Google::Cloud.configure.add_config! :logging do |config|
|
|
156
156
|
mrconfig.add_field! :type, nil, match: String
|
157
157
|
mrconfig.add_field! :labels, nil, match: Hash
|
158
158
|
end
|
159
|
+
config.add_field! :set_default_logger_on_rails_init, nil, enum: [true, false]
|
160
|
+
config.add_field! :on_error, nil, match: Proc
|
159
161
|
end
|
data/lib/google/cloud/logging.rb
CHANGED
@@ -83,22 +83,25 @@ module Google
|
|
83
83
|
#
|
84
84
|
def self.new project_id: nil, credentials: nil, scope: nil, timeout: nil,
|
85
85
|
client_config: nil, project: nil, keyfile: nil
|
86
|
-
project_id
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
scope ||= configure.scope
|
91
|
-
timeout ||= configure.timeout
|
86
|
+
project_id ||= (project || default_project_id)
|
87
|
+
scope ||= configure.scope
|
88
|
+
timeout ||= configure.timeout
|
92
89
|
client_config ||= configure.client_config
|
90
|
+
credentials ||= (keyfile || default_credentials(scope: scope))
|
93
91
|
|
94
|
-
credentials ||= (keyfile || default_credentials(scope: scope))
|
95
92
|
unless credentials.is_a? Google::Auth::Credentials
|
96
93
|
credentials = Logging::Credentials.new credentials, scope: scope
|
97
94
|
end
|
98
95
|
|
96
|
+
if credentials.respond_to? :project_id
|
97
|
+
project_id ||= credentials.project_id
|
98
|
+
end
|
99
|
+
project_id = project_id.to_s # Always cast to a string
|
100
|
+
raise ArgumentError, "project_id is missing" if project_id.empty?
|
101
|
+
|
99
102
|
Logging::Project.new(
|
100
103
|
Logging::Service.new(
|
101
|
-
project_id, credentials, timeout:
|
104
|
+
project_id, credentials, timeout: timeout,
|
102
105
|
client_config: client_config
|
103
106
|
)
|
104
107
|
)
|
@@ -136,6 +139,17 @@ module Google
|
|
136
139
|
# * `labels` - (Hash) User defined labels. A `Hash` of label names to
|
137
140
|
# string label values or callables/`Proc` which are functions of the
|
138
141
|
# Rack environment.
|
142
|
+
# * `set_default_logger_on_rails_init` - (Boolean) Whether Google Cloud
|
143
|
+
# Logging Logger should be allowed to start background threads and open
|
144
|
+
# gRPC connections during Rails initialization. This should only be used
|
145
|
+
# with a non-forking web server. Web servers such as Puma and Unicorn
|
146
|
+
# should not set this, and instead set the Rails logger to a Google
|
147
|
+
# Cloud Logging Logger object on the worker process by calling
|
148
|
+
# {Railtie.set_default_logger} at the appropriate time, such as a
|
149
|
+
# post-fork hook.
|
150
|
+
# * `on_error` - (Proc) A Proc to be run when an error is encountered
|
151
|
+
# on a background thread. The Proc must take the error object as the
|
152
|
+
# single argument. (See {AsyncWriter.on_error}.)
|
139
153
|
#
|
140
154
|
# See the [Configuration
|
141
155
|
# Guide](https://googleapis.github.io/google-cloud-ruby/docs/stackdriver/latest/file.INSTRUMENTATION_CONFIGURATION)
|
@@ -13,8 +13,9 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
|
16
|
-
require "
|
17
|
-
require "
|
16
|
+
require "monitor"
|
17
|
+
require "concurrent"
|
18
|
+
require "google/cloud/logging/errors"
|
18
19
|
|
19
20
|
module Google
|
20
21
|
module Cloud
|
@@ -22,12 +23,13 @@ module Google
|
|
22
23
|
##
|
23
24
|
# # AsyncWriter
|
24
25
|
#
|
25
|
-
#
|
26
|
+
# AsyncWriter buffers, batches, and transmits log entries efficiently.
|
27
|
+
# Writing log entries is asynchronous and will not block.
|
26
28
|
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
# the
|
30
|
-
#
|
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.
|
31
33
|
#
|
32
34
|
# This object is thread-safe; it may accept write requests from
|
33
35
|
# multiple threads simultaneously, and will serialize them when
|
@@ -54,58 +56,37 @@ module Google
|
|
54
56
|
# labels: labels
|
55
57
|
#
|
56
58
|
class AsyncWriter
|
57
|
-
include
|
58
|
-
|
59
|
-
DEFAULT_MAX_QUEUE_SIZE = 10000
|
60
|
-
CLEANUP_TIMEOUT = Stackdriver::Core::AsyncActor::CLEANUP_TIMEOUT
|
61
|
-
WAIT_INTERVAL = Stackdriver::Core::AsyncActor::WAIT_INTERVAL
|
59
|
+
include MonitorMixin
|
62
60
|
|
63
61
|
##
|
64
|
-
# @private
|
65
|
-
|
66
|
-
|
67
|
-
if log_name == next_item.log_name &&
|
68
|
-
resource == next_item.resource &&
|
69
|
-
labels == next_item.labels
|
70
|
-
entries.concat(next_item.entries)
|
71
|
-
true
|
72
|
-
else
|
73
|
-
false
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
62
|
+
# @private Implementation accessors
|
63
|
+
attr_reader :logging, :max_bytes, :max_count, :interval,
|
64
|
+
:threads, :max_queue, :partial_success
|
77
65
|
|
78
66
|
##
|
79
|
-
# @private
|
80
|
-
|
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
|
+
@logging = logging
|
81
72
|
|
82
|
-
|
83
|
-
|
84
|
-
|
73
|
+
@max_count = max_count
|
74
|
+
@max_bytes = max_bytes
|
75
|
+
@max_queue = max_queue
|
76
|
+
@interval = interval
|
77
|
+
@threads = threads
|
85
78
|
|
86
|
-
|
87
|
-
# The current state. Either :running, :suspended, :stopping, or :stopped
|
88
|
-
#
|
89
|
-
# DEPRECATED. Use #async_state instead.
|
90
|
-
alias state async_state
|
79
|
+
@partial_success = partial_success
|
91
80
|
|
92
|
-
|
93
|
-
# The last exception thrown by the background thread, or nil if nothing
|
94
|
-
# has been thrown.
|
95
|
-
attr_reader :last_exception
|
81
|
+
@error_callbacks = []
|
96
82
|
|
97
|
-
|
98
|
-
# @private Creates a new AsyncWriter instance.
|
99
|
-
def initialize logging, max_queue_size = DEFAULT_MAX_QUEUE_SIZE,
|
100
|
-
partial_success = false
|
101
|
-
super()
|
83
|
+
@cond = new_cond
|
102
84
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
@queue_size = 0
|
85
|
+
# Make sure all buffered messages are sent when process exits.
|
86
|
+
at_exit { stop }
|
87
|
+
|
88
|
+
# init MonitorMixin
|
89
|
+
super()
|
109
90
|
end
|
110
91
|
|
111
92
|
##
|
@@ -149,19 +130,35 @@ module Google
|
|
149
130
|
# async.write_entries entry
|
150
131
|
#
|
151
132
|
def write_entries entries, log_name: nil, resource: nil, labels: nil
|
152
|
-
ensure_thread
|
153
|
-
entries = Array(entries)
|
154
133
|
synchronize do
|
155
|
-
raise "AsyncWriter has been stopped"
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
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
|
164
152
|
end
|
153
|
+
|
154
|
+
@thread_pool ||= \
|
155
|
+
Concurrent::CachedThreadPool.new max_threads: @threads,
|
156
|
+
max_queue: @max_queue
|
157
|
+
@thread ||= Thread.new { run_background }
|
158
|
+
|
159
|
+
publish_batch! if @batch.ready?
|
160
|
+
|
161
|
+
@cond.broadcast
|
165
162
|
end
|
166
163
|
self
|
167
164
|
end
|
@@ -201,182 +198,306 @@ module Google
|
|
201
198
|
end
|
202
199
|
|
203
200
|
##
|
204
|
-
#
|
205
|
-
#
|
206
|
-
#
|
207
|
-
#
|
208
|
-
# issued writes will complete. Once any existing backlog has been
|
209
|
-
# cleared, the state will change to :stopped.
|
210
|
-
#
|
211
|
-
# DEPRECATED. Use #async_stop instead.
|
201
|
+
# Begins the process of stopping the writer. Entries already in the
|
202
|
+
# queue will be published, but no new entries can be added. Use {#wait!}
|
203
|
+
# to block until the writer is fully stopped and all pending entries
|
204
|
+
# have been published.
|
212
205
|
#
|
213
|
-
# @return [
|
214
|
-
|
215
|
-
|
216
|
-
|
206
|
+
# @return [AsyncWriter] returns self so calls can be chained.
|
207
|
+
def stop
|
208
|
+
synchronize do
|
209
|
+
break if @stopped
|
210
|
+
|
211
|
+
@stopped = true
|
212
|
+
publish_batch!
|
213
|
+
@cond.broadcast
|
214
|
+
@thread_pool.shutdown if @thread_pool
|
215
|
+
end
|
216
|
+
|
217
|
+
self
|
218
|
+
end
|
217
219
|
|
218
220
|
##
|
219
|
-
#
|
220
|
-
#
|
221
|
-
#
|
222
|
-
#
|
223
|
-
#
|
224
|
-
# DEPRECATED. Use #async_suspend instead.
|
221
|
+
# Blocks until the writer is fully stopped, all pending entries have
|
222
|
+
# been published, and all callbacks have completed. Does not stop the
|
223
|
+
# writer. To stop the writer, first call {#stop} and then call {#wait!}
|
224
|
+
# to block until the writer is stopped.
|
225
225
|
#
|
226
|
-
# @return [
|
227
|
-
|
228
|
-
|
229
|
-
|
226
|
+
# @return [AsyncWriter] returns self so calls can be chained.
|
227
|
+
def wait! timeout = nil
|
228
|
+
synchronize do
|
229
|
+
if @thread_pool
|
230
|
+
@thread_pool.shutdown
|
231
|
+
@thread_pool.wait_for_termination timeout
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
self
|
236
|
+
end
|
230
237
|
|
231
238
|
##
|
232
|
-
#
|
233
|
-
#
|
234
|
-
# After this call succeeds, the state will change to :running, and
|
235
|
-
# the writer will resume sending RPCs.
|
239
|
+
# Stop this asynchronous writer and block until it has been stopped.
|
236
240
|
#
|
237
|
-
#
|
241
|
+
# @param [Number] timeout Timeout in seconds.
|
242
|
+
# @param [Boolean] force If set to true, and the writer hasn't stopped
|
243
|
+
# within the given timeout, kill it forcibly by terminating the
|
244
|
+
# thread. This should be used with extreme caution, as it can
|
245
|
+
# leave RPCs unfinished. Default is false.
|
238
246
|
#
|
239
|
-
# @return [
|
240
|
-
#
|
247
|
+
# @return [Symbol] Returns `:stopped` if the AsyncWriter was already
|
248
|
+
# stopped at the time of invocation, `:waited` if it stopped
|
249
|
+
# during the timeout period, `:timeout` if it is still running
|
250
|
+
# after the timeout, or `:forced` if it was forcibly killed.
|
241
251
|
#
|
242
|
-
|
252
|
+
def stop! timeout = nil, force: nil
|
253
|
+
return :stopped if stopped?
|
254
|
+
|
255
|
+
stop
|
256
|
+
wait! timeout
|
257
|
+
|
258
|
+
if synchronize { @thread_pool.shutdown? }
|
259
|
+
return :waited if timeout
|
260
|
+
elsif force
|
261
|
+
@thread_pool.kill
|
262
|
+
return :forced
|
263
|
+
end
|
264
|
+
:timeout
|
265
|
+
end
|
266
|
+
alias async_stop! stop!
|
243
267
|
|
244
268
|
##
|
245
|
-
#
|
269
|
+
# Forces all entries in the current batch to be published
|
270
|
+
# immediately.
|
246
271
|
#
|
247
|
-
#
|
272
|
+
# @return [AsyncWriter] returns self so calls can be chained.
|
273
|
+
def flush
|
274
|
+
synchronize do
|
275
|
+
publish_batch!
|
276
|
+
@cond.broadcast
|
277
|
+
end
|
278
|
+
|
279
|
+
self
|
280
|
+
end
|
281
|
+
|
282
|
+
##
|
283
|
+
# Whether the writer has been started.
|
248
284
|
#
|
249
|
-
# @return [
|
285
|
+
# @return [boolean] `true` when started, `false` otherwise.
|
286
|
+
def started?
|
287
|
+
!stopped?
|
288
|
+
end
|
289
|
+
|
290
|
+
##
|
291
|
+
# Whether the writer has been stopped.
|
250
292
|
#
|
251
|
-
|
293
|
+
# @return [boolean] `true` when stopped, `false` otherwise.
|
294
|
+
def stopped?
|
295
|
+
synchronize { @stopped }
|
296
|
+
end
|
252
297
|
|
253
298
|
##
|
254
|
-
#
|
299
|
+
# Register to be notified of errors when raised.
|
255
300
|
#
|
256
|
-
#
|
301
|
+
# If an unhandled error has occurred the writer will attempt to
|
302
|
+
# recover from the error and resume buffering, batching, and
|
303
|
+
# transmitting log entries
|
257
304
|
#
|
258
|
-
#
|
305
|
+
# Multiple error handlers can be added.
|
259
306
|
#
|
260
|
-
|
261
|
-
|
262
|
-
##
|
263
|
-
# Returns true if this writer is still accepting writes. This means
|
264
|
-
# it is either running or suspended.
|
307
|
+
# @yield [callback] The block to be called when an error is raised.
|
308
|
+
# @yieldparam [Exception] error The error raised.
|
265
309
|
#
|
266
|
-
#
|
310
|
+
# @example
|
311
|
+
# require "google/cloud/logging"
|
312
|
+
# require "google/cloud/error_reporting"
|
267
313
|
#
|
268
|
-
#
|
314
|
+
# logging = Google::Cloud::Logging.new
|
269
315
|
#
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
#
|
316
|
+
# resource = logging.resource "gae_app",
|
317
|
+
# module_id: "1",
|
318
|
+
# version_id: "20150925t173233"
|
319
|
+
#
|
320
|
+
# async = logging.async_writer
|
274
321
|
#
|
275
|
-
#
|
322
|
+
# # Register to be notified when unhandled errors occur.
|
323
|
+
# async.on_error do |error|
|
324
|
+
# # error can be a AsyncWriterError or AsyncWriteEntriesError
|
325
|
+
# Google::Cloud::ErrorReporting.report error
|
326
|
+
# end
|
276
327
|
#
|
277
|
-
#
|
328
|
+
# logger = async.logger "my_app_log", resource, env: :production
|
329
|
+
# logger.info "Job started."
|
278
330
|
#
|
279
|
-
|
331
|
+
def on_error &block
|
332
|
+
synchronize do
|
333
|
+
@error_callbacks << block
|
334
|
+
end
|
335
|
+
end
|
280
336
|
|
281
337
|
##
|
282
|
-
#
|
283
|
-
#
|
338
|
+
# The most recent unhandled error to occur while transmitting log
|
339
|
+
# entries.
|
284
340
|
#
|
285
|
-
#
|
341
|
+
# If an unhandled error has occurred the subscriber will attempt to
|
342
|
+
# recover from the error and resume buffering, batching, and
|
343
|
+
# transmitting log entries.
|
286
344
|
#
|
287
|
-
# @
|
288
|
-
# timeout.
|
345
|
+
# @return [Exception, nil] error The most recent error raised.
|
289
346
|
#
|
290
|
-
# @
|
291
|
-
#
|
347
|
+
# @example
|
348
|
+
# require "google/cloud/logging"
|
292
349
|
#
|
293
|
-
|
294
|
-
|
295
|
-
##
|
296
|
-
# Stop this asynchronous writer and block until it has been stopped.
|
350
|
+
# logging = Google::Cloud::Logging.new
|
297
351
|
#
|
298
|
-
#
|
352
|
+
# resource = logging.resource "gae_app",
|
353
|
+
# module_id: "1",
|
354
|
+
# version_id: "20150925t173233"
|
299
355
|
#
|
300
|
-
#
|
301
|
-
# @param [Boolean] force If set to true, and the writer hasn't stopped
|
302
|
-
# within the given timeout, kill it forcibly by terminating the
|
303
|
-
# thread. This should be used with extreme caution, as it can
|
304
|
-
# leave RPCs unfinished. Default is false.
|
356
|
+
# async = logging.async_writer
|
305
357
|
#
|
306
|
-
#
|
307
|
-
#
|
308
|
-
# during the timeout period, `:timeout` if it is still running
|
309
|
-
# after the timeout, or `:forced` if it was forcibly killed.
|
358
|
+
# logger = async.logger "my_app_log", resource, env: :production
|
359
|
+
# logger.info "Job started."
|
310
360
|
#
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
361
|
+
# # If an error was raised, it can be retrieved here:
|
362
|
+
# async.last_error #=> nil
|
363
|
+
#
|
364
|
+
def last_error
|
365
|
+
synchronize { @last_error }
|
316
366
|
end
|
367
|
+
alias last_exception last_error
|
317
368
|
|
318
|
-
|
319
|
-
|
320
|
-
def
|
369
|
+
protected
|
370
|
+
|
371
|
+
def run_background
|
321
372
|
synchronize do
|
322
|
-
@
|
373
|
+
until @stopped
|
374
|
+
if @batch.nil?
|
375
|
+
@cond.wait
|
376
|
+
next
|
377
|
+
end
|
378
|
+
|
379
|
+
if @batch.ready?
|
380
|
+
# interval met, publish the batch...
|
381
|
+
publish_batch!
|
382
|
+
@cond.wait
|
383
|
+
else
|
384
|
+
# still waiting for the interval to publish the batch...
|
385
|
+
@cond.wait(@batch.publish_wait)
|
386
|
+
end
|
387
|
+
end
|
323
388
|
end
|
324
389
|
end
|
325
390
|
|
326
|
-
|
391
|
+
def publish_batch!
|
392
|
+
return unless @batch
|
327
393
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
log_name: queue_item.log_name,
|
339
|
-
resource: queue_item.resource,
|
340
|
-
labels: queue_item.labels,
|
341
|
-
partial_success: @partial_success
|
342
|
-
)
|
343
|
-
rescue StandardError => e
|
344
|
-
# Ignore any exceptions thrown from the background thread, but
|
345
|
-
# keep running to ensure its state behavior remains consistent.
|
346
|
-
@last_exception = e
|
394
|
+
batch_to_be_published = @batch
|
395
|
+
@batch = nil
|
396
|
+
publish_batch_async batch_to_be_published
|
397
|
+
end
|
398
|
+
|
399
|
+
# Sets the last_error and calls all error callbacks.
|
400
|
+
def error! error
|
401
|
+
error_callbacks = synchronize do
|
402
|
+
@last_error = error
|
403
|
+
@error_callbacks
|
347
404
|
end
|
405
|
+
error_callbacks = default_error_callbacks if error_callbacks.empty?
|
406
|
+
error_callbacks.each { |error_callback| error_callback.call error }
|
348
407
|
end
|
349
408
|
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
while state == :suspended ||
|
360
|
-
(state == :running && @queue.empty?)
|
361
|
-
@queue_resource.wait
|
362
|
-
end
|
363
|
-
queue_item = nil
|
364
|
-
unless @queue.empty?
|
365
|
-
queue_item = @queue.shift
|
366
|
-
@queue_size -= queue_item.entries.size
|
409
|
+
def default_error_callbacks
|
410
|
+
# This is memoized to reduce calls to the configuration.
|
411
|
+
@default_error_callbacks ||= begin
|
412
|
+
error_callback = Google::Cloud::Logging.configuration.on_error
|
413
|
+
error_callback ||= Google::Cloud.configure.on_error
|
414
|
+
if error_callback
|
415
|
+
[error_callback]
|
416
|
+
else
|
417
|
+
[]
|
367
418
|
end
|
368
|
-
@queue_resource.broadcast
|
369
|
-
queue_item
|
370
419
|
end
|
371
420
|
end
|
372
421
|
|
422
|
+
def publish_batch_async batch
|
423
|
+
Concurrent::Promises.future_on(
|
424
|
+
@thread_pool, batch.entries
|
425
|
+
) do |entries|
|
426
|
+
write_entries_with entries
|
427
|
+
end
|
428
|
+
rescue Concurrent::RejectedExecutionError => e
|
429
|
+
async_error = AsyncWriterError.new(
|
430
|
+
"Error writing entries: #{e.message}",
|
431
|
+
batch.entries
|
432
|
+
)
|
433
|
+
# Manually set backtrace so we don't have to raise
|
434
|
+
async_error.set_backtrace caller
|
435
|
+
error! async_error
|
436
|
+
end
|
437
|
+
|
438
|
+
def write_entries_with entries
|
439
|
+
logging.write_entries entries, partial_success: partial_success
|
440
|
+
rescue StandardError => e
|
441
|
+
write_error = AsyncWriteEntriesError.new(
|
442
|
+
"Error writing entries: #{e.message}",
|
443
|
+
entries
|
444
|
+
)
|
445
|
+
# Manually set backtrace so we don't have to raise
|
446
|
+
write_error.set_backtrace caller
|
447
|
+
error! write_error
|
448
|
+
end
|
449
|
+
|
373
450
|
##
|
374
|
-
# @private
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
@
|
451
|
+
# @private
|
452
|
+
class Batch
|
453
|
+
attr_reader :created_at, :entries
|
454
|
+
|
455
|
+
def initialize writer
|
456
|
+
@writer = writer
|
457
|
+
@entries = []
|
458
|
+
@entries_bytes = 2 # initial size w/ partial_success
|
459
|
+
@created_at = nil
|
460
|
+
end
|
461
|
+
|
462
|
+
def add entry, addl_bytes: nil
|
463
|
+
addl_bytes ||= addl_bytes_for entry
|
464
|
+
@entries << entry
|
465
|
+
@entries_bytes += addl_bytes
|
466
|
+
@created_at ||= Time.now
|
467
|
+
nil
|
468
|
+
end
|
469
|
+
|
470
|
+
def try_add entry
|
471
|
+
addl_bytes = addl_bytes_for entry
|
472
|
+
new_message_count = @entries.count + 1
|
473
|
+
new_message_bytes = @entries_bytes + addl_bytes
|
474
|
+
if new_message_count > @writer.max_count ||
|
475
|
+
new_message_bytes >= @writer.max_bytes
|
476
|
+
return false
|
477
|
+
end
|
478
|
+
add entry, addl_bytes: addl_bytes
|
479
|
+
true
|
480
|
+
end
|
481
|
+
|
482
|
+
def ready?
|
483
|
+
@entries.count >= @writer.max_count ||
|
484
|
+
@entries_bytes >= @writer.max_bytes ||
|
485
|
+
(@created_at.nil? || (publish_at < Time.now))
|
486
|
+
end
|
487
|
+
|
488
|
+
def publish_at
|
489
|
+
return nil if @created_at.nil?
|
490
|
+
@created_at + @writer.interval
|
491
|
+
end
|
492
|
+
|
493
|
+
def publish_wait
|
494
|
+
publish_wait = publish_at - Time.now
|
495
|
+
return 0 if publish_wait < 0
|
496
|
+
publish_wait
|
497
|
+
end
|
498
|
+
|
499
|
+
def addl_bytes_for entry
|
500
|
+
entry.to_grpc.to_proto.bytesize + 2
|
380
501
|
end
|
381
502
|
end
|
382
503
|
end
|