google-cloud-logging 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.yardopts +18 -0
- data/AUTHENTICATION.md +178 -0
- data/CHANGELOG.md +407 -0
- data/CODE_OF_CONDUCT.md +40 -0
- data/CONTRIBUTING.md +188 -0
- data/INSTRUMENTATION.md +71 -0
- data/LICENSE +201 -0
- data/LOGGING.md +32 -0
- data/OVERVIEW.md +321 -0
- data/TROUBLESHOOTING.md +31 -0
- data/lib/google-cloud-logging.rb +161 -0
- data/lib/google/cloud/logging.rb +188 -0
- data/lib/google/cloud/logging/async_writer.rb +513 -0
- data/lib/google/cloud/logging/convert.rb +70 -0
- data/lib/google/cloud/logging/credentials.rb +44 -0
- data/lib/google/cloud/logging/entry.rb +528 -0
- data/lib/google/cloud/logging/entry/http_request.rb +167 -0
- data/lib/google/cloud/logging/entry/list.rb +178 -0
- data/lib/google/cloud/logging/entry/operation.rb +91 -0
- data/lib/google/cloud/logging/entry/source_location.rb +85 -0
- data/lib/google/cloud/logging/errors.rb +101 -0
- data/lib/google/cloud/logging/log/list.rb +156 -0
- data/lib/google/cloud/logging/logger.rb +633 -0
- data/lib/google/cloud/logging/metric.rb +168 -0
- data/lib/google/cloud/logging/metric/list.rb +170 -0
- data/lib/google/cloud/logging/middleware.rb +307 -0
- data/lib/google/cloud/logging/project.rb +838 -0
- data/lib/google/cloud/logging/rails.rb +232 -0
- data/lib/google/cloud/logging/resource.rb +85 -0
- data/lib/google/cloud/logging/resource_descriptor.rb +137 -0
- data/lib/google/cloud/logging/resource_descriptor/list.rb +175 -0
- data/lib/google/cloud/logging/service.rb +239 -0
- data/lib/google/cloud/logging/sink.rb +315 -0
- data/lib/google/cloud/logging/sink/list.rb +168 -0
- data/lib/google/cloud/logging/version.rb +22 -0
- 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
|