fluent-plugin-google-cloud 0.6.4.pre.3 → 0.6.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dacc67fcefff073a113c1c73ef7644ae802916e9
4
- data.tar.gz: b2caa333b793ae00c8541e8b0c475fd0e71824a3
3
+ metadata.gz: ab431f0283f1c6bc88669159be387f21bb1e908f
4
+ data.tar.gz: 77d320fd8b6e4c5370542142fd296581fcd9186e
5
5
  SHA512:
6
- metadata.gz: ee282c69851a0bf8dee3e2110f7a6635016c573af24f1b0757cf8d0a541b2396560976e8a064950087fc601a90441983dabb6312465d7873f90509236047717c
7
- data.tar.gz: 5142fb67ba45c13759dfdd79d15f7e1cbfdf1a354da1423c2442bffc21003a1177410cbd8cb6c85495b9a501a3a74ea70a8dd1c87397199d38c1b7d74810640a
6
+ metadata.gz: 15fc471919366c52a612cb2b1941d0e9ffdf5e1d5c94ad6f3395c1860ca2f03edd888959a0ed267375bf80133458251bc1555c7416ef81f8be4f4aa83fd5c405
7
+ data.tar.gz: 4aed5e4e2686eb4776d32f3b44f61fc86849f4f02a64bd12399d0e0ac4a6cc1a8752489513fa5e482fdb7cac432d517ed5b7b26e71365e344e2dcf2ca8e96832
@@ -0,0 +1,159 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ fluent-plugin-google-cloud (0.6.4)
5
+ fluentd (~> 0.10)
6
+ google-api-client (~> 0.9.0)
7
+ google-cloud-logging (~> 0.23.2)
8
+ googleapis-common-protos (~> 1.3)
9
+ googleauth (~> 0.4)
10
+ grpc (~> 1.0, < 1.3)
11
+ json (~> 1.8)
12
+
13
+ GEM
14
+ remote: https://rubygems.org/
15
+ specs:
16
+ addressable (2.5.1)
17
+ public_suffix (~> 2.0, >= 2.0.2)
18
+ ast (2.3.0)
19
+ astrolabe (1.3.1)
20
+ parser (~> 2.2)
21
+ cool.io (1.5.0)
22
+ crack (0.4.3)
23
+ safe_yaml (~> 1.0.0)
24
+ faraday (0.12.1)
25
+ multipart-post (>= 1.2, < 3)
26
+ fluentd (0.14.18)
27
+ cool.io (>= 1.4.5, < 2.0.0)
28
+ http_parser.rb (>= 0.5.1, < 0.7.0)
29
+ msgpack (>= 0.7.0, < 2.0.0)
30
+ serverengine (>= 2.0.4, < 3.0.0)
31
+ sigdump (~> 0.2.2)
32
+ strptime (~> 0.1.7)
33
+ tzinfo (~> 1.0)
34
+ tzinfo-data (~> 1.0)
35
+ yajl-ruby (~> 1.0)
36
+ google-api-client (0.9.28)
37
+ addressable (~> 2.3)
38
+ googleauth (~> 0.5)
39
+ httpclient (~> 2.7)
40
+ hurley (~> 0.1)
41
+ memoist (~> 0.11)
42
+ mime-types (>= 1.6)
43
+ representable (~> 2.3.0)
44
+ retriable (~> 2.0)
45
+ google-cloud-core (0.21.1)
46
+ googleauth (~> 0.5.1)
47
+ google-cloud-logging (0.23.2)
48
+ google-cloud-core (~> 0.21.1)
49
+ google-gax (~> 0.6.0)
50
+ google-protobuf (~> 3.0)
51
+ googleapis-common-protos (~> 1.3)
52
+ grpc (~> 1.0)
53
+ orderedhash (= 0.0.6)
54
+ stackdriver-core (~> 0.21.0)
55
+ google-gax (0.6.0)
56
+ googleapis-common-protos (~> 1.3.1)
57
+ googleauth (~> 0.5.1)
58
+ grpc (~> 1.0)
59
+ rly (~> 0.2.3)
60
+ google-protobuf (3.3.0)
61
+ googleapis-common-protos (1.3.5)
62
+ google-protobuf (~> 3.2)
63
+ grpc (~> 1.0)
64
+ googleauth (0.5.1)
65
+ faraday (~> 0.9)
66
+ jwt (~> 1.4)
67
+ logging (~> 2.0)
68
+ memoist (~> 0.12)
69
+ multi_json (~> 1.11)
70
+ os (~> 0.9)
71
+ signet (~> 0.7)
72
+ grpc (1.2.5)
73
+ google-protobuf (~> 3.1)
74
+ googleauth (~> 0.5.1)
75
+ hashdiff (0.3.4)
76
+ http_parser.rb (0.6.0)
77
+ httpclient (2.8.3)
78
+ hurley (0.2)
79
+ json (1.8.6)
80
+ jwt (1.5.6)
81
+ little-plugger (1.1.4)
82
+ logging (2.2.2)
83
+ little-plugger (~> 1.1)
84
+ multi_json (~> 1.10)
85
+ memoist (0.16.0)
86
+ metaclass (0.0.4)
87
+ mime-types (3.1)
88
+ mime-types-data (~> 3.2015)
89
+ mime-types-data (3.2016.0521)
90
+ mocha (1.2.1)
91
+ metaclass (~> 0.0.1)
92
+ msgpack (1.1.0)
93
+ multi_json (1.12.1)
94
+ multipart-post (2.0.0)
95
+ orderedhash (0.0.6)
96
+ os (0.9.6)
97
+ parser (2.4.0.0)
98
+ ast (~> 2.2)
99
+ power_assert (1.0.2)
100
+ powerpack (0.1.1)
101
+ prometheus-client (0.7.1)
102
+ quantile (~> 0.2.0)
103
+ public_suffix (2.0.5)
104
+ quantile (0.2.0)
105
+ rainbow (2.2.2)
106
+ rake
107
+ rake (10.5.0)
108
+ representable (2.3.0)
109
+ uber (~> 0.0.7)
110
+ retriable (2.1.0)
111
+ rly (0.2.3)
112
+ rubocop (0.35.1)
113
+ astrolabe (~> 1.3)
114
+ parser (>= 2.2.3.0, < 3.0)
115
+ powerpack (~> 0.1)
116
+ rainbow (>= 1.99.1, < 3.0)
117
+ ruby-progressbar (~> 1.7)
118
+ tins (<= 1.6.0)
119
+ ruby-progressbar (1.8.1)
120
+ safe_yaml (1.0.4)
121
+ serverengine (2.0.5)
122
+ sigdump (~> 0.2.2)
123
+ sigdump (0.2.4)
124
+ signet (0.7.3)
125
+ addressable (~> 2.3)
126
+ faraday (~> 0.9)
127
+ jwt (~> 1.5)
128
+ multi_json (~> 1.10)
129
+ stackdriver-core (0.21.0)
130
+ strptime (0.1.9)
131
+ test-unit (3.2.5)
132
+ power_assert
133
+ thread_safe (0.3.6)
134
+ tins (1.6.0)
135
+ tzinfo (1.2.3)
136
+ thread_safe (~> 0.1)
137
+ tzinfo-data (1.2017.2)
138
+ tzinfo (>= 1.0.0)
139
+ uber (0.0.15)
140
+ webmock (1.24.6)
141
+ addressable (>= 2.3.6)
142
+ crack (>= 0.3.2)
143
+ hashdiff
144
+ yajl-ruby (1.3.0)
145
+
146
+ PLATFORMS
147
+ ruby
148
+
149
+ DEPENDENCIES
150
+ fluent-plugin-google-cloud!
151
+ mocha (~> 1.1)
152
+ prometheus-client (~> 0.7.1)
153
+ rake (~> 10.3)
154
+ rubocop (~> 0.35.0)
155
+ test-unit (~> 3.0)
156
+ webmock (~> 1.17)
157
+
158
+ BUNDLED WITH
159
+ 1.15.0
@@ -10,7 +10,7 @@ eos
10
10
  gem.homepage = \
11
11
  'https://github.com/GoogleCloudPlatform/fluent-plugin-google-cloud'
12
12
  gem.license = 'Apache-2.0'
13
- gem.version = '0.6.4.pre.3'
13
+ gem.version = '0.6.4'
14
14
  gem.authors = ['Todd Derr', 'Alex Robinson']
15
15
  gem.email = ['salty@google.com']
16
16
  gem.required_ruby_version = Gem::Requirement.new('>= 2.0')
@@ -19,7 +19,6 @@ eos
19
19
  gem.test_files = gem.files.grep(/^(test)/)
20
20
  gem.require_paths = ['lib']
21
21
 
22
- gem.add_runtime_dependency 'excon', '~> 0.57.1'
23
22
  gem.add_runtime_dependency 'fluentd', '~> 0.10'
24
23
  gem.add_runtime_dependency 'googleapis-common-protos', '~> 1.3'
25
24
  gem.add_runtime_dependency 'google-api-client', '~> 0.9.0'
@@ -33,4 +32,5 @@ eos
33
32
  gem.add_development_dependency 'rubocop', '~> 0.35.0'
34
33
  gem.add_development_dependency 'webmock', '~> 1.17'
35
34
  gem.add_development_dependency 'test-unit', '~> 3.0'
35
+ gem.add_development_dependency 'prometheus-client', '~> 0.7.1'
36
36
  end
