fluent-plugin-detect-exceptions 0.0.10 → 0.0.14

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.
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