google-cloud-logging 1.5.7 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/CONTRIBUTING.md +1 -1
  4. data/lib/google-cloud-logging.rb +7 -5
  5. data/lib/google/cloud/logging.rb +22 -8
  6. data/lib/google/cloud/logging/async_writer.rb +308 -187
  7. data/lib/google/cloud/logging/convert.rb +15 -9
  8. data/lib/google/cloud/logging/entry.rb +43 -13
  9. data/lib/google/cloud/logging/entry/source_location.rb +3 -3
  10. data/lib/google/cloud/logging/errors.rb +101 -0
  11. data/lib/google/cloud/logging/log/list.rb +1 -1
  12. data/lib/google/cloud/logging/logger.rb +6 -4
  13. data/lib/google/cloud/logging/middleware.rb +24 -11
  14. data/lib/google/cloud/logging/project.rb +38 -15
  15. data/lib/google/cloud/logging/rails.rb +56 -5
  16. data/lib/google/cloud/logging/resource.rb +1 -1
  17. data/lib/google/cloud/logging/service.rb +42 -32
  18. data/lib/google/cloud/logging/v2/config_service_v2_client.rb +1 -1
  19. data/lib/google/cloud/logging/v2/credentials.rb +1 -1
  20. data/lib/google/cloud/logging/v2/doc/google/api/distribution.rb +1 -37
  21. data/lib/google/cloud/logging/v2/doc/google/api/label.rb +1 -1
  22. data/lib/google/cloud/logging/v2/doc/google/api/metric.rb +1 -13
  23. data/lib/google/cloud/logging/v2/doc/google/api/monitored_resource.rb +1 -1
  24. data/lib/google/cloud/logging/v2/doc/google/logging/type/http_request.rb +1 -1
  25. data/lib/google/cloud/logging/v2/doc/google/logging/v2/log_entry.rb +1 -1
  26. data/lib/google/cloud/logging/v2/doc/google/logging/v2/logging.rb +1 -12
  27. data/lib/google/cloud/logging/v2/doc/google/logging/v2/logging_config.rb +1 -1
  28. data/lib/google/cloud/logging/v2/doc/google/logging/v2/logging_metrics.rb +1 -1
  29. data/lib/google/cloud/logging/v2/doc/google/protobuf/any.rb +1 -1
  30. data/lib/google/cloud/logging/v2/doc/google/protobuf/duration.rb +1 -1
  31. data/lib/google/cloud/logging/v2/doc/google/protobuf/empty.rb +1 -1
  32. data/lib/google/cloud/logging/v2/doc/google/protobuf/field_mask.rb +1 -1
  33. data/lib/google/cloud/logging/v2/doc/google/protobuf/struct.rb +1 -1
  34. data/lib/google/cloud/logging/v2/doc/google/protobuf/timestamp.rb +1 -1
  35. data/lib/google/cloud/logging/v2/logging_service_v2_client.rb +1 -1
  36. data/lib/google/cloud/logging/v2/logging_service_v2_client_config.json +1 -1
  37. data/lib/google/cloud/logging/v2/metrics_service_v2_client.rb +1 -1
  38. data/lib/google/cloud/logging/version.rb +1 -1
  39. metadata +5 -4
@@ -23,25 +23,31 @@ module Google
23
23
  # @private Convert a Hash to a Google::Protobuf::Struct.
24
24
  def self.hash_to_struct hash
25
25
  # TODO: ArgumentError if hash is not a Hash
26
- Google::Protobuf::Struct.new fields:
27
- Hash[hash.map { |k, v| [String(k), object_to_value(v)] }]
26
+ Google::Protobuf::Struct.new \
27
+ fields: Hash[hash.map { |k, v| [String(k), object_to_value(v)] }]
28
+ end
29
+
30
+ ##
31
+ # @private Convert an Array to a Google::Protobuf::ListValue.
32
+ def self.array_to_list array
33
+ # TODO: ArgumentError if array is not an Array
34
+ Google::Protobuf::ListValue.new \
35
+ values: array.map { |o| object_to_value(o) }
28
36
  end
