fluent-plugin-kubernetes_metadata_filter 1.2.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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)