fluent-plugin-kubernetes_metadata_filter 1.2.2 → 2.0.0

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
- SHA256:
3
- metadata.gz: d31ac807f14fb0b2022253ea499c950cf3a549fa805b4145cc68704bf89f94c2
4
- data.tar.gz: e4b1065aaa81240212171a9c661da46ec7df01ee6696531cf2a54a4b5c27864a
2
+ SHA1:
3
+ metadata.gz: 3e76bf94f6dd2d7d375b01379c3a0533670291e7
4
+ data.tar.gz: 0cf265d4423a322313a6233a9360c0438081f878
5
5
  SHA512:
6
- metadata.gz: 1161bed66cd4da96c0baf942ad17fbb7fa67de144d2aa53a0c3984c3114adffcd67e47a91602869a491056faecb44beffa5ca04cec12df5493b6b55870b3378b
7
- data.tar.gz: '0822363e158a8f0e970a068f72357bc1e2137c24dac17ba01be94463c6e111bb39f9f508187af6a08fddb1c265bced97411a511fadd7988b6d0e5804853b5647'
6
+ metadata.gz: c52356d4f5edc444bfb26b9f812257cafc250328f7de81708bb7f3e64711c1c85895a46877537d7fd5d17e486f75328900a848d80625e6450922672005b7ce6a
7
+ data.tar.gz: 4d0e4b4cdfd0734736419d32fe4237130e81fd04cc6612a5ea5f1eca22c69b8300b8ea4baeeca943b63dad93f256aeb7f35bd160f753a1a28ad650d395ba43b4
data/Gemfile CHANGED
@@ -1,11 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gem 'codeclimate-test-reporter', '<1.0.0', :group => :test, :require => nil
4
- if RUBY_VERSION == "2.0.0"
5
- gem 'rubocop', '<0.51.0', require: false
6
- else
7
- gem 'rubocop', require: false
8
- end
4
+ gem 'rubocop', require: false
9
5
 
10
6
  # Specify your gem's dependencies in fluent-plugin-add.gemspec
11
7
  gemspec
