right_support 2.11.3 → 2.12.1

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.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +4 -0
  3. data/VERSION +1 -1
  4. data/lib/right_support/notifiers/airbrake.rb +194 -0
  5. data/lib/right_support/notifiers/base.rb +73 -0
  6. data/lib/right_support/notifiers/blacklisters/base.rb +48 -0
  7. data/lib/right_support/notifiers/blacklisters/canonical.rb +60 -0
  8. data/lib/right_support/notifiers/blacklisters/regular_expression.rb +86 -0
  9. data/{features/support/file_utils_bundler_mixin.rb → lib/right_support/notifiers/blacklisters/simple.rb} +21 -20
  10. data/lib/right_support/notifiers/blacklisters/snake_case.rb +60 -0
  11. data/lib/right_support/notifiers/blacklisters/wildcard.rb +65 -0
  12. data/lib/right_support/notifiers/blacklisters.rb +34 -0
  13. data/lib/right_support/notifiers/logger.rb +94 -0
  14. data/lib/right_support/notifiers/notification.rb +57 -0
  15. data/lib/right_support/notifiers/utilities/backtrace_decoder.rb +234 -0
  16. data/lib/right_support/notifiers/utilities.rb +29 -0
  17. data/lib/right_support/notifiers.rb +32 -0
  18. data/lib/right_support/rack/request_logger.rb +13 -9
  19. data/lib/right_support.rb +1 -0
  20. data/right_support.gemspec +19 -70
  21. metadata +17 -69
  22. data/.coveralls.yml +0 -2
  23. data/.rspec +0 -3
  24. data/.simplecov +0 -6
  25. data/.travis.yml +0 -13
  26. data/Gemfile +0 -51
  27. data/Gemfile.lock +0 -153
  28. data/features/balancer_error_handling.feature +0 -34
  29. data/features/balancer_health_check.feature +0 -33
  30. data/features/hash_tools.feature +0 -27
  31. data/features/http_client_timeout.feature +0 -19
  32. data/features/serialization.feature +0 -113
  33. data/features/step_definitions/hash_tools_steps.rb +0 -41
  34. data/features/step_definitions/http_client_steps.rb +0 -27
  35. data/features/step_definitions/request_balancer_steps.rb +0 -93
  36. data/features/step_definitions/ruby_steps.rb +0 -176
  37. data/features/step_definitions/serialization_steps.rb +0 -133
  38. data/features/step_definitions/server_steps.rb +0 -134
  39. data/features/support/env.rb +0 -148
  40. data/right_support.rconf +0 -9
  41. data/spec/config/feature_set_spec.rb +0 -83
  42. data/spec/crypto/signed_hash_spec.rb +0 -73
  43. data/spec/data/hash_tools_spec.rb +0 -602
  44. data/spec/data/mash_spec.rb +0 -313
  45. data/spec/data/token_spec.rb +0 -21
  46. data/spec/data/uuid_spec.rb +0 -45
  47. data/spec/db/cassandra_model_part1_spec.rb +0 -84
  48. data/spec/db/cassandra_model_part2_spec.rb +0 -73
  49. data/spec/db/cassandra_model_spec.rb +0 -375
  50. data/spec/fixtures/encrypted_priv_rsa.pem +0 -30
  51. data/spec/fixtures/good_priv_dsa.pem +0 -12
  52. data/spec/fixtures/good_priv_rsa.pem +0 -15
  53. data/spec/fixtures/good_pub_dsa.ssh +0 -1
  54. data/spec/fixtures/good_pub_rsa.pem +0 -5
  55. data/spec/fixtures/good_pub_rsa.ssh +0 -1
  56. data/spec/log/exception_logger_spec.rb +0 -76
  57. data/spec/log/filter_logger_spec.rb +0 -66
  58. data/spec/log/mixin_spec.rb +0 -141
  59. data/spec/log/multiplexer_spec.rb +0 -54
  60. data/spec/log/null_logger_spec.rb +0 -36
  61. data/spec/log/step_level_logger_spec.rb +0 -49
  62. data/spec/log/system_logger_spec.rb +0 -172
  63. data/spec/net/address_helper_spec.rb +0 -57
  64. data/spec/net/dns_spec.rb +0 -187
  65. data/spec/net/http_client_spec.rb +0 -181
  66. data/spec/net/lb/health_check_spec.rb +0 -417
  67. data/spec/net/lb/round_robin_spec.rb +0 -15
  68. data/spec/net/lb/sticky_spec.rb +0 -92
  69. data/spec/net/request_balancer_spec.rb +0 -690
  70. data/spec/net/s3_helper_spec.rb +0 -160
  71. data/spec/net/ssl_spec.rb +0 -42
  72. data/spec/net/string_encoder_spec.rb +0 -58
  73. data/spec/rack/log_setter_spec.rb +0 -5
  74. data/spec/rack/request_logger_spec.rb +0 -225
  75. data/spec/rack/request_tracker_spec.rb +0 -115
  76. data/spec/rack/runtime_spec.rb +0 -49
  77. data/spec/ruby/easy_singleton_spec.rb +0 -72
  78. data/spec/ruby/object_extensions_spec.rb +0 -27
  79. data/spec/ruby/string_extensions_spec.rb +0 -98
  80. data/spec/spec_helper.rb +0 -188
  81. data/spec/stats/activity_spec.rb +0 -425
  82. data/spec/stats/exceptions_spec.rb +0 -247
  83. data/spec/stats/helpers_spec.rb +0 -685
  84. data/spec/validation/openssl_spec.rb +0 -37
  85. data/spec/validation/ssh_spec.rb +0 -39