@@ -0,0 +1,55 @@
1
+ # Copyright 2017 Google Inc. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Monitoring
16
+ # Base class for the monitoring registry.
17
+ class BaseMonitoringRegistry
18
+ def counter(_name, _desc)
19
+ _undefined
20
+ end
21
+ end
22
+
23
+ # Prometheus implementation of the monitoring registry, that uses the default
24
+ # registry in the official Prometheus client library.
25
+ class PrometheusMonitoringRegistry < BaseMonitoringRegistry
26
+ def self.name
27
+ 'prometheus'
28
+ end
29
+
30
+ def initialize
31
+ require 'prometheus/client'
32
+ @registry = Prometheus::Client.registry
33
+ end
34
+
35
+ # Exception-driven behavior to avoid synchronization errors.
36
+ def counter(name, desc)
37
+ return @registry.counter(name, desc)
38
+ rescue Prometheus::Client::Registry::AlreadyRegisteredError
39
+ return @registry.get(name)
40
+ end
41
+ end
42
+
43
+ # Factory that is used to create a monitoring registry based on
44
+ # the monitoring solution name.
45
+ class MonitoringRegistryFactory
46
+ @known_registry_types = {
47
+ PrometheusMonitoringRegistry.name =>
48
+ PrometheusMonitoringRegistry
49
+ }
50
+
51
+ def self.create(name)
52
+ (@known_registry_types[name] || BaseMonitoringRegistry).new
53
+ end
54
+ end
55
+ end
@@ -11,11 +11,9 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
- require 'excon'
15
14
  require 'grpc'
16
15
  require 'json'
17
16
  require 'open-uri'
18
- require 'rubygems'
19
17
  require 'socket'
20
18
  require 'time'
21
19
  require 'yaml'
@@ -26,6 +24,8 @@ require 'google/logging/v2/logging_services_pb'
26
24
  require 'google/logging/v2/log_entry_pb'
27
25
  require 'googleauth'
28
26
 
27
+ require_relative 'monitoring'
28
+
29
29
  module Google
30
30
  module Protobuf
31
31
  # Alias the has_key? method to have the same interface as a regular map.
@@ -38,10 +38,8 @@ end
38
38
  module Fluent
39
39
  # fluentd output plugin for the Stackdriver Logging API
40
40
  class GoogleCloudOutput < BufferedOutput
41
- # Constants.
41
+ # Constants for service names and resource types.
42
42
  module Constants
43
- # Service names and resource types.
44
-
45
43
  APPENGINE_CONSTANTS = {
46
44
  service: 'appengine.googleapis.com',
47
45
  resource_type: 'gae_app'
@@ -58,10 +56,6 @@ module Fluent
58
56
  service: 'container.googleapis.com',
59
57
  resource_type: 'container'
60
58
  }
61
- DOCKER_CONSTANTS = {
62
- service: 'dockercontainer.googleapis.com',
63
- resource_type: 'docker_container'
64
- }
65
59
  DATAFLOW_CONSTANTS = {
66
60
  service: 'dataflow.googleapis.com',
67
61
  resource_type: 'dataflow_step'
@@ -78,20 +72,6 @@ module Fluent
78
72
  service: 'ml.googleapis.com',
79
73
  resource_type: 'ml_job'
80
74
  }
81
-
82
- # Metadata Agent support.
83
-
84
- # Use empty string as request path when locally-unique key of monitored
85
- # resource can be implicitly inferred by Metadata Agent.
86
- IMPLICIT_MONITORED_RESOURCE_UNIQUE_KEY = ''
87
-
88
- # The label name of locally unique id in the json payload. When a record
89
- # has this field in the payload, we will use the value to retrieve
90
- # monitored resource from Stackdriver Metadata agent.
91
- LOCALLY_UNIQUE_ID_LABEL_NAME = 'logging.googleapis.com/locally_unique_id'
92
-
93
- # Docker container support.
94
- DEFAULT_DOCKER_API_SOCKET_PATH = '/var/run/docker.sock'
95
75
  end
96
76
 
97
77
  include self::Constants
@@ -99,7 +79,7 @@ module Fluent
99
79
  Fluent::Plugin.register_output('google_cloud', self)
100
80
 
101
81
  PLUGIN_NAME = 'Fluentd Google Cloud Logging plugin'
102
- PLUGIN_VERSION = '0.6.4.pre.3'
82
+ PLUGIN_VERSION = '0.6.3'
103
83
 
104
84
  # Name of the the Google cloud logging write scope.
105
85
  LOGGING_SCOPE = 'https://www.googleapis.com/auth/logging.write'
@@ -146,7 +126,7 @@ module Fluent
146
126
  config_param :require_valid_tags, :bool, :default => false
147
127
 
148
128
  # The regular expression to use on Kubernetes logs to extract some basic
149
- # information about the log source. The regexp must contain capture groups
129
+ # information about the log source. The regex must contain capture groups
150
130
  # for pod_name, namespace_name, and container_name.
151
131
  config_param :kubernetes_tag_regexp, :string, :default =>
152
132
  '\.(?<pod_name>[^_]+)_(?<namespace_name>[^_]+)_(?<container_name>.+)$'
@@ -208,17 +188,18 @@ module Fluent
208
188
  :default => nil,
209
189
  :secret => true
210
190
 
211
- # Whether to call metadata agent to retrieve monitored resource.
212
- config_param :enable_metadata_agent, :bool, :default => false
213
- config_param :metadata_agent_url, :string,
214
- :default => 'http://local-metadata-agent.stackdriver.com:8000'
191
+ # Whether to collect metrics about the plugin usage. The mechanism for
192
+ # collecting and exposing metrics is controlled by the monitoring_type
193
+ # parameter.
194
+ config_param :enable_monitoring, :bool, :default => false
215
195
 
216
- # Whether to call Docker Remote API locally when Metadata Agent is not
217
- # enabled or if the request fails.
218
- config_param :call_docker_api_locally, :bool, :default => true
219
- # Docker Remote API unix socket path.
220
- config_param :docker_remote_api_socket_path, :string,
221
- :default => DEFAULT_DOCKER_API_SOCKET_PATH
196
+ # What system to use when collecting metrics. Possible values are:
197
+ # - 'prometheus', in this case default registry in the Prometheus
198
+ # client library is used, without actually exposing the endpoint
199
+ # to serve metrics in the Prometheus format.
200
+ # - any other value will result in the absence of metrics.
201
+ config_param :monitoring_type, :string,
202
+ :default => Monitoring::PrometheusMonitoringRegistry.name
222
203
 
223
204
  # rubocop:enable Style/HashSyntax
224
205
 
@@ -229,6 +210,9 @@ module Fluent
229
210
  attr_reader :project_id
230
211
  attr_reader :zone
231
212
  attr_reader :vm_id
213
+ attr_reader :running_on_managed_vm
214
+ attr_reader :gae_backend_name
215
+ attr_reader :gae_backend_version
232
216
  attr_reader :resource
233
217
  attr_reader :common_labels
234
218
 
@@ -238,38 +222,28 @@ module Fluent
238
222
  @log = $log # rubocop:disable Style/GlobalVars
239
223
  end
240
224
 
241
- # Set up regex patterns used to parse tags and logs.
242
- def setup_regex_patterns
243
- @compiled_kubernetes_tag_regexp = nil
244
- if @kubernetes_tag_regexp
245
- @compiled_kubernetes_tag_regexp = Regexp.new(@kubernetes_tag_regexp)
246
- end
247
-
248
- @cloudfunctions_tag_regexp =
249
- /\.(?<encoded_function_name>.+)\.\d+-[^-]+_default_worker$/
250
- @cloudfunctions_log_regexp = /^
251
- (?:\[(?<severity>.)\])?
252
- \[(?<timestamp>.{24})\]
253
- (?:\[(?<execution_id>[^\]]+)\])?
254
- [ ](?<text>.*)$/x
255
-
256
- # Docker container tag format:
257
- # "container.<container_id>.<container_name>".
258
- @dockercontainer_tag_regexp =
259
- /^container\.(?<container_id>[a-zA-Z0-9]+)\.
260
- (?<container_name>[a-zA-Z0-9_.-]+)$/x
261
- # Docker container with application tag format:
262
- # "application-container.<container_name>.<additional_tag>".
263
- @dockercontainer_tag_with_application_regexp =
264
- /^application-container\.(?<container_name>[a-zA-Z0-9_.-]+)\.
265
- (?<additional_tag>.+)$/x
266
-
267
- @http_latency_regexp = /^\s*(?<seconds>\d+)(?<decimal>\.\d+)?\s*s\s*$/
268
- end
269
-
270
225
  def configure(conf)
271
226
  super
272
227
 
228
+ # If monitoring is enabled, register metrics in the default registry
229
+ # and store metric objects for future use.
230
+ if @enable_monitoring
231
+ registry = Monitoring::MonitoringRegistryFactory.create @monitoring_type
232
+ @successful_requests_count = registry.counter(
233
+ :stackdriver_successful_requests_count,
234
+ 'A number of successful requests to the Stackdriver Logging API')
235
+ @failed_requests_count = registry.counter(
236
+ :stackdriver_failed_requests_count,
237
+ 'A number of failed requests to the Stackdriver Logging API,'\
238
+ ' broken down by the error code')
239
+ @ingested_entries_count = registry.counter(
240
+ :stackdriver_ingested_entries_count,
241
+ 'A number of log entries ingested by Stackdriver Logging')
242
+ @dropped_entries_count = registry.counter(
243
+ :stackdriver_dropped_entries_count,
244
+ 'A number of log entries dropped by the Stackdriver output plugin')
245
+ end
246
+
273
247
  # Alert on old authentication configuration.
274
248
  unless @auth_method.nil? && @private_key_email.nil? &&
275
249
  @private_key_path.nil? && @private_key_passphrase.nil?
@@ -285,57 +259,160 @@ module Fluent
285
259
  extra.join(' ')
286
260
  end
287
261
 
