fluent-plugin-grok-parser 1.0.1 → 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: abba93dea7171b49379a1c09b97e93775a71c60f07975d8be225467ef9a02f3e
4
- data.tar.gz: 53cabdd496599b84fda4a5facebbd93b59be9a5426dfa3142f6b94344411431d
2
+ SHA1:
3
+ metadata.gz: 5c95b3d25597cea368b25f55f45c222a373097ea
4
+ data.tar.gz: b42a68ae28f5ffd37bc9be15b511fc0a0d34fcce
5
5
  SHA512:
6
- metadata.gz: 0fed7231df5bddfe56b57eaa53155afb29e400d16994e3454d80a1f7dde75ff0528fd778137a65d557d71529b1de75f501206f3dfd5220cb5b2b2e86fd44d8ad
7
- data.tar.gz: 37e00405e9c28e9d4bac1fe02ea609e297ea98554d98802f8deff575d5c8104953149dbbc129b00953fb5e162390904af2cf707df76f073bae3e608594fe2052
6
+ metadata.gz: 64f5933dfbee0819f2a26c63a5fd6c586c9013dbead10f7df0af1d9761f56a66545e808cc06493e987b13d0b5fc4ff8d20bd7c9bc46742c0e4d5c23da5d71434
7
+ data.tar.gz: 971aeca2d5f7fb2f8c88ffb8370414f67beab55697218d0504ed2997d3a2b117ff73412ae41045e0f82007ad3e38c51cb9889618f516616999c6c52f78e86ac9
data/README.md CHANGED
@@ -17,9 +17,23 @@ extracts the first IP address that matches in the log.
17
17
  <source>
18
18
  @type tail
19
19
  path /path/to/log
20
+ tag grokked_log
21
+ <parse>
22
+ @type grok
23
+ grok_pattern %{IP:ip_address}
24
+ </parse>
25
+ </source>
26
+ ```
27
+
28
+ You can also use Fluentd v0.12 style:
29
+
30
+ ```aconf
31
+ <source>
32
+ @type tail
33
+ path /path/to/log
34
+ tag grokked_log
20
35
  format grok
21
36
  grok_pattern %{IP:ip_address}
22
- tag grokked_log
23
37
  </source>
24
38
  ```
25
39
 
@@ -29,6 +43,30 @@ extracts the first IP address that matches in the log.
29
43
  <source>
30
44
  @type tail
31
45
  path /path/to/log
46
+ tag grokked_log
47
+ <parse>
48
+ @type grok
49
+ <grok>
50
+ pattern %{COMBINEDAPACHELOG}
51
+ time_format "%d/%b/%Y:%H:%M:%S %z"
52
+ </grok>
53
+ <grok>
54
+ pattern %{IP:ip_address}
55
+ </grok>
56
+ <grok>
57
+ pattern %{GREEDYDATA:message}
58
+ </grok>
59
+ </parse>
60
+ </source>
61
+ ```
62
+
63
+ You can also use Fluentd v0.12 style:
64
+
65
+ ```aconf
66
+ <source>
67
+ @type tail
68
+ path /path/to/log
69
+ tag grokked_log
32
70
  format grok
33
71
  <grok>
34
72
  pattern %{COMBINEDAPACHELOG}
@@ -40,7 +78,6 @@ extracts the first IP address that matches in the log.
40
78
  <grok>
41
79
  pattern %{GREEDYDATA:message}
42
80
  </grok>
43
- tag grokked_log
44
81
  </source>
45
82
  ```
46
83
 
@@ -48,6 +85,21 @@ extracts the first IP address that matches in the log.
48
85
 
49
86
  You can parse multiple line text.
50
87
 