@@ -0,0 +1,34 @@
1
+ #
2
+ # Copyright (c) 2016 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ module RightSupport
24
+ module Notifier
25
+ module Blacklister
26
+ autoload :Base, 'right_support/notifiers/blacklisters/base'
27
+ autoload :Canonical, 'right_support/notifiers/blacklisters/canonical'
28
+ autoload :RegularExpression, 'right_support/notifiers/blacklisters/regular_expression'
29
+ autoload :Simple, 'right_support/notifiers/blacklisters/simple'
30
+ autoload :SnakeCase, 'right_support/notifiers/blacklisters/snake_case'
31
+ autoload :Wildcard, 'right_support/notifiers/blacklisters/wildcard'
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,94 @@
1
+ #
2
+ # Copyright (c) 2016 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require 'airbrake-ruby'
24
+
25
+ class RightSupport::Notifier::Logger < RightSupport::Notifier::Base
26
+
27
+ # arbitrary upper bound on payload dumped to log output.
28
+ MAX_PAYLOAD = 4096
29
+
30
+ def notify(notification)
31
+ # payload is usually not logged unless DEBUG_MODE=true but for consistency
32
+ # we will always include it in the log with the error message.
33
+ #
34
+ # note that there is no payload blacklisting as access to view the logs in
35
+ # production is already restricted. what is most important about
36
+ # blacklisting data is that it not reach the less secure error notification
37
+ # services where it is stored permanently.
38
+ payload = notification.payload
39
+ if payload.empty?
40
+ payload = nil
41
+ else
42
+ payload = payload.inspect[0, MAX_PAYLOAD]
43
+ end
44
+
45
+ # we are interested in the root cause for logging purposes except when
46
+ # DEBUG_MODE==true, in which case iterate over all causes without the limit
47
+ # on backtrace size.
48
+ if debug_mode?
49
+ backtrace_decoder.walk_error(notification.error, raw_trace: true) do |cause|
50
+ log_error(notification, cause, cause.backtrace, payload)
51
+ payload = nil # do not repeat payload
52
+ end
53
+ else
54
+ cause, frames = backtrace_decoder.walk_error(notification.error)
55
+ log_error(notification, cause, frames, payload)
56
+ end
57
+ true
58
+ rescue ::Exception => e
59
+ lines = [e.class.name, e.message, e.backtrace].flatten.compact
60
+ msg = "Failed to notify: #{lines.join("\n")}"
61
+ logger.error(msg)
62
+ true
63
+ end
64
+
65
+ private
66
+
67
+ def log_error(notification, cause, trace_or_frames, payload)
68
+ lines = []
69
+ lines << "[%s] Error in %s#%s" % [
70
+ notification.error_token,
71
+ notification.component,
72
+ notification.action
73
+ ]
74
+ lines << ''
75
+ lines << "Error: %s %s" % [cause.class.name, cause.message]
76
+ lines << ''
77
+ if (trace_or_frames || []).empty?
78
+ lines << 'Missing backtrace.'
79
+ else
80
+ lines << 'Dump:'
81
+ if trace_or_frames.first.is_a?(::String)
82
+ lines += trace_or_frames.map { |t| " #{t}" }
83
+ else
84
+ lines << ::RightSupport::Notifier::Utility::BacktraceDecoder.format_frames(trace_or_frames)
85
+ end
86
+ end
87
+ if payload
88
+ lines << ''
89
+ lines << "Payload: #{payload}"
90
+ end
91
+ logger.error(lines.join("\n"))
92
+ end
93
+
94
+ end # RightSupport::Notifier::Logger
@@ -0,0 +1,57 @@
1
+ #
2
+ # Copyright (c) 2016 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ # definition for a notification to be sent to notifiers.
24
+ class RightSupport::Notifier::Notification
25
+
26
+ attr_reader :error, :error_token, :env, :payload, :global_session
27
+ attr_reader :component, :action
28
+
29
+ # @param [Exception] error leading to notification
30
+ # @param [Hash] options
31
+ # @option options [String] :error_token or nil to generate a new token
32
+ # @option options [Hash] :env from Rack or nil or empty
33
+ # @option options [Hash] :global_session or nil or empty
34
+ # @option options [String] :component as optional name of component (the
35
+ # controller name, etc.). default='unknown'.
36
+ # @option options [String] :action as optional name of action (the controller
37
+ # method invoked by request URI, etc.). default='unknown'.
38
+ def initialize(error, options = {})
39
+ options = {
40
+ error_token: nil,
41
+ env: nil,
42
+ payload: nil,
43
+ global_session: nil,
44
+ component: 'unknown',
45
+ action: 'unknown',
46
+ }.merge(options)
47
+
48
+ @error = error
49
+ @error_token = (options[:error_token] || ::RightSupport::Data::Token.generate).to_s
50
+ @env = (options[:env] || {}).to_hash
51
+ @payload = (options[:payload] || {}).to_hash
52
+ @global_session = (options[:global_session] || {}).to_hash
53
+ @component = options[:component].to_s
54
+ @action = options[:action].to_s
55
+ end
56
+
57
+ end
@@ -0,0 +1,234 @@
1
+ #
2
+ # Copyright (c) 2016 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ # decoder for ruby-formatted backtraces.
24
+ class RightSupport::Notifier::Utility::BacktraceDecoder
25
+
26
+ # default limit on size of decoded stack trace arrays.
27
+ DEFAULT_BACKTRACE_LIMIT = 10
28
+
29
+ # hard limit on size of decoded stack trace arrays.
30
+ MAX_BACKTRACE_LIMIT = 255
31
+
32
+ # limit on error walk when following cause chain.
33
+ WALK_LIMIT = 32
34
+
35
+ # regular expression used to decode a line of ruby backtrace.
36
+ BACKTRACE_REGEXP = /^(.*):(\d+)?:in `(.*)'$/
37
+
38
+ # ellipsis used for limited trace.
39
+ ELLIPSIS = '...'.freeze
40
+
41
+ attr_reader :backtrace_offset, :backtrace_limit, :path_blacklist, :root_path
42
+
43
+ class Frame
44
+ attr_reader :file, :line, :function
45
+
46
+ def initialize(file, line, function)
47
+ @file = file
48
+ @line = line
49
+ @function = function
50
+ end
51
+
52
+ def to_s
53
+ # the following encoded format is the ruby style (strange quotes, etc.)
54
+ "#{@file}:#{@line}:in `#{@function}'"
55
+ end
56
+ end
57
+
58
+ # @param [Hash] options
59
+ # @option options [Integer] :backtrace_offset as number of stack frames to
60
+ # skip initially. default = 0.
61
+ # @option options [Integer] :backtrace_limit as maximum number of stack frames
62
+ # to decode or negative for all. default = 10.
63
+ # @option options [String|Array] :path_blacklist for unwanted paths in
64
+ # backtrace. the blacklisted path substring causes the frame to be omitted
65
+ # when it appears anywhere in the traced path. default = none.
66
+ # @option options [String] :root_path for application to be removed from
67
+ # decoded file paths. default = <working directory>
68
+ def initialize(options = {})
69
+ options = {
70
+ backtrace_offset: 0,
71
+ backtrace_limit: DEFAULT_BACKTRACE_LIMIT,
72
+ path_blacklist: nil
73
+ }.merge(options)
74
+ @backtrace_offset = Integer(options[:backtrace_offset])
75
+ @backtrace_limit = Integer(options[:backtrace_limit])
76
+ if @backtrace_limit < 0 || @backtrace_limit > MAX_BACKTRACE_LIMIT
77
+ @backtrace_limit = MAX_BACKTRACE_LIMIT
78
+ end
79
+ @path_blacklist = Array(options[:path_blacklist])
80
+
81
+ # resolve current application root path.
82
+ @root_path = ::File.expand_path(options[:root_path] || ::Dir.pwd) + '/'
83
+ @root_parent_path = ::File.dirname(@root_path) + '/'
84
+
85
+ # resolve current 'lib/ruby' path.
86
+ @ruby_lib_path = ::File.expand_path(::RbConfig::CONFIG['rubylibprefix']) + '/'
87
+ @ruby_lib_parent_path = ::File.dirname(@ruby_lib_path) + '/'
88
+ end
89
+
90
+ # decodes lines of backtrace in string form into their component parts.
91
+ #
92
+ # borrowed originally from:
93
+ # @see https://github.com/airbrake/airbrake-ruby/blob/master/lib/airbrake-ruby/backtrace.rb
94
+ #
95
+ # @param [Array] trace from error.backtrace or Kernel.caller or empty or nil
96
+ #
97
+ # @return [Array] frames of decoded trace or empty
98
+ def decode(trace)
99
+ frames = []
100
+ trace ||= []
101
+ trace = trace[@backtrace_offset..-1] if @backtrace_offset > 0
102
+ gems_finder = '/gems/'
103
+ seen_root_path = false
104
+ trace.each do |t|
105
+ has_root_path = false
106
+ omit = false
107
+ file = nil
108
+ line = 0
109
+ function = nil
110
+ if m = BACKTRACE_REGEXP.match(t)
111
+ file = m[1]
112
+ line = Integer(m[2])
113
+ function = m[3]
114
+ @path_blacklist.each do |pbl|
115
+ if file.include?(pbl)
116
+ omit = true
117
+ break
118
+ end
119
+ end
120
+ unless omit
121
+ # remove base path from the frame file path for simplicity and because
122
+ # the absolute root path is only meaningful on the file system where
123
+ # the code is running. the root path basename (i.e. the application
124
+ # directory name) is kept for display.
125
+ if file.start_with?(@root_path)
126
+ file = file[@root_parent_path.length..-1]
127
+ has_root_path = true
128
+ elsif file.start_with?(@ruby_lib_path)
129
+ # remove absoluteness of the lib/ruby path for the same reason.
130
+ file = file[@ruby_lib_parent_path.length..-1]
131
+ end
132
+
133
+ # remove everything before '/gems/' on the assumption that the
134
+ # rubygems or '/vendor/bundle/.../gems/' prefixes are only noise that
135
+ # makes the trace harder to read for a human. '/gems/' may also appear
136
+ # more than once so use the last found.
137
+ if gems_offset = file.rindex(gems_finder)
138
+ file = file[gems_offset + gems_finder.length..-1]
139
+ end
140
+ end
141
+ else
142
+ # show a failure message for other patterns such as java plugins for
143
+ # ruby and nonsense only because we have no known use cases.
144
+ # FIX: support any needed patterns.
145
+ file = t
146
+ function = '<< trace decoder error >>'
147
+ end
148
+ unless omit
149
+ # enforce the backtrace limit when configured *unless* we have not yet
150
+ # seen the root path (i.e. the application root). in this case we want
151
+ # to keep walking the backtrace until we have shown at least one frame
152
+ # that references the application. otherwise the backtrace may not be
153
+ # useful for debugging purposes.
154
+ done = seen_root_path && @backtrace_limit >= 0 && frames.size >= @backtrace_limit
155
+
156
+ # show at least one full application frame before appending ellipsis.
157
+ seen_root_path ||= has_root_path
158
+
159
+ # set an ellipsis as function name of last frame when over limit.
160
+ frames << Frame.new(file, line, done ? ELLIPSIS : function)
161
+ break if done
162
+ end
163
+ end
164
+ frames
165
+ end
166
+
167
+ # @return [Array] the current frames for the caller with limit, filters, etc.
168
+ def caller
169
+ # note that Kernel.caller always omits the immediate frame so that the
170
+ # caller of this method is at the top of the trace.
171
+ decode(::Kernel.caller)
172
+ end
173
+
174
+ # walks the error to find the backtrace closest to the root cause.
175
+ #
176
+ # @param [Exception] error to walk
177
+ # @param [Hash] options
178
+ # @option options [TrueClass|FalseClass] :raw_trace true to return the raw
179
+ # backtrace that was closest to cause, false to decode it (default).
180
+ #
181
+ # borrowed originally from:
182
+ # @see "https://github.com/airbrake/airbrake-ruby/blob/master/lib/airbrake-ruby/nested_exception.rb"
183
+ #
184
+ # @yield [cause] yields each walked error without decoding its backtrace.
185
+ # @yieldparam [Exception] error in cause chain that is currently being walked.
186
+ #
187
+ # @return [Array] tuple of [cause, trace_or_frames]
188
+ def walk_error(error, options={}, &callback)
189
+ options = {
190
+ raw_trace: false
191
+ }.merge(options)
192
+ cause = error
193
+ trace = cause.backtrace || ::Kernel.caller[1..-1]
194
+ callback.call(cause) if callback
195
+ counter = 0
196
+ while cause.respond_to?(:cause)
197
+ next_cause = cause.cause
198
+ if next_cause && next_cause != cause
199
+ cause = next_cause
200
+ callback.call(cause) if callback
201
+ trace = cause.backtrace if cause.backtrace
202
+
203
+ # sanity check that the error is wrapped an unbelievable number of
204
+ # times or that the cause != next_cause logic has failed somehow; no
205
+ # infinite loops.
206
+ counter += 1
207
+ break if counter >= WALK_LIMIT
208
+ else
209
+ break
210
+ end
211
+ end
212
+ unless options[:raw_trace]
213
+ trace = decode(trace)
214
+ end
215
+ [cause, trace]
216
+ end
217
+
218
+ # formats the given frames for display on the console.
219
+ #
220
+ # @param [Array] frames to format
221
+ #
222
+ # @return [String] formatted frames
223
+ def self.format_frames(frames)
224
+ frames.map do |frame|
225
+ if frame.function == ELLIPSIS
226
+ line = ELLIPSIS
227
+ else
228
+ line = frame
229
+ end
230
+ " #{line}" # indent and .to_s
231
+ end.join("\n")
232
+ end
233
+
234
+ end # RightSupport::Notifier::Utility::BacktraceDecoder
@@ -0,0 +1,29 @@
1
+ #
2
+ # Copyright (c) 2016 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ module RightSupport
24
+ module Notifier
25
+ module Utility
26
+ autoload :BacktraceDecoder, 'right_support/notifiers/utilities/backtrace_decoder'
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,32 @@
1
+ #
2
+ # Copyright (c) 2016 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ module RightSupport
24
+ module Notifier
25
+ autoload :Airbrake, 'right_support/notifiers/airbrake'
26
+ autoload :Base, 'right_support/notifiers/base'
27
+ autoload :Blacklister, 'right_support/notifiers/blacklisters'
28
+ autoload :Logger, 'right_support/notifiers/logger'
29
+ autoload :Notification, 'right_support/notifiers/notification'
30
+ autoload :Utility, 'right_support/notifiers/utilities'
31
+ end
32
+ end
@@ -35,8 +35,8 @@ module RightSupport::Rack
35
35
  # with Rack::Logger or another middleware that provides logging services.