288
- setup_regex_patterns
262
+ # TODO: Send instance tags as labels as well?
263
+ @common_labels = {}
264
+ @common_labels.merge!(@labels) if @labels
265
+
266
+ # TODO: Construct Google::Api::MonitoredResource when @use_grpc is
267
+ # true after the protobuf map corruption issue is fixed.
268
+ @resource = Google::Apis::LoggingV2beta1::MonitoredResource.new(
269
+ labels: {})
270
+
271
+ @compiled_kubernetes_tag_regexp = nil
272
+ if @kubernetes_tag_regexp
273
+ @compiled_kubernetes_tag_regexp = Regexp.new(@kubernetes_tag_regexp)
274
+ end
289
275
 
276
+ @cloudfunctions_tag_regexp =
277
+ /\.(?<encoded_function_name>.+)\.\d+-[^-]+_default_worker$/
278
+ @cloudfunctions_log_regexp = /^
279
+ (?:\[(?<severity>.)\])?
280
+ \[(?<timestamp>.{24})\]
281
+ (?:\[(?<execution_id>[^\]]+)\])?
282
+ [ ](?<text>.*)$/x
283
+
284
+ @http_latency_regexp = /^\s*(?<seconds>\d+)(?<decimal>\.\d+)?\s*s\s*$/
285
+
286
+ # set attributes from metadata (unless overriden by static config)
287
+ @vm_name = Socket.gethostname if @vm_name.nil?
290
288
  @platform = detect_platform
289
+ case @platform
290
+ when Platform::GCE
291
+ if @project_id.nil?
292
+ @project_id = fetch_gce_metadata('project/project-id')
293
+ end
294
+ if @zone.nil?
295
+ # this returns "projects/<number>/zones/<zone>"; we only want
296
+ # the part after the final slash.
297
+ fully_qualified_zone = fetch_gce_metadata('instance/zone')
298
+ @zone = fully_qualified_zone.rpartition('/')[2]
299
+ end
300
+ @vm_id = fetch_gce_metadata('instance/id') if @vm_id.nil?
301
+ when Platform::EC2
302
+ metadata = fetch_ec2_metadata
303
+ if @zone.nil? && metadata.key?('availabilityZone')
304
+ @zone = 'aws:' + metadata['availabilityZone']
305
+ end
306
+ if @vm_id.nil? && metadata.key?('instanceId')
307
+ @vm_id = metadata['instanceId']
308
+ end
309
+ if metadata.key?('accountId')
310
+ @resource.labels['aws_account'] = metadata['accountId']
311
+ end
312
+ when Platform::OTHER
313
+ # do nothing
314
+ else
315
+ fail Fluent::ConfigError, 'Unknown platform ' + @platform
316
+ end
291
317
 
292
- # Set agent-level monitored resource. This monitored resource is initiated
293
- # as the logging agent starts up. It will be inherited by all log entries
294
- # processed by this agent. First try to retrieve it via Metadata Agent.
295
- if @enable_metadata_agent
296
- # The locally-unique key for this should be the instance id. Since this
297
- # can be implicitly inferred by Metadata Agent, we do not need to
298
- # explicitly send the key.
299
- @resource = call_metadata_agent_for_monitored_resource(
300
- IMPLICIT_MONITORED_RESOURCE_UNIQUE_KEY)
318
+ # If we still don't have a project ID, try to obtain it from the
319
+ # credentials.
320
+ if @project_id.nil?
321
+ @project_id = CredentialsInfo.project_id
322
+ @log.info 'Set Project ID from credentials: ', @project_id unless
323
+ @project_id.nil?
301
324
  end
302
325
 
303
- # Set required variables: @project_id, @vm_id, @vm_name and @zone.
304
- # If any info above is included in the response from Metadata Agent, make
305
- # use of that. Otherwise make some additional requests to metadata server.
306
- #
307
- # Note: Once we support metadata injection on the Logging API side, we
308
- # might no longer need to require all these metadata in logging agent. But
309
- # for now, they are still required.
310
- set_required_metadata_variables
311
-
312
- # Fail over to retrieve monitored resource via the legacy path if we fail
313
- # to get it from Metadata Agent.
314
- @resource ||= determine_agent_level_monitored_resource_via_legacy
315
-
316
- # Set variables specific to CLoud Functions. This has to be called after
317
- # we have determined the resource type. The purpose is to avoid repeated
318
- # calls to metadata server.
319
- @running_cloudfunctions = false
320
- # We only support Cloud Functions logs for GKE right now.
321
- if @resource.type == CONTAINER_CONSTANTS[:resource_type] &&
322
- fetch_gce_metadata('instance/attributes/').split.include?('gcf_region')
323
- # We are not setting resource type as Cloud Functions here because
324
- # whether a log entry is truly coming from a Cloud Functions function
325
- # depends on the log tag. Only when @running_cloudfunctions is true will
326
- # we try to match log tags against Cloud Functions tag regexp when
327
- # processing log entries.
328
- @running_cloudfunctions = true
329
- # Fetch this info and store it to avoid recurring metadata server calls.
330
- @gcf_region = fetch_gce_metadata('instance/attributes/gcf_region')
326
+ # all metadata parameters must now be set
327
+ unless @project_id && @zone && @vm_id
328
+ missing = []
329
+ missing << 'project_id' unless @project_id
330
+ missing << 'zone' unless @zone
331
+ missing << 'vm_id' unless @vm_id
332
+ fail Fluent::ConfigError, 'Unable to obtain metadata parameters: ' +
333
+ missing.join(' ')
331
334
  end
332
335
 
333
- # Determine the common labels that should be added to all log entries
334
- # processed by this logging agent.
335
- @common_labels = determine_agent_level_common_labels
336
+ # Default this to false; it is only overwritten if we detect Managed VM.
337
+ @running_on_managed_vm = false
336
338
 
337
- # For each resource type, there is a list of labels that we want to report
338
- # as monitored resource instead of metadata labels. Move them if present.
339
+ # Default this to false; it is only overwritten if we detect Cloud
340
+ # Functions.
341
+ @running_cloudfunctions = false
342
+
343
+ # Set up the MonitoredResource, labels, etc. based on the config.
344
+ case @platform
345
+ when Platform::GCE
346
+ @resource.type = COMPUTE_CONSTANTS[:resource_type]
347
+ # TODO: introduce a new MonitoredResource-centric configuration and
348
+ # deprecate subservice-name; for now, translate known uses.
349
+ if @subservice_name
350
+ # TODO: what should we do if we encounter an unknown value?
351
+ if @subservice_name == DATAFLOW_CONSTANTS[:service]
352
+ @resource.type = DATAFLOW_CONSTANTS[:resource_type]
353
+ elsif @subservice_name == ML_CONSTANTS[:service]
354
+ @resource.type = ML_CONSTANTS[:resource_type]
355
+ end
356
+ elsif @detect_subservice
357
+ # Check for specialized GCE environments.
358
+ # TODO: Add config options for these to allow for running outside GCE?
359
+ attributes = fetch_gce_metadata('instance/attributes/').split
360
+ # Do nothing, just don't populate other service's labels.
361
+ if attributes.include?('gae_backend_name') &&
362
+ attributes.include?('gae_backend_version')
363
+ # Managed VM
364
+ @running_on_managed_vm = true
365
+ @gae_backend_name =
366
+ fetch_gce_metadata('instance/attributes/gae_backend_name')
367
+ @gae_backend_version =
368
+ fetch_gce_metadata('instance/attributes/gae_backend_version')
369
+ @resource.type = APPENGINE_CONSTANTS[:resource_type]
370
+ @resource.labels['module_id'] = @gae_backend_name
371
+ @resource.labels['version_id'] = @gae_backend_version
372
+ elsif attributes.include?('kube-env')
373
+ # Kubernetes/Container Engine
374
+ @resource.type = CONTAINER_CONSTANTS[:resource_type]
375
+ @raw_kube_env = fetch_gce_metadata('instance/attributes/kube-env')
376
+ @kube_env = YAML.load(@raw_kube_env)
377
+ @resource.labels['cluster_name'] =
378
+ cluster_name_from_kube_env(@kube_env)
379
+ detect_cloudfunctions(attributes)
380
+ elsif attributes.include?('dataproc-cluster-uuid') &&
381
+ attributes.include?('dataproc-cluster-name')
382
+ # Dataproc
383
+ @resource.type = DATAPROC_CONSTANTS[:resource_type]
384
+ @resource.labels['cluster_uuid'] =
385
+ fetch_gce_metadata('instance/attributes/dataproc-cluster-uuid')
386
+ @resource.labels['cluster_name'] =
387
+ fetch_gce_metadata('instance/attributes/dataproc-cluster-name')
388
+ @resource.labels['region'] =
389
+ fetch_gce_metadata('instance/attributes/dataproc-region')
390
+ end
391
+ end
392
+ # Some services have the GCE instance_id and zone as MonitoredResource
393
+ # labels; for other services we send them as entry labels.
394
+ if @resource.type == COMPUTE_CONSTANTS[:resource_type] ||
395
+ @resource.type == CONTAINER_CONSTANTS[:resource_type]
396
+ @resource.labels['instance_id'] = @vm_id
397
+ @resource.labels['zone'] = @zone
398
+ else
399
+ common_labels["#{COMPUTE_CONSTANTS[:service]}/resource_id"] = @vm_id
400
+ common_labels["#{COMPUTE_CONSTANTS[:service]}/zone"] = @zone
401
+ end
402
+ common_labels["#{COMPUTE_CONSTANTS[:service]}/resource_name"] = @vm_name
403
+ when Platform::EC2
404
+ @resource.type = EC2_CONSTANTS[:resource_type]
405
+ @resource.labels['instance_id'] = @vm_id
406
+ @resource.labels['region'] = @zone
407
+ # the aws_account label is populated above.
408
+ common_labels["#{EC2_CONSTANTS[:service]}/resource_name"] = @vm_name
409
+ when Platform::OTHER
410
+ # Use GCE as the default environment.
411
+ @resource.type = COMPUTE_CONSTANTS[:resource_type]
412
+ @resource.labels['instance_id'] = @vm_id
413
+ @resource.labels['zone'] = @zone
414
+ common_labels["#{COMPUTE_CONSTANTS[:service]}/resource_name"] = @vm_name
415
+ end
339
416
  @resource.labels.merge!(
340
417
  extract_resource_labels(@resource.type, common_labels))
