fluent-plugin-detect-ft-memb-exceptions 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,46 @@
1
+ # Copyright 2016 Google Inc. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'rubygems'
16
+ require 'bundler'
17
+
18
+ begin
19
+ Bundler.setup(:default, :development)
20
+ rescue Bundler::BundlerError => e
21
+ $stderr.puts e.message
22
+ $stderr.puts 'Run `bundle install` to install missing gems'
23
+ exit e.status_code
24
+ end
25
+
26
+ require 'test/unit'
27
+
28
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
29
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
30
+ require 'fluent/test'
31
+
32
+ unless ENV.key?('VERBOSE')
33
+ nulllogger = Object.new
34
+ nulllogger.instance_eval do |_|
35
+ def respond_to_missing?
36
+ true
37
+ end
38
+
39
+ def method_missing(_method, *_args) # rubocop:disable Style/MethodMissing
40
+ end
41
+ end
42
+ # global $log variable is used by fluentd
43
+ $log = nulllogger # rubocop:disable Style/GlobalVars
44
+ end
45
+
46
+ require 'fluent/plugin/out_detect_exceptions'
@@ -0,0 +1,73 @@
1
+ # Copyright 2016 Google Inc. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'benchmark'
16
+
17
+ require 'fluent/plugin/exception_detector'
18
+
19
+ size_in_m = 25
20
+ line_length = 50
21
+
22
+ size = size_in_m << 20
23
+
24
+ JAVA_EXC = <<END.freeze
25
+ Jul 09, 2015 3:23:29 PM com.google.devtools.search.cloud.feeder.MakeLog: RuntimeException: Run from this message!
26
+ at com.my.app.Object.do$a1(MakeLog.java:50)
27
+ at java.lang.Thing.call(Thing.java:10)
28
+ at com.my.app.Object.help(MakeLog.java:40)
29
+ at sun.javax.API.method(API.java:100)
30
+ at com.jetty.Framework.main(MakeLog.java:30)
31
+ END
32
+
33
+ PYTHON_EXC = <<END.freeze
34
+ Traceback (most recent call last):
35
+ File "/base/data/home/runtimes/python27/python27_lib/versions/third_party/webapp2-2.5.2/webapp2.py", line 1535, in __call__
36
+ rv = self.handle_exception(request, response, e)
37
+ File "/base/data/home/apps/s~nearfieldspy/1.378705245900539993/nearfieldspy.py", line 17, in start
38
+ return get()
39
+ File "/base/data/home/apps/s~nearfieldspy/1.378705245900539993/nearfieldspy.py", line 5, in get
40
+ raise Exception('spam', 'eggs')
41
+ Exception: ('spam', 'eggs')
42
+ END
43
+
44
+ chars = [('a'..'z'), ('A'..'Z')].map(&:to_a).flatten
45
+
46
+ random_text = (1..(size / line_length)).collect do
47
+ (0...line_length).map { chars[rand(chars.length)] }.join
48
+ end
49
+
50
+ exceptions = {
51
+ java: (JAVA_EXC * (size / JAVA_EXC.length)).lines,
52
+ python: (PYTHON_EXC * (size / PYTHON_EXC.length)).lines
53
+ }
54
+
55
+ puts "Start benchmark. Input size #{size_in_m}M."
56
+ Benchmark.bm do |x|
57
+ languages = Fluent::ExceptionDetectorConfig::RULES_BY_LANG.keys
58
+ languages.each do |lang|
59
+ buffer = Fluent::TraceAccumulator.new(nil, lang) {}
60
+ x.report("#{lang}_detector_random_text") do
61
+ random_text.each { |l| buffer.push(0, l) }
62
+ end
63
+ end
64
+ [:java, :python, :all].each do |detector_lang|
65
+ buffer = Fluent::TraceAccumulator.new(nil, detector_lang) {}
66
+ exc_languages = detector_lang == :all ? exceptions.keys : [detector_lang]
67
+ exc_languages.each do |exc_lang|
68
+ x.report("#{detector_lang}_detector_#{exc_lang}_stacks") do
69
+ exceptions[exc_lang].each { |l| buffer.push(0, l) }
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,426 @@
1
+ # Copyright 2016 Google Inc. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require_relative '../helper'
16
+ require 'fluent/plugin/exception_detector'
17
+
18
+ class ExceptionDetectorTest < Test::Unit::TestCase
19
+ JAVA_EXC_PART1 = <<END.freeze
20
+ Jul 09, 2015 3:23:29 PM com.google.devtools.search.cloud.feeder.MakeLog: RuntimeException: Run from this message!
21
+ at com.my.app.Object.do$a1(MakeLog.java:50)
22
+ at java.lang.Thing.call(Thing.java:10)
23
+ END
24
+
25
+ JAVA_EXC_PART2 = <<END.freeze
26
+ at com.my.app.Object.help(MakeLog.java:40)
27
+ at sun.javax.API.method(API.java:100)
28
+ at com.jetty.Framework.main(MakeLog.java:30)
29
+ END
30
+
31
+ JAVA_EXC = (JAVA_EXC_PART1 + JAVA_EXC_PART2).freeze
32
+
33
+ COMPLEX_JAVA_EXC = <<END.freeze
34
+ javax.servlet.ServletException: Something bad happened
35
+ at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.java:60)
36
+ at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
37
+ at com.example.myproject.ExceptionHandlerFilter.doFilter(ExceptionHandlerFilter.java:28)
38
+ at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
39
+ at com.example.myproject.OutputBufferFilter.doFilter(OutputBufferFilter.java:33)
40
+ at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
41
+ at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
42
+ at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
43
+ at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
44
+ at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
45
+ at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
46
+ at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
47
+ at org.mortbay.jetty.Server.handle(Server.java:326)
48
+ at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
49
+ at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:943)
50
+ at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:756)
51
+ at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
52
+ at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
53
+ at org.mortbay.jetty.bio.SocketConnector$Connection.run(SocketConnector.java:228)
54
+ at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
55
+ Caused by: com.example.myproject.MyProjectServletException
56
+ at com.example.myproject.MyServlet.doPost(MyServlet.java:169)
57
+ at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
58
+ at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
59
+ at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
60
+ at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
61
+ at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.java:30)
62
+ ... 27 more
63
+ END
64
+
65
+ NODE_JS_EXC = <<END.freeze
66
+ ReferenceError: myArray is not defined
67
+ at next (/app/node_modules/express/lib/router/index.js:256:14)
68
+ at /app/node_modules/express/lib/router/index.js:615:15
69
+ at next (/app/node_modules/express/lib/router/index.js:271:10)
70
+ at Function.process_params (/app/node_modules/express/lib/router/index.js:330:12)
71
+ at /app/node_modules/express/lib/router/index.js:277:22
72
+ at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)
73
+ at Route.dispatch (/app/node_modules/express/lib/router/route.js:112:3)
74
+ at next (/app/node_modules/express/lib/router/route.js:131:13)
75
+ at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)
76
+ at /app/app.js:52:3
77
+ END
78
+
79
+ CLIENT_JS_EXC = <<END.freeze
80
+ Error
81
+ at bls (<anonymous>:3:9)
82
+ at <anonymous>:6:4
83
+ at a_function_name
84
+ at Object.InjectedScript._evaluateOn (http://<anonymous>/file.js?foo=bar:875:140)
85
+ at Object.InjectedScript.evaluate (<anonymous>)
86
+ END
87
+
88
+ V8_JS_EXC = <<END.freeze
89
+ V8 errors stack trace
90
+ eval at Foo.a (eval at Bar.z (myscript.js:10:3))
91
+ at new Contructor.Name (native)
92
+ at new FunctionName (unknown location)
93
+ at Type.functionName [as methodName] (file(copy).js?query='yes':12:9)
94
+ at functionName [as methodName] (native)
95
+ at Type.main(sample(copy).js:6:4)
96
+ END
97
+
98
+ PYTHON_EXC = <<END.freeze
99
+ Traceback (most recent call last):
100
+ File "/base/data/home/runtimes/python27/python27_lib/versions/third_party/webapp2-2.5.2/webapp2.py", line 1535, in __call__
101
+ rv = self.handle_exception(request, response, e)
102
+ File "/base/data/home/apps/s~nearfieldspy/1.378705245900539993/nearfieldspy.py", line 17, in start
103
+ return get()
104
+ File "/base/data/home/apps/s~nearfieldspy/1.378705245900539993/nearfieldspy.py", line 5, in get
105
+ raise Exception('spam', 'eggs')
106
+ Exception: ('spam', 'eggs')
107
+ END
108
+
109
+ PHP_EXC = <<END.freeze
110
+ exception 'Exception' with message 'Custom exception' in /home/joe/work/test-php/test.php:5
111
+ Stack trace:
112
+ #0 /home/joe/work/test-php/test.php(9): func1()
113
+ #1 /home/joe/work/test-php/test.php(13): func2()
114
+ #2 {main}
115
+ END
116
+
117
+ PHP_ON_GAE_EXC = <<END.freeze
118
+ PHP Fatal error: Uncaught exception 'Exception' with message 'message' in /base/data/home/apps/s~crash-example-php/1.388306779641080894/errors.php:60
119
+ Stack trace:
120
+ #0 [internal function]: ErrorEntryGenerator::{closure}()
121
+ #1 /base/data/home/apps/s~crash-example-php/1.388306779641080894/errors.php(20): call_user_func_array(Object(Closure), Array)
122
+ #2 /base/data/home/apps/s~crash-example-php/1.388306779641080894/index.php(36): ErrorEntry->__call('raise', Array)
123
+ #3 /base/data/home/apps/s~crash-example-php/1.388306779641080894/index.php(36): ErrorEntry->raise()
124
+ #4 {main}
125
+ thrown in /base/data/home/apps/s~crash-example-php/1.388306779641080894/errors.php on line 60
126
+ END
127
+
128
+ GO_EXC = <<END.freeze
129
+ panic: runtime error: index out of range
130
+
131
+ goroutine 12 [running]:
132
+ main88989.memoryAccessException()
133
+ crash_example_go.go:58 +0x12a
134
+ main88989.handler(0x2afb7042a408, 0xc01042f880, 0xc0104d3450)
135
+ crash_example_go.go:36 +0x7ec
136
+ net/http.HandlerFunc.ServeHTTP(0x13e5128, 0x2afb7042a408, 0xc01042f880, 0xc0104d3450)
137
+ go/src/net/http/server.go:1265 +0x56
138
+ net/http.(*ServeMux).ServeHTTP(0xc01045cab0, 0x2afb7042a408, 0xc01042f880, 0xc0104d3450)
139
+ go/src/net/http/server.go:1541 +0x1b4
140
+ appengine_internal.executeRequestSafely(0xc01042f880, 0xc0104d3450)
141
+ go/src/appengine_internal/api_prod.go:288 +0xb7
142
+ appengine_internal.(*server).HandleRequest(0x15819b0, 0xc010401560, 0xc0104c8180, 0xc010431380, 0x0, 0x0)
143
+ go/src/appengine_internal/api_prod.go:222 +0x102b
144
+ reflect.Value.call(0x1243fe0, 0x15819b0, 0x113, 0x12c8a20, 0x4, 0xc010485f78, 0x3, 0x3, 0x0, 0x0, ...)
145
+ /tmp/appengine/go/src/reflect/value.go:419 +0x10fd
146
+ reflect.Value.Call(0x1243fe0, 0x15819b0, 0x113, 0xc010485f78, 0x3, 0x3, 0x0, 0x0, 0x0)
147
+ /tmp/ap"
148
+ END
149
+
150
+ CSHARP_EXC = <<END.freeze
151
+ System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
152
+ at System.Collections.Generic.Dictionary`2[System.String,System.Collections.Generic.Dictionary`2[System.Int32,System.Double]].get_Item (System.String key) [0x00000] in <filename unknown>:0
153
+ at File3.Consolidator_Class.Function5 (System.Collections.Generic.Dictionary`2 names, System.Text.StringBuilder param_4) [0x00007] in /usr/local/google/home/Csharp/another file.csharp:9
154
+ at File3.Consolidator_Class.Function4 (System.Text.StringBuilder param_4, System.Double[,,] array) [0x00013] in /usr/local/google/home/Csharp/another file.csharp:23
155
+ at File3.Consolidator_Class.Function3 (Int32 param_3) [0x0000f] in /usr/local/google/home/Csharp/another file.csharp:27
156
+ at File3.Consolidator_Class.Function3 (System.Text.StringBuilder param_3) [0x00007] in /usr/local/google/home/Csharp/another file.csharp:32
157
+ at File2.Processor.Function2 (System.Int32& param_2, System.Collections.Generic.Stack`1& numbers) [0x00003] in /usr/local/google/home/Csharp/File2.csharp:19
158
+ at File2.Processor.Random2 () [0x00037] in /usr/local/google/home/Csharp/File2.csharp:28
159
+ at File2.Processor.Function1 (Int32 param_1, System.Collections.Generic.Dictionary`2 map) [0x00007] in /usr/local/google/home/Csharp/File2.csharp:34
160
+ at Main.Welcome+<Main>c__AnonStorey0.<>m__0 () [0x00006] in /usr/local/google/home/Csharp/hello.csharp:48
161
+ at System.Threading.Thread.StartInternal () [0x00000] in <filename unknown>:0
162
+ END
163
+
164
+ RUBY_EXC = <<END.freeze
165
+ NoMethodError (undefined method `resursivewordload' for #<BooksController:0x007f8dd9a0c738>):
166
+ app/controllers/books_controller.rb:69:in `recursivewordload'
167
+ app/controllers/books_controller.rb:75:in `loadword'
168
+ app/controllers/books_controller.rb:79:in `loadline'
169
+ app/controllers/books_controller.rb:83:in `loadparagraph'
170
+ app/controllers/books_controller.rb:87:in `loadpage'
171
+ app/controllers/books_controller.rb:91:in `onload'
172
+ app/controllers/books_controller.rb:95:in `loadrecursive'
173
+ app/controllers/books_controller.rb:99:in `requestload'
174
+ app/controllers/books_controller.rb:118:in `generror'
175
+ config/error_reporting_logger.rb:62:in `tagged'
176
+ END
177
+
178
+ ARBITRARY_TEXT = <<END.freeze
179
+ This arbitrary text.
180
+ I am glad it contains no exception.
181
+ END
182
+
183
+ ELIXIR_EXC = <<END.freeze
184
+ 12:08:17.930 [error] Ranch protocol #PID<0.28713.3> (:cowboy_protocol) of listener Graphql.Web.Endpoint.HTTP terminated
185
+ ** (exit) an exception was raised:
186
+ ** (FunctionClauseError) no function clause matching in Graphql.Schema.Charge.map_to_schema/1
187
+ (graphql) lib/graphql/schema/offer/offer.ex:159: Graphql.Schema.Charge.map_to_schema(%{"amount" => %{"currency" => "AUD", "nativeSymbol" => "$", "symbol" => "AU$", "value" => "1.00"}, "basis" => "ONE_TIME", "id" => "2c92a0fb4a38064c014a4e6d9a74281a", "name" => "P2-Monthly-Trial"})
188
+ (elixir) lib/enum.ex:1255: Enum."-map/2-lists^map/1-0-"/2
189
+ (graphql) lib/graphql/schema/offer/offer.ex:137: Graphql.Schema.Pricing.map_to_schema/1
190
+ (elixir) lib/enum.ex:1255: Enum."-map/2-lists^map/1-0-"/2
191
+ (graphql) lib/graphql/schema/offer/offer.ex:34: Graphql.Schema.Offer.map_to_schema/1
192
+ (graphql) lib/graphql/resolvers/offer_resolver.ex:29: Graphql.OfferResolver.map_result/1
193
+ (tapper_absinthe_plug) lib/helper.ex:38: Tapper.Absinthe.Helper.in_span/2
194
+ (elixir) lib/task/supervised.ex:85: Task.Supervised.do_apply/2
195
+ END
196
+
197
+ def check_multiline(detector, expected_first, expected_last, multiline)
198
+ lines = multiline.lines
199
+ lines.each_with_index do |line, index|
200
+ action = detector.update(line)
201
+ case index
202
+ when 0
203
+ assert_equal(expected_first, action,
204
+ "unexpected action on first line: #{line}")
205
+ when lines.length - 1
206
+ assert_equal(expected_last, action,
207
+ "unexpected action on last line: #{line}")
208
+ else
209
+ assert_equal(:inside_trace, action, "line not buffered: #{line}")
210
+ end
211
+ end
212
+ end
213
+
214
+ def check_no_multiline(detector, text)
215
+ text.lines.each do |line|
216
+ action = detector.update(line)
217
+ assert_equal(:no_trace, action, "unexpected action on line: #{line}")
218
+ end
219
+ end
220
+
221
+ def check_exception(exception, detects_end)
222
+ detector = Fluent::ExceptionDetector.new
223
+ after_exc = detects_end ? :end_trace : :inside_trace
224
+ before_second_exc = detects_end ? :inside_trace : :start_trace
225
+ check_multiline(detector, :no_trace, :no_trace, '12:08:17.930 This is not an exception.')
226
+ check_multiline(detector, :inside_trace, after_exc, exception)
227
+ check_multiline(detector, :no_trace, :no_trace, '12:08:17.930 This is not an exception.')
228
+ check_multiline(detector, :inside_trace, after_exc, exception)
229
+ check_multiline(detector, before_second_exc, after_exc, exception)
230
+ end
231
+
232
+ def test_java
233
+ check_exception(JAVA_EXC, false)
234
+ check_exception(COMPLEX_JAVA_EXC, false)
235
+ end
236
+
237
+ def test_elixir
238
+ check_exception(ELIXIR_EXC, false)
239
+ end
240
+
241
+ def test_js
242
+ check_exception(NODE_JS_EXC, false)
243
+ check_exception(CLIENT_JS_EXC, false)
244
+ check_exception(V8_JS_EXC, false)
245
+ end
246
+
247
+ def test_csharp
248
+ check_exception(CSHARP_EXC, false)
249
+ end
250
+
251
+ def test_python
252
+ check_exception(PYTHON_EXC, true)
253
+ end
254
+
255
+ def test_php
256
+ check_exception(PHP_EXC, false)
257
+ check_exception(PHP_ON_GAE_EXC, true)
258
+ end
259
+
260
+ def test_go
261
+ check_exception(GO_EXC, false)
262
+ end
263
+
264
+ def test_ruby
265
+ check_exception(RUBY_EXC, false)
266
+ end
267
+
268
+ def test_mixed_languages
269
+ check_exception(JAVA_EXC, false)
270
+ check_exception(PYTHON_EXC, true)
271
+ check_exception(COMPLEX_JAVA_EXC, false)
272
+ check_exception(NODE_JS_EXC, false)
273
+ check_exception(PHP_EXC, false)
274
+ check_exception(PHP_ON_GAE_EXC, true)
275
+ check_exception(CLIENT_JS_EXC, false)
276
+ check_exception(GO_EXC, false)
277
+ check_exception(CSHARP_EXC, false)
278
+ check_exception(V8_JS_EXC, false)
279
+ check_exception(RUBY_EXC, false)
280
+ check_exception(ELIXIR_EXC, false)
281
+ end
282
+
283
+ def test_reset
284
+ detector = Fluent::ExceptionDetector.new
285
+
286
+ check_multiline(detector, :inside_trace, :inside_trace, JAVA_EXC_PART1)
287
+ check_multiline(detector, :inside_trace, :inside_trace, JAVA_EXC_PART2)
288
+
289
+ check_multiline(detector, :start_trace, :inside_trace, JAVA_EXC_PART1)
290
+ detector.reset
291
+ check_no_multiline(detector, JAVA_EXC_PART2)
292
+ end
293
+
294
+ def feed_lines(buffer, *messages)
295
+ messages.each do |m|
296
+ m.each_line do |line|
297
+ buffer.push(0, line)
298
+ end
299
+ buffer.flush
300
+ end
301
+ end
302
+
303
+ Struct.new('TestBufferScenario', :desc, :languages, :input, :expected)
304
+
305
+ def buffer_scenario(desc, languages, input, expected)
306
+ Struct::TestBufferScenario.new(desc, languages, input, expected)
307
+ end
308
+
309
+ def test_buffer
310
+ [
311
+ buffer_scenario('mixed languages',
312
+ [:all],
313
+ [JAVA_EXC, ARBITRARY_TEXT, PYTHON_EXC, GO_EXC],
314
+ [JAVA_EXC] + ARBITRARY_TEXT.lines + [PYTHON_EXC, GO_EXC]),
315
+ buffer_scenario('single language',
316
+ [:go],
317
+ [JAVA_EXC, ARBITRARY_TEXT, GO_EXC],
318
+ JAVA_EXC.lines + ARBITRARY_TEXT.lines + [GO_EXC]),
319
+ buffer_scenario('some exceptions from non-configured languages',
320
+ [:python],
321
+ [JAVA_EXC, PYTHON_EXC, GO_EXC],
322
+ JAVA_EXC.lines + [PYTHON_EXC] + GO_EXC.lines),
323
+ buffer_scenario('all exceptions from non-configured languages',
324
+ [:ruby],
325
+ [JAVA_EXC, PYTHON_EXC, GO_EXC],
326
+ JAVA_EXC.lines + PYTHON_EXC.lines + GO_EXC.lines)
327
+ ].each do |s|
328
+ out = []
329
+ buffer = Fluent::TraceAccumulator.new(nil,
330
+ s.languages) { |_, m| out << m }
331
+ feed_lines(buffer, *s.input)
332
+ assert_equal(s.expected, out, s.desc)
333
+ end
334
+ end
335
+
336
+ def feed_json(buffer, message_field, messages)
337
+ messages.each do |m|
338
+ m.each_line do |line|
339
+ buffer.push(0, message_field => line)
340
+ end
341
+ buffer.flush
342
+ end
343
+ end
344
+
345
+ def expected_json(message_field, messages)
346
+ messages.collect { |m| { message_field => [m].flatten.join } }
347
+ end
348
+
349
+ Struct.new('TestJsonScenario',
350
+ :desc, :configured_field, :actual_field, :input, :output)
351
+
352
+ def json_scenario(desc, configured_field, actual_field, input, output)
353
+ Struct::TestJsonScenario.new(desc, configured_field, actual_field,
354
+ input, output)
355
+ end
356
+
357
+ def test_json_messages
358
+ [
359
+ json_scenario('User-defined message field', 'mydata', 'mydata',
360
+ [PYTHON_EXC, ARBITRARY_TEXT, GO_EXC],
361
+ [PYTHON_EXC] + ARBITRARY_TEXT.lines + [GO_EXC]),
362
+ json_scenario('Default message field "message"', '', 'message',
363
+ [PYTHON_EXC, ARBITRARY_TEXT, GO_EXC],
364
+ [PYTHON_EXC] + ARBITRARY_TEXT.lines + [GO_EXC]),
365
+ json_scenario('Default message field "log"', '', 'log',
366
+ [PYTHON_EXC, ARBITRARY_TEXT, GO_EXC],
367
+ [PYTHON_EXC] + ARBITRARY_TEXT.lines + [GO_EXC]),
368
+ json_scenario('Wrongly defined message field', 'doesnotexist', 'mydata',
369
+ [PYTHON_EXC, ARBITRARY_TEXT, GO_EXC],
370
+ PYTHON_EXC.lines + ARBITRARY_TEXT.lines + GO_EXC.lines),
371
+ json_scenario('Undefined message field', '', 'mydata',
372
+ [PYTHON_EXC, ARBITRARY_TEXT, GO_EXC],
373
+ PYTHON_EXC.lines + ARBITRARY_TEXT.lines + GO_EXC.lines)
374
+ ].each do |s|
375
+ out = []
376
+ buffer = Fluent::TraceAccumulator.new(s.configured_field,
377
+ [:all]) { |_, m| out << m }
378
+ feed_json(buffer, s.actual_field, s.input)
379
+ assert_equal(expected_json(s.actual_field, s.output), out, s.desc)
380
+ end
381
+ end
382
+
383
+ def test_max_lines_limit
384
+ # Limit is equal to the first part of the exception and forces it to be
385
+ # flushed before the rest of the exception is processed.
386
+ max_lines = JAVA_EXC_PART1.lines.length
387
+ out = []
388
+ buffer = Fluent::TraceAccumulator.new(nil,
389
+ [:all],
390
+ max_lines: max_lines) do |_, m|
391
+ out << m
392
+ end
393
+ feed_lines(buffer, JAVA_EXC)
394
+ assert_equal([JAVA_EXC_PART1] + JAVA_EXC_PART2.lines, out)
395
+ end
396
+
397
+ def test_high_max_bytes_limit
398
+ # Limit is just too small to add one more line to the buffered first part of
399
+ # the exception.
400
+ max_bytes = JAVA_EXC_PART1.length + JAVA_EXC_PART2.lines[0].length - 1
401
+ out = []
402
+ buffer = Fluent::TraceAccumulator.new(nil,
403
+ [:all],
404
+ max_bytes: max_bytes) do |_, m|
405
+ out << m
406
+ end
407
+ feed_lines(buffer, JAVA_EXC)
408
+ # Check that the trace is flushed after the first part.
409
+ assert_equal([JAVA_EXC_PART1] + JAVA_EXC_PART2.lines, out)
410
+ end
411
+
412
+ def test_low_max_bytes_limit
413
+ # Limit is exceeded by the character that follows the buffered first part of
414
+ # the exception.
415
+ max_bytes = JAVA_EXC_PART1.length
416
+ out = []
417
+ buffer = Fluent::TraceAccumulator.new(nil,
418
+ [:all],
419
+ max_bytes: max_bytes) do |_, m|
420
+ out << m
421
+ end
422
+ feed_lines(buffer, JAVA_EXC)
423
+ # Check that the trace is flushed after the first part.
424
+ assert_equal([JAVA_EXC_PART1] + JAVA_EXC_PART2.lines, out)
425
+ end
426
+ end