fluent-plugin-detect-exceptions 0.0.13 → 0.0.14

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
  SHA256:
3
- metadata.gz: 3fa3afee1139adec8fe98c8406bd72035d844aadf9f1715c01c5db3e45bd7e1c
4
- data.tar.gz: 63b1bba51e317c9931d4904551b64ef94c56b0946bccec6f67edc2311c0bbba2
3
+ metadata.gz: f846941463ccc37acd71280e182d5ae7b0ffd0c95c609bfbfe67de64f86ff478
4
+ data.tar.gz: 1454c4ddae5ba4efa214972f2109f0a71d4984e87b49cefa3541cb87d92d6cc7
5
5
  SHA512:
6
- metadata.gz: 60bbd111f5818ebfa4eda68c87236c3d985df25e66acf12e29f2851f52c4f9e0c74a0d8bbeeb5f077f1b154c4f23fa2bb223ac26f230aff29fdc5d0ddfa69272
7
- data.tar.gz: 85e20f53eb5e72b7062b6d60d724f55edc53997530dce6ca6adc252bb422a7c5466c73dc5370909ec944a52f7097a8d632649b2ed32fb16962689073dfe3415b
6
+ metadata.gz: 585f0de72a4f845f39c0f7e753ca3661ad335b10c18d26930afc19bab246b205cb9d68d1fa506c87e5ea674fc9b1ce51b04c278aed5590d997875ea9b50b21d8
7
+ data.tar.gz: 987c453e5667c1736507500859a1f46612a56451f791afaf194574380afd31527da73f9ebd87de53faf2c40e5929ce0cdd0f2c3fbd0c9d4c89a2137bad40a9a1
data/README.rdoc CHANGED
@@ -42,6 +42,16 @@ will also install and configure the gem.
42
42
 
43
43
  The plugin supports the following parameters:
44
44
 
45
+ === Required
46
+
47
+ [remove_tag_prefix] The prefix to remove from the input tag when outputting
48
+ a record. A prefix has to be a complete tag part.
49
+ Example: If remove_tag_prefix is set to 'foo', the input
50
+ tag foo.bar.baz is transformed to bar.baz and the input tag
51
+ 'foofoo.bar' is not modified.
52
+
53
+ === Optional
54
+
45
55
  [message] Name of the field in the JSON record that contains the
46
56
  single-line log messages that shall be scanned for exceptions.
47
57
  If this is set to '', the plugin will try 'message' and 'log',
@@ -49,12 +59,6 @@ The plugin supports the following parameters:
49
59
  This parameter is only applicable to structured (JSON) log streams.
50
60
  Default: ''.
51
61
 
52
- [remove_tag_prefix] The prefix to remove from the input tag when outputting
53
- a record. A prefix has to be a complete tag part.
54
- Example: If remove_tag_prefix is set to 'foo', the input
55
- tag foo.bar.baz is transformed to bar.baz and the input tag
56
- 'foofoo.bar' is not modified. Default: empty string.
57
-
58
62
  [languages] A list of language for which exception stack traces shall be
59
63
  detected. The values in the list can be separated by commas or
60
64
  written as JSON list.
@@ -66,6 +70,12 @@ The plugin supports the following parameters:
66
70
  forwarded. If not set, incomplete exceptions stacks
67
71
  are not flushed.
68
72
 
73
+ [force_line_breaks] Force line breaks between each lines when comibining exception stacks.
74
+ This is useful if your exception is formatted
75
+ as a single line. i.e., logs retrieved from the docker's
76
+ logging driver don't have any line break.
77
+ Default: false.
78
+
69
79
  [max_lines] Maximum number of lines in a detected exception stack trace.
70
80
  If this maximum number is exceeded, the exception stack trace
71
81
  that has been detected so far will be output as a single
@@ -11,7 +11,7 @@ eos
11
11
  gem.homepage = \
12
12
  'https://github.com/GoogleCloudPlatform/fluent-plugin-detect-exceptions'
13
13
  gem.license = 'Apache-2.0'
14
- gem.version = '0.0.13'
14
+ gem.version = '0.0.14'
15
15
  gem.authors = ['Stackdriver Agents']
16
16
  gem.email = ['stackdriver-agents@google.com']
17
17
  gem.required_ruby_version = Gem::Requirement.new('>= 2.0')
@@ -256,20 +256,22 @@ module Fluent
256
256
  # message_field may contain the empty string. In this case, the