341
418
 
@@ -347,7 +424,7 @@ module Fluent
347
424
 
348
425
  # Log an informational message containing the Logs viewer URL
349
426
  @log.info 'Logs viewer address: https://console.cloud.google.com/logs/',
350
- "viewer?project=#{@project_id}&resource=#{@resource.type}/",
427
+ "viewer?project=#{@project_id}&resource=#{@resource_type}/",
351
428
  "instance_id/#{@vm_id}"
352
429
  end
353
430
 
@@ -362,15 +439,135 @@ module Fluent
362
439
  super
363
440
  end
364
441
 
442
+ def format(tag, time, record)
443
+ [tag, time, record].to_msgpack
444
+ end
445
+
446
+ # Given a tag, returns the corresponding valid tag if possible, or nil if
447
+ # the tag should be rejected. If 'require_valid_tags' is false, non-string
448
+ # tags are converted to strings, and invalid characters are sanitized;
449
+ # otherwise such tags are rejected.
450
+ def sanitize_tag(tag)
451
+ if @require_valid_tags &&
452
+ (!tag.is_a?(String) || tag == '' || convert_to_utf8(tag) != tag)
453
+ return nil
454
+ end
455
+ tag = convert_to_utf8(tag.to_s)
456
+ tag = '_' if tag == ''
457
+ tag
458
+ end
459
+
460
+ # Compute the monitored resource and common labels shared by a collection of
461
+ # entries.
462
+ def compute_group_resource_and_labels(tag)
463
+ # Note that we assume that labels added to group_common_labels below are
464
+ # not 'service' labels (i.e. we do not call extract_resource_labels
465
+ # again).
466
+ group_resource = @resource.dup
467
+ group_common_labels = @common_labels.dup
468
+
469
+ if @running_cloudfunctions
470
+ # If the current group of entries is coming from a Cloud Functions
471
+ # function, the function name can be extracted from the tag.
472
+ match_data = @cloudfunctions_tag_regexp.match(tag)
473
+ if match_data
474
+ # Resource type is set to Cloud Functions only for logs actually
475
+ # coming from a function, otherwise we leave it as Container.
476
+ group_resource.type = CLOUDFUNCTIONS_CONSTANTS[:resource_type]
477
+ group_resource.labels['region'] = @gcf_region
478
+ group_resource.labels['function_name'] =
479
+ decode_cloudfunctions_function_name(
480
+ match_data['encoded_function_name'])
481
+ # Move GKE container labels from the MonitoredResource to the
482
+ # LogEntry.
483
+ instance_id = group_resource.labels.delete('instance_id')
484
+ group_common_labels["#{CONTAINER_CONSTANTS[:service]}/cluster_name"] =
485
+ group_resource.labels.delete('cluster_name')
486
+ group_common_labels["#{CONTAINER_CONSTANTS[:service]}/instance_id"] =
487
+ instance_id
488
+ group_common_labels["#{COMPUTE_CONSTANTS[:service]}/resource_id"] =
489
+ instance_id
490
+ group_common_labels["#{COMPUTE_CONSTANTS[:service]}/zone"] =
491
+ group_resource.labels.delete('zone')
492
+ end
493
+ end
494
+ if group_resource.type == CONTAINER_CONSTANTS[:resource_type] &&
495
+ @compiled_kubernetes_tag_regexp
496
+ # Container logs in Kubernetes are tagged based on where they came
497
+ # from, so we can extract useful metadata from the tag.
498
+ # Do this here to avoid having to repeat it for each record.
499
+ match_data = @compiled_kubernetes_tag_regexp.match(tag)
500
+ if match_data
501
+ group_resource.labels['container_name'] = match_data['container_name']
502
+ group_resource.labels['namespace_id'] = match_data['namespace_name']
503
+ group_resource.labels['pod_id'] = match_data['pod_name']
504
+ %w(namespace_name pod_name).each do |field|
505
+ group_common_labels["#{CONTAINER_CONSTANTS[:service]}/#{field}"] =
506
+ match_data[field]
507
+ end
508
+ end
509
+ end
510
+
511
+ # Freeze the per-request state. Any further changes must be made on a
512
+ # per-entry basis.
513
+ group_resource.freeze
514
+ group_resource.labels.freeze
515
+ group_common_labels.freeze
516
+
517
+ [group_resource, group_common_labels]
518
+ end
519
+
520
+ # Extract entry resource and common labels that should be applied to
521
+ # individual entries from the group resource.
522
+ def extract_entry_labels(group_resource, record)
523
+ resource_labels = {}
524
+ common_labels = {}
525
+
526
+ if group_resource.type == CLOUDFUNCTIONS_CONSTANTS[:resource_type] &&
527
+ record.key?('log')
528
+ @cloudfunctions_log_match =
529
+ @cloudfunctions_log_regexp.match(record['log'])
530
+ end
531
+
532
+ if group_resource.type == CONTAINER_CONSTANTS[:resource_type]
533
+ # Move the stdout/stderr annotation from the record into a label
534
+ common_labels.merge!(
535
+ fields_to_labels(
536
+ record, 'stream' => "#{CONTAINER_CONSTANTS[:service]}/stream"))
537
+
538
+ # If the record has been annotated by the kubernetes_metadata_filter
539
+ # plugin, then use that metadata. Otherwise, rely on commonLabels
540
+ # populated at the grouped_entries level from the group's tag.
541
+ if record.key?('kubernetes')
542
+ extracted_resource_labels, extracted_common_labels = \
543
+ extract_container_metadata(record)
544
+ resource_labels.merge!(extracted_resource_labels)
545
+ common_labels.merge!(extracted_common_labels)
546
+ end
547
+ end
548
+
549
+ # If a field is present in the label_map, send its value as a label
550
+ # (mapping the field name to label name as specified in the config)
551
+ # and do not send that field as part of the payload.
552
+ common_labels.merge!(fields_to_labels(record, @label_map))
553
+
554
+ if group_resource.type == CLOUDFUNCTIONS_CONSTANTS[:resource_type] &&
555
+ @cloudfunctions_log_match &&
556
+ @cloudfunctions_log_match['execution_id']
557
+ common_labels['execution_id'] =
558
+ @cloudfunctions_log_match['execution_id']
559
+ end
560
+ resource_labels.merge!(
561
+ extract_resource_labels(group_resource.type, common_labels))
562
+
563
+ [resource_labels, common_labels]
564
+ end
565
+
365
566
  def write(chunk)
366
567
  # Group the entries since we have to make one call per tag.
367
568
  grouped_entries = {}
368
569
  chunk.msgpack_each do |tag, *arr|
369
570
  sanitized_tag = sanitize_tag(tag)
370
- @log.error("tag is: ")
371
- @log.error(tag.inspect)
372
- @log.error("arr is: ")
373
- @log.error(arr.inspect)
374
571
  if sanitized_tag.nil?
375
572
  @log.warn "Dropping log entries with invalid tag: '#{tag}'. " \
376
573
  'A tag should be a string with utf8 characters.'
@@ -382,22 +579,20 @@ module Fluent
382
579
 
383
580
  grouped_entries.each do |tag, arr|
384
581
  entries = []
385
- group_resource, group_common_labels =
386
- determine_group_level_monitored_resource_and_labels(tag)
582
+ group_resource, group_common_labels = compute_group_resource_and_labels(
583
+ tag)
387
584
 
388
585
  arr.each do |time, record|
389
586
  next unless record.is_a?(Hash)
390
587
 
391
- resource_type, extracted_resource_labels, extracted_common_labels = \
392
- determine_entry_level_labels(group_resource, record)
588
+ extracted_resource_labels, extracted_common_labels = \
589
+ extract_entry_labels(group_resource, record)
393
590
  entry_resource = group_resource.dup
394
- entry_resource.type = resource_type
395
591
  entry_resource.labels.merge!(extracted_resource_labels)
396
592
  entry_common_labels = \
397
593
  group_common_labels.merge(extracted_common_labels)
398
594
 
399
- if [CONTAINER_CONSTANTS[:resource_type],
400
- DOCKER_CONSTANTS[:resource_type]].include?(entry_resource.type)
595
+ if entry_resource.type == CONTAINER_CONSTANTS[:resource_type]
401
596
  # Save the timestamp if available, then clear it out to allow for
402
597
  # determining whether we should parse the log or message field.
403
598
  timestamp = record.key?('time') ? record['time'] : nil
@@ -491,6 +686,8 @@ module Fluent
491
686
  )
492
687
 
493
688
  client.write_log_entries(write_request)
689
+ increment_successful_requests_count
690
+ increment_ingested_entries_count(entries.length)
494
691
 
495
692
  # Let the user explicitly know when the first call succeeded,
496
693
  # to aid with verification and troubleshooting.
@@ -500,10 +697,12 @@ module Fluent
500
697
  end
501
698
 
502
699
  rescue GRPC::Cancelled => error
700
+ increment_failed_requests_count(GRPC::Core::StatusCodes::CANCELLED)
503
701
  # RPC cancelled, so retry via re-raising the error.