88
+ ```aconf
89
+ <source>
90
+ @type tail
91
+ path /path/to/log
92
+ tag grokked_log
93
+ <parse>
94
+ @type multiline_grok
95
+ grok_pattern %{IP:ip_address}%{GREEDYDATA:message}
96
+ multiline_start_regexp /^[^\s]/
97
+ </parse>
98
+ </source>
99
+ ```
100
+
101
+ You can also use Fluentd v0.12 style:
102
+
51
103
  ```aconf
52
104
  <source>
53
105
  @type tail
@@ -61,6 +113,22 @@ You can parse multiple line text.
61
113
 
62
114
  You can use multiple grok patterns to parse your data.
63
115
 
116
+ ```aconf
117
+ <source>
118
+ @type tail
119
+ path /path/to/log
120
+ tag grokked_log
121
+ <parse>
122
+ @type multiline_grok
123
+ <grok>
124
+ pattern Started %{WORD:verb} "%{URIPATH:pathinfo}" for %{IP:ip} at %{TIMESTAMP_ISO8601:timestamp}\nProcessing by %{WORD:controller}#%{WORD:action} as %{WORD:format}%{DATA:message}Completed %{NUMBER:response} %{WORD} in %{NUMBER:elapsed} (%{DATA:elapsed_details})
125
+ </grok>
126
+ </parse>
127
+ </source>
128
+ ```
129
+
130
+ You can also use Fluentd v0.12 style:
131
+
64
132
  ```aconf
65
133
  <source>
66
134
  @type tail
@@ -105,9 +173,11 @@ This is what the `custom_pattern_path` parameter is for.
105
173
  <source>
106
174
  @type tail
107
175
  path /path/to/log
108
- format grok
109
- grok_pattern %{MY_SUPER_PATTERN}
110
- custom_pattern_path /path/to/my_pattern
176
+ <parse>
177
+ @type grok
178
+ grok_pattern %{MY_SUPER_PATTERN}
179
+ custom_pattern_path /path/to/my_pattern
180
+ </parse>
111
181
  </source>
112
182
  ```
113
183
 
@@ -158,6 +228,10 @@ Here is a sample config using the Grok parser with `in_tail` and the `types` par
158
228
  </source>
159
229
  ```
160
230
 
231
+ ## Notice
232
+
233
+ If you want to use this plugin with Fluentd v0.12.x or earlier, you can use this plugin version v1.0.0.
234
+
161
235
  ## License
162
236
 
163
237
  Apache 2.0 License
data/Rakefile CHANGED
@@ -9,8 +9,8 @@ desc 'Run test_unit based test'
9
9
  Rake::TestTask.new(:base_test) do |t|
10
10
  t.libs << "test"
11
11
  t.test_files = (Dir["test/test_*.rb"] + Dir["test/plugin/test_*.rb"] - ["helper.rb"]).sort
12
- t.verbose = false
13
- t.warning = false
12
+ t.verbose = true
13
+ # t.warning = false
14
14
  end
15
15
 
16
16
  desc 'Import patterns from submodules'
@@ -20,42 +20,6 @@ task 'patterns:import' do
20
20
  cp(pattern, "patterns/", verbose: true)
21
21
  end
22
22
  end
23
-
24
- # copied from "./lib/fluent/plugin/grok"
25
- pattern_re =
26
- /%\{ # match '%{' not prefixed with '\'
27
- (?<name> # match the pattern name
28
- (?<pattern>[A-z0-9]+)
29
- (?::(?<subname>[@\[\]A-z0-9_:.-]+?)
30
- (?::(?<type>(?:string|bool|integer|float|int|
31
- time(?::.+)?|
32
- array(?::.)?)))?)?
33
- )
34
- \}/x
35
-
36
- Dir.glob("patterns/*") do |pattern_file|
37
- new_lines = ""
38
- File.readlines(pattern_file).each do |line|
39
- case
40
- when line.strip.empty?
41
- new_lines << line
42
- when line.start_with?("#")
43
- new_lines << line
44
- else
45
- name, pattern = line.split(/\s+/, 2)
46
- new_pattern = pattern.gsub(pattern_re) do |m|
47
- matched = $~
48
- if matched[:type] == "int"
49
- "%{#{matched[:pattern]}:#{matched[:subname]}:integer}"
50
- else
51
- m
52
- end
53
- end
54
- new_lines << "#{name} #{new_pattern}"
55
- end
56
- end
57
- File.write(pattern_file, new_lines)
58
- end
59
23
  end
