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 +5 -5
- data/README.rdoc +26 -6
- data/Rakefile +17 -7
- data/fluent-plugin-detect-exceptions.gemspec +3 -3
- data/lib/fluent/plugin/exception_detector.rb +48 -18
- data/lib/fluent/plugin/out_detect_exceptions.rb +11 -5
- data/test/plugin/test_exception_detector.rb +82 -1
- data/test/plugin/test_out_detect_exceptions.rb +71 -8
- metadata +5 -9
- data/Gemfile.lock +0 -66
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f846941463ccc37acd71280e182d5ae7b0ffd0c95c609bfbfe67de64f86ff478
|
4
|
+
data.tar.gz: 1454c4ddae5ba4efa214972f2109f0a71d4984e87b49cefa3541cb87d92d6cc7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 585f0de72a4f845f39c0f7e753ca3661ad335b10c18d26930afc19bab246b205cb9d68d1fa506c87e5ea674fc9b1ce51b04c278aed5590d997875ea9b50b21d8
|
7
|
+
data.tar.gz: 987c453e5667c1736507500859a1f46612a56451f791afaf194574380afd31527da73f9ebd87de53faf2c40e5929ce0cdd0f2c3fbd0c9d4c89a2137bad40a9a1
|
data/README.rdoc
CHANGED
@@ -42,6 +42,16 @@ will also install and configure the gem.
|
|
42
42
|
|
43
43
|
The plugin supports the following parameters:
|
44
44
|
|
45
|
+
=== Required
|
46
|
+
|
47
|
+
[remove_tag_prefix] The prefix to remove from the input tag when outputting
|
48
|
+
a record. A prefix has to be a complete tag part.
|
49
|
+
Example: If remove_tag_prefix is set to 'foo', the input
|
50
|
+
tag foo.bar.baz is transformed to bar.baz and the input tag
|
51
|
+
'foofoo.bar' is not modified.
|
52
|
+
|
53
|
+
=== Optional
|
54
|
+
|
45
55
|
[message] Name of the field in the JSON record that contains the
|
46
56
|
single-line log messages that shall be scanned for exceptions.
|
47
57
|
If this is set to '', the plugin will try 'message' and 'log',
|
@@ -49,12 +59,6 @@ The plugin supports the following parameters:
|
|
49
59
|
This parameter is only applicable to structured (JSON) log streams.
|
50
60
|
Default: ''.
|
51
61
|
|
52
|
-
[remove_tag_prefix] The prefix to remove from the input tag when outputting
|
53
|
-
a record. A prefix has to be a complete tag part.
|
54
|
-
Example: If remove_tag_prefix is set to 'foo', the input
|
55
|
-
tag foo.bar.baz is transformed to bar.baz and the input tag
|
56
|
-
'foofoo.bar' is not modified. Default: empty string.
|
57
|
-
|
58
62
|
[languages] A list of language for which exception stack traces shall be
|
59
63
|
detected. The values in the list can be separated by commas or
|
60
64
|
written as JSON list.
|
@@ -66,6 +70,12 @@ The plugin supports the following parameters:
|
|
66
70
|
forwarded. If not set, incomplete exceptions stacks
|
67
71
|
are not flushed.
|
68
72
|
|
73
|
+
[force_line_breaks] Force line breaks between each lines when comibining exception stacks.
|
74
|
+
This is useful if your exception is formatted
|
75
|
+
as a single line. i.e., logs retrieved from the docker's
|
76
|
+
logging driver don't have any line break.
|
77
|
+
Default: false.
|
78
|
+
|
69
79
|
[max_lines] Maximum number of lines in a detected exception stack trace.
|
70
80
|
If this maximum number is exceeded, the exception stack trace
|
71
81
|
that has been detected so far will be output as a single
|
@@ -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
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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, :
|
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.
|
15
|
-
gem.authors = ['
|
16
|
-
gem.email = ['
|
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', :
|
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(
|
45
|
-
|
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
|
-
|
57
|
-
rule(:
|
58
|
-
|
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
|
-
|
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
|
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,
|
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
|
-
|
342
|
-
|
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(
|
95
|
-
|
96
|
-
|
96
|
+
Fluent::TraceAccumulator.new(
|
97
|
+
@message,
|
98
|
+
@languages,
|
99
|
+
force_line_breaks: @force_line_breaks,
|
100
|
+
max_lines: @max_lines,
|
101
|
+
max_bytes: @max_bytes
|
102
|
+
) do |t, r|
|
97
103
|
router.emit(out_tag, t, r)
|
98
104
|
end
|
99
105
|
end
|
@@ -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
|
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 =
|
141
|
+
cfg = %(
|
142
|
+
#{CONFIG}
|
143
|
+
languages #{language})
|
129
144
|
d = create_driver(cfg)
|
130
145
|
t = Time.now.to_i
|
131
146
|
|
@@ -156,12 +171,12 @@ END
|
|
156
171
|
|
157
172
|
# Validate that each line received is emitted separately as expected.
|
158
173
|
router_mock.should_receive(:emit)
|
159
|
-
.once.with(
|
174
|
+
.once.with(DEFAULT_TAG_STRIPPED, Integer,
|
160
175
|
'message' => json_line_with_exception,
|
161
176
|
'count' => 0)
|
162
177
|
|
163
178
|
router_mock.should_receive(:emit)
|
164
|
-
.once.with(
|
179
|
+
.once.with(DEFAULT_TAG_STRIPPED, Integer,
|
165
180
|
'message' => json_line_without_exception,
|
166
181
|
'count' => 1)
|
167
182
|
|
@@ -174,7 +189,9 @@ END
|
|
174
189
|
end
|
175
190
|
|
176
191
|
def test_single_language_config
|
177
|
-
cfg =
|
192
|
+
cfg = %(
|
193
|
+
#{CONFIG}
|
194
|
+
languages java)
|
178
195
|
d = create_driver(cfg)
|
179
196
|
t = Time.now.to_i
|
180
197
|
d.run do
|
@@ -185,7 +202,9 @@ END
|
|
185
202
|
end
|
186
203
|
|
187
204
|
def test_multi_language_config
|
188
|
-
cfg =
|
205
|
+
cfg = %(
|
206
|
+
#{CONFIG}
|
207
|
+
languages python, java)
|
189
208
|
d = create_driver(cfg)
|
190
209
|
t = Time.now.to_i
|
191
210
|
d.run do
|
@@ -196,7 +215,9 @@ END
|
|
196
215
|
end
|
197
216
|
|
198
217
|
def test_split_exception_after_timeout
|
199
|
-
cfg =
|
218
|
+
cfg = %(
|
219
|
+
#{CONFIG}
|
220
|
+
multiline_flush_interval 1)
|
200
221
|
d = create_driver(cfg)
|
201
222
|
t1 = 0
|
202
223
|
t2 = 0
|
@@ -227,6 +248,12 @@ END
|
|
227
248
|
assert_equal(make_logs(t1, JAVA_EXC + " at x\n at y\n"), d.events)
|
228
249
|
end
|
229
250
|
|
251
|
+
def test_remove_tag_prefix_is_required
|
252
|
+
cfg = ''
|
253
|
+
e = assert_raises(Fluent::ConfigError) { create_driver(cfg) }
|
254
|
+
assert_match(/remove_tag_prefix/, e.message)
|
255
|
+
end
|
256
|
+
|
230
257
|
def get_out_tags(remove_tag_prefix, original_tag)
|
231
258
|
cfg = "remove_tag_prefix #{remove_tag_prefix}"
|
232
259
|
d = create_driver(cfg, original_tag)
|
@@ -243,8 +270,42 @@ END
|
|
243
270
|
assert_equal(['prefix.plus.rest.of.the.tag'], tags)
|
244
271
|
end
|
245
272
|
|
273
|
+
def test_force_line_breaks_false
|
274
|
+
cfg = %(
|
275
|
+
#{CONFIG}
|
276
|
+
force_line_breaks true)
|
277
|
+
d = create_driver(cfg)
|
278
|
+
t = Time.now.to_i
|
279
|
+
d.run do
|
280
|
+
feed_lines(d, t, JAVA_EXC)
|
281
|
+
end
|
282
|
+
expected = JAVA_EXC
|
283
|
+
assert_equal(make_logs(t, *expected), d.events)
|
284
|
+
end
|
285
|
+
|
286
|
+
def test_force_line_breaks_true
|
287
|
+
cfg = %(
|
288
|
+
#{CONFIG}
|
289
|
+
force_line_breaks true)
|
290
|
+
d = create_driver(cfg)
|
291
|
+
t = Time.now.to_i
|
292
|
+
d.run do
|
293
|
+
feed_lines_without_line_breaks(d, t, JAVA_EXC)
|
294
|
+
end
|
295
|
+
# Expected: the first two lines of the exception are buffered and combined.
|
296
|
+
# Then the max_lines setting kicks in and the rest of the Python exception
|
297
|
+
# is logged line-by-line (since it's not an exception stack in itself).
|
298
|
+
# For the following Java stack trace, the two lines of the first exception
|
299
|
+
# are buffered and combined. So are the first two lines of the second
|
300
|
+
# exception. Then the rest is logged line-by-line.
|
301
|
+
expected = JAVA_EXC.chomp
|
302
|
+
assert_equal(make_logs(t, *expected), d.events)
|
303
|
+
end
|
304
|
+
|
246
305
|
def test_flush_after_max_lines
|
247
|
-
cfg =
|
306
|
+
cfg = %(
|
307
|
+
#{CONFIG}
|
308
|
+
max_lines 2)
|
248
309
|
d = create_driver(cfg)
|
249
310
|
t = Time.now.to_i
|
250
311
|
d.run do
|
@@ -263,7 +324,9 @@ END
|
|
263
324
|
end
|
264
325
|
|
265
326
|
def test_separate_streams
|
266
|
-
cfg =
|
327
|
+
cfg = %(
|
328
|
+
#{CONFIG}
|
329
|
+
stream stream)
|
267
330
|
d = create_driver(cfg)
|
268
331
|
t = Time.now.to_i
|
269
332
|
d.run do
|
metadata
CHANGED
@@ -1,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.
|
4
|
+
version: 0.0.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
8
|
-
- Igor Peshansky
|
7
|
+
- Stackdriver Agents
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
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
|
-
-
|
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
|
-
|
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
|