google-cloud-logging 2.0.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 +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
|