data/README.md CHANGED
@@ -42,10 +42,11 @@ This must used named capture groups for `container_name`, `pod_name` & `namespac
42
42
  * `cache_size` - size of the cache of Kubernetes metadata to reduce requests to the API server (default: `1000`)
43
43
  * `cache_ttl` - TTL in seconds of each cached element. Set to negative value to disable TTL eviction (default: `3600` - 1 hour)
44
44
  * `watch` - set up a watch on pods on the API server for updates to metadata (default: `true`)
45
+ * `merge_json_log` - merge logs in JSON format as top level keys (default: `true`)
46
+ * `preserve_json_log` - preserve JSON logs in raw form in the `log` key, only used if the previous option is true (default: `true`)
45
47
  * `de_dot` - replace dots in labels and annotations with configured `de_dot_separator`, required for ElasticSearch 2.x compatibility (default: `true`)
46
48
  * `de_dot_separator` - separator to use if `de_dot` is enabled (default: `_`)
47
- * *DEPRECATED* `use_journal` - If false, messages are expected to be formatted and tagged as if read by the fluentd in\_tail plugin with wildcard filename. If true, messages are expected to be formatted as if read from the systemd journal. The `MESSAGE` field has the full message. The `CONTAINER_NAME` field has the encoded k8s metadata (see below). The `CONTAINER_ID_FULL` field has the full container uuid. This requires docker to use the `--log-driver=journald` log driver. If unset (the default), the plugin will use the `CONTAINER_NAME` and `CONTAINER_ID_FULL` fields
48
- if available, otherwise, will use the tag in the `tag_to_kubernetes_name_regexp` format.
49
+ * `use_journal` - If false (default), messages are expected to be formatted and tagged as if read by the fluentd in\_tail plugin with wildcard filename. If true, messages are expected to be formatted as if read from the systemd journal. The `MESSAGE` field has the full message. The `CONTAINER_NAME` field has the encoded k8s metadata (see below). The `CONTAINER_ID_FULL` field has the full container uuid. This requires docker to use the `--log-driver=journald` log driver.
49
50
  * `container_name_to_kubernetes_regexp` - The regular expression used to extract the k8s metadata encoded in the journal `CONTAINER_NAME` field (default: `'^(?<name_prefix>[^_]+)_(?<container_name>[^\._]+)(\.(?<container_hash>[^_]+))?_(?<pod_name>[^_]+)_(?<namespace>[^_]+)_[^_]+_[^_]+$'`
50
51
  * This corresponds to the definition [in the source](https://github.com/kubernetes/kubernetes/blob/master/pkg/kubelet/dockertools/docker.go#L317)
51
52
  * `annotation_match` - Array of regular expressions matching annotation field names. Matched annotations are added to a log record.
@@ -53,23 +54,6 @@ if available, otherwise, will use the tag in the `tag_to_kubernetes_name_regexp`
53
54
  when true (default: `true`)
54
55
  * `orphaned_namespace_name` - The namespace to associate with records where the namespace can not be determined (default: `.orphaned`)
55
56
  * `orphaned_namespace_id` - The namespace id to associate with records where the namespace can not be determined (default: `orphaned`)
56
- * `lookup_from_k8s_field` - If the field `kubernetes` is present, lookup the metadata from the given subfields such as `kubernetes.namespace_name`, `kubernetes.pod_name`, etc. This allows you to avoid having to pass in metadata to lookup in an explicitly formatted tag name or in an explicitly formatted `CONTAINER_NAME` value. For example, set `kubernetes.namespace_name`, `kubernetes.pod_name`, `kubernetes.container_name`, and `docker.id` in the record, and the filter will fill in the rest. (default: `true`)
57
-
58
- **NOTE:** As of the release 1.1.x of this plugin, it no longer supports parsing the source message into JSON and attaching it to the
59
- payload. The following configuration options are removed:
60
-
61
- * `merge_json_log`
62
- * `preserve_json_log`
63
-
64
- **NOTE** As of this release, the use of `use_journal` is **DEPRECATED**. If this setting is not present, the plugin will
65
- attempt to figure out the source of the metadata fields from the following:
66
- - If `lookup_from_k8s_field true` (the default) and the following fields are present in the record:
67
- `docker.container_id`, `kubernetes.namespace_name`, `kubernetes.pod_name`, `kubernetes.container_name`,
68
- then the plugin will use those values as the source to use to lookup the metadata
69
- - If `use_journal true`, or `use_journal` is unset, and the fields `CONTAINER_NAME` and `CONTAINER_ID_FULL` are present in the record,
70
- then the plugin will parse those values using `container_name_to_kubernetes_regexp` and use those as the source to lookup the metadata
71
- - Otherwise, if the tag matches `tag_to_kubernetes_name_regexp`, the plugin will parse the tag and use those values to
72
- lookup the metdata
73
57
 
74
58
  Reading from the JSON formatted log files with `in_tail` and wildcard filenames:
75
59
  ```