29
37
 
30
38
  ##
31
39
  # @private Convert an Object to a Google::Protobuf::Value.
32
40
  def self.object_to_value obj
33
41
  case obj
34
- when NilClass then Google::Protobuf::Value.new null_value:
35
- :NULL_VALUE
42
+ when NilClass then Google::Protobuf::Value.new null_value: :NULL_VALUE
36
43
  when Numeric then Google::Protobuf::Value.new number_value: obj
37
44
  when String then Google::Protobuf::Value.new string_value: obj
38
45
  when TrueClass then Google::Protobuf::Value.new bool_value: true
39
46
  when FalseClass then Google::Protobuf::Value.new bool_value: false
40
- when Hash then Google::Protobuf::Value.new struct_value:
41
- hash_to_struct(obj)
42
- when Array then Google::Protobuf::Value.new list_value:
43
- Google::Protobuf::ListValue.new(values:
44
- obj.map { |o| object_to_value(o) })
47
+ when Hash then
48
+ Google::Protobuf::Value.new struct_value: hash_to_struct(obj)
49
+ when Array then
50
+ Google::Protobuf::Value.new list_value: array_to_list(obj)
45
51
  else
46
52
  # TODO: Could raise ArgumentError here, or convert to a string
47
53
  Google::Protobuf::Value.new string_value: obj.to_s
@@ -71,6 +71,21 @@ module Google
71
71
  # logging.write_entries entry
72
72
  #
73
73
  class Entry
74
+ ##
75
+ # Generate a pseudo-random, 16-character ID suitable for use as the log
76
+ # entry's {#insert_id}.
77
+ #
78
+ # @return [String]
79
+ #
80
+ # @example
81
+ # require "google/cloud/logging"
82
+ #
83
+ # insert_id = Google::Cloud::Logging::Entry.insert_id
84
+ #
85
+ def self.insert_id
86
+ rand(36**16).to_s 36
87
+ end
88
+
74
89
  ##
75
90
  # Create a new Entry instance. The {#resource} attribute is
76
91
  # pre-populated with a new {Google::Cloud::Logging::Resource} instance.
@@ -82,6 +97,7 @@ module Google
82
97
  @operation = Operation.new
83
98
  @severity = :DEFAULT
84
99
  @source_location = SourceLocation.new
100
+ @insert_id = Entry.insert_id
85
101
  end
86
102
 
87
103
  ##
@@ -379,36 +395,48 @@ module Google
379
395
  # @return [Google::Cloud::Logging::Entry::SourceLocation]
380
396
  attr_reader :source_location
381
397
 
398
+ ##
399
+ # The sampling decision of the trace associated with the log entry.
400
+ # Optional. A `true` value means that the trace resource name in the
401
+ # `trace` field was sampled for storage in a trace backend. A `false`
402
+ # means that the trace was not sampled for storage when this log entry
403
+ # was written, or the sampling decision was unknown at the time. A
404
+ # non-sampled `trace` value is still useful as a request correlation
405
+ # identifier. The default is `false`.
406
+ # @return [Boolean]
407
+ attr_accessor :trace_sampled
408
+
382
409
  ##
383
410
  # @private Determines if the Entry has any data.
384
411
  def empty?
385
412
  log_name.nil? &&
386
413
  timestamp.nil? &&
387
- insert_id.nil? &&
388
414
  (labels.nil? || labels.empty?) &&
389
415
  payload.nil? &&
390
416
  resource.empty? &&
391
417
  http_request.empty? &&
392
418
  operation.empty? &&
393
419
  trace.nil? &&
394
- source_location.empty?
420
+ source_location.empty? &&
421
+ trace_sampled.nil?
395
422
  end
396
423
 
397
424
  ##
398
425
  # @private Exports the Entry to a Google::Logging::V2::LogEntry object.
399
426
  def to_grpc
