fluent-plugin-detect-exceptions 0.0.10 → 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
- SHA1:
3
- metadata.gz: 22408c782d12b0ae5b6c632e67b4d2145272e137
4
- data.tar.gz: e2ab10c60dd80aab406de9b12135d6abcc1ff45e
2
+ SHA256:
3
+ metadata.gz: f846941463ccc37acd71280e182d5ae7b0ffd0c95c609bfbfe67de64f86ff478
4
+ data.tar.gz: 1454c4ddae5ba4efa214972f2109f0a71d4984e87b49cefa3541cb87d92d6cc7
5
5
  SHA512:
6
- metadata.gz: 01ed3e8e3e473ed25e2c6f9c4979bcb57453524c5bf559a337ab9acea679e8224acd07c239daf9313cee067061a67106a1953f7dc5cb2f3d8345e10996923201
7
- data.tar.gz: e1ba01791f3ba5c10d8b402efd1e678724cacdb68868ebf644f57e0f19b0cacd18dc27f869e707039d4a82f05d6894b3d78f6a377a6bf9578aae02552b48dcb3
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
@@ -98,12 +108,22 @@ The plugin supports the following parameters:
98
108
  Example configuration:
99
109
 
100
110
  <match **>
111
+ @type detect_exceptions
101
112
  remove_tag_prefix foo
102
113
  message log
103
114
  languages java, python
104
115
  multiline_flush_interval 0.1
105
116
  </match>
106
117
 