257
257
  # TraceAccumulator 'learns' the field name from the first record by checking
258
258
  # for some pre-defined common field names of text logs.
259
- # The named parameters max_lines and max_bytes limit the maximum amount
259
+ # The option parameter can be used to pass the following parameters:
260
+ # force_line_breaks adds line breaks when combining exception stacks
261
+ # max_lines and max_bytes limit the maximum amount
260
262
  # of data to be buffered. The default value 0 indicates 'no limit'.
261
- def initialize(message_field, languages, max_lines: 0, max_bytes: 0,
262
- &emit_callback)
263
+ def initialize(message_field, languages, **options, &emit_callback)
263
264
  @exception_detector = Fluent::ExceptionDetector.new(*languages)
264
- @max_lines = max_lines
265
- @max_bytes = max_bytes
266
265
  @message_field = message_field
266
+ @force_line_breaks = options[:force_line_breaks] || false
267
+ @max_lines = options[:max_lines] || 0
268
+ @max_bytes = options[:max_bytes] || 0
269
+ @emit = emit_callback
267
270
  @messages = []
268
271
  @buffer_start_time = Time.now
269
272
  @buffer_size = 0
270
273
  @first_record = nil
271
274
  @first_timestamp = nil
272
- @emit = emit_callback
273
275
  end
274
276
 
275
277
  def push(time_sec, record)
@@ -360,8 +362,14 @@ module Fluent
360
362
  @buffer_start_time = Time.now
361
363
  end
362
364
  unless message.nil?
363
- @messages << message
364
- @buffer_size += message.length
365
+ message_with_line_break =
366
+ if @force_line_breaks && !@messages.empty? && !message.include?("\n")
367
+ "\n" + message
368
+ else
369
+ message
370
+ end
371
+ @messages << message_with_line_break
372
+ @buffer_size += message_with_line_break.length
365
373
  end
366
374
  end
367
375
  end
@@ -22,14 +22,16 @@ module Fluent
22
22
  # an exception stack trace, they forwarded as a single, combined JSON
23
23
  # object. Otherwise, the input log data is forwarded as is.
24
24
  class DetectExceptionsOutput < Output
25
+ desc 'The prefix to be removed from the input tag when outputting a record.'
26
+ config_param :remove_tag_prefix, :string
25
27
  desc 'The field which contains the raw message text in the input JSON data.'
26
28
  config_param :message, :string, default: ''
27
- desc 'The prefix to be removed from the input tag when outputting a record.'
28
- config_param :remove_tag_prefix, :string, default: ''
29
29
  desc 'The interval of flushing the buffer for multiline format.'
30
30
  config_param :multiline_flush_interval, :time, default: nil
31
31
  desc 'Programming languages for which to detect exceptions. Default: all.'
32
32
  config_param :languages, :array, value_type: :string, default: []
33
+ desc 'Force live breaks when combining exception stacks. Default: false.'
34
+ config_param :force_line_breaks, :bool, default: false
33
35
  desc 'Maximum number of lines to flush (0 means no limit). Default: 1000.'
34
36
  config_param :max_lines, :integer, default: 1000
35
37
  desc 'Maximum number of bytes to flush (0 means no limit). Default: 0.'
@@ -91,9 +93,13 @@ module Fluent
91
93
  unless @accumulators.key?(log_id)
92
94
  out_tag = tag.sub(/^#{Regexp.escape(@remove_tag_prefix)}\./, '')
93
95
  @accumulators[log_id] =
94
- Fluent::TraceAccumulator.new(@message, @languages,
95
- max_lines: @max_lines,
96
- max_bytes: @max_bytes) do |t, r|
96
+ Fluent::TraceAccumulator.new(
97
+ @message,
98
+ @languages,
99
+ force_line_breaks: @force_line_breaks,
100
+ max_lines: @max_lines,
101
+ max_bytes: @max_bytes
102
+ ) do |t, r|
97
103
  router.emit(out_tag, t, r)
98
104
  end
99
105
  end
@@ -28,6 +28,8 @@ END
28
28
 
29
29
  DEFAULT_TAG = 'prefix.test.tag'.freeze
30
30
 
31
+ DEFAULT_TAG_STRIPPED = 'test.tag'.freeze
32
+
31
33
  ARBITRARY_TEXT = 'This line is not an exception.'.freeze
32
34
 
33
35
  JAVA_EXC = <<END.freeze
@@ -74,6 +76,17 @@ END
74
76
  log_entry
75
77
  end
76
78
 
79
+ def feed_lines_without_line_breaks(driver, t, *messages, stream: nil)
80
+ count = 0
81
+ messages.each do |m|
82
+ m.each_line do |line|
83
+ line.delete!("\n")
84
+ driver.emit(log_entry(line, count, stream), t + count)
85
+ count += 1
86
+ end
87
+ end
88
+ end
89
+
77
90
  def feed_lines(driver, t, *messages, stream: nil)
78
91
  count = 0
79
92
  messages.each do |m|
@@ -125,7 +138,9 @@ END
125
138
  }
