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