400
427
  grpc = Google::Logging::V2::LogEntry.new(
401
- log_name: log_name.to_s,
402
- timestamp: timestamp_grpc,
428
+ log_name: log_name.to_s,
429
+ timestamp: timestamp_grpc,
403
430
  # TODO: verify severity is the correct type?
404
- severity: severity,
405
- insert_id: insert_id.to_s,
406
- labels: labels_grpc,
407
- resource: resource.to_grpc,
408
- http_request: http_request.to_grpc,
409
- operation: operation.to_grpc,
410
- trace: trace.to_s,
411
- source_location: source_location.to_grpc
431
+ severity: severity,
432
+ insert_id: insert_id.to_s,
433
+ labels: labels_grpc,
434
+ resource: resource.to_grpc,
435
+ http_request: http_request.to_grpc,
436
+ operation: operation.to_grpc,
437
+ trace: trace.to_s,
438
+ source_location: source_location.to_grpc,
439
+ trace_sampled: !(!trace_sampled)
412
440
  )
413
441
  # Add payload
414
442
  append_payload grpc
@@ -437,6 +465,7 @@ module Google
437
465
  SourceLocation.from_grpc(
438
466
  grpc.source_location
439
467
  )
468
+ e.trace_sampled = grpc.trace_sampled
440
469
  end
441
470
  end
442
471
 
@@ -448,7 +477,7 @@ module Google
448
477
  # TODO: ArgumentError if timestamp is not a Time object?
449
478
  Google::Protobuf::Timestamp.new(
450
479
  seconds: timestamp.to_i,
451
- nanos: timestamp.nsec
480
+ nanos: timestamp.nsec
452
481
  )
453
482
  end
454
483
 
@@ -456,6 +485,7 @@ module Google
456
485
  # @private Formats the labels so they can be saved to a
457
486
  # Google::Logging::V2::LogEntry object.
458
487
  def labels_grpc
488
+ return {} if labels.nil?
459
489
  # Coerce symbols to strings