36
36
  class RequestLogger
37
37
 
38
- # limit exception backtrace length; usually only the first few lines are
39
- # significant. the same error dumped repeatedly can make grepping difficult.
38
+ # default backtrace limit that can be overridden by configuring the
39
+ # middleware with a specific :backtrace_limit value.
40
40
  BACKTRACE_LIMIT = 10
41
41
 
42
42
  # for debug-mode only logging. debug-mode can also be enabled by the
@@ -79,6 +79,8 @@ module RightSupport::Rack
79
79
  # override the usual global session behavior of always responding with a
80
80
  # 'Set-Cookie' header regardless of status code. defaults to having the
81
81
  # same value as the :normalize_40x option.
82
+ # @option options [Integer] :backtrace_limit maximum number of stack frames
83
+ # to display on error or negative for all (default = 10)
82
84
  def initialize(app, options = {})
83
85
  @app = app
84
86
  @only = options[:only] || []
@@ -89,6 +91,8 @@ module RightSupport::Rack
89
91
  @normalize_40x = !!options[:normalize_40x]
90
92
  @normalize_40x_set_cookie = options[:normalize_40x_set_cookie]
91
93
  @normalize_40x_set_cookie = @normalize_40x if @normalize_40x_set_cookie.nil?
94
+ @backtrace_decoder = ::RightSupport::Notifier::Utility::BacktraceDecoder.new(
95
+ backtrace_limit: Integer(options[:backtrace_limit] || BACKTRACE_LIMIT))
92
96
  end