60
24
 
61
25
  task :default => [:test, :build]
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "fluent-plugin-grok-parser"
7
- spec.version = "1.0.1"
7
+ spec.version = "2.0.0"
8
8
  spec.authors = ["kiyoto"]
9
9
  spec.email = ["kiyoto@treasure-data.com"]
10
10
  spec.summary = %q{Fluentd plugin to support Logstash-inspired Grok format for parsing logs}
@@ -19,5 +19,5 @@ Gem::Specification.new do |spec|
19
19
  spec.add_development_dependency "bundler"
20
20
  spec.add_development_dependency "rake"
21
21
  spec.add_development_dependency "test-unit", ">=3.1.5"
22
- spec.add_runtime_dependency "fluentd", [">=0.10.58", "~>0.12.0"]
22
+ spec.add_runtime_dependency "fluentd", ">=0.14.6"
23
23
  end
@@ -82,7 +82,7 @@ module Fluent
82
82
  replacement_pattern = "(?<#{m["subname"]}>#{curr_pattern})"
83
83
  type_map[m["subname"]] = m["type"] || "string"
84
84
  else
85
- replacement_pattern = "(?:#{curr_pattern})"
85
+ replacement_pattern = curr_pattern
86
86
  end
87
87
  pattern.sub!(m[0]) do |s| replacement_pattern end
88
88
  end
@@ -1,19 +1,11 @@
1
1
  require "fluent/plugin/grok"
2
2
 
3
3
  module Fluent
4
- class TextParser
4
+ module Plugin
5
5
  class GrokPatternNotFoundError < Exception; end
6
6
 
7
7
  class GrokParser < Parser
8
- Plugin.register_parser('grok', self)
9
-
10
- # For fluentd v0.12.16 or earlier
11
- class << self
12
- unless method_defined?(:desc)
13
- def desc(description)
14
- end
15
- end
16
- end
8
+ Fluent::Plugin.register_parser('grok', self)
17
9
 
18
10
  desc 'The format of the time field.'
19
11
  config_param :time_format, :string, :default => nil
@@ -38,11 +30,11 @@ module Fluent
38
30
  end
39
31
 
40
32
  if @custom_pattern_path
41
- if Dir.exists? @custom_pattern_path
33
+ if Dir.exist? @custom_pattern_path
42
34
  Dir.glob(@custom_pattern_path + '/*') do |pattern_file_path|
43
35
  @grok.add_patterns_from_file(pattern_file_path)
44
36
  end
45
- elsif File.exists? @custom_pattern_path
37
+ elsif File.exist? @custom_pattern_path
46
38
  @grok.add_patterns_from_file(@custom_pattern_path)
47
39
  end
48
40
  end
@@ -50,27 +42,16 @@ module Fluent
50
42
  @grok.setup
51
43
  end
52
44
 
53
- def parse(text, &block)
54
- if block_given?
55
- @grok.parsers.each do |parser|
56
- parser.parse(text) do |time, record|
57
- if time and record
58
- yield time, record
59
- return
60
- end
61
- end
62
- end
63
- yield @default_parser.parse(text)
64
- else
65
- @grok.parsers.each do |parser|
66
- parser.parse(text) do |time, record|
67
- if time and record
68
- return time, record
69
- end
45
+ def parse(text)
46
+ @grok.parsers.each do |parser|
47
+ parser.parse(text) do |time, record|
48
+ if time and record
49
+ yield time, record
50
+ return
70
51
  end
71
52
  end
72
- return @default_parser.parse(text)
73
53
  end
54
+ yield @default_parser.parse(text)
74
55
  end
75
56
  end
76
57
  end
@@ -1,9 +1,9 @@
1
1
  require 'fluent/plugin/parser_grok'
2
2
 
3
3
  module Fluent
4
- class TextParser
4
+ module Plugin
5
5
  class MultilineGrokParser < GrokParser
6
- Plugin.register_parser('multiline_grok', self)
6
+ Fluent::Plugin.register_parser('multiline_grok', self)
7
7
 
8
8
  desc 'The regexp to match beginning of multiline'