126
139
 
127
140
  test_cases.each do |language, exception|
128
- cfg = "languages #{language}"
141
+ cfg = %(
142
+ #{CONFIG}
143
+ languages #{language})
129
144
  d = create_driver(cfg)
130
145
  t = Time.now.to_i
131
146
 
@@ -156,12 +171,12 @@ END
156
171
 
157
172
  # Validate that each line received is emitted separately as expected.
158
173
  router_mock.should_receive(:emit)
159
- .once.with(DEFAULT_TAG, Integer,
174
+ .once.with(DEFAULT_TAG_STRIPPED, Integer,
160
175
  'message' => json_line_with_exception,
161
176
  'count' => 0)
162
177
 
163
178
  router_mock.should_receive(:emit)
164
- .once.with(DEFAULT_TAG, Integer,
179
+ .once.with(DEFAULT_TAG_STRIPPED, Integer,
165
180
  'message' => json_line_without_exception,
166
181
  'count' => 1)
167
182
 
@@ -174,7 +189,9 @@ END
174
189
  end
175
190
 
176
191
  def test_single_language_config
177
- cfg = 'languages java'
192
+ cfg = %(
193
+ #{CONFIG}
194
+ languages java)
178
195
  d = create_driver(cfg)
179
196
  t = Time.now.to_i
180
197
  d.run do
@@ -185,7 +202,9 @@ END
185
202
  end
186
203
 
187
204
  def test_multi_language_config
188
- cfg = 'languages python, java'
205
+ cfg = %(
206
+ #{CONFIG}
207
+ languages python, java)
189
208
  d = create_driver(cfg)
190
209
  t = Time.now.to_i
191
210
  d.run do
@@ -196,7 +215,9 @@ END
196
215
  end
197
216
 
198
217
  def test_split_exception_after_timeout
199
- cfg = 'multiline_flush_interval 1'
218
+ cfg = %(
219
+ #{CONFIG}
220
+ multiline_flush_interval 1)
200
221
  d = create_driver(cfg)
201
222
  t1 = 0
202
223
  t2 = 0
@@ -227,6 +248,12 @@ END
227
248
  assert_equal(make_logs(t1, JAVA_EXC + " at x\n at y\n"), d.events)
228
249
  end
229
250
 
251
+ def test_remove_tag_prefix_is_required
252
+ cfg = ''
253
+ e = assert_raises(Fluent::ConfigError) { create_driver(cfg) }
254
+ assert_match(/remove_tag_prefix/, e.message)
255
+ end
256
+
230
257
  def get_out_tags(remove_tag_prefix, original_tag)
231
258
  cfg = "remove_tag_prefix #{remove_tag_prefix}"
232
259
  d = create_driver(cfg, original_tag)
@@ -243,8 +270,42 @@ END
243
270
  assert_equal(['prefix.plus.rest.of.the.tag'], tags)
244
271
  end
245
272
 
273
+ def test_force_line_breaks_false
274
+ cfg = %(
275
+ #{CONFIG}
276
+ force_line_breaks true)
277
+ d = create_driver(cfg)
278
+ t = Time.now.to_i
279
+ d.run do
280
+ feed_lines(d, t, JAVA_EXC)
281
+ end
282
+ expected = JAVA_EXC
283
+ assert_equal(make_logs(t, *expected), d.events)
284
+ end
285
+
286
+ def test_force_line_breaks_true
287
+ cfg = %(
288
+ #{CONFIG}
289
+ force_line_breaks true)
290
+ d = create_driver(cfg)
291
+ t = Time.now.to_i
292
+ d.run do
293
+ feed_lines_without_line_breaks(d, t, JAVA_EXC)
294
+ end
295
+ # Expected: the first two lines of the exception are buffered and combined.
296
+ # Then the max_lines setting kicks in and the rest of the Python exception
297
+ # is logged line-by-line (since it's not an exception stack in itself).
298
+ # For the following Java stack trace, the two lines of the first exception
299
+ # are buffered and combined. So are the first two lines of the second
300
+ # exception. Then the rest is logged line-by-line.
301
+ expected = JAVA_EXC.chomp
302
+ assert_equal(make_logs(t, *expected), d.events)
303
+ end
304
+
246
305
  def test_flush_after_max_lines
247
- cfg = 'max_lines 2'
306
+ cfg = %(
307
+ #{CONFIG}
308
+ max_lines 2)
248
309
  d = create_driver(cfg)
249
310
  t = Time.now.to_i
250
311
  d.run do
@@ -263,7 +324,9 @@ END
263
324
  end
264
325
 
265
326
  def test_separate_streams
266
- cfg = 'stream stream'
327
+ cfg = %(
328
+ #{CONFIG}
329
+ stream stream)
267
330
  d = create_driver(cfg)
268
331
  t = Time.now.to_i
269
332
  d.run do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-detect-exceptions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.13
4
+ version: 0.0.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stackdriver Agents
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-11-04 00:00:00.000000000 Z
11
+ date: 2021-09-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd
@@ -93,7 +93,6 @@ extra_rdoc_files: []
93
93
  files:
94
94
  - CONTRIBUTING
95
95
  - Gemfile
96
- - Gemfile.lock
97
96
  - LICENSE
98
97
  - README.rdoc
99
98
  - Rakefile
@@ -123,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
122
  - !ruby/object:Gem::Version
124
123
  version: '0'
125
124
  requirements: []
126
- rubygems_version: 3.0.4
125
+ rubygems_version: 3.0.8
127
126
  signing_key:
128
127
  specification_version: 4
129
128
  summary: fluentd output plugin for combining stack traces as multi-line JSON logs
data/Gemfile.lock DELETED
@@ -1,66 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- fluent-plugin-detect-exceptions (0.0.13)
5
- fluentd (>= 0.10)
6
-
7
- GEM
8
- remote: https://rubygems.org/
9
- specs:
10
- ast (2.4.0)
11
- concurrent-ruby (1.1.5)
12
- cool.io (1.5.4)
13
- dig_rb (1.0.1)
14
- flexmock (2.3.6)
15
- fluentd (1.7.4)
16
- cool.io (>= 1.4.5, < 2.0.0)
17
- dig_rb (~> 1.0.0)
18
- http_parser.rb (>= 0.5.1, < 0.7.0)
19
- msgpack (>= 1.2.0, < 2.0.0)
20
- serverengine (>= 2.0.4, < 3.0.0)
21
- sigdump (~> 0.2.2)
22
- strptime (>= 0.2.2, < 1.0.0)
23
- tzinfo (~> 2.0)
24
- tzinfo-data (~> 1.0)
25
- yajl-ruby (~> 1.0)
26
- http_parser.rb (0.6.0)
27
- msgpack (1.3.1)
28
- parser (2.6.5.0)
29
- ast (~> 2.4.0)
30
- power_assert (1.1.5)
31
- powerpack (0.1.2)
32
- rainbow (2.2.2)
33
- rake
34
- rake (10.5.0)
35
- rubocop (0.42.0)
36
- parser (>= 2.3.1.1, < 3.0)
37
- powerpack (~> 0.1)
38
- rainbow (>= 1.99.1, < 3.0)
39
- ruby-progressbar (~> 1.7)
40
- unicode-display_width (~> 1.0, >= 1.0.1)
41
- ruby-progressbar (1.10.1)
42
- serverengine (2.1.1)
43
- sigdump (~> 0.2.2)
44
- sigdump (0.2.4)
45
- strptime (0.2.3)
46
- test-unit (3.3.4)
47
- power_assert
48
- tzinfo (2.0.0)
49
- concurrent-ruby (~> 1.0)
50
- tzinfo-data (1.2019.3)
51
- tzinfo (>= 1.0.0)
52
- unicode-display_width (1.6.0)
53
- yajl-ruby (1.4.1)
54
-
55
- PLATFORMS
56
- ruby
57
-
58
- DEPENDENCIES
59
- flexmock (~> 2.0)
60
- fluent-plugin-detect-exceptions!
61
- rake (~> 10.3)
62
- rubocop (= 0.42.0)
63
- test-unit (~> 3.0)
64
-
65
- BUNDLED WITH
66
- 2.0.2