fluent-plugin-json-in-json-2 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e99746f88495ff8ed13fd6d295a4522cc641172c
4
+ data.tar.gz: 870714d1b589915f6642de346c7ca21f19f7a0cb
5
+ SHA512:
6
+ metadata.gz: f0ebd7c9647c3e36afa83c732929339c8ddab29b5d02b592799e76687024842f5f51c15cc29640a2a4397577290c128f8bbfe798d087a7e25a40ae2f5f51f38b
7
+ data.tar.gz: 4ee7b4768620d0179f60e63776b631207b63650d766de565a689ead87ac75a8deda9ed913f2b0f234a29f05ef98a0bb42abf4437b63b4f9339bcffddd78c81e6
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fluent-plugin-json-in-json.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # fluent-plugin-json-in-json
2
+
3
+ ## Requirements
4
+
5
+ | fluent-plugin-json-in-json| Fluentd | Ruby |
6
+ |---------------------------|-------------|--------|
7
+ | >= 1.0.0 | >= v0.14.0 | >= 2.1 |
8
+ | < 1.0.0 | >= v0.12.0 | >= 1.9 |
9
+
10
+ This fluentd parser plugin parses JSON log lines with nested JSON strings. For
11
+ example, given a docker log of ``{"log": "{\"foo\": \"bar\"}"}``, the log record
12
+ will be parsed into ``{:log => { :foo => "bar" }}``.
13
+
14
+ ## Installation
15
+
16
+ Add this line to your application's Gemfile:
17
+
18
+ gem 'fluent-plugin-json-in-json'
19
+
20
+ And then execute:
21
+
22
+ $ bundle
23
+
24
+ Or install it yourself as:
25
+
26
+ $ gem install fluent-plugin-json-in-json
27
+
28
+
29
+ ## Usage
30
+
31
+ ```
32
+ <source>
33
+ type tail
34
+ path /var/lib/docker/containers/*/*-json.log
35
+ pos_file /var/log/fluentd-docker.pos
36
+ time_format %Y-%m-%dT%H:%M:%S
37
+ tag docker.*
38
+ format json_in_json
39
+ read_from_head true
40
+ </source>
41
+ ```
42
+
43
+ ## Contributing
44
+
45
+ 1. Fork it
46
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
47
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
48
+ 4. Push to the branch (`git push origin my-new-feature`)
49
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ t.test_files = FileList['test/test*.rb']
7
+ t.verbose = true
8
+ t.warning = true
9
+ t.ruby_opts = ['-Eascii-8bit:ascii-8bit']
10
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ Gem::Specification.new do |spec|
3
+ spec.name = "fluent-plugin-json-in-json-2"
4
+ spec.version = "1.0.0"
5
+ spec.authors = ["Gavin M. Roy", "Arcadiy Ivanov", "Alik Khilazhev"]
6
+ spec.email = ["gavinmroy@gmail.com", "arcadiy@ivanov.biz", "alikhil@mail.ru"]
7
+ spec.description = %q{Parser plugin that parses JSON attributes with JSON strings in them}
8
+ spec.summary = %q{Parser plugin that parses JSON attributes with JSON strings in them}
9
+ spec.homepage = "https://github.com/arcivanov/fluent-plugin-json-in-json"
10
+ spec.license = "BSD"
11
+
12
+ spec.files = `git ls-files`.split($/)
13
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
14
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
15
+ spec.require_paths = ["lib"]
16
+
17
+ spec.add_runtime_dependency 'fluentd', ['>= 0.14.0', '< 2']
18
+ spec.add_runtime_dependency 'yajl-ruby', '~> 1.0'
19
+
20
+ spec.add_development_dependency 'rake', '~> 12.3'
21
+ spec.add_development_dependency 'bundler', '~> 1.16'
22
+
23
+ spec.add_development_dependency 'test-unit', ['~> 3.2']
24
+ spec.add_development_dependency 'test-unit-rr', ['~> 1.0']
25
+ end
@@ -0,0 +1,46 @@
1
+ require 'fluent/parser'
2
+ require 'yajl'
3
+
4
+ module Fluent
5
+ module Plugin
6
+ class JSONInJSONParser < Parser
7
+ Plugin.register_parser('json_in_json', self)
8
+
9
+ config_set_default :time_key, 'time'
10
+ config_set_default :time_type, :float
11
+
12
+ def configure(conf)
13
+ if conf.has_key?('time_format')
14
+ conf['time_type'] ||= 'string'
15
+ end
16
+
17
+ super
18
+ end
19
+
20
+ def parse(text)
21
+ record = Yajl.load(text)
22
+
23
+ values = Hash.new
24
+
25
+ record.each do |k, v|
26
+ if v.is_a?(String) && /^\s*(\{|\[)/ =~ v
27
+ deserialized = Yajl.load(v)
28
+ if deserialized.is_a?(Hash)
29
+ values.merge!(deserialized)
30
+ record.delete k
31
+ elsif deserialized.is_a?(Array)
32
+ values[k] = deserialized
33
+ end
34
+ end
35
+ end
36
+ record.merge!(values)
37
+
38
+ time, record = convert_values(parse_time(record), record)
39
+
40
+ yield time, record
41
+ rescue Yajl::ParseError
42
+ yield nil, nil
43
+ end
44
+ end
45
+ end
46
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,128 @@
1
+ # Some tests use Hash instead of Element for configure.
2
+ # We should rewrite these tests in the future and remove this ad-hoc code
3
+ class Hash
4
+ def corresponding_proxies
5
+ @corresponding_proxies ||= []
6
+ end
7
+
8
+ def to_masked_element
9
+ self
10
+ end
11
+ end
12
+
13
+ require 'rr'
14
+ require 'test/unit'
15
+ require 'test/unit/rr'
16
+ require 'fileutils'
17
+ require 'fluent/config/element'
18
+ require 'fluent/log'
19
+ require 'fluent/test'
20
+ require 'fluent/test/helpers'
21
+ require 'fluent/plugin/base'
22
+ require 'fluent/plugin_id'
23
+ require 'fluent/plugin_helper'
24
+ require 'fluent/msgpack_factory'
25
+ require 'fluent/time'
26
+ require 'serverengine'
27
+
28
+ module Fluent
29
+ module Plugin
30
+ class TestBase < Base
31
+ # a base plugin class, but not input nor output
32
+ # mainly for helpers and owned plugins
33
+ include PluginId
34
+ include PluginLoggerMixin
35
+ include PluginHelper::Mixin
36
+ end
37
+ end
38
+ end
39
+
40
+ unless defined?(Test::Unit::AssertionFailedError)
41
+ class Test::Unit::AssertionFailedError < StandardError
42
+ end
43
+ end
44
+
45
+ include Fluent::Test::Helpers
46
+
47
+ def unused_port(num = 1, protocol: :tcp, bind: "0.0.0.0")
48
+ case protocol
49
+ when :tcp
50
+ unused_port_tcp(num)
51
+ when :udp
52
+ unused_port_udp(num, bind: bind)
53
+ else
54
+ raise ArgumentError, "unknown protocol: #{protocol}"
55
+ end
56
+ end
57
+
58
+ def unused_port_tcp(num = 1)
59
+ ports = []
60
+ sockets = []
61
+ num.times do
62
+ s = TCPServer.open(0)
63
+ sockets << s
64
+ ports << s.addr[1]
65
+ end
66
+ sockets.each{|s| s.close }
67
+ if num == 1
68
+ return ports.first
69
+ else
70
+ return *ports
71
+ end
72
+ end
73
+
74
+ PORT_RANGE_AVAILABLE = (1024...65535)
75
+
76
+ def unused_port_udp(num = 1, bind: "0.0.0.0")
77
+ family = IPAddr.new(IPSocket.getaddress(bind)).ipv4? ? ::Socket::AF_INET : ::Socket::AF_INET6
78
+ ports = []
79
+ sockets = []
80
+ while ports.size < num
81
+ port = rand(PORT_RANGE_AVAILABLE)
82
+ u = UDPSocket.new(family)
83
+ if (u.bind(bind, port) rescue nil)
84
+ ports << port
85
+ sockets << u
86
+ else
87
+ u.close
88
+ end
89
+ end
90
+ sockets.each{|s| s.close }
91
+ if num == 1
92
+ return ports.first
93
+ else
94
+ return *ports
95
+ end
96
+ end
97
+
98
+ def waiting(seconds, logs: nil, plugin: nil)
99
+ begin
100
+ Timeout.timeout(seconds) do
101
+ yield
102
+ end
103
+ rescue Timeout::Error
104
+ if logs
105
+ STDERR.print(*logs)
106
+ elsif plugin
107
+ STDERR.print(*plugin.log.out.logs)
108
+ end
109
+ raise
110
+ end
111
+ end
112
+
113
+ def ipv6_enabled?
114
+ require 'socket'
115
+
116
+ begin
117
+ TCPServer.open("::1", 0)
118
+ true
119
+ rescue
120
+ false
121
+ end
122
+ end
123
+
124
+ dl_opts = {}
125
+ dl_opts[:log_level] = ServerEngine::DaemonLogger::WARN
126
+ logdev = Fluent::Test::DummyLogDevice.new
127
+ logger = ServerEngine::DaemonLogger.new(logdev, dl_opts)
128
+ $log ||= Fluent::Log.new(logger)
@@ -0,0 +1,54 @@
1
+ require_relative 'helper'
2
+ require 'fluent/test/driver/parser'
3
+ require 'fluent/plugin/parser_json_in_json'
4
+
5
+ class JsonInJsonParserTest < ::Test::Unit::TestCase
6
+ def setup
7
+ Fluent::Test.setup
8
+ @parser = Fluent::Test::Driver::Parser.new(Fluent::Plugin::JSONInJSONParser)
9
+ end
10
+
11
+ def test_parse_float_time()
12
+ @parser.configure({})
13
+ @parser.instance.parse('{"time":1362020400,"host":"192.168.0.1","size":777,"method":"PUT","log_json":" {\\"field1\\":\\"field1 value\\",\\"field2\\":40}"}') { |time, record|
14
+ assert_equal(event_time('2013-02-28 12:00:00 +0900').to_f, time.to_f)
15
+ assert_equal({
16
+ 'host' => '192.168.0.1',
17
+ 'size' => 777,
18
+ 'method' => 'PUT',
19
+ 'field1' => 'field1 value',
20
+ 'field2' => 40
21
+ }, record)
22
+ }
23
+ @parser.instance.parse('{"time":1362020400,"host":"192.168.0.1","size":777,"method":"PUT","log_json":"{\\"field1\\":"}') { |time, record|
24
+ assert_equal(event_time('2013-02-28 12:00:00 +0900').to_f, time.to_f)
25
+ assert_equal({
26
+ 'host' => '192.168.0.1',
27
+ 'size' => 777,
28
+ 'method' => 'PUT',
29
+ 'log_json' => '{"field1":'
30
+ }, record)
31
+ }
32
+ @parser.instance.parse('{"time":1362020400,"host":"192.168.0.1","size":777,"method":"PUT","log_json":"[\\"val1\\",\\"val2\\"]"') { |time, record|
33
+ assert_equal(event_time('2013-02-28 12:00:00 +0900').to_f, time.to_f)
34
+ assert_equal({
35
+ 'host' => '192.168.0.1',
36
+ 'size' => 777,
37
+ 'method' => 'PUT',
38
+ 'log_json' => ['val1', 'val2']
39
+ }, record)
40
+ }
41
+ end
42
+
43
+ def test_parse_string_time()
44
+ @parser.configure('time_format' => '%Y-%m-%dT%H:%M:%S.%NZ', 'keep_time_key' => 'true')
45
+ @parser.instance.parse('{"log":"2018-06-26 13:20:44.075 INFO --- [pool-8-thread-3] outgoing","stream":"stdout","time":"2018-06-26T13:20:44.076022960Z"}') { |time, record|
46
+ assert_equal(event_time('2018-06-26 13:20:44.076022960 -0400').to_f, time.to_f)
47
+ assert_equal({
48
+ 'log'=>'2018-06-26 13:20:44.075 INFO --- [pool-8-thread-3] outgoing',
49
+ 'stream'=>'stdout',
50
+ 'time'=>'2018-06-26T13:20:44.076022960Z'
51
+ }, record)
52
+ }
53
+ end
54
+ end
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-json-in-json-2
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Gavin M. Roy
8
+ - Arcadiy Ivanov
9
+ - Alik Khilazhev
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2019-06-17 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: fluentd
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: 0.14.0
22
+ - - "<"
23
+ - !ruby/object:Gem::Version
24
+ version: '2'
25
+ type: :runtime
26
+ prerelease: false
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 0.14.0
32
+ - - "<"
33
+ - !ruby/object:Gem::Version
34
+ version: '2'
35
+ - !ruby/object:Gem::Dependency
36
+ name: yajl-ruby
37
+ requirement: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.0'
42
+ type: :runtime
43
+ prerelease: false
44
+ version_requirements: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '1.0'
49
+ - !ruby/object:Gem::Dependency
50
+ name: rake
51
+ requirement: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '12.3'
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '12.3'
63
+ - !ruby/object:Gem::Dependency
64
+ name: bundler
65
+ requirement: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '1.16'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '1.16'
77
+ - !ruby/object:Gem::Dependency
78
+ name: test-unit
79
+ requirement: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '3.2'
84
+ type: :development
85
+ prerelease: false
86
+ version_requirements: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '3.2'
91
+ - !ruby/object:Gem::Dependency
92
+ name: test-unit-rr
93
+ requirement: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '1.0'
98
+ type: :development
99
+ prerelease: false
100
+ version_requirements: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '1.0'
105
+ description: Parser plugin that parses JSON attributes with JSON strings in them
106
+ email:
107
+ - gavinmroy@gmail.com
108
+ - arcadiy@ivanov.biz
109
+ - alikhil@mail.ru
110
+ executables: []
111
+ extensions: []
112
+ extra_rdoc_files: []
113
+ files:
114
+ - ".gitignore"
115
+ - Gemfile
116
+ - README.md
117
+ - Rakefile
118
+ - fluent-plugin-json-in-json.gemspec
119
+ - lib/fluent/plugin/parser_json_in_json.rb
120
+ - test/helper.rb
121
+ - test/test_parser.rb
122
+ homepage: https://github.com/arcivanov/fluent-plugin-json-in-json
123
+ licenses:
124
+ - BSD
125
+ metadata: {}
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubyforge_project:
142
+ rubygems_version: 2.5.2
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: Parser plugin that parses JSON attributes with JSON strings in them
146
+ test_files:
147
+ - test/helper.rb
148
+ - test/test_parser.rb