9
9
  config_param :multiline_start_regexp, :string, :default => nil
@@ -24,22 +24,12 @@ module Fluent
24
24
  @multiline_start_regexp && !!@grok.multiline_start_regexp.match(text)
25
25
  end
26
26
 
27
- def parse(text, &block)
28
- if block_given?
29
- @grok.parsers.each do |parser|
30
- parser.parse(text) do |time, record|
31
- if time and record
32
- yield time, record
33
- return
34
- end
35
- end
36
- end
37
- else
38
- @grok.parsers.each do |parser|
39
- parser.parse(text) do |time, record|
40
- if time and record
41
- return time, record
42
- end
27
+ def parse(text)
28
+ @grok.parsers.each do |parser|
29
+ parser.parse(text) do |time, record|
30
+ if time and record
31
+ yield time, record
32
+ return
43
33
  end
44
34
  end
45
35
  end
data/patterns/aws CHANGED
@@ -1,6 +1,6 @@
1
1
  S3_REQUEST_LINE (?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})
2
2
 
3
- S3_ACCESS_LOG %{WORD:owner} %{NOTSPACE:bucket} \[%{HTTPDATE:timestamp}\] %{IP:clientip} %{NOTSPACE:requester} %{NOTSPACE:request_id} %{NOTSPACE:operation} %{NOTSPACE:key} (?:"%{S3_REQUEST_LINE}"|-) (?:%{INT:response:integer}|-) (?:-|%{NOTSPACE:error_code}) (?:%{INT:bytes:integer}|-) (?:%{INT:object_size:integer}|-) (?:%{INT:request_time_ms:integer}|-) (?:%{INT:turnaround_time_ms:integer}|-) (?:%{QS:referrer}|-) (?:"?%{QS:agent}"?|-) (?:-|%{NOTSPACE:version_id})
3
+ S3_ACCESS_LOG %{WORD:owner} %{NOTSPACE:bucket} \[%{HTTPDATE:timestamp}\] %{IP:clientip} %{NOTSPACE:requester} %{NOTSPACE:request_id} %{NOTSPACE:operation} %{NOTSPACE:key} (?:"%{S3_REQUEST_LINE}"|-) (?:%{INT:response:int}|-) (?:-|%{NOTSPACE:error_code}) (?:%{INT:bytes:int}|-) (?:%{INT:object_size:int}|-) (?:%{INT:request_time_ms:int}|-) (?:%{INT:turnaround_time_ms:int}|-) (?:%{QS:referrer}|-) (?:"?%{QS:agent}"?|-) (?:-|%{NOTSPACE:version_id})
4
4
 
5
5
  ELB_URIPATHPARAM %{URIPATH:path}(?:%{URIPARAM:params})?
6
6
 
@@ -8,7 +8,7 @@ ELB_URI %{URIPROTO:proto}://(?:%{USER}(?::[^@]*)?@)?(?:%{URIHOST:urihost})?(?:%{
8
8
 
9
9
  ELB_REQUEST_LINE (?:%{WORD:verb} %{ELB_URI:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})
10
10
 
11
- ELB_ACCESS_LOG %{TIMESTAMP_ISO8601:timestamp} %{NOTSPACE:elb} %{IP:clientip}:%{INT:clientport:integer} (?:(%{IP:backendip}:?:%{INT:backendport:integer})|-) %{NUMBER:request_processing_time:float} %{NUMBER:backend_processing_time:float} %{NUMBER:response_processing_time:float} %{INT:response:integer} %{INT:backend_response:integer} %{INT:received_bytes:integer} %{INT:bytes:integer} "%{ELB_REQUEST_LINE}"
11
+ ELB_ACCESS_LOG %{TIMESTAMP_ISO8601:timestamp} %{NOTSPACE:elb} %{IP:clientip}:%{INT:clientport:int} (?:(%{IP:backendip}:?:%{INT:backendport:int})|-) %{NUMBER:request_processing_time:float} %{NUMBER:backend_processing_time:float} %{NUMBER:response_processing_time:float} %{INT:response:int} %{INT:backend_response:int} %{INT:received_bytes:int} %{INT:bytes:int} "%{ELB_REQUEST_LINE}"
12
12
 