504
702
  raise error
505
703
 
506
704
  rescue GRPC::BadStatus => error
705
+ increment_failed_requests_count(error.code)
507
706
  case error.code
508
707
  when GRPC::Core::StatusCodes::CANCELLED,
509
708
  GRPC::Core::StatusCodes::UNAVAILABLE,
@@ -518,6 +717,7 @@ module Fluent
518
717
  # Most client errors indicate a problem with the request itself
519
718
  # and should not be retried.
520
719
  dropped = entries.length
720
+ increment_dropped_entries_count(dropped)
521
721
  @log.warn "Dropping #{dropped} log message(s)",
522
722
  error: error.to_s, error_code: error.code.to_s
523
723
  when GRPC::Core::StatusCodes::UNAUTHENTICATED
@@ -525,12 +725,14 @@ module Fluent
525
725
  # These are usually solved via a `gcloud auth` call, or by
526
726
  # modifying the permissions on the Google Cloud project.
527
727
  dropped = entries.length
728
+ increment_dropped_entries_count(dropped)
528
729
  @log.warn "Dropping #{dropped} log message(s)",
529
730
  error: error.to_s, error_code: error.code.to_s
530
731
  else
531
732
  # Assume this is a problem with the request itself
532
733
  # and don't retry.
533
734
  dropped = entries.length
735
+ increment_dropped_entries_count(dropped)
534
736
  @log.error "Unknown response code #{error.code} from the "\
535
737
  "server, dropping #{dropped} log message(s)",
536
738
  error: error.to_s, error_code: error.code.to_s
@@ -546,7 +748,14 @@ module Fluent
546
748
  entries: entries)
547
749
 
548
750
  # TODO: RequestOptions
549
- client.write_entry_log_entries(write_request)
751
+ begin
752
+ client.write_entry_log_entries(write_request)
753
+ rescue Google::Apis::Error => error
754
+ increment_failed_requests_count(error.status_code)
755
+ raise error
756
+ end
757
+ increment_successful_requests_count
758
+ increment_ingested_entries_count(entries.length)
550
759
 
551
760
  # Let the user explicitly know when the first call succeeded,
552
761
  # to aid with verification and troubleshooting.
@@ -564,6 +773,7 @@ module Fluent
564
773
  # These are usually solved via a `gcloud auth` call, or by modifying
565
774
  # the permissions on the Google Cloud project.
566
775
  dropped = entries.length
776
+ increment_dropped_entries_count(dropped)
567
777
  @log.warn "Dropping #{dropped} log message(s)",
568
778
  error_class: error.class.to_s, error: error.to_s
569
779
 
@@ -571,6 +781,7 @@ module Fluent
571
781
  # Most ClientErrors indicate a problem with the request itself and
572
782
  # should not be retried.
573
783
  dropped = entries.length
784
+ increment_dropped_entries_count(dropped)
574
785
  @log.warn "Dropping #{dropped} log message(s)",
575
786
  error_class: error.class.to_s, error: error.to_s
576
787
  end
@@ -630,7 +841,7 @@ module Fluent
630
841
  end
631
842
  end
632
843
  rescue StandardError => e
633
- @log.error 'Failed to access metadata service: ', error: e
844
+ @log.debug 'Failed to access metadata service: ', error: e
634
845
  end
635
846
 
636
847
  @log.info 'Unable to determine platform'
@@ -645,600 +856,15 @@ module Fluent
645
856
  metadata_path, 'Metadata-Flavor' => 'Google', &:read)
646
857
  end
647
858
 
648
- # EC2 Metadata server returns everything in one call. Store it after the
649
- # first fetch to avoid making multiple calls.
650
- def ec2_metadata
651
- fail "Called ec2_metadata with platform=#{@platform}" unless
859
+ def fetch_ec2_metadata
860
+ fail "Called fetch_ec2_metadata with platform=#{@platform}" unless
652
861
  @platform == Platform::EC2
653
- unless @ec2_metadata
654
- # See http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html
655
- open('http://' + METADATA_SERVICE_ADDR +
656
- '/latest/dynamic/instance-identity/document') do |f|
657
- contents = f.read
658
- @ec2_metadata = JSON.parse(contents)
659
- end
660
- end
661
-
662
- @ec2_metadata
663
- end
664
-
665
- # Set regexp patterns to parse tags and logs.
666
- def set_regexp_patterns
667
- @compiled_kubernetes_tag_regexp = nil
668
- if @kubernetes_tag_regexp
669
- @compiled_kubernetes_tag_regexp = Regexp.new(@kubernetes_tag_regexp)
862
+ # See http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html
863
+ open('http://' + METADATA_SERVICE_ADDR +
864
+ '/latest/dynamic/instance-identity/document') do |f|
865
+ contents = f.read
866
+ return JSON.parse(contents)
670
867
  end