118
+ == Extending language support
119
+
120
+ Supporting a new language requires new detection rules in this gem and
121
+ additional changes in the
122
+ {Stackdriver Error Reporting}[https://cloud.google.com/error-reporting/]
123
+ service. Please contact our product team by
124
+ {filing a support case}[https://cloud.google.com/support-hub/#google-cloud-platform]
125
+ if you'd like to see support for a new language.
126
+
107
127
  == Copyright
108
128
 
109
129
  Copyright:: Copyright 2016 Google Inc. All rights reserved.
data/Rakefile CHANGED
@@ -24,15 +24,25 @@ Rake::TestTask.new(:test) do |test|
24
24
  end
25
25
 
26
26
  # Building the gem will use the local file mode, so ensure it's world-readable.
27
- desc 'Check plugin file permissions'
28
- task :check_perms do
29
- plugin = 'lib/fluent/plugin/out_detect_exceptions.rb'
30
- mode = File.stat(plugin).mode & 0o777
31
- raise "Unexpected mode #{mode.to_s(8)} for #{plugin}" unless
32
- mode & 0o444 == 0o444
27
+ # https://github.com/GoogleCloudPlatform/fluent-plugin-detect-exceptions/issues/32
28
+ desc 'Fix file permissions'
29
+ task :fix_perms do
30
+ files = [
31
+ 'lib/fluent/plugin/*.rb'
32
+ ].flat_map do |file|
33
+ file.include?('*') ? Dir.glob(file) : [file]
34
+ end
35
+
36
+ files.each do |file|
37
+ mode = File.stat(file).mode & 0o777
38
+ next unless mode & 0o444 != 0o444
39
+ puts "Changing mode of #{file} from #{mode.to_s(8)} to "\
40
+ "#{(mode | 0o444).to_s(8)}"
41
+ chmod mode | 0o444, file
42
+ end
33
43
  end
34
44
 
35
45
  desc 'Run unit tests and RuboCop to check for style violations'
36
- task all: [:test, :rubocop, :check_perms]
46
+ task all: [:test, :rubocop, :fix_perms]
37
47
 
38
48
  task default: :all
@@ -11,9 +11,9 @@ 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.10'
15
- gem.authors = ['Thomas Schickinger', 'Igor Peshansky']
16
- gem.email = ['schickin@google.com', 'igorp@google.com']
14
+ gem.version = '0.0.14'
15
+ gem.authors = ['Stackdriver Agents']
16
+ gem.email = ['stackdriver-agents@google.com']
17
17
  gem.required_ruby_version = Gem::Requirement.new('>= 2.0')
18
18
 
19
19
  gem.files = Dir['**/*'].keep_if { |file| File.file?(file) }
@@ -13,7 +13,7 @@
13
13
  # limitations under the License.
14
14
  #
15
15
  module Fluent
16
- Struct.new('Rule', :from_state, :pattern, :to_state)
16
+ Struct.new('Rule', :from_states, :pattern, :to_state)
17
17
 
18
18
  # Configuration of the state machine that detects exceptions.
19
19
  module ExceptionDetectorConfig
@@ -41,8 +41,10 @@ module Fluent
41
41
  end
42
42
  end
43
43
 
44
- def self.rule(from_state, pattern, to_state)
45
- Struct::Rule.new(from_state, pattern, to_state)
44
+ def self.rule(from_state_or_states, pattern, to_state)
45
+ from_state_or_states = [from_state_or_states] unless
46
+ from_state_or_states.is_a?(Array)
47
+ Struct::Rule.new(from_state_or_states, pattern, to_state)
46
48
  end
47
49
 
48
50
  def self.supported
@@ -50,12 +52,29 @@ module Fluent
50
52
  end
51
53
 
52
54
  JAVA_RULES = [
53
- rule(:start_state,
55
+ rule([:start_state, :java_start_exception],
54
56
  /(?:Exception|Error|Throwable|V8 errors stack trace)[:\r\n]/,
57
+ :java_after_exception),
58
+ rule(:java_after_exception, /^[\t ]*nested exception is:[\t ]*/,
59
+ :java_start_exception),
60
+ rule(:java_after_exception, /^[\r\n]*$/, :java_after_exception),
61
+ rule([:java_after_exception, :java], /^[\t ]+(?:eval )?at /, :java),
62
+
63
+ rule([:java_after_exception, :java],
64
+ # C# nested exception.
65
+ /^[\t ]+--- End of inner exception stack trace ---$/,
55
66
  :java),
56
- rule(:java, /^[\t ]+(?:eval )?at /, :java),
57
- rule(:java, /^[\t ]*(?:Caused by|Suppressed):/, :java),
58
- rule(:java, /^[\t ]*... \d+\ more/, :java)
67
+
68
+ rule([:java_after_exception, :java],
69
+ # C# exception from async code.
70
+ /^--- End of stack trace from previous (?x:
71
+ )location where exception was thrown ---$/,
72
+ :java),
73
+
74
+ rule([:java_after_exception, :java], /^[\t ]*(?:Caused by|Suppressed):/,
75
+ :java_after_exception),
76
+ rule([:java_after_exception, :java],
77
+ /^[\t ]*... \d+ (?:more|common frames omitted)/, :java)
59
78
  ].freeze
60
79
 
61
80
  PYTHON_RULES = [
@@ -76,13 +95,14 @@ module Fluent
76
95
 
77
96
  GO_RULES = [
78
97
  rule(:start_state, /\bpanic: /, :go_after_panic),
98
+ rule(:start_state, /http: panic serving/, :go_goroutine),
79
99
  rule(:go_after_panic, /^$/, :go_goroutine),
100
+ rule([:go_after_panic, :go_after_signal, :go_frame_1],
101
+ /^$/, :go_goroutine),
80
102
  rule(:go_after_panic, /^\[signal /, :go_after_signal),
81
- rule(:go_after_signal, /^$/, :go_goroutine),
82
103
  rule(:go_goroutine, /^goroutine \d+ \[[^\]]+\]:$/, :go_frame_1),
83
104
  rule(:go_frame_1, /^(?:[^\s.:]+\.)*[^\s.():]+\(|^created by /,
84
105
  :go_frame_2),
85
- rule(:go_frame_1, /^$/, :go_goroutine),
86
106
  rule(:go_frame_2, /^\s/, :go_frame_1)
87
107
  ].freeze
88
108
 
@@ -169,7 +189,9 @@ module Fluent
169
189
  rule_config.each do |r|
170
190
  target = ExceptionDetectorConfig::RuleTarget.new(r[:pattern],
171
191
  r[:to_state])
172
- @rules[r[:from_state]] << target
192
+ r[:from_states].each do |from_state|
193
+ @rules[from_state] << target
194
+ end
173
195
  end
174
196
  end
175
197
 
@@ -234,20 +256,22 @@ module Fluent
234
256
  # message_field may contain the empty string. In this case, the
235
257
  # TraceAccumulator 'learns' the field name from the first record by checking
236
258
  # for some pre-defined common field names of text logs.
237
- # 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
238
262
  # of data to be buffered. The default value 0 indicates 'no limit'.
239
- def initialize(message_field, languages, max_lines: 0, max_bytes: 0,
240
- &emit_callback)
263
+ def initialize(message_field, languages, **options, &emit_callback)
241
264
  @exception_detector = Fluent::ExceptionDetector.new(*languages)
242
- @max_lines = max_lines
243
- @max_bytes = max_bytes
244
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
245
270
  @messages = []
246
271
  @buffer_start_time = Time.now
247
272
  @buffer_size = 0
248
273
  @first_record = nil
249
274
  @first_timestamp = nil
250
- @emit = emit_callback
251
275
  end
252
276
 
253
277
  def push(time_sec, record)
@@ -338,8 +362,14 @@ module Fluent
338
362
  @buffer_start_time = Time.now
339
363
  end
340
364
  unless message.nil?
341
- @messages << message
342
- @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
343
373
  end
344
374
  end
345
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
@@ -59,7 +59,35 @@ Caused by: com.example.myproject.MyProjectServletException
59
59
  at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
60
60
  at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
61
61
  at com.example.myproject.OpenSessionInViewFilter.doFilter(OpenSessionInViewFilter.java:30)
62
- ... 27 more
62
+ ... 27 common frames omitted
63
+ END
64
+
65
+ NESTED_JAVA_EXC = <<END.freeze
66
+ java.lang.RuntimeException: javax.mail.SendFailedException: Invalid Addresses;
67
+ nested exception is:
68
+ com.sun.mail.smtp.SMTPAddressFailedException: 550 5.7.1 <[REDACTED_EMAIL_ADDRESS]>... Relaying denied
69
+
70
+ at com.nethunt.crm.api.server.adminsync.AutomaticEmailFacade.sendWithSmtp(AutomaticEmailFacade.java:236)
71
+ at com.nethunt.crm.api.server.adminsync.AutomaticEmailFacade.sendSingleEmail(AutomaticEmailFacade.java:285)
72
+ at com.nethunt.crm.api.server.adminsync.AutomaticEmailFacade.lambda$sendSingleEmail$3(AutomaticEmailFacade.java:254)
73
+ at java.util.Optional.ifPresent(Optional.java:159)
74
+ at com.nethunt.crm.api.server.adminsync.AutomaticEmailFacade.sendSingleEmail(AutomaticEmailFacade.java:253)
75
+ at com.nethunt.crm.api.server.adminsync.AutomaticEmailFacade.sendSingleEmail(AutomaticEmailFacade.java:249)
76
+ at com.nethunt.crm.api.email.EmailSender.lambda$notifyPerson$0(EmailSender.java:80)
77
+ at com.nethunt.crm.api.util.ManagedExecutor.lambda$execute$0(ManagedExecutor.java:36)
78
+ at com.nethunt.crm.api.util.RequestContextActivator.lambda$withRequestContext$0(RequestContextActivator.java:36)
79
+ at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
80
+ at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
81
+ at java.base/java.lang.Thread.run(Thread.java:748)
82
+ Caused by: javax.mail.SendFailedException: Invalid Addresses;
83
+ nested exception is:
84
+ com.sun.mail.smtp.SMTPAddressFailedException: 550 5.7.1 <[REDACTED_EMAIL_ADDRESS]>... Relaying denied
85
+
86
+ at com.sun.mail.smtp.SMTPTransport.rcptTo(SMTPTransport.java:2064)
87
+ at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1286)
88
+ at com.nethunt.crm.api.server.adminsync.AutomaticEmailFacade.sendWithSmtp(AutomaticEmailFacade.java:229)
89
+ ... 12 more
90
+ Caused by: com.sun.mail.smtp.SMTPAddressFailedException: 550 5.7.1 <[REDACTED_EMAIL_ADDRESS]>... Relaying denied
63
91
  END
64
92
 
65
93
  NODE_JS_EXC = <<END.freeze
@@ -214,6 +242,26 @@ created by main.main
214
242
  server.go:20 +0x91
215
243
  END
216
244
 
245
+ GO_HTTP = <<END.freeze
246
+ 2019/01/15 07:48:05 http: panic serving [::1]:54143: test panic
247
+ goroutine 24 [running]:
248
+ net/http.(*conn).serve.func1(0xc00007eaa0)
249
+ /usr/local/go/src/net/http/server.go:1746 +0xd0
250
+ panic(0x12472a0, 0x12ece10)
251
+ /usr/local/go/src/runtime/panic.go:513 +0x1b9
252
+ main.doPanic(0x12f0ea0, 0xc00010e1c0, 0xc000104400)
253
+ /Users/ingvar/src/go/src/httppanic.go:8 +0x39
254
+ net/http.HandlerFunc.ServeHTTP(0x12be2e8, 0x12f0ea0, 0xc00010e1c0, 0xc000104400)
255
+ /usr/local/go/src/net/http/server.go:1964 +0x44
256
+ net/http.(*ServeMux).ServeHTTP(0x14a17a0, 0x12f0ea0, 0xc00010e1c0, 0xc000104400)
257
+ /usr/local/go/src/net/http/server.go:2361 +0x127
258
+ net/http.serverHandler.ServeHTTP(0xc000085040, 0x12f0ea0, 0xc00010e1c0, 0xc000104400)
259
+ /usr/local/go/src/net/http/server.go:2741 +0xab
260
+ net/http.(*conn).serve(0xc00007eaa0, 0x12f10a0, 0xc00008a780)
261
+ /usr/local/go/src/net/http/server.go:1847 +0x646
262
+ created by net/http.(*Server).Serve
263
+ /usr/local/go/src/net/http/server.go:2851 +0x2f5
264
+ END
217
265
  CSHARP_EXC = <<END.freeze
218
266
  System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.
219
267
  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
@@ -228,6 +276,33 @@ System.Collections.Generic.KeyNotFoundException: The given key was not present i
228
276
  at System.Threading.Thread.StartInternal () [0x00000] in <filename unknown>:0
229
277
  END
230
278
 
279
+ CSHARP_NESTED_EXC = <<END.freeze
280
+ System.InvalidOperationException: This is the outer exception ---> System.InvalidOperationException: This is the inner exception
281
+ at ExampleApp.NestedExceptionExample.LowestLevelMethod() in c:/ExampleApp/ExampleApp/NestedExceptionExample.cs:line 33
282
+ at ExampleApp.NestedExceptionExample.ThirdLevelMethod() in c:/ExampleApp/ExampleApp/NestedExceptionExample.cs:line 28
283
+ at ExampleApp.NestedExceptionExample.SecondLevelMethod() in c:/ExampleApp/ExampleApp/NestedExceptionExample.cs:line 18
284
+ --- End of inner exception stack trace ---
285
+ at ExampleApp.NestedExceptionExample.SecondLevelMethod() in c:/ExampleApp/ExampleApp/NestedExceptionExample.cs:line 22
286
+ at ExampleApp.NestedExceptionExample.TopLevelMethod() in c:/ExampleApp/ExampleApp/NestedExceptionExample.cs:line 11
287
+ at ExampleApp.Program.Main(String[] args) in c:/ExampleApp/ExampleApp/Program.cs:line 11
288
+ END
289
+
290
+ CSHARP_ASYNC_EXC = <<END.freeze
291
+ System.InvalidOperationException: This is an exception
292
+ at ExampleApp2.AsyncExceptionExample.LowestLevelMethod() in c:/ExampleApp/ExampleApp/AsyncExceptionExample.cs:line 36
293
+ at ExampleApp2.AsyncExceptionExample.<ThirdLevelMethod>d__2.MoveNext() in c:/ExampleApp/ExampleApp/AsyncExceptionExample.cs:line 31
294
+ --- End of stack trace from previous location where exception was thrown ---
295
+ at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
296
+ at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
297
+ at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
298
+ at ExampleApp2.AsyncExceptionExample.<SecondLevelMethod>d__1.MoveNext() in c:/ExampleApp/ExampleApp/AsyncExceptionExample.cs:line 25
299
+ --- End of stack trace from previous location where exception was thrown ---
300
+ at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
301
+ at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
302
+ at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
303
+ at ExampleApp2.AsyncExceptionExample.<TopLevelMethod>d__0.MoveNext() in c:/ExampleApp/ExampleApp/AsyncExceptionExample.cs:line 14
304
+ END
305
+
231
306
  RUBY_EXC = <<END.freeze
232
307
  NoMethodError (undefined method `resursivewordload' for #<BooksController:0x007f8dd9a0c738>):
233
308
  app/controllers/books_controller.rb:69:in `recursivewordload'
@@ -531,6 +606,7 @@ END
531
606
  def test_java
532
607
  check_exception(JAVA_EXC, false)
533
608
  check_exception(COMPLEX_JAVA_EXC, false)
609
+ check_exception(NESTED_JAVA_EXC, false)
534
610
  end
535
611
 
536
612
  def test_js
@@ -541,6 +617,8 @@ END
541
617
 
542
618
  def test_csharp
543
619
  check_exception(CSHARP_EXC, false)
620
+ check_exception(CSHARP_NESTED_EXC, false)
621
+ check_exception(CSHARP_ASYNC_EXC, false)
544
622
  end
545
623
 
546
624
  def test_python
@@ -556,6 +634,7 @@ END
556
634
  check_exception(GO_EXC, false)
557
635
  check_exception(GO_ON_GAE_EXC, false)
558
636
  check_exception(GO_SIGNAL_EXC, false)
637
+ check_exception(GO_HTTP, false)
559
638
  end
560
639
 
561
640
  def test_ruby
@@ -598,6 +677,8 @@ END
598
677
  check_exception(GO_ON_GAE_EXC, false)
599
678
  check_exception(GO_SIGNAL_EXC, false)
600
679
  check_exception(CSHARP_EXC, false)
680
+ check_exception(CSHARP_NESTED_EXC, false)
681
+ check_exception(CSHARP_ASYNC_EXC, false)
601
682
  check_exception(V8_JS_EXC, false)
602
683
  check_exception(RUBY_EXC, false)
603
684
  check_exception(DART_ERR, false)
@@ -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,15 +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.10
4
+ version: 0.0.14
5
5
  platform: ruby
6
6
  authors:
7
- - Thomas Schickinger
8
- - Igor Peshansky
7
+ - Stackdriver Agents
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2018-04-12 00:00:00.000000000 Z
11
+ date: 2021-09-07 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: fluentd
@@ -87,15 +86,13 @@ description: |2
87
86
  same stack trace into one multi-line message.
88
87
  This is an official Google Ruby gem.
89
88
  email:
90
- - schickin@google.com
91
- - igorp@google.com
89
+ - stackdriver-agents@google.com
92
90
  executables: []
93
91
  extensions: []
94
92
  extra_rdoc_files: []
95
93
  files:
96
94
  - CONTRIBUTING
97
95
  - Gemfile
98
- - Gemfile.lock
99
96
  - LICENSE
100
97
  - README.rdoc
101
98
  - Rakefile
@@ -125,8 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
125
122
  - !ruby/object:Gem::Version
126
123
  version: '0'
127
124
  requirements: []
128
- rubyforge_project:
129
- rubygems_version: 2.6.14
125
+ rubygems_version: 3.0.8
130
126
  signing_key:
131
127
  specification_version: 4
132
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.10)
5
- fluentd (>= 0.10)
6
-
7
- GEM
8
- remote: https://rubygems.org/
9
- specs:
10
- ast (2.4.0)
11
- cool.io (1.5.3)
12
- dig_rb (1.0.1)
13
- flexmock (2.3.6)
14
- fluentd (1.1.0)
15
- cool.io (>= 1.4.5, < 2.0.0)
16
- dig_rb (~> 1.0.0)
17
- http_parser.rb (>= 0.5.1, < 0.7.0)
18
- msgpack (>= 0.7.0, < 2.0.0)
19
- serverengine (>= 2.0.4, < 3.0.0)
20
- sigdump (~> 0.2.2)
21
- strptime (>= 0.2.2, < 1.0.0)
22
- tzinfo (~> 1.0)
23
- tzinfo-data (~> 1.0)
24
- yajl-ruby (~> 1.0)
25
- http_parser.rb (0.6.0)
26
- msgpack (1.2.4)
27
- parser (2.5.0.3)
28
- ast (~> 2.4.0)
29
- power_assert (1.1.1)
30
- powerpack (0.1.1)
31
- rainbow (2.2.2)
32
- rake
33
- rake (10.5.0)
34
- rubocop (0.42.0)
35
- parser (>= 2.3.1.1, < 3.0)
36
- powerpack (~> 0.1)
37
- rainbow (>= 1.99.1, < 3.0)
38
- ruby-progressbar (~> 1.7)
39
- unicode-display_width (~> 1.0, >= 1.0.1)
40
- ruby-progressbar (1.9.0)
41
- serverengine (2.0.6)
42
- sigdump (~> 0.2.2)
43
- sigdump (0.2.4)
44
- strptime (0.2.3)
45
- test-unit (3.2.7)
46
- power_assert
47
- thread_safe (0.3.6)
48
- tzinfo (1.2.5)
49
- thread_safe (~> 0.1)
50
- tzinfo-data (1.2018.3)
51
- tzinfo (>= 1.0.0)
52
- unicode-display_width (1.3.0)
53
- yajl-ruby (1.3.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
- 1.16.1