@@ -120,22 +104,6 @@ Reading from the systemd journal (requires the fluentd `fluent-plugin-systemd` a
120
104
  </match>
121
105
  ```
122
106
 
123
- ## Environment variables for Kubernetes
124
-
125
- If the name of the Kubernetes node the plugin is running on is set as
126
- an environment variable with the name `K8S_NODE_NAME`, it will reduce cache
127
- misses and needless calls to the Kubernetes API.
128
-
129
- In the Kubernetes container definition, this is easily accomplished by:
130
-
131
- ```yaml
132
- env:
133
- - name: K8S_NODE_NAME
134
- valueFrom:
135
- fieldRef:
136
- fieldPath: spec.nodeName
137
- ```
138
-
139
107
  ## Example input/output
140
108
 
141
109
  Kubernetes creates symlinks to Docker log files in `/var/log/containers/*.log`. Docker logs in JSON format.
data/circle.yml CHANGED
@@ -7,9 +7,11 @@ dependencies:
7
7
  - 'rvm-exec 2.1.9 bundle install'
8
8
  - 'rvm-exec 2.2.5 bundle install'
9
9
  - 'rvm-exec 2.3.1 bundle install'
10
+ - 'rvm-exec 2.4.3 bundle install'
10
11
 
11
12
  test:
12
13
  override:
13
14
  - 'rvm-exec 2.1.9 bundle exec rake test'
14
15
  - 'rvm-exec 2.2.5 bundle exec rake test'
15
16
  - 'rvm-exec 2.3.1 bundle exec rake test'
17
+ - 'rvm-exec 2.4.3 bundle exec rake test'
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |gem|
6
6
  gem.name = "fluent-plugin-kubernetes_metadata_filter"
7
- gem.version = "1.2.2"
7
+ gem.version = "2.0.0"
8
8
  gem.authors = ["Jimmi Dyson"]
9
9
  gem.email = ["jimmidyson@gmail.com"]
10
10
  gem.description = %q{Filter plugin to add Kubernetes metadata}
@@ -18,23 +18,14 @@ Gem::Specification.new do |gem|
18
18
  gem.require_paths = ["lib"]
19
19
  gem.has_rdoc = false
20
20
 
21
- gem.required_ruby_version = '>= 2.0.0'
21
+ gem.required_ruby_version = '>= 2.1.0'
22
22
 
23
- gem.add_runtime_dependency "fluentd", ">= 0.12.0"
23
+ gem.add_runtime_dependency 'fluentd', ['>= 0.14.0', '< 2']
24
24
  gem.add_runtime_dependency "lru_redux"
25
25
  gem.add_runtime_dependency "kubeclient", "~> 1.1.4"
26
- if RUBY_VERSION == "2.0.0"
27
- gem.add_runtime_dependency "public_suffix", "< 3"
28
- gem.add_runtime_dependency "parallel", "< 1.14"
29
- gem.add_runtime_dependency "rainbow", "< 3"
30
- end
31
26
 
32
27
  gem.add_development_dependency "bundler", "~> 1.3"
33
- if RUBY_VERSION == "2.0.0"
34
- gem.add_development_dependency "rake", "< 13"
35
- else
36
- gem.add_development_dependency "rake"
37
- end
28
+ gem.add_development_dependency "rake"
38
29
  gem.add_development_dependency "minitest", "~> 4.0"
39
30
  gem.add_development_dependency "test-unit", "~> 3.0.2"
40
31
  gem.add_development_dependency "test-unit-rr", "~> 1.0.3"
@@ -23,8 +23,10 @@ require_relative 'kubernetes_metadata_stats'
23
23
  require_relative 'kubernetes_metadata_watch_namespaces'
24
24
  require_relative 'kubernetes_metadata_watch_pods'
25
25
 
26
- module Fluent
27
- class KubernetesMetadataFilter < Fluent::Filter
26
+ require 'fluent/plugin/filter'
27
+
28
+ module Fluent::Plugin
29
+ class KubernetesMetadataFilter < Fluent::Plugin::Filter
28
30
  K8_POD_CA_CERT = 'ca.crt'
29
31
  K8_POD_TOKEN = 'token'
30
32
 
@@ -48,6 +50,8 @@ module Fluent
48
50
  :string,
49
51
  :default => 'var\.log\.containers\.(?<pod_name>[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)_(?<namespace>[^_]+)_(?<container_name>.+)-(?<docker_id>[a-z0-9]{64})\.log$'
50
52
  config_param :bearer_token_file, :string, default: nil
53
+ config_param :merge_json_log, :bool, default: true
54
+ config_param :preserve_json_log, :bool, default: true
51
55
  config_param :secret_dir, :string, default: '/var/run/secrets/kubernetes.io/serviceaccount'
52
56
  config_param :de_dot, :bool, default: true
53
57
  config_param :de_dot_separator, :string, default: '_'
@@ -55,7 +59,7 @@ module Fluent
55
59
  # format:
56
60
  # CONTAINER_NAME=k8s_$containername.$containerhash_$podname_$namespacename_$poduuid_$rand32bitashex
57
61
  # CONTAINER_FULL_ID=dockeridassha256hexvalue
58
- config_param :use_journal, :bool, default: nil
62
+ config_param :use_journal, :bool, default: false
59
63
  # Field 2 is the container_hash, field 5 is the pod_id, and field 6 is the pod_randhex
60
64
  # I would have included them as named groups, but you can't have named groups that are
61
65
  # non-capturing :P
@@ -69,7 +73,6 @@ module Fluent
69
73
  config_param :allow_orphans, :bool, default: true
70
74
  config_param :orphaned_namespace_name, :string, default: '.orphaned'
71
75
  config_param :orphaned_namespace_id, :string, default: 'orphaned'
72
- config_param :lookup_from_k8s_field, :bool, default: true
73
76
 
74
77
  def fetch_pod_metadata(namespace_name, pod_name)
75
78
  log.trace("fetching pod metadata: #{namespace_name}/#{pod_name}") if log.trace?
@@ -89,12 +92,12 @@ module Fluent
89
92
  rescue Exception=>e
90
93
  log.debug(e)
91
94
  @stats.bump(:pod_cache_api_nil_bad_resp_payload)
92
- log.trace("returning empty metadata for #{namespace_name}/#{pod_name} due to error '#{e}'") if log.trace?
95
+ log.trace("returning empty metadata for #{namespace_name}/#{pod_name} due to error") if log.trace?
93
96
  end
94
97
  end
95
- rescue Exception=>e
98
+ rescue KubeException=>e
96
99
  @stats.bump(:pod_cache_api_nil_error)
97
- log.debug "Exception '#{e}' encountered fetching pod metadata from Kubernetes API #{@apiVersion} endpoint #{@kubernetes_url}"
100
+ log.debug "Exception encountered fetching pod metadata from Kubernetes API #{@apiVersion} endpoint #{@kubernetes_url}: #{e.message}"
98
101
  end
99
102
  {}
100
103
  end
@@ -126,17 +129,17 @@ module Fluent
126
129
  metadata = parse_namespace_metadata(metadata)
127
130
  @stats.bump(:namespace_cache_api_updates)
128
131
  log.trace("parsed metadata for #{namespace_name}: #{metadata}") if log.trace?
129
- @namespace_cache[metadata['namespace_id']] = metadata
132
+ @namespace_cache[metadata['namespace_id']] = metadata
130
133
  return metadata
131
134
  rescue Exception => e
132
135
  log.debug(e)
133
136
  @stats.bump(:namespace_cache_api_nil_bad_resp_payload)
134
- log.trace("returning empty metadata for #{namespace_name} due to error '#{e}'") if log.trace?
137
+ log.trace("returning empty metadata for #{namespace_name} due to error") if log.trace?
135
138
  end
136
139
  end
137
- rescue Exception => kube_error
140
+ rescue KubeException => kube_error
138
141
  @stats.bump(:namespace_cache_api_nil_error)
139
- log.debug "Exception '#{kube_error}' encountered fetching namespace metadata from Kubernetes API #{@apiVersion} endpoint #{@kubernetes_url}"
142
+ log.debug "Exception encountered fetching namespace metadata from Kubernetes API #{@apiVersion} endpoint #{@kubernetes_url}: #{kube_error.message}"
140
143
  end
141
144
  {}
142
145
  end
@@ -154,6 +157,7 @@ module Fluent
154
157
  end
155
158
 
156
159
  require 'kubeclient'
160
+ require 'active_support/core_ext/object/blank'
157
161
  require 'lru_redux'
158
162
  @stats = KubernetesMetadata::Stats.new
159
163
 
@@ -180,23 +184,29 @@ module Fluent
180
184
 
181
185
  # Use Kubernetes default service account if we're in a pod.
182
186
  if @kubernetes_url.nil?
187
+ log.debug "Kubernetes URL is not set - inspecting environ"
188
+
183
189
  env_host = ENV['KUBERNETES_SERVICE_HOST']
184
190
  env_port = ENV['KUBERNETES_SERVICE_PORT']
185
191
  if env_host.present? && env_port.present?
186
192
  @kubernetes_url = "https://#{env_host}:#{env_port}/api"
193
+ log.debug "Kubernetes URL is now '#{@kubernetes_url}'"
187
194
  end
188
195
  end
189
196
 
190
197
  # Use SSL certificate and bearer token from Kubernetes service account.
191
198
  if Dir.exist?(@secret_dir)
199
+ log.debug "Found directory with secrets: #{@secret_dir}"
192
200
  ca_cert = File.join(@secret_dir, K8_POD_CA_CERT)
193
201
  pod_token = File.join(@secret_dir, K8_POD_TOKEN)
194
202
 
195
203
  if !@ca_file.present? and File.exist?(ca_cert)
204
+ log.debug "Found CA certificate: #{ca_cert}"
196
205
  @ca_file = ca_cert
197
206
  end
198
207
 
199
208
  if !@bearer_token_file.present? and File.exist?(pod_token)
209
+ log.debug "Found pod token: #{pod_token}"
200
210
  @bearer_token_file = pod_token
201
211
  end
202
212
  end
@@ -217,6 +227,7 @@ module Fluent
217
227
  auth_options[:bearer_token] = bearer_token
218
228
  end
219
229
 
230
+ log.debug "Creating K8S client"
220
231
  @client = Kubeclient::Client.new @kubernetes_url, @apiVersion,
221
232
  ssl_options: ssl_options,
222
233
  auth_options: auth_options
@@ -234,10 +245,15 @@ module Fluent
234
245
  namespace_thread.abort_on_exception = true
235
246
  end
236
247
  end
237
- @time_fields = []
238
- @time_fields.push('_SOURCE_REALTIME_TIMESTAMP', '__REALTIME_TIMESTAMP') if @use_journal || @use_journal.nil?
239
- @time_fields.push('time') unless @use_journal
240
- @time_fields.push('@timestamp') if @lookup_from_k8s_field
248
+ if @use_journal
249
+ log.debug "Will stream from the journal"
250
+ @merge_json_log_key = 'MESSAGE'
251
+ self.class.class_eval { alias_method :filter_stream, :filter_stream_from_journal }
252
+ else
253
+ log.debug "Will stream from the files"
254
+ @merge_json_log_key = 'log'
255
+ self.class.class_eval { alias_method :filter_stream, :filter_stream_from_files }
256
+ end
241
257
 
242
258
  @annotations_regexps = []
243
259
  @annotation_match.each do |regexp|
@@ -250,93 +266,114 @@ module Fluent
250
266
 
251
267
  end
252
268
 
253
- def get_metadata_for_record(namespace_name, pod_name, container_name, container_id, create_time, batch_miss_cache)
269
+ def get_metadata_for_record(match_data, cache_key, create_time, batch_miss_cache)
270
+ namespace_name = match_data['namespace']
271
+ pod_name = match_data['pod_name']
254
272
  metadata = {
255
- 'docker' => {'container_id' => container_id},
256
- 'kubernetes' => {
257
- 'container_name' => container_name,
258
- 'namespace_name' => namespace_name,
259
- 'pod_name' => pod_name
260
- }
273
+ 'container_name' => match_data['container_name'],
274
+ 'namespace_name' => namespace_name,
275
+ 'pod_name' => pod_name
261
276
  }
262
277
  if @kubernetes_url.present?
263
- pod_metadata = get_pod_metadata(container_id, namespace_name, pod_name, create_time, batch_miss_cache)
278
+ pod_metadata = get_pod_metadata(cache_key, namespace_name, pod_name, create_time, batch_miss_cache)
279
+ metadata.merge!(pod_metadata) if pod_metadata
280
+ end
281
+ metadata
282
+ end
264
283
 
265
- if (pod_metadata.include? 'containers') && (pod_metadata['containers'].include? container_id)
266
- metadata['kubernetes']['container_image'] = pod_metadata['containers'][container_id]['image']
267
- metadata['kubernetes']['container_image_id'] = pod_metadata['containers'][container_id]['image_id']
284
+ def create_time_from_record(record)
285
+ time = if @use_journal
286
+ record['_SOURCE_REALTIME_TIMESTAMP'].nil? ? record['_SOURCE_REALTIME_TIMESTAMP'] : record['__REALTIME_TIMESTAMP']
287
+ else
288
+ record['time']
268
289
  end
290
+ (time.nil? || time.chop.empty?) ? Time.now : Time.parse(time)
291
+ end
269
292
 
270
- metadata['kubernetes'].merge!(pod_metadata) if pod_metadata
271
- metadata['kubernetes'].delete('containers')
272
- end
273
- metadata
293
+ def filter_stream(tag, es)
294
+ es
274
295
  end
275
296
 
276
- def create_time_from_record(record, internal_time)
277
- time_key = @time_fields.detect{ |ii| record.has_key?(ii) }
278
- time = record[time_key]
279
- if time.nil? || time.chop.empty?
280
- return internal_time
281
- end
282
- if ['_SOURCE_REALTIME_TIMESTAMP', '__REALTIME_TIMESTAMP'].include?(time_key)
283
- timei= time.to_i
284
- return Time.at(timei / 1000000, timei % 1000000)
297
+ def filter_stream_from_files(tag, es)
298
+ new_es = Fluent::MultiEventStream.new
299
+
300
+ match_data = tag.match(@tag_to_kubernetes_name_regexp_compiled)
301
+ batch_miss_cache = {}
302
+ if match_data
303
+ container_id = match_data['docker_id']
304
+ metadata = {
305
+ 'docker' => {
306
+ 'container_id' => container_id
307
+ },
308
+ 'kubernetes' => get_metadata_for_record(match_data, container_id, create_time_from_record(es.first[1]), batch_miss_cache)
309
+ }
285
310
  end
286
- return Time.parse(time)
311
+
312
+ es.each { |time, record|
313
+ record = merge_json_log(record) if @merge_json_log
314
+
315
+ record = record.merge(Marshal.load(Marshal.dump(metadata))) if metadata
316
+
317
+ new_es.add(time, record)
318
+ }
319
+ dump_stats
320
+ new_es
287
321
  end
288
322
 
289
- def filter_stream(tag, es)
290
- return es if (es.respond_to?(:empty?) && es.empty?) || !es.is_a?(Fluent::EventStream)
323
+ def filter_stream_from_journal(tag, es)
291
324
  new_es = Fluent::MultiEventStream.new
292
- tag_match_data = tag.match(@tag_to_kubernetes_name_regexp_compiled) unless @use_journal
293
- tag_metadata = nil
294
325
  batch_miss_cache = {}
295
- es.each do |time, record|
296
- if tag_match_data && tag_metadata.nil?
297
- tag_metadata = get_metadata_for_record(tag_match_data['namespace'], tag_match_data['pod_name'], tag_match_data['container_name'],
298
- tag_match_data['docker_id'], create_time_from_record(record, time), batch_miss_cache)
299
- end
300
- metadata = Marshal.load(Marshal.dump(tag_metadata)) if tag_metadata
301
- if (@use_journal || @use_journal.nil?) &&
302
- (j_metadata = get_metadata_for_journal_record(record, time, batch_miss_cache))
303
- metadata = j_metadata
326
+ es.each { |time, record|
327
+ record = merge_json_log(record) if @merge_json_log
328
+ metadata = nil
329
+ if record.has_key?('CONTAINER_NAME') && record.has_key?('CONTAINER_ID_FULL')
330
+ metadata = record['CONTAINER_NAME'].match(@container_name_to_kubernetes_regexp_compiled) do |match_data|
331
+ container_id = record['CONTAINER_ID_FULL']
332
+ metadata = {
333
+ 'docker' => {
334
+ 'container_id' => container_id
335
+ },
336
+ 'kubernetes' => get_metadata_for_record(match_data, container_id, create_time_from_record(record), batch_miss_cache)
337
+ }
338
+
339
+ metadata
340
+ end
341
+ unless metadata
342
+ log.debug "Error: could not match CONTAINER_NAME from record #{record}"
343
+ @stats.bump(:container_name_match_failed)
344
+ end
345
+ elsif record.has_key?('CONTAINER_NAME') && record['CONTAINER_NAME'].start_with?('k8s_')
346
+ log.debug "Error: no container name and id in record #{record}"
347
+ @stats.bump(:container_name_id_missing)
304
348
  end
305
- if @lookup_from_k8s_field && record.has_key?('kubernetes') && record.has_key?('docker') &&
306
- record['kubernetes'].respond_to?(:has_key?) && record['docker'].respond_to?(:has_key?) &&
307
- record['kubernetes'].has_key?('namespace_name') &&
308
- record['kubernetes'].has_key?('pod_name') &&
309
- record['kubernetes'].has_key?('container_name') &&
310
- record['docker'].has_key?('container_id') &&
311
- (k_metadata = get_metadata_for_record(record['kubernetes']['namespace_name'], record['kubernetes']['pod_name'],
312
- record['kubernetes']['container_name'], record['docker']['container_id'],
313
- create_time_from_record(record, time), batch_miss_cache))
314
- metadata = k_metadata
349
+
350
+ if metadata
351
+ record = record.merge(metadata)
315
352
  end
316
353
 
317
- record = record.merge(metadata) if metadata
318
354
  new_es.add(time, record)
319
- end
355
+ }
356
+
320
357
  dump_stats
321
358
  new_es
322
359
  end
323
360
 
324
- def get_metadata_for_journal_record(record, time, batch_miss_cache)
325
- metadata = nil
326
- if record.has_key?('CONTAINER_NAME') && record.has_key?('CONTAINER_ID_FULL')
327
- metadata = record['CONTAINER_NAME'].match(@container_name_to_kubernetes_regexp_compiled) do |match_data|
328
- get_metadata_for_record(match_data['namespace'], match_data['pod_name'], match_data['container_name'],
329
- record['CONTAINER_ID_FULL'], create_time_from_record(record, time), batch_miss_cache)
330
- end
331
- unless metadata
332
- log.debug "Error: could not match CONTAINER_NAME from record #{record}"
333
- @stats.bump(:container_name_match_failed)
361
+ def merge_json_log(record)
362
+ if record.has_key?(@merge_json_log_key)
363
+ value = record[@merge_json_log_key].strip
364
+ if value[0].eql?('{') && value[-1].eql?('}')
365
+ begin
366
+ record = JSON.parse(value).merge(record)
367
+ unless @preserve_json_log
368
+ record.delete(@merge_json_log_key)
369
+ end
370
+ rescue JSON::ParserError=>e
371
+ @stats.bump(:merge_json_parse_errors)
372
+ log.debug(e)
373
+ end
334
374
  end
335
- elsif record.has_key?('CONTAINER_NAME') && record['CONTAINER_NAME'].start_with?('k8s_')
336
- log.debug "Error: no container name and id in record #{record}"
337
- @stats.bump(:container_name_id_missing)
338
375
  end
339
- metadata
376
+ record
340
377
  end
341
378
 
342
379
  def de_dot!(h)