671
-
672
- @cloudfunctions_tag_regexp =
673
- /\.(?<encoded_function_name>.+)\.\d+-[^-]+_default_worker$/
674
- @cloudfunctions_log_regexp = /^
675
- (?:\[(?<severity>.)\])?
676
- \[(?<timestamp>.{24})\]
677
- (?:\[(?<execution_id>[^\]]+)\])?
678
- [ ](?<text>.*)$/x
679
-
680
- @http_latency_regexp = /^\s*(?<seconds>\d+)(?<decimal>\.\d+)?\s*s\s*$/
681
- end
682
-
683
- # Set required variables like @project_id, @vm_id, @vm_name and @zone.
684
- def set_required_metadata_variables
685
- set_project_id
686
- set_vm_id
687
- set_vm_name
688
- set_location
689
-
690
- # All metadata parameters must now be set.
691
- return if @project_id && @zone && @vm_id
692
- missing = []
693
- missing << 'project_id' unless @project_id
694
- missing << 'zone' unless @zone
695
- missing << 'vm_id' unless @vm_id
696
- fail Fluent::ConfigError, 'Unable to obtain metadata parameters: ' +
697
- missing.join(' ')
698
- end
699
-
700
- # 1. Return the value if it is explicitly set in the config already.
701
- # 2. If not, try to retrieve it by calling metadata server directly.
702
- # 3. If still not set, try to obtain it from the credentials.
703
- def set_project_id
704
- @project_id ||= fetch_gce_metadata('project/project-id') if
705
- @platform == Platform::GCE
706
- @project_id ||= CredentialsInfo.project_id
707
- rescue StandardError => e
708
- @log.error 'Failed to obtain project id: ', error: e
709
- end
710
-
711
- # 1. Return the value if it is explicitly set in the config already.
712
- # 2. If not, check if the response from Metadata Agent includes this info.
713
- # 3. If not, try to retrieve it by calling metadata servers directly.
714
- def set_vm_id
715
- @vm_id ||= @resource.labels['instance_id'] if
716
- !@resource.nil? && @resource.labels.key?('instance_id')
717
- @vm_id ||= fetch_gce_metadata('instance/id') if @platform == Platform::GCE
718
- @vm_id ||= ec2_metadata['instanceId'] if @platform == Platform::EC2
719
- rescue StandardError => e
720
- @log.error 'Failed to obtain vm_id: ', error: e
721
- end
722
-
723
- # 1. Return the value if it is explicitly set in the config already.
724
- # 2. If not, check if the response from Metadata Agent includes this info.
725
- # 3. If not, try to retrieve it locally.
726
- def set_vm_name
727
- @vm_name ||= @resource.labels['instance_name'] if
728
- !@resource.nil? && @resource.labels.key?('instance_name')
729
- @vm_name ||= Socket.gethostname
730
- rescue StandardError => e
731
- @log.error 'Failed to obtain vm name: ', error: e
732
- end
733
-
734
- # 1. Return the value if it is explicitly set in the config already.
735
- # 2. If not, check if the response from Metadata Agent includes this info.
736
- # 3. If not, try to retrieve it locally.
737
- def set_location
738
- unless @resource.nil?
739
- @zone ||= @resource.labels['location'] if
740
- @resource.type == DOCKER_CONSTANTS[:resource_type] &&
741
- @resource.labels.key?('location')
742
- @zone ||= @resource.labels['zone'] if
743
- @platform == Platform::GCE && @resource.labels.key?('zone')
744
- @zone ||= @resource.labels['region'] if
745
- @platform == Platform::EC2 && @resource.labels.key?('region')
746
- end
747
- # Response format: "projects/<number>/zones/<zone>"
748
- @zone ||= fetch_gce_metadata('instance/zone').rpartition('/')[2] if
749
- @platform == Platform::GCE
750
- @zone ||= 'aws:' + ec2_metadata['availabilityZone'] if
751
- @platform == Platform::EC2 && ec2_metadata.key?('availabilityZone')
752
- rescue StandardError => e
753
- @log.error 'Failed to obtain location: ', error: e
754
- end
755
-
756
- # Retrieve monitored resource via the legacy way.
757
- #
758
- # Note: This is just a failover plan if we fail to get metadata from
759
- # Metadata Agent. Thus it should be equivalent to what Metadata Agent
760
- # returns.
761
- def determine_agent_level_monitored_resource_via_legacy
762
- resource = Google::Apis::LoggingV2beta1::MonitoredResource.new(
763
- labels: {})
764
- resource.type = determine_agent_level_monitored_resource_type
765
- resource.labels = determine_agent_level_monitored_resource_labels(
766
- resource.type)
767
- resource
768
- end
769
-
770
- # Determine agent level monitored resource type.
771
- def determine_agent_level_monitored_resource_type
772
- # EC2 instance.
773
- return EC2_CONSTANTS[:resource_type] if
774
- @platform == Platform::EC2
775
-
776
- # Unknown platform will be defaulted to GCE instance..
777
- return COMPUTE_CONSTANTS[:resource_type] if
778
- @platform == Platform::OTHER
779
-
780
- # Resource types determined by @subservice_name config.
781
- # Cloud Dataflow.
782
- return DATAFLOW_CONSTANTS[:resource_type] if
783
- @subservice_name == DATAFLOW_CONSTANTS[:service]
784
- # Cloud ML.
785
- return ML_CONSTANTS[:resource_type] if
786
- @subservice_name == ML_CONSTANTS[:service]
787
- # Default back to GCE if invalid value is detected.
788
- return COMPUTE_CONSTANTS[:resource_type] if
789
- @subservice_name
790
-
791
- # Resource types determined by @detect_subservice config.
792
- if @detect_subservice
793
- begin
794
- attributes = fetch_gce_metadata('instance/attributes/').split
795
- rescue StandardError => e
796
- @log.error 'Failed to detect subservice: ', error: e
797
- end
798
- # GAE app.
799
- return APPENGINE_CONSTANTS[:resource_type] if
800
- attributes.include?('gae_backend_name') &&
801
- attributes.include?('gae_backend_version')
802
- # GKE container.
803
- return CONTAINER_CONSTANTS[:resource_type] if
804
- attributes.include?('kube-env')
805
- # Cloud Dataproc.
806
- return DATAPROC_CONSTANTS[:resource_type] if
807
- attributes.include?('dataproc-cluster-uuid') &&
808
- attributes.include?('dataproc-cluster-name')
809
- end
810
- # GCE instance.
811
- COMPUTE_CONSTANTS[:resource_type]
812
- end
813
-
814
- # Determine agent level monitored resource labels based on the resource
815
- # type. Each resource type has its own labels that need to be filled in.
816
- def determine_agent_level_monitored_resource_labels(type)
817
- labels = {}
818
-
819
- case type
820
-
821
- # GAE app.
822
- when APPENGINE_CONSTANTS[:resource_type]
823
- begin
824
- labels['module_id'] = fetch_gce_metadata(
825
- 'instance/attributes/gae_backend_name')
826
- labels['version_id'] = fetch_gce_metadata(
827
- 'instance/attributes/gae_backend_version')
828
- rescue StandardError => e
829
- @log.error 'Failed to set monitored resource labels for GAE: ',
830
- error: e
831
- end
832
-
833
- # GCE.
834
- when COMPUTE_CONSTANTS[:resource_type]
835
- labels['instance_id'] = @vm_id
836
- labels['zone'] = @zone
837
-
838
- # GKE container.
839
- when CONTAINER_CONSTANTS[:resource_type]
840
- labels['instance_id'] = @vm_id
841
- labels['zone'] = @zone
842
- begin
843
- raw_kube_env = fetch_gce_metadata('instance/attributes/kube-env')
844
- kube_env = YAML.load(raw_kube_env)
845
- labels['cluster_name'] =
846
- cluster_name_from_kube_env(kube_env)
847
- rescue StandardError => e
848
- @log.error 'Failed to set monitored resource labels for GKE: ',
849
- error: e
850
- end
851
-
852
- # Cloud Dataproc.
853
- when DATAPROC_CONSTANTS[:resource_type]
854
- begin
855
- labels['cluster_uuid'] =
856
- fetch_gce_metadata('instance/attributes/dataproc-cluster-uuid')
857
- labels['cluster_name'] =
858
- fetch_gce_metadata('instance/attributes/dataproc-cluster-name')
859
- labels['region'] =
860
- fetch_gce_metadata('instance/attributes/dataproc-region')
861
- rescue StandardError => e
862
- @log.error 'Failed to set monitored resource labels for Cloud ' \
863
- 'Dataproc: ', error: e
864
- end
865
-
866
- # EC2.
867
- when EC2_CONSTANTS[:resource_type]
868
- labels['instance_id'] = @vm_id
869
- labels['region'] = @zone
870
- labels['aws_account'] = ec2_metadata['accountId'] if
871
- ec2_metadata.key?('accountId')
872
- end
873
- labels
874
- end
875
-
876
- # Determine the common labels that should be added to all log entries
877
- # processed by this logging agent.
878
- def determine_agent_level_common_labels
879
- labels = {}
880
- # User can specify labels via config. We want to capture those as well.
881
- # TODO: Send instance tags as labels as well?
882
- labels.merge!(@labels) if @labels
883
-
884
- case @resource.type
885
-
886
- # GAE app.
887
- when APPENGINE_CONSTANTS[:resource_type]
888
- labels["#{COMPUTE_CONSTANTS[:service]}/resource_id"] = @vm_id
889
- labels["#{COMPUTE_CONSTANTS[:service]}/resource_name"] = @vm_name
890
- labels["#{COMPUTE_CONSTANTS[:service]}/zone"] = @zone
891
-
892
- # GCE and GKE container.
893
- when COMPUTE_CONSTANTS[:resource_type],
894
- CONTAINER_CONSTANTS[:resource_type]
895
- labels["#{COMPUTE_CONSTANTS[:service]}/resource_name"] = @vm_name
896
-
897
- # Cloud Dataflow and Cloud Dataproc.
898
- when DATAFLOW_CONSTANTS[:resource_type],
899
- DATAPROC_CONSTANTS[:resource_type]
900
- labels["#{COMPUTE_CONSTANTS[:service]}/resource_id"] = @vm_id
901
- labels["#{COMPUTE_CONSTANTS[:service]}/resource_name"] = @vm_name
902
- labels["#{COMPUTE_CONSTANTS[:service]}/zone"] = @zone
903
-
904
- # EC2.
905
- when EC2_CONSTANTS[:resource_type]
906
- labels["#{EC2_CONSTANTS[:service]}/resource_name"] = @vm_name
907
-
908
- # Cloud ML.
909
- when ML_CONSTANTS[:resource_type]
910
- labels["#{COMPUTE_CONSTANTS[:service]}/resource_id"] = @vm_id
911
- labels["#{COMPUTE_CONSTANTS[:service]}/resource_name"] = @vm_name
912
- labels["#{COMPUTE_CONSTANTS[:service]}/zone"] = @zone
913
- end
914
- labels
915
- end
916
-
917
- # Determine the group level monitored resource and common labels shared by a
918
- # collection of entries.
919
- def determine_group_level_monitored_resource_and_labels(tag)
920
- # Determine group level monitored resource type. For certain types,
921
- # extract useful info from the tag and store those in
922
- # matched_regexp_group.
923
- group_resource_type, matched_regexp_group =
924
- determine_group_level_monitored_resource_type(tag)
925
-
926
- # Determine group level monitored resource labels and common labels.
927
- group_resource_type, group_resource_labels, group_common_labels = \
928
- determine_group_level_labels_and_adjust_type(
929
- group_resource_type, matched_regexp_group)
930
-
931
- group_resource = Google::Apis::LoggingV2beta1::MonitoredResource.new(
932
- type: group_resource_type,
933
- labels: group_resource_labels.to_h
934
- )
935
-
936
- # Freeze the per-request state. Any further changes must be made on a
937
- # per-entry basis.
938
- group_resource.freeze
939
- group_resource.labels.freeze
940
- group_common_labels.freeze
941
-
942
- [group_resource, group_common_labels]
943
- end
944
-
945
- # Determine group level monitored resource type shared by a collection of
946
- # entries.
947
- # Returns the resource type and tag regexp matched groups. The matched
948
- # groups only apply to some resource types. Return nil if not applicable or
949
- # if there is no match.
950
- def determine_group_level_monitored_resource_type(tag)
951
- # Match tag against Cloud Functions format.
952
- if @running_cloudfunctions
953
- matched_regexp_group = @cloudfunctions_tag_regexp.match(tag)
954
- return [CLOUDFUNCTIONS_CONSTANTS[:resource_type],
955
- matched_regexp_group] if matched_regexp_group
956
- end
957
-
958
- # Match tag against Docker container stderr / stdout log format and
959
- # Docker container application log format.
960
- matched_regexp_group =
961
- # Format: "container.<container_id>.<container_name>"
962
- @dockercontainer_tag_regexp.match(tag) ||
963
- # Format: "application-container.<container_name>.<additional_tag>"
964
- @dockercontainer_tag_with_application_regexp.match(tag)
965
- return [DOCKER_CONSTANTS[:resource_type], matched_regexp_group] if
966
- matched_regexp_group
967
-
968
- # Match tag against GKE Container format.
969
- if @resource.type == CONTAINER_CONSTANTS[:resource_type] &&
970
- @compiled_kubernetes_tag_regexp
971
- # Container logs in Kubernetes are tagged based on where they came from,
972
- # so we can extract useful metadata from the tag. Do this here to avoid
973
- # having to repeat it for each record.
974
- matched_regexp_group = @compiled_kubernetes_tag_regexp.match(tag)
975
- return [@resource.type, matched_regexp_group] if matched_regexp_group
976
- end
977
-
978
- # Otherwise, return the original type.
979
- [@resource.type, nil]
980
- end
981
-
982
- # Determine group level monitored resource labels and common labels. These
983
- # labels will be shared by a collection of entries. In certain cases, we
984
- # might also adjust the resource type.
985
- def determine_group_level_labels_and_adjust_type(group_resource_type,
986
- matched_regexp_group)
987
- group_resource_labels = @resource.labels.dup
988
- group_common_labels = @common_labels.dup
989
-
990
- case group_resource_type
991
-
992
- # Cloud Functions.
993
- when CLOUDFUNCTIONS_CONSTANTS[:resource_type]
994
- group_resource_labels['region'] = @gcf_region
995
- group_resource_labels['function_name'] =
996
- decode_cloudfunctions_function_name(
997
- matched_regexp_group['encoded_function_name'])
998
- instance_id = group_resource_labels.delete('instance_id')
999
- group_common_labels["#{CONTAINER_CONSTANTS[:service]}/cluster_name"] =
1000
- group_resource_labels.delete('cluster_name')
1001
- group_common_labels["#{CONTAINER_CONSTANTS[:service]}/instance_id"] =
1002
- instance_id
1003
- group_common_labels["#{COMPUTE_CONSTANTS[:service]}/resource_id"] =
1004
- instance_id
1005
- group_common_labels["#{COMPUTE_CONSTANTS[:service]}/zone"] =
1006
- group_resource_labels.delete('zone')
1007
-
1008
- # GKE container.
1009
- when CONTAINER_CONSTANTS[:resource_type]
1010
- if matched_regexp_group
1011
- group_resource_labels['container_name'] =
1012
- matched_regexp_group['container_name']
1013
- group_resource_labels['namespace_id'] =
1014
- matched_regexp_group['namespace_name']
1015
- group_resource_labels['pod_id'] =
1016
- matched_regexp_group['pod_name']
1017
- %w(namespace_name pod_name).each do |field|
1018
- group_common_labels["#{CONTAINER_CONSTANTS[:service]}/#{field}"] =
1019
- matched_regexp_group[field]
1020
- end
1021
- end
1022
-
1023
- # Docker container.
1024
- when DOCKER_CONSTANTS[:resource_type]
1025
- # For Docker container stderr / stdout logs generated by Docker Fluentd
1026
- # Logging Driver, tags are in the format of "container.<container_id>.
1027
- # <container_name>", thus they include 'container_id' info.
1028
- # For logs generated by applications running in Docker containers,
1029
- # tags are in the format of "application-container.<container_name>.
1030
- # <additional_tag>", thus 'container_id' info is unknown yet.
1031
- # 'container_name' info on the other hand is always available.
1032
- container_id = matched_regexp_group['container_id'] if
1033
- matched_regexp_group.names.include? 'container_id'
1034
- container_name = matched_regexp_group['container_name']
1035
-
1036
- if @enable_metadata_agent
1037
- # Call Metadata Agent with "container.<container_id>" or
1038
- # "application-container.<container_name>" as the locally-unique key
1039
- # to retrieve monitored resource. This should be different from the
1040
- # original @resource value that got initiated when the agent starts up
1041
- # because that one is always at the VM level.
1042
- if container_id
1043
- locally_unique_id = "container.#{container_id}"
1044
- else
1045
- locally_unique_id = "containerName.#{container_name}"
1046
- end
1047
- retrieved_resource = call_metadata_agent_for_monitored_resource(
1048
- locally_unique_id)
1049
- end
1050
-
1051
- if !retrieved_resource.nil?
1052
- # If we successfully get a monitored resource from Metadata Agent,
1053
- # use this one instead of the original instance monitored resource.
1054
- group_resource_labels = retrieved_resource.labels.dup
1055
- @log.debug 'Retrieved monitored resource from Metadata Agent: ' \
1056
- "#{retrieved_resource.inspect}."
1057
- else
1058
- # If Metadata Agent is not enabled, or we failed to get a monitored
1059
- # resource, we need to have some backup plan.
1060
- @log.debug 'Metadata Agent not enabled or failed to retrieve ' \
1061
- 'docker container monitored resource from Metadata ' \
1062
- 'Agent.'
1063
-
1064
- # 1. Check if 'container_id' is set already. It should be available
1065
- # for stdout / stderr). If so, use that.
1066
- # 2. If not, call Docker Remote API to retrieve the container ID from
1067
- # container name, but only if @call_docker_api_locally is true.
1068
- container_id ||= retrieve_container_id_by_name_locally(
1069
- container_name) if @call_docker_api_locally
1070
- unless container_id
1071
- @log.debug 'No docker container id retrieved. Falling back to
1072
- instance monitored resource.'
1073
- # If a container id is not available, fall back to the instance
1074
- # monitored resource.
1075
- return [COMPUTE_CONSTANTS[:resource_type], group_resource_labels,
1076
- group_common_labels]
1077
- end
1078
- group_resource_labels['container_id'] = container_id
1079
- # 'zone' for GCP and 'region' for EC2 must have been set at this
1080
- # point. Rename them to 'location'.
1081
- group_resource_labels['location'] = @zone
1082
- if @platform == Platform::EC2
1083
- group_resource_labels.delete('region')
1084
- else
1085
- group_resource_labels.delete('zone')
1086
- end
1087
- # vm id info should be reported as a metadata label instead.
1088
- group_resource_labels.delete('instance_id')
1089
-
1090
- end
1091
- # Set metadata labels.
1092
- group_common_labels["#{DOCKER_CONSTANTS[:service]}/container_name"] =
1093
- matched_regexp_group['container_name']
1094
- group_common_labels["#{COMPUTE_CONSTANTS[:service]}/resource_id"] =
1095
- @vm_id
1096
- end
1097
-
1098
- [group_resource_type, group_resource_labels, group_common_labels]
1099
- end
1100
-
1101
- # Extract entry resource and common labels that should be applied to
1102
- # individual entries from the group resource.
1103
- def determine_entry_level_labels(group_resource, record)
1104
- resource_type = group_resource.type
1105
- resource_labels = {}
1106
- common_labels = {}
1107
-
1108
- # The format of the locally unique key varies by monitored resource.
1109
- #
1110
- # Docker container:
1111
- # "container.<container_id>"
1112
- # "containerName.<container_name>"
1113
- # GKE container:
1114
- # "gke_containerName.<namespace_id>.<pod_name>.<container_name>"
1115
- if @enable_metadata_agent && record.key?(LOCALLY_UNIQUE_ID_LABEL_NAME)
1116
- # Call Metadata Agent with "gke_containerName.<namespace_id>.
1117
- # <pod_name>.<container_name>" as the locally-unique key to retrieve
1118
- # monitored resource.
1119
- locally_unique_id = record[LOCALLY_UNIQUE_ID_LABEL_NAME]
1120
- @log.debug 'Calling metadata agent with locally unique id: ' \
1121
- "#{locally_unique_id}."
1122
- retrieved_resource = call_metadata_agent_for_monitored_resource(
1123
- locally_unique_id)
1124
- @log.debug 'Retrieved monitored resource from metadata agent: ' \
1125
- "#{retrieved_resource.inspect}."
1126
- unless retrieved_resource.nil?
1127
- resource_type = retrieved_resource.type
1128
- # Temporarily renaming 'gke_container' to 'container'.
1129
- resource_type = 'container' if resource_type == 'gke_container'
1130
- # If we successfully get a monitored resource from Metadata Agent,
1131
- # use this one instead of the original VM-level monitored resource.
1132
- resource_labels = retrieved_resource.labels.dup
1133
- record.delete(LOCALLY_UNIQUE_ID_LABEL_NAME)
1134
- @log.debug 'Retrieved gke_container monitored resource from' \
1135
- 'Stackdriver Metadata agent: ' \
1136
- "#{retrieved_resource.inspect}."
1137
- end
1138
- end
1139
-
1140
- # Cloud Functions.
1141
- if resource_type == CLOUDFUNCTIONS_CONSTANTS[:resource_type] &&
1142
- record.key?('log')
1143
- @cloudfunctions_log_match =
1144
- @cloudfunctions_log_regexp.match(record['log'])
1145
- common_labels['execution_id'] =
1146
- @cloudfunctions_log_match['execution_id'] if \
1147
- @cloudfunctions_log_match &&
1148
- @cloudfunctions_log_match['execution_id']
1149
- end
1150
-
1151
- # GKE containers.
1152
- if resource_type == CONTAINER_CONSTANTS[:resource_type]
1153
- # Move the stdout/stderr annotation from the record into a label.
1154
- common_labels.merge!(
1155
- fields_to_labels(
1156
- record, 'stream' => "#{CONTAINER_CONSTANTS[:service]}/stream"))
1157
-
1158
- # If the record has been annotated by the kubernetes_metadata_filter
1159
- # plugin, then use that metadata. Otherwise, rely on commonLabels
1160
- # populated at the grouped_entries level from the group's tag.
1161
- if record.key?('kubernetes')
1162
- extracted_resource_labels, extracted_common_labels = \
1163
- extract_container_metadata(record)
1164
- resource_labels.merge!(extracted_resource_labels)
1165
- common_labels.merge!(extracted_common_labels)
1166
- end
1167
- end
1168
-
1169
- # Docker containers.
1170
- if resource_type == DOCKER_CONSTANTS[:resource_type]
1171
- # For logs coming from Docker Fluentd Logging Driver, the log record
1172
- # has 4 fields: 'container_id', 'container_name', 'source' and 'log'.
1173
- # Extract 'container_id', 'container_name' and 'source' from json
1174
- # record, set corresponding labels, and remove these fields from record.
1175
- {
1176
- 'container_name' => 'container_name',
1177
- 'source' => 'stream'
1178
- }.each do |field_name, label_name|
1179
- common_labels.merge!(
1180
- fields_to_labels(
1181
- record,
1182
- field_name => "#{DOCKER_CONSTANTS[:service]}/#{label_name}"
1183
- )
1184
- )
1185
- end
1186
- resource_labels.merge!(
1187
- fields_to_labels(record, 'container_id' => 'container_id'))
1188
- end
1189
-
1190
- # If the name of a field in the record is present in the @label_map
1191
- # configured by users, report its value as a label and do not send that
1192
- # field as part of the payload.
1193
- common_labels.merge!(fields_to_labels(record, @label_map))
1194
-
1195
- resource_labels.merge!(
1196
- extract_resource_labels(resource_type, common_labels))
1197
-
1198
- [resource_type, resource_labels, common_labels]
1199
- end
1200
-
1201
- # Call Metadata Agent to get monitored resource information and parse
1202
- # response to Google::Api::MonitoredResource.
1203
- def call_metadata_agent_for_monitored_resource(unique_key)
1204
- response = call_metadata_agent("monitoredResource/#{unique_key}")
1205
- return nil if response.nil?
1206
- begin
1207
- resource = Google::Api::MonitoredResource.decode_json(response.to_json)
1208
- rescue Google::Protobuf::ParseError, ArgumentError => e
1209
- @log.error 'Error paring monitored resource from Metadata Agent. ' \
1210
- "response: #{response.inspect}", error: e
1211
- return nil
1212
- end
1213
-
1214
- # TODO(qingling128): Use Google::Api::MonitoredResource directly after we
1215
- # upgrade gRPC version to include the fix for the protobuf map
1216
- # corruption issue.
1217
- Google::Apis::LoggingV2beta1::MonitoredResource.new(
1218
- type: resource.type,
1219
- labels: resource.labels.to_h
1220
- )
1221
- end
1222
-
1223
- # Call Metadata Agent and parse response to json. Return nil in case of any
1224
- # error / failure.
1225
- def call_metadata_agent(path)
1226
- url = "#{@metadata_agent_url}/#{path}"
1227
- @log.debug("Calling Metadata Agent: #{url}")
1228
- open(url) do |f|
1229
- response = f.read
1230
- parsed_hash = parse_json_or_nil(response)
1231
- if parsed_hash.nil?
1232
- @log.error 'Response from Metadata Agent is not in valid json ' \
1233
- "format: '#{response.inspect}'."
1234
- return nil
1235
- end
1236
- @log.debug "Response from Metadata Agent: #{parsed_hash}"
1237
- return parsed_hash
1238
- end
1239
- rescue StandardError => e
1240
- @log.error 'Error calling Metadata Agent.', error: e
1241
- return nil
1242
868
  end
