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 +4 -4
- data/README.rdoc +16 -6
- data/fluent-plugin-detect-exceptions.gemspec +1 -1
- data/lib/fluent/plugin/exception_detector.rb +16 -8
- data/lib/fluent/plugin/out_detect_exceptions.rb +11 -5
- data/test/plugin/test_out_detect_exceptions.rb +71 -8
- metadata +3 -4
- data/Gemfile.lock +0 -66
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f846941463ccc37acd71280e182d5ae7b0ffd0c95c609bfbfe67de64f86ff478
|
4
|
+
data.tar.gz: 1454c4ddae5ba4efa214972f2109f0a71d4984e87b49cefa3541cb87d92d6cc7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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
|
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,
|
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
|
-
|
364
|
-
|
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(
|
95
|
-
|
96
|
-
|
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 =
|
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(
|
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(
|
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 =
|
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 =
|
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 =
|
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 =
|
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 =
|
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.
|
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:
|
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.
|
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
|