93
97
 
94
98
  # Add a logger to the Rack environment and call the next middleware.
@@ -224,18 +228,18 @@ module RightSupport::Rack
224
228
  # Formats a concise error message with limited backtrace, etc.
225
229
  #
226
230
  # @param [Exception] e to format
231
+ # @param [Exception] e to format
227
232
  #
228
233
  # @return [String] formatted error
229
- def self.format_exception_message(e)
230
- trace = e.backtrace || []
231
- if trace.size > BACKTRACE_LIMIT
232
- trace = trace[0, BACKTRACE_LIMIT] << '...'
233
- end
234
+ def self.format_exception_message(e, backtrace_decoder = nil)
235
+ backtrace_decoder ||= ::RightSupport::Notifier::Utility::BacktraceDecoder.new
236
+ trace = ::RightSupport::Notifier::Utility::BacktraceDecoder.format_frames(
237
+ backtrace_decoder.decode(e.backtrace))
234
238
  kind = e.class.name
235
239
  if (msg = e.message) && !msg.empty? && msg != kind
236
240
  kind = "#{kind}: #{msg}"
237
241
  end
238
- [kind, *trace].join("\n")
242
+ [kind, trace].join("\n")
239
243
  end
240
244
 
241
245
  # @return [String] accepted/generated request UUID or else 'missing' to
@@ -380,7 +384,7 @@ module RightSupport::Rack
380
384
  #
381
385
  # @return [TrueClass] always true
382
386
  def log_exception(logger, e)
383
- logger.error(self.class.format_exception_message(e))
387
+ logger.error(self.class.format_exception_message(e, @backtrace_decoder))
384
388
  rescue
385
389
  # no-op; something is seriously messed up by this point...
386
390
  end
data/lib/right_support.rb CHANGED
@@ -15,4 +15,5 @@ module RightSupport
15
15
  autoload :Stats, 'right_support/stats'
16
16
  autoload :CI, 'right_support/ci'
17
17
  autoload :Deployment, 'right_support/deployment'
18
+ autoload :Notifier, 'right_support/notifiers'
18
19
  end