1243
869
 
1244
870
  # TODO: This functionality should eventually be available in another
@@ -1276,28 +902,11 @@ module Fluent
1276
902
  end
1277
903
  end
1278
904
 
1279
- # Calling Docker Remote API to get container id by name.
1280
- def retrieve_container_id_by_name_locally(container_name)
1281
- response = Excon.get(
1282
- "unix:///containers/#{container_name}/json",
1283
- socket: @docker_remote_api_socket_path)
1284
- @log.debug "Response from Docker API with name '#{container_name}': " \
1285
- "#{response.inspect}."
1286
- return parse_container_id_from_docker_api_response(response)
1287
- rescue StandardError => e
1288
- @log.error 'Error calling Docker API to get container id.', error: e
1289
- return nil
1290
- end
1291
-
1292
- # Parse the container id from Docker Remote API response.
1293
- # TODO(qingling128) Add a config for Docker API version to support parsing
1294
- # different versions of Docker Remote API when the format varies.
1295
- def parse_container_id_from_docker_api_response(response)
1296
- JSON.parse(response.data[:body])['Id']
1297
- rescue StandardError => e
1298
- @log.error 'Error parsing Docker API response to get container id.',
1299
- error: e
1300
- return nil
905
+ def detect_cloudfunctions(attributes)
906
+ return unless attributes.include?('gcf_region')
907
+ # Cloud Functions detected
908
+ @running_cloudfunctions = true
909
+ @gcf_region = fetch_gce_metadata('instance/attributes/gcf_region')
1301
910
  end