460
490
  Hash[labels.map do |k, v|
461
491
  v = String(v) if v.is_a? Symbol
@@ -61,9 +61,9 @@ module Google
61
61
  def to_grpc
62
62
  return nil if empty?
63
63
  Google::Logging::V2::LogEntrySourceLocation.new(
64
- file: file.to_s,
65
- line: line,
66
- function: function.to_s
64
+ file: file.to_s,
65
+ line: line,
66
+ function: function.to_s
67
67
  )
68
68
  end
69
69
 
@@ -0,0 +1,101 @@
1
+ # Copyright 2018 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/errors"
17
+
18
+ module Google
19
+ module Cloud
20
+ module Logging
21
+ ##
22
+ # # AsyncWriterError
23
+ #
24
+ # Used to indicate a problem preventing {AsyncWriter} from asynchronously
25
+ # calling the API. This can occur when the {AsyncWriter} has too few
26
+ # resources allocated for the amount of usage.
27
+ #
28
+ # @example
29
+ # require "google/cloud/logging"
30
+ # require "google/cloud/error_reporting"
31
+ #
32
+ # logging = Google::Cloud::Logging.new
33
+ #
34
+ # resource = logging.resource "gae_app",
35
+ # module_id: "1",
36
+ # version_id: "20150925t173233"
37
+ #
38
+ # async = logging.async_writer
39
+ #
40
+ # # Register to be notified when unhandled errors occur.
41
+ # async.on_error do |error|
42
+ # # error can be a AsyncWriterError, with entries
43
+ # Google::Cloud::ErrorReporting.report error
44
+ # end
45
+ #
46
+ # logger = async.logger "my_app_log", resource, env: :production
47
+ # logger.info "Job started."
48
+ #
49
+ class AsyncWriterError < Google::Cloud::Error
50
+ # @!attribute [r] count
51
+ # @return [Array<Google::Cloud::Logging::Entry>] entries The entry
52
+ # objects that were not written to the API due to the error.
53
+ attr_reader :entries
54
+
55
+ def initialize message, entries = nil
56
+ super(message)
57
+ @entries = entries if entries
58
+ end
59
+ end
60
+
61
+ ##
62
+ # # AsyncWriteEntriesError
63
+ #
64
+ # Used to indicate a problem when {AsyncWriter} writes log entries to the
65
+ # API. This can occur when the API returns an error.
66
+ #
67
+ # @example
68
+ # require "google/cloud/logging"
69
+ # require "google/cloud/error_reporting"
70
+ #
71
+ # logging = Google::Cloud::Logging.new
72
+ #
73
+ # resource = logging.resource "gae_app",
74
+ # module_id: "1",
75
+ # version_id: "20150925t173233"
76
+ #
77
+ # async = logging.async_writer
78
+ #
79
+ # # Register to be notified when unhandled errors occur.
80
+ # async.on_error do |error|
81
+ # # error can be a AsyncWriteEntriesError, with entries
82
+ # Google::Cloud::ErrorReporting.report error
83
+ # end
84
+ #
85
+ # logger = async.logger "my_app_log", resource, env: :production
86
+ # logger.info "Job started."
87
+ #
88
+ class AsyncWriteEntriesError < Google::Cloud::Error
89
+ # @!attribute [r] count
90
+ # @return [Array<Google::Cloud::Logging::Entry>] entries The entry
91
+ # objects that were not written to the API due to the error.
92
+ attr_reader :entries
93
+
94
+ def initialize message, entries = nil
95
+ super(message)
96
+ @entries = entries if entries
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -73,7 +73,7 @@ module Google
73
73
  grpc = @service.list_logs token: token, resource: @resource,
74
74
  max: @max
75
75
  self.class.from_grpc grpc, @service, resource: @resource,
76
- max: @max
76
+ max: @max
77
77
  end
78
78
 
79
79
  ##
@@ -62,7 +62,7 @@ module Google
62
62
  # The log_name is a String that controls the name of the Stackdriver
63
63
  # log to write to. If it is nil, the default log_name for this Logger
64
64
  # is used.
65
- RequestInfo = ::Struct.new :trace_id, :log_name, :env
65
+ RequestInfo = ::Struct.new :trace_id, :log_name, :env, :trace_sampled
66
66
 
67
67
  ##
68
68
  # The Google Cloud writer object that calls to `#write_entries` are made
@@ -456,8 +456,9 @@ module Google
456
456
  # @param [Hash, nil] env The request's Rack environment or `nil` if not
457
457
  # available.
458
458
  #
459
- def add_request_info info: nil, env: nil, trace_id: nil, log_name: nil
460
- info ||= RequestInfo.new trace_id, log_name, env
459
+ def add_request_info info: nil, env: nil, trace_id: nil, log_name: nil,
460
+ trace_sampled: nil
461
+ info ||= RequestInfo.new trace_id, log_name, env, trace_sampled
461
462
 
462
463
  @request_info_var.value = info
463
464
 
@@ -549,11 +550,12 @@ module Google
549
550
  unless info.trace_id.nil? || @project.nil?
550
551
  entry.trace = "projects/#{@project}/traces/#{info.trace_id}"
551
552
  end
553
+ entry.trace_sampled = info.trace_sampled if entry.trace_sampled.nil?
552
554
  end
553
555
 
554
556
  writer.write_entries entry, log_name: actual_log_name,
555
557
  resource: resource,
556
- labels: entry_labels(info)
558
+ labels: entry_labels(info)
557
559
  end
558
560
 
559
561
  ##
@@ -28,7 +28,8 @@ module Google
28
28
  # A default value for the log_name_map argument. Directs health check
29
29
  # logs to a separate log name so they don't spam the main log.
30
30
  DEFAULT_LOG_NAME_MAP =
31
- { "/_ah/health" => "ruby_health_check_log" }.freeze
31
+ { "/_ah/health" => "ruby_health_check_log",
32
+ "/healthz" => "ruby_health_check_log" }.freeze
32
33
 
33
34
  ##
34
35
  # The Google::Cloud::Logging::Logger instance
@@ -43,6 +44,8 @@ module Google
43
44
  # to track Stackdriver request trace ID. It also properly sets
44
45
  # env["rack.logger"] to this assigned logger for accessing. If not
45
46
  # specified, a default logger with be used.
47
+ # @param [Proc] on_init A callback to be invoked when the middleware is
48
+ # initialized. The callback takes no arguments. Optional.
46
49
  # @param [Hash] kwargs Hash of configuration settings. Used for
47
50
  # backward API compatibility. See the [Configuration
48
51
  # Guide](https://googleapis.github.io/google-cloud-ruby/docs/stackdriver/latest/file.INSTRUMENTATION_CONFIGURATION)
@@ -51,23 +54,26 @@ module Google
51
54
  # @return [Google::Cloud::Logging::Middleware] A new
52
55
  # Google::Cloud::Logging::Middleware instance
53
56
  #
54
- def initialize app, logger: nil, **kwargs
57
+ def initialize app, logger: nil, on_init: nil, **kwargs
55
58
  @app = app
56
59
 
57
60
  load_config kwargs
58
61
 
59
- if logger
60
- @logger = logger
61
- else
62
+ logger ||= Middleware.logger
63
+ logger ||= begin
62
64
  log_name = configuration.log_name
63
- logging = Logging.new project_id: configuration.project_id,
65
+ logging = Logging.new project_id: configuration.project_id,
64
66
  credentials: configuration.credentials
65
67
  resource = Middleware.build_monitored_resource(
66
68
  configuration.monitored_resource.type,
67
69
  configuration.monitored_resource.labels
68
70
  )
69
- @logger = logging.logger log_name, resource
71
+ Middleware.logger = logging.logger log_name, resource
70
72
  end
73
+
74
+ on_init.call if on_init
75
+
76
+ @logger = logger
71
77
  end
72
78
 
73
79
  ##
@@ -185,6 +191,12 @@ module Google
185
191
  end
186
192
  end
187
193
 
194
+ class << self
195
+ ##
196
+ # @private Global logger object
197
+ attr_accessor :logger
198
+ end
199
+
188
200
  ##
189
201
  # @private Extract information from current environment and construct
190
202
  # the correct monitoring resource types and labels.
@@ -226,19 +238,20 @@ module Google
226
238
  type, labels =
227
239
  if Google::Cloud.env.app_engine?
228
240
  ["gae_app", {
229
- module_id: Google::Cloud.env.app_engine_service_id,
241
+ module_id: Google::Cloud.env.app_engine_service_id,
230
242
  version_id: Google::Cloud.env.app_engine_service_version
231
243
  }]
232
244
  elsif Google::Cloud.env.container_engine?
245
+ namespace_id = Google::Cloud.env.container_engine_namespace_id
246
+ namespace_id ||= "default"
233
247
  ["container", {
234
248
  cluster_name: Google::Cloud.env.container_engine_cluster_name,
235
- namespace_id: \
236
- Google::Cloud.env.container_engine_namespace_id || "default"
249
+ namespace_id: namespace_id
237
250
  }]
238
251
  elsif Google::Cloud.env.compute_engine?
239
252
  ["gce_instance", {
240
253
  instance_id: Google::Cloud.env.instance_name,
241
- zone: Google::Cloud.env.instance_zone
254
+ zone: Google::Cloud.env.instance_zone
242
255
  }]
243
256
  else
244
257
  ["global", {}]
@@ -206,8 +206,10 @@ module Google
206
206
  #
207
207
  def entry log_name: nil, resource: nil, timestamp: nil, severity: nil,
208
208
  insert_id: nil, labels: nil, payload: nil
209
+ ensure_service!
210
+
209
211
  e = Entry.new
210
- e.log_name = log_name if log_name
212
+ e.log_name = service.log_path(log_name) if log_name
211
213
  e.resource = resource if resource
212
214
  e.timestamp = timestamp if timestamp
213
215
  e.severity = severity if severity
@@ -240,8 +242,8 @@ module Google
240
242
  # `entries`, except when a log entry specifies its own `key:value`
241
243
  # item with the same key. See also {Entry#labels=}.
242
244
  # @param [Boolean] partial_success Whether valid entries should be
243
- # written even if some other entries fail due to INVALID_ARGUMENT or
244
- # PERMISSION_DENIED errors when communicating to the Stackdriver
245
+ # written even if some other entries fail due to `INVALID_ARGUMENT` or
246
+ # `PERMISSION_DENIED` errors when communicating to the Stackdriver
245
247
  # Logging API.
246
248
  #
247
249
  # @return [Boolean] Returns `true` if the entries were written.
@@ -304,22 +306,36 @@ module Google
304
306
  end
305
307
 
306
308
  ##
307
- # Creates an object that batches and transmits log entries
308
- # asynchronously.
309
+ # Creates an object that buffers, batches, and transmits log entries
310
+ # efficiently. Writing log entries to this object is asynchronous and
311
+ # will not block.
309
312
  #
310
- # Use this object to transmit log entries efficiently. It keeps a queue
311
- # of log entries, and runs a background thread that transmits them to
312
- # the logging service in batches. Generally, adding to the queue will
313
- # not block.
313
+ # Batches that cannot be delivered immediately are queued. When the
314
+ # queue is full new batch requests will raise errors that can be
315
+ # consumed using the {AsyncWriter#on_error} callback. This provides back
316
+ # pressure in case the writer cannot keep up with requests.
314
317
  #
315
318
  # This object is thread-safe; it may accept write requests from
316
319
  # multiple threads simultaneously, and will serialize them when
317
320
  # executing in the background thread.
318
321
  #
319
- # @param [Integer] max_queue_size The maximum number of log entries
320
- # that may be queued before write requests will begin to block.
321
- # This provides back pressure in case the transmitting thread cannot
322
- # keep up with requests.
322
+ # @param [Integer] max_batch_count The maximum number of log entries
323
+ # that may be buffered and sent in a batch.
324
+ # @param [Integer] max_batch_bytes The maximum byte size of log entries
325
+ # that may be buffered and sent in a batch.
326
+ # @param [Integer] max_queue_size The maximum number of pending
327
+ # write_entries requests that may be queued.
328
+ # @param [Numeric] interval The number of seconds to buffer log entries
329
+ # before a batch is written. Default is 5.
330
+ # @param [Integer] threads The number of threads used to make
331
+ # batched write_entries requests. Default is 10.
332
+ # @param [Boolean] partial_success Whether valid entries should be
333
+ # written even if some other entries fail due to `INVALID_ARGUMENT` or
334
+ # `PERMISSION_DENIED` errors when communicating to the Stackdriver
335
+ # Logging API.
336
+ #
337
+ # @return [Google::Cloud::Logging::AsyncWriter] an AsyncWriter object
338
+ # that buffers, batches, and transmits log entries efficiently.
323
339
  #
324
340
  # @example
325
341
  # require "google/cloud/logging"
@@ -341,8 +357,15 @@ module Google
341
357
  # resource: resource,
342
358
  # labels: labels
343
359
  #
344
- def async_writer max_queue_size: AsyncWriter::DEFAULT_MAX_QUEUE_SIZE
345
- AsyncWriter.new self, max_queue_size
360
+ def async_writer max_batch_count: 10000, max_batch_bytes: 10000000,
361
+ max_queue_size: 100, interval: 5, threads: 10,
362
+ partial_success: false
363
+
364
+ AsyncWriter.new self, max_count: max_batch_count,
365
+ max_bytes: max_batch_bytes,
366
+ max_queue: max_queue_size,
367
+ interval: interval, threads: threads,
368
+ partial_success: partial_success
346
369
  end
347
370
 
348
371
  ##