fluent-plugin-detect-exceptions 0.0.13 → 0.0.15

Sign up to get free protection for your applications and to get access to all the features.
@@ -22,45 +22,47 @@ class DetectExceptionsOutputTest < Test::Unit::TestCase
22
22
  Fluent::Test.setup
23
23
  end
24
24
 
25
- CONFIG = <<END.freeze
26
- remove_tag_prefix prefix
27
- END
25
+ CONFIG = <<~END_CONFIG.freeze
26
+ remove_tag_prefix prefix
27
+ END_CONFIG
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
- JAVA_EXC = <<END.freeze
34
- SomeException: foo
35
- at bar
36
- Caused by: org.AnotherException
37
- at bar2
38
- at bar3
39
- END
40
-
41
- PHP_EXC = <<END.freeze
42
- exception 'Exception' with message 'Custom exception' in /home/joe/work/test-php/test.php:5
43
- Stack trace:
44
- #0 /home/joe/work/test-php/test.php(9): func1()
45
- #1 /home/joe/work/test-php/test.php(13): func2()
46
- #2 {main}
47
- END
48
-
49
- PYTHON_EXC = <<END.freeze
50
- Traceback (most recent call last):
51
- File "/base/data/home/runtimes/python27/python27_lib/versions/third_party/webapp2-2.5.2/webapp2.py", line 1535, in __call__
52
- rv = self.handle_exception(request, response, e)
53
- Exception: ('spam', 'eggs')
54
- END
55
-
56
- RUBY_EXC = <<END.freeze
57
- examble.rb:18:in `thrower': An error has occurred. (RuntimeError)
58
- from examble.rb:14:in `caller'
59
- from examble.rb:10:in `helper'
60
- from examble.rb:6:in `writer'
61
- from examble.rb:2:in `runner'
62
- from examble.rb:21:in `<main>'
63
- END
35
+ JAVA_EXC = <<~END_JAVA.freeze
36
+ SomeException: foo
37
+ at bar
38
+ Caused by: org.AnotherException
39
+ at bar2
40
+ at bar3
41
+ END_JAVA
42
+
43
+ PHP_EXC = <<~END_PHP.freeze
44
+ exception 'Exception' with message 'Custom exception' in /home/joe/work/test-php/test.php:5
45
+ Stack trace:
46
+ #0 /home/joe/work/test-php/test.php(9): func1()
47
+ #1 /home/joe/work/test-php/test.php(13): func2()
48
+ #2 {main}
49
+ END_PHP
50
+
51
+ PYTHON_EXC = <<~END_PYTHON.freeze
52
+ Traceback (most recent call last):
53
+ File "/base/data/home/runtimes/python27/python27_lib/versions/third_party/webapp2-2.5.2/webapp2.py", line 1535, in __call__
54
+ rv = self.handle_exception(request, response, e)
55
+ Exception: ('spam', 'eggs')
56
+ END_PYTHON
57
+
58
+ RUBY_EXC = <<~END_RUBY.freeze
59
+ examble.rb:18:in `thrower': An error has occurred. (RuntimeError)
60
+ from examble.rb:14:in `caller'
61
+ from examble.rb:10:in `helper'
62
+ from examble.rb:6:in `writer'
63
+ from examble.rb:2:in `runner'
64
+ from examble.rb:21:in `<main>'
65
+ END_RUBY
64
66
 
65
67
  def create_driver(conf = CONFIG, tag = DEFAULT_TAG)
66
68
  d = Fluent::Test::OutputTestDriver.new(Fluent::DetectExceptionsOutput, tag)
@@ -74,11 +76,22 @@ END
74
76
  log_entry
75
77
  end
76
78
 
77
- def feed_lines(driver, t, *messages, stream: nil)
79
+ def feed_lines_without_line_breaks(driver, timestamp, *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), timestamp + count)
85
+ count += 1
86
+ end
87
+ end
88
+ end
89
+
90
+ def feed_lines(driver, timestamp, *messages, stream: nil)
78
91
  count = 0
79
92
  messages.each do |m|
80
93
  m.each_line do |line|
81
- driver.emit(log_entry(line, count, stream), t + count)
94
+ driver.emit(log_entry(line, count, stream), timestamp + count)
82
95
  count += 1
83
96
  end
84
97
  end
@@ -91,11 +104,11 @@ END
91
104
  end
92
105
  end
93
106
 
94
- def make_logs(t, *messages, stream: nil)
107
+ def make_logs(timestamp, *messages, stream: nil)
95
108
  count = 0
96
109
  logs = []
97
110
  messages.each do |m|