13
- CLOUDFRONT_ACCESS_LOG (?<timestamp>%{YEAR}-%{MONTHNUM}-%{MONTHDAY}\t%{TIME})\t%{WORD:x_edge_location}\t(?:%{NUMBER:sc_bytes:integer}|-)\t%{IPORHOST:clientip}\t%{WORD:cs_method}\t%{HOSTNAME:cs_host}\t%{NOTSPACE:cs_uri_stem}\t%{NUMBER:sc_status:integer}\t%{GREEDYDATA:referrer}\t%{GREEDYDATA:agent}\t%{GREEDYDATA:cs_uri_query}\t%{GREEDYDATA:cookies}\t%{WORD:x_edge_result_type}\t%{NOTSPACE:x_edge_request_id}\t%{HOSTNAME:x_host_header}\t%{URIPROTO:cs_protocol}\t%{INT:cs_bytes:integer}\t%{GREEDYDATA:time_taken:float}\t%{GREEDYDATA:x_forwarded_for}\t%{GREEDYDATA:ssl_protocol}\t%{GREEDYDATA:ssl_cipher}\t%{GREEDYDATA:x_edge_response_result_type}
13
+ CLOUDFRONT_ACCESS_LOG (?<timestamp>%{YEAR}-%{MONTHNUM}-%{MONTHDAY}\t%{TIME})\t%{WORD:x_edge_location}\t(?:%{NUMBER:sc_bytes:int}|-)\t%{IPORHOST:clientip}\t%{WORD:cs_method}\t%{HOSTNAME:cs_host}\t%{NOTSPACE:cs_uri_stem}\t%{NUMBER:sc_status:int}\t%{GREEDYDATA:referrer}\t%{GREEDYDATA:agent}\t%{GREEDYDATA:cs_uri_query}\t%{GREEDYDATA:cookies}\t%{WORD:x_edge_result_type}\t%{NOTSPACE:x_edge_request_id}\t%{HOSTNAME:x_host_header}\t%{URIPROTO:cs_protocol}\t%{INT:cs_bytes:int}\t%{GREEDYDATA:time_taken:float}\t%{GREEDYDATA:x_forwarded_for}\t%{GREEDYDATA:ssl_protocol}\t%{GREEDYDATA:ssl_cipher}\t%{GREEDYDATA:x_edge_response_result_type}
14
14
 
data/test/helper.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require "fluent/test/driver/input"
2
+ require "fluent/test/driver/parser"
1
3
 
2
4
  def unused_port
3
5
  s = TCPServer.open(0)
@@ -37,11 +37,6 @@ class GrokParserTest < ::Test::Unit::TestCase
37
37
  end
38
38
  end
39
39
 
40
- def test_date
41
- internal_test_grok_pattern("\\[(?<date>%{DATE} %{TIME} (?:AM|PM))\\]", "[2/16/2018 10:19:34 AM]",
42
- nil, { "date" => "2/16/2018 10:19:34 AM" })
43
- end
44
-
45
40
  def test_call_for_grok_pattern_not_found
46
41
  assert_raise Grok::GrokPatternNotFoundError do
47
42
  internal_test_grok_pattern('%{THIS_PATTERN_DOESNT_EXIST}', 'Some stuff at somewhere', nil, {})
@@ -135,15 +130,10 @@ class GrokParserTest < ::Test::Unit::TestCase
135
130
  private
136
131
 
137
132
  def internal_test_grok_pattern(grok_pattern, text, expected_time, expected_record, options = {})
138
- parser = Fluent::Test::ParserTestDriver.new(TextParser::GrokParser).configure({"grok_pattern" => grok_pattern}.merge(options))
139
-
140
- # for the old, return based API
141
- time, record = parser.parse(text)
142
- assert_equal(expected_time, time) if expected_time
143
- assert_equal(expected_record, record)
133
+ d = Fluent::Test::Driver::Parser.new(Fluent::Plugin::GrokParser).configure({"grok_pattern" => grok_pattern}.merge(options))
144
134
 