1302
911
 
1303
912
  def cluster_name_from_kube_env(kube_env)
@@ -1370,14 +979,9 @@ module Fluent
1370
979
  end
1371
980
  elsif record.key?('severity')
1372
981
  return parse_severity(record.delete('severity'))
1373
- elsif [CONTAINER_CONSTANTS[:resource_type],
1374
- DOCKER_CONSTANTS[:resource_type]].include?(resource_type)
1375
- stream = entry_common_labels[
1376
- "#{CONTAINER_CONSTANTS[:service]}/stream"] if
1377
- resource_type == CONTAINER_CONSTANTS[:resource_type]
1378
- stream = entry_common_labels[
1379
- "#{DOCKER_CONSTANTS[:service]}/stream"] if
1380
- resource_type == DOCKER_CONSTANTS[:resource_type]
982
+ elsif resource_type == CONTAINER_CONSTANTS[:resource_type] &&
983
+ entry_common_labels.key?("#{CONTAINER_CONSTANTS[:service]}/stream")
984
+ stream = entry_common_labels["#{CONTAINER_CONSTANTS[:service]}/stream"]
1381
985
  if stream == 'stdout'
1382
986
  return 'INFO'
1383
987
  elsif stream == 'stderr'
@@ -1585,24 +1189,6 @@ module Fluent
1585
1189
  [resource_labels, common_labels]
1586
1190
  end
1587
1191
 
1588
- def format(tag, time, record)
1589
- [tag, time, record].to_msgpack
1590
- end
1591
-
1592
- # Given a tag, returns the corresponding valid tag if possible, or nil if
1593
- # the tag should be rejected. If 'require_valid_tags' is false, non-string
1594
- # tags are converted to strings, and invalid characters are sanitized;
1595
- # otherwise such tags are rejected.
1596
- def sanitize_tag(tag)
1597
- if @require_valid_tags &&
1598
- (!tag.is_a?(String) || tag == '' || convert_to_utf8(tag) != tag)
1599
- return nil
1600
- end
1601
- tag = convert_to_utf8(tag.to_s)
1602
- tag = '_' if tag == ''
1603
- tag
1604
- end
1605
-
1606
1192
  # For every original_label => new_label pair in the label_map, delete the
1607
1193
  # original_label from the record if it exists, and extract the value to form
1608
1194
  # a map with the new_label as the key.
@@ -1630,8 +1216,7 @@ module Fluent
1630
1216
  entry.text_payload = record['log']
1631
1217
  elsif is_json
1632
1218
  entry.json_payload = record
1633
- elsif [CONTAINER_CONSTANTS[:resource_type],
1634
- DOCKER_CONSTANTS[:resource_type]].include?(resource_type) &&
1219
+ elsif resource_type == CONTAINER_CONSTANTS[:resource_type] &&
1635
1220
  record.key?('log')
1636
1221
  entry.text_payload = record['log']
1637
1222
  elsif record.size == 1 && record.key?('message')
@@ -1701,8 +1286,7 @@ module Fluent
1701
1286
  entry.text_payload = convert_to_utf8(record['log'])
1702
1287
  elsif is_json
1703
1288
  entry.json_payload = struct_from_ruby(record)
1704
- elsif [CONTAINER_CONSTANTS[:resource_type],
1705
- DOCKER_CONSTANTS[:resource_type]].include?(resource_type) &&
1289
+ elsif resource_type == CONTAINER_CONSTANTS[:resource_type] &&
1706
1290
  record.key?('log')
1707
1291
  entry.text_payload = convert_to_utf8(record['log'])
1708
1292
  elsif record.size == 1 && record.key?('message')
@@ -1715,7 +1299,7 @@ module Fluent
1715
1299
  def log_name(tag, resource)
1716
1300
  if resource.type == CLOUDFUNCTIONS_CONSTANTS[:resource_type]
1717
1301
  tag = 'cloud-functions'
1718
- elsif resource.type == APPENGINE_CONSTANTS[:resource_type]
1302
+ elsif @running_on_managed_vm
1719
1303
  # Add a prefix to Managed VM logs to prevent namespace collisions.
1720
1304
  tag = "#{APPENGINE_CONSTANTS[:service]}/#{tag}"
1721
1305
  elsif resource.type == CONTAINER_CONSTANTS[:resource_type]
@@ -1745,9 +1329,6 @@ module Fluent
1745
1329
  elsif resource_type == ML_CONSTANTS[:resource_type]
1746
1330
  label_prefix = ML_CONSTANTS[:service]
1747
1331
  labels_to_extract = %w(job_id task_name)
1748
- elsif resource_type == DOCKER_CONSTANTS[:resource_type]
1749
- label_prefix = DOCKER_CONSTANTS[:service]
1750
- labels_to_extract = %w(container_id)
1751
1332
  else
1752
1333
  return extracted_labels
1753
1334
  end
@@ -1815,6 +1396,33 @@ module Fluent
1815
1396
  end
1816
1397
  end
1817
1398
  end
1399
+
1400
+ # Increment the metric for the number of successful requests.
1401
+ def increment_successful_requests_count
1402
+ return unless @successful_requests_count
1403
+ @successful_requests_count.increment(grpc: @use_grpc)
1404
+ end
1405
+
1406
+ # Increment the metric for the number of failed requests, labeled by
1407
+ # the provided status code.
1408
+ def increment_failed_requests_count(code)
1409
+ return unless @failed_requests_count
1410
+ @failed_requests_count.increment(grpc: @use_grpc, code: code)
1411
+ end
1412
+
1413
+ # Increment the metric for the number of log entries, successfully
1414
+ # ingested by the Stackdriver Logging API.
1415
+ def increment_ingested_entries_count(count)
1416
+ return unless @ingested_entries_count
1417
+ @ingested_entries_count.increment({}, count)
1418
+ end
1419
+
1420
+ # Increment the metric for the number of log entries that were dropped
1421
+ # and not ingested by the Stackdriver Logging API.
1422
+ def increment_dropped_entries_count(count)
1423
+ return unless @dropped_entries_count
1424
+ @dropped_entries_count.increment({}, count)
1425
+ end
1818
1426
  end
1819
1427
  end
1820
1428