98
- logs << [t + count, log_entry(m, count, stream)]
111
+ logs << [timestamp + count, log_entry(m, count, stream)]
99
112
  count += m.lines.count
100
113
  end
101
114
  logs
@@ -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
 
@@ -133,7 +148,7 @@ END
133
148
  single_line_exception = exception.gsub("\n", '\\n')
134
149
 
135
150
  # There is a nested exception within the body, we should ignore those!
136
- json_line_with_exception = {
151
+ json_with_exception = {
137
152
  'timestamp' => {
138
153
  'nanos' => 998_152_494,
139
154
  'seconds' => 1_496_420_064
@@ -141,8 +156,9 @@ END
141
156
  'message' => single_line_exception,
142
157
  'thread' => 139_658_267_147_048,
143
158
  'severity' => 'ERROR'
144
- }.to_json + "\n"
145
- json_line_without_exception = {
159
+ }
160
+ json_line_with_exception = "#{json_with_exception.to_json}\n"
161
+ json_without_exception = {
146
162
  'timestamp' => {
147
163
  'nanos' => 5_990_266,
148
164
  'seconds' => 1_496_420_065
@@ -150,18 +166,19 @@ END
150
166
  'message' => 'next line',
151
167
  'thread' => 139_658_267_147_048,
152
168
  'severity' => 'INFO'
153
- }.to_json + "\n"
169
+ }
170
+ json_line_without_exception = "#{json_without_exception.to_json}\n"
154
171
 
155
172
  router_mock = flexmock('router')
156
173
 
157
174
  # Validate that each line received is emitted separately as expected.
158
175
  router_mock.should_receive(:emit)
159
- .once.with(DEFAULT_TAG, Integer,
176
+ .once.with(DEFAULT_TAG_STRIPPED, Integer,
160
177
  'message' => json_line_with_exception,
161
178
  'count' => 0)
162
179
 
163
180
  router_mock.should_receive(:emit)
164
- .once.with(DEFAULT_TAG, Integer,
181
+ .once.with(DEFAULT_TAG_STRIPPED, Integer,
165
182
  'message' => json_line_without_exception,
166
183
  'count' => 1)
167
184
 
@@ -174,7 +191,9 @@ END
174
191
  end
175
192
 
176
193
  def test_single_language_config
177
- cfg = 'languages java'
194
+ cfg = %(
195
+ #{CONFIG}
196
+ languages java)
178
197
  d = create_driver(cfg)
179
198
  t = Time.now.to_i
180
199
  d.run do
@@ -185,7 +204,9 @@ END
185
204
  end
186
205
 
187
206
  def test_multi_language_config
188
- cfg = 'languages python, java'
207
+ cfg = %(
208
+ #{CONFIG}
209
+ languages python, java)
189
210
  d = create_driver(cfg)
190
211
  t = Time.now.to_i
191
212
  d.run do
@@ -196,7 +217,9 @@ END
196
217
  end
197
218
 
198
219
  def test_split_exception_after_timeout
199
- cfg = 'multiline_flush_interval 1'
220
+ cfg = %(
221
+ #{CONFIG}
222
+ multiline_flush_interval 1)
200
223
  d = create_driver(cfg)
201
224
  t1 = 0
202
225
  t2 = 0
@@ -224,7 +247,13 @@ END
224
247
  feed_lines(d, t2, " at x\n at y\n")
225
248
  d.instance.before_shutdown
226
249
  end
227
- assert_equal(make_logs(t1, JAVA_EXC + " at x\n at y\n"), d.events)
250
+ assert_equal(make_logs(t1, "#{JAVA_EXC} at x\n at y\n"), d.events)
251
+ end
252
+
253
+ def test_remove_tag_prefix_is_required
254
+ cfg = ''
255
+ e = assert_raises(Fluent::ConfigError) { create_driver(cfg) }
256
+ assert_match(/remove_tag_prefix/, e.message)
228
257
  end
229
258
 
230
259
  def get_out_tags(remove_tag_prefix, original_tag)
@@ -243,8 +272,42 @@ END
243
272
  assert_equal(['prefix.plus.rest.of.the.tag'], tags)
244
273
  end
245
274
 
275
+ def test_force_line_breaks_false
276
+ cfg = %(
277
+ #{CONFIG}
278
+ force_line_breaks true)
279
+ d = create_driver(cfg)
280
+ t = Time.now.to_i
281
+ d.run do
282
+ feed_lines(d, t, JAVA_EXC)
283
+ end
284
+ expected = JAVA_EXC
285
+ assert_equal(make_logs(t, *expected), d.events)
286
+ end
287
+
288
+ def test_force_line_breaks_true
289
+ cfg = %(
290
+ #{CONFIG}
291
+ force_line_breaks true)
292
+ d = create_driver(cfg)
293
+ t = Time.now.to_i
294
+ d.run do
295
+ feed_lines_without_line_breaks(d, t, JAVA_EXC)
296
+ end
297
+ # Expected: the first two lines of the exception are buffered and combined.
298
+ # Then the max_lines setting kicks in and the rest of the Python exception
299
+ # is logged line-by-line (since it's not an exception stack in itself).
300
+ # For the following Java stack trace, the two lines of the first exception
301
+ # are buffered and combined. So are the first two lines of the second
302
+ # exception. Then the rest is logged line-by-line.
303
+ expected = JAVA_EXC.chomp
304
+ assert_equal(make_logs(t, *expected), d.events)
305
+ end
306
+
246
307
  def test_flush_after_max_lines
247
- cfg = 'max_lines 2'
308
+ cfg = %(
309
+ #{CONFIG}
310
+ max_lines 2)
248
311
  d = create_driver(cfg)
249
312
  t = Time.now.to_i
250
313
  d.run do
@@ -256,22 +319,24 @@ END
256
319
  # For the following Java stack trace, the two lines of the first exception
257
320
  # are buffered and combined. So are the first two lines of the second
258
321
  # exception. Then the rest is logged line-by-line.
259
- expected = [PYTHON_EXC.lines[0..1].join] + PYTHON_EXC.lines[2..-1] + \
322
+ expected = [PYTHON_EXC.lines[0..1].join] + PYTHON_EXC.lines[2..] + \
260
323
  [JAVA_EXC.lines[0..1].join] + [JAVA_EXC.lines[2..3].join] + \
261
- JAVA_EXC.lines[4..-1]
324
+ JAVA_EXC.lines[4..]
262
325
  assert_equal(make_logs(t, *expected), d.events)
263
326
  end
264
327
 
265
328
  def test_separate_streams
266
- cfg = 'stream stream'
329
+ cfg = %(
330
+ #{CONFIG}
331
+ stream stream)
267
332
  d = create_driver(cfg)
268
333
  t = Time.now.to_i
269
334
  d.run do
270
335
  feed_lines(d, t, JAVA_EXC.lines[0], stream: 'java')
271
336
  feed_lines(d, t, PYTHON_EXC.lines[0..1].join, stream: 'python')
272
- feed_lines(d, t, JAVA_EXC.lines[1..-1].join, stream: 'java')
337
+ feed_lines(d, t, JAVA_EXC.lines[1..].join, stream: 'java')
273
338
  feed_lines(d, t, JAVA_EXC, stream: 'java')
274
- feed_lines(d, t, PYTHON_EXC.lines[2..-1].join, stream: 'python')
339
+ feed_lines(d, t, PYTHON_EXC.lines[2..].join, stream: 'python')
275
340
  feed_lines(d, t, 'something else', stream: 'java')
276
341
  end
277
342
  # Expected: the Python and the Java exceptions are handled separately
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.15
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: 2023-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd
@@ -25,61 +25,61 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0.10'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: flexmock
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.3'
33
+ version: '2.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10.3'
40
+ version: '2.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rubocop
42
+ name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '='
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.42.0
47
+ version: '10.3'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '='
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.42.0
54
+ version: '10.3'
55
55
  - !ruby/object:Gem::Dependency
56
- name: test-unit
56
+ name: rubocop
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - "~>"
59
+ - - '='
60
60
  - !ruby/object:Gem::Version
61
- version: '3.0'
61
+ version: 1.48.1
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - "~>"
66
+ - - '='
67
67
  - !ruby/object:Gem::Version
68
- version: '3.0'
68
+ version: 1.48.1
69
69
  - !ruby/object:Gem::Dependency
70
- name: flexmock
70
+ name: test-unit
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '2.0'
75
+ version: '3.0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '2.0'
82
+ version: '3.0'
83
83
  description: |2
84
84
  Fluentd output plugin which detects exception stack traces in a stream of
85
85
  JSON log messages and combines all single-line messages that belong to the
@@ -116,14 +116,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
116
116
  requirements:
117
117
  - - ">="
118
118
  - !ruby/object:Gem::Version
119
- version: '2.0'
119
+ version: '2.6'
120
120
  required_rubygems_version: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
125
  requirements: []
126
- rubygems_version: 3.0.4
126
+ rubygems_version: 3.1.6
127
127
  signing_key:
128
128
  specification_version: 4
129
129
  summary: fluentd output plugin for combining stack traces as multi-line JSON logs