145
135
  # for the new API
146
- parser.parse(text) {|time, record|
136
+ d.instance.parse(text) {|time, record|
147
137
  assert_equal(expected_time, time) if expected_time
148
138
  assert_equal(expected_record, record)
149
139
  }
@@ -35,7 +35,7 @@ class TcpInputWithGrokTest < Test::Unit::TestCase
35
35
  ]
36
36
 
37
37
  def create_driver(conf)
38
- Fluent::Test::InputTestDriver.new(Fluent::TcpInput).configure(conf)
38
+ Fluent::Test::Driver::Input.new(Fluent::TcpInput).configure(conf)
39
39
  end
40
40
 
41
41
  def test_configure
@@ -83,22 +83,21 @@ class TcpInputWithGrokTest < Test::Unit::TestCase
83
83
 
84
84
  def internal_test_grok(conf, tests)
85
85
  d = create_driver(BASE_CONFIG + conf)
86
- d.run do
86
+ d.run(expect_emits: tests.size) do
87
87
  tests.each {|test|
88
88
  TCPSocket.open('127.0.0.1', PORT) do |s|
89
89
  s.send(test['msg'], 0)
90
90
  end
91
91
  }
92
- sleep 1
93
92
  end
94
93
 
95
- compare_test_result(d.emits, tests)
94
+ compare_test_result(d.events, tests)
96
95
  end
97
96
 
98
- def compare_test_result(emits, tests)
99
- assert_equal(2, emits.size)
100
- emits.each_index {|i|
101
- assert_equal(tests[i]['expected'], emits[i][2]['message'])
97
+ def compare_test_result(events, tests)
98
+ assert_equal(2, events.size)
99
+ events.each_index {|i|
100
+ assert_equal(tests[i]['expected'], events[i][2]['message'])
102
101
  }
103
102
  end
104
103
  end
@@ -21,7 +21,7 @@ MESSAGE
21
21
  ]
22
22
  d = create_driver(conf)
23
23
 
24
- d.parse(text) do |time, record|
24
+ d.instance.parse(text) do |_time, record|
25
25
  assert_equal({ "hostname" => "host1", "message" => message }, record)
26
26
  end
27
27
  end
@@ -44,7 +44,7 @@ TEXT
44
44
  "message2" => "message2",
45
45
  "message3" => "message3"
46
46
  }
47
- d.parse(text) do |time, record|
47
+ d.instance.parse(text) do |_time, record|
48
48
  assert_equal(expected, record)
49
49
  end
50
50
  end
@@ -63,6 +63,6 @@ TEXT
63
63
  private
64
64
 
65
65
  def create_driver(conf)
66
- Fluent::Test::ParserTestDriver.new(TextParser::MultilineGrokParser).configure(conf)
66
+ Fluent::Test::Driver::Parser.new(Fluent::Plugin::MultilineGrokParser).configure(conf)
67
67
  end
68
68
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-grok-parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - kiyoto
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-26 00:00:00.000000000 Z
11
+ date: 2016-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -58,20 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: 0.10.58
62
- - - "~>"
63
- - !ruby/object:Gem::Version
64
- version: 0.12.0
61
+ version: 0.14.6
65
62
  type: :runtime
66
63
  prerelease: false
67
64
  version_requirements: !ruby/object:Gem::Requirement
68
65
  requirements:
69
66
  - - ">="
70
67
  - !ruby/object:Gem::Version
71
- version: 0.10.58
72
- - - "~>"
73
- - !ruby/object:Gem::Version
74
- version: 0.12.0
68
+ version: 0.14.6
75
69
  description:
76
70
  email:
77
71
  - kiyoto@treasure-data.com
@@ -135,7 +129,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
135
129
  version: '0'
136
130
  requirements: []
137
131
  rubyforge_project:
138
- rubygems_version: 2.7.3
132
+ rubygems_version: 2.6.4
139
133
  signing_key:
140
134
  specification_version: 4
141
135
  summary: Fluentd plugin to support Logstash-inspired Grok format for parsing logs