honeybadger 1.16.7 → 2.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (200) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +19 -0
  3. data/README.md +37 -16
  4. data/bin/honeybadger +5 -0
  5. data/lib/honeybadger.rb +167 -191
  6. data/lib/honeybadger/agent.rb +136 -0
  7. data/lib/honeybadger/backend.rb +26 -0
  8. data/lib/honeybadger/backend/base.rb +66 -0
  9. data/lib/honeybadger/backend/debug.rb +12 -0
  10. data/lib/honeybadger/backend/null.rb +16 -0
  11. data/lib/honeybadger/backend/server.rb +51 -0
  12. data/lib/honeybadger/backend/test.rb +24 -0
  13. data/lib/honeybadger/backtrace.rb +29 -24
  14. data/lib/honeybadger/cli.rb +367 -0
  15. data/lib/honeybadger/config.rb +333 -0
  16. data/lib/honeybadger/config/callbacks.rb +70 -0
  17. data/lib/honeybadger/config/defaults.rb +175 -0
  18. data/lib/honeybadger/config/env.rb +40 -0
  19. data/lib/honeybadger/config/yaml.rb +43 -0
  20. data/lib/honeybadger/const.rb +28 -0
  21. data/lib/honeybadger/init/rails.rb +84 -0
  22. data/lib/honeybadger/init/sinatra.rb +27 -0
  23. data/lib/honeybadger/logging.rb +133 -0
  24. data/lib/honeybadger/notice.rb +243 -280
  25. data/lib/honeybadger/plugin.rb +110 -0
  26. data/lib/honeybadger/plugins/delayed_job.rb +22 -0
  27. data/lib/honeybadger/{integrations → plugins}/delayed_job/plugin.rb +6 -7
  28. data/lib/honeybadger/{integrations → plugins}/local_variables.rb +7 -8
  29. data/lib/honeybadger/{integrations → plugins}/net_http.rb +10 -8
  30. data/lib/honeybadger/plugins/passenger.rb +24 -0
  31. data/lib/honeybadger/plugins/rails.rb +61 -0
  32. data/lib/honeybadger/plugins/sidekiq.rb +35 -0
  33. data/lib/honeybadger/{integrations → plugins}/thor.rb +9 -8
  34. data/lib/honeybadger/{integrations → plugins}/unicorn.rb +10 -9
  35. data/lib/honeybadger/rack/error_notifier.rb +44 -27
  36. data/lib/honeybadger/rack/metrics_reporter.rb +41 -0
  37. data/lib/honeybadger/rack/request_hash.rb +50 -0
  38. data/lib/honeybadger/rack/user_feedback.rb +15 -10
  39. data/lib/honeybadger/rack/user_informer.rb +14 -3
  40. data/lib/honeybadger/trace.rb +185 -0
  41. data/lib/honeybadger/util/http.rb +79 -0
  42. data/lib/honeybadger/util/request_sanitizer.rb +35 -0
  43. data/lib/honeybadger/util/sanitizer.rb +71 -0
  44. data/lib/honeybadger/util/stats.rb +31 -0
  45. data/lib/honeybadger/version.rb +4 -0
  46. data/lib/honeybadger/worker.rb +224 -0
  47. data/lib/honeybadger/worker/batch.rb +50 -0
  48. data/lib/honeybadger/worker/metered_queue.rb +80 -0
  49. data/lib/honeybadger/worker/metrics_collection.rb +61 -0
  50. data/lib/honeybadger/worker/metrics_collector.rb +96 -0
  51. data/{lib/honeybadger/capistrano.rb → vendor/capistrano-honeybadger/lib/capistrano/honeybadger.rb} +1 -3
  52. data/vendor/capistrano-honeybadger/lib/capistrano/tasks/deploy.cap +76 -0
  53. data/vendor/capistrano-honeybadger/lib/honeybadger/capistrano.rb +2 -0
  54. data/{lib → vendor/capistrano-honeybadger/lib}/honeybadger/capistrano/legacy.rb +16 -15
  55. data/vendor/thor/lib/thor.rb +484 -0
  56. data/vendor/thor/lib/thor/actions.rb +319 -0
  57. data/vendor/thor/lib/thor/actions/create_file.rb +103 -0
  58. data/vendor/thor/lib/thor/actions/create_link.rb +59 -0
  59. data/vendor/thor/lib/thor/actions/directory.rb +118 -0
  60. data/vendor/thor/lib/thor/actions/empty_directory.rb +135 -0
  61. data/vendor/thor/lib/thor/actions/file_manipulation.rb +316 -0
  62. data/vendor/thor/lib/thor/actions/inject_into_file.rb +107 -0
  63. data/vendor/thor/lib/thor/base.rb +656 -0
  64. data/vendor/thor/lib/thor/command.rb +133 -0
  65. data/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +77 -0
  66. data/vendor/thor/lib/thor/core_ext/io_binary_read.rb +10 -0
  67. data/vendor/thor/lib/thor/core_ext/ordered_hash.rb +98 -0
  68. data/vendor/thor/lib/thor/error.rb +32 -0
  69. data/vendor/thor/lib/thor/group.rb +281 -0
  70. data/vendor/thor/lib/thor/invocation.rb +178 -0
  71. data/vendor/thor/lib/thor/line_editor.rb +17 -0
  72. data/vendor/thor/lib/thor/line_editor/basic.rb +35 -0
  73. data/vendor/thor/lib/thor/line_editor/readline.rb +88 -0
  74. data/vendor/thor/lib/thor/parser.rb +4 -0
  75. data/vendor/thor/lib/thor/parser/argument.rb +73 -0
  76. data/vendor/thor/lib/thor/parser/arguments.rb +175 -0
  77. data/vendor/thor/lib/thor/parser/option.rb +125 -0
  78. data/vendor/thor/lib/thor/parser/options.rb +218 -0
  79. data/vendor/thor/lib/thor/rake_compat.rb +71 -0
  80. data/vendor/thor/lib/thor/runner.rb +322 -0
  81. data/vendor/thor/lib/thor/shell.rb +81 -0
  82. data/vendor/thor/lib/thor/shell/basic.rb +421 -0
  83. data/vendor/thor/lib/thor/shell/color.rb +149 -0
  84. data/vendor/thor/lib/thor/shell/html.rb +126 -0
  85. data/vendor/thor/lib/thor/util.rb +267 -0
  86. data/vendor/thor/lib/thor/version.rb +3 -0
  87. metadata +97 -305
  88. data/Appraisals +0 -95
  89. data/CHANGELOG.md +0 -422
  90. data/Gemfile +0 -8
  91. data/Gemfile.lock +0 -136
  92. data/Guardfile +0 -5
  93. data/MIT-LICENSE +0 -32
  94. data/Rakefile +0 -159
  95. data/features/metal.feature +0 -20
  96. data/features/rack.feature +0 -55
  97. data/features/rails.feature +0 -343
  98. data/features/rails3.x.feature +0 -26
  99. data/features/rake.feature +0 -25
  100. data/features/sinatra.feature +0 -27
  101. data/features/standalone.feature +0 -73
  102. data/features/step_definitions/metal_steps.rb +0 -24
  103. data/features/step_definitions/rack_steps.rb +0 -18
  104. data/features/step_definitions/rails_steps.rb +0 -270
  105. data/features/step_definitions/rake_steps.rb +0 -17
  106. data/features/step_definitions/standalone_steps.rb +0 -12
  107. data/features/step_definitions/thor_steps.rb +0 -4
  108. data/features/support/env.rb +0 -22
  109. data/features/support/honeybadger_failure_shim.rb.template +0 -5
  110. data/features/support/honeybadger_shim.rb.template +0 -6
  111. data/features/support/rails.rb +0 -202
  112. data/features/support/rake/Rakefile +0 -68
  113. data/features/support/test.thor +0 -22
  114. data/features/thor.feature +0 -5
  115. data/gemfiles/binding_of_caller.gemfile +0 -13
  116. data/gemfiles/delayed_job.gemfile +0 -13
  117. data/gemfiles/rack.gemfile +0 -13
  118. data/gemfiles/rails.gemfile +0 -16
  119. data/gemfiles/rails2.3.gemfile +0 -15
  120. data/gemfiles/rails3.0.gemfile +0 -16
  121. data/gemfiles/rails3.1.gemfile +0 -16
  122. data/gemfiles/rails3.2.gemfile +0 -16
  123. data/gemfiles/rails4.0.gemfile +0 -16
  124. data/gemfiles/rails4.1.gemfile +0 -16
  125. data/gemfiles/rake.gemfile +0 -13
  126. data/gemfiles/sinatra.gemfile +0 -13
  127. data/gemfiles/standalone.gemfile +0 -12
  128. data/gemfiles/thor.gemfile +0 -13
  129. data/generators/honeybadger/honeybadger_generator.rb +0 -95
  130. data/generators/honeybadger/lib/insert_commands.rb +0 -34
  131. data/generators/honeybadger/lib/rake_commands.rb +0 -24
  132. data/generators/honeybadger/templates/capistrano_hook.rb +0 -6
  133. data/generators/honeybadger/templates/honeybadger_tasks.rake +0 -25
  134. data/generators/honeybadger/templates/initializer.rb +0 -6
  135. data/honeybadger.gemspec +0 -174
  136. data/lib/honeybadger/array.rb +0 -53
  137. data/lib/honeybadger/capistrano/tasks.rake +0 -73
  138. data/lib/honeybadger/configuration.rb +0 -397
  139. data/lib/honeybadger/dependency.rb +0 -65
  140. data/lib/honeybadger/integrations.rb +0 -9
  141. data/lib/honeybadger/integrations/delayed_job.rb +0 -20
  142. data/lib/honeybadger/integrations/passenger.rb +0 -18
  143. data/lib/honeybadger/integrations/sidekiq.rb +0 -37
  144. data/lib/honeybadger/monitor.rb +0 -17
  145. data/lib/honeybadger/monitor/railtie.rb +0 -53
  146. data/lib/honeybadger/monitor/sender.rb +0 -44
  147. data/lib/honeybadger/monitor/trace.rb +0 -187
  148. data/lib/honeybadger/monitor/worker.rb +0 -169
  149. data/lib/honeybadger/payload.rb +0 -101
  150. data/lib/honeybadger/rack.rb +0 -12
  151. data/lib/honeybadger/rails.rb +0 -45
  152. data/lib/honeybadger/rails/action_controller_catcher.rb +0 -30
  153. data/lib/honeybadger/rails/controller_methods.rb +0 -78
  154. data/lib/honeybadger/rails/middleware/exceptions_catcher.rb +0 -29
  155. data/lib/honeybadger/rails3_tasks.rb +0 -94
  156. data/lib/honeybadger/railtie.rb +0 -52
  157. data/lib/honeybadger/rake_handler.rb +0 -66
  158. data/lib/honeybadger/sender.rb +0 -185
  159. data/lib/honeybadger/shared_tasks.rb +0 -56
  160. data/lib/honeybadger/stats.rb +0 -29
  161. data/lib/honeybadger/tasks.rb +0 -95
  162. data/lib/honeybadger/user_feedback.rb +0 -8
  163. data/lib/honeybadger/user_informer.rb +0 -8
  164. data/lib/honeybadger_tasks.rb +0 -69
  165. data/lib/rails/generators/honeybadger/honeybadger_generator.rb +0 -99
  166. data/rails/init.rb +0 -1
  167. data/resources/README.md +0 -34
  168. data/script/integration_test.rb +0 -38
  169. data/spec/allocation_stats.rb +0 -32
  170. data/spec/honeybadger/backtrace_spec.rb +0 -242
  171. data/spec/honeybadger/capistrano_spec.rb +0 -36
  172. data/spec/honeybadger/configuration_spec.rb +0 -328
  173. data/spec/honeybadger/dependency_spec.rb +0 -134
  174. data/spec/honeybadger/integrations/delayed_job_spec.rb +0 -82
  175. data/spec/honeybadger/integrations/local_variables_spec.rb +0 -60
  176. data/spec/honeybadger/integrations/net_http_spec.rb +0 -29
  177. data/spec/honeybadger/integrations/passenger_spec.rb +0 -29
  178. data/spec/honeybadger/integrations/sidekiq_spec.rb +0 -60
  179. data/spec/honeybadger/integrations/thor_spec.rb +0 -32
  180. data/spec/honeybadger/integrations/unicorn_spec.rb +0 -40
  181. data/spec/honeybadger/logger_spec.rb +0 -79
  182. data/spec/honeybadger/monitor/trace_spec.rb +0 -65
  183. data/spec/honeybadger/monitor/worker_spec.rb +0 -274
  184. data/spec/honeybadger/notice_spec.rb +0 -669
  185. data/spec/honeybadger/notifier_spec.rb +0 -328
  186. data/spec/honeybadger/payload_spec.rb +0 -162
  187. data/spec/honeybadger/rack_spec.rb +0 -85
  188. data/spec/honeybadger/rails/action_controller_spec.rb +0 -328
  189. data/spec/honeybadger/rails_spec.rb +0 -37
  190. data/spec/honeybadger/sender_spec.rb +0 -317
  191. data/spec/honeybadger/stats_spec.rb +0 -57
  192. data/spec/honeybadger/user_feedback_spec.rb +0 -80
  193. data/spec/honeybadger/user_informer_spec.rb +0 -30
  194. data/spec/honeybadger_tasks_spec.rb +0 -171
  195. data/spec/spec_helper.rb +0 -24
  196. data/spec/support/array_including.rb +0 -31
  197. data/spec/support/backtraced_exception.rb +0 -9
  198. data/spec/support/collected_sender.rb +0 -12
  199. data/spec/support/defines_constants.rb +0 -18
  200. data/spec/support/helpers.rb +0 -101
@@ -0,0 +1,27 @@
1
+ module Honeybadger
2
+ module Init
3
+ module Sinatra
4
+ ::Sinatra::Base.class_eval do
5
+ class << self
6
+ def build_with_honeybadger(*args, &block)
7
+ config = Honeybadger::Config.new(honeybadger_config(self))
8
+ if Honeybadger.start(config)
9
+ use(Honeybadger::Rack::ErrorNotifier, config)
10
+ use(Honeybadger::Rack::MetricsReporter, config)
11
+ end
12
+
13
+ build_without_honeybadger(*args, &block)
14
+ end
15
+ alias :build_without_honeybadger :build
16
+ alias :build :build_with_honeybadger
17
+
18
+ def honeybadger_config(app)
19
+ {
20
+ api_key: defined?(honeybadger_api_key) ? honeybadger_api_key : nil
21
+ }
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,133 @@
1
+ require 'logger'
2
+ require 'singleton'
3
+ require 'delegate'
4
+
5
+ module Honeybadger
6
+ module Logging
7
+ PREFIX = '** [Honeybadger] '.freeze
8
+
9
+ module Helper
10
+ def debug(msg = nil)
11
+ return if Logger::Severity::DEBUG < logger.level
12
+ msg = yield if block_given?
13
+ logger.debug(msg)
14
+ end
15
+
16
+ def info(msg = nil)
17
+ return if Logger::Severity::INFO < logger.level
18
+ msg = yield if block_given?
19
+ logger.info(msg)
20
+ end
21
+
22
+ def warn(msg = nil)
23
+ return if Logger::Severity::WARN < logger.level
24
+ msg = yield if block_given?
25
+ logger.warn(msg)
26
+ end
27
+
28
+ def error(msg = nil)
29
+ return if Logger::Severity::ERROR < logger.level
30
+ msg = yield if block_given?
31
+ logger.error(msg)
32
+ end
33
+
34
+ def logger
35
+ @config.logger
36
+ end
37
+ end
38
+
39
+ class Base
40
+ Logger::Severity.constants.each do |severity|
41
+ define_method severity.downcase do |msg|
42
+ add(Logger::Severity.const_get(severity), msg)
43
+ end
44
+ end
45
+
46
+ def add(severity, message)
47
+ raise NotImplementedError, 'must define #add on subclass.'
48
+ end
49
+
50
+ def level
51
+ Logger::Severity::Debug
52
+ end
53
+ end
54
+
55
+ class FormattedLogger < Base
56
+ def initialize(logger = Logger.new('/dev/null'))
57
+ raise ArgumentError, 'logger not specified' unless logger
58
+ raise ArgumentError, 'logger must be a logger' unless logger.respond_to?(:add)
59
+
60
+ @logger = logger
61
+ end
62
+
63
+ def add(severity, message)
64
+ @logger.add(severity, format_message(message))
65
+ end
66
+
67
+ def level
68
+ @logger.level
69
+ end
70
+
71
+ private
72
+
73
+ def format_message(message)
74
+ return message unless message.kind_of?(String)
75
+ PREFIX + message
76
+ end
77
+ end
78
+
79
+ class BootLogger < Base
80
+ include Singleton
81
+
82
+ def initialize
83
+ @messages = []
84
+ end
85
+
86
+ def add(severity, message)
87
+ @messages << [severity, message]
88
+ end
89
+
90
+ def flush(logger)
91
+ @messages.each do |msg|
92
+ logger.add(*msg)
93
+ end
94
+ @messages.clear
95
+ end
96
+ end
97
+
98
+ class SupplementedLogger < SimpleDelegator
99
+ LOCATE_CALLER_LOCATION = Regexp.new("#{Regexp.escape(__FILE__)}").freeze
100
+ CALLER_LOCATION = Regexp.new("#{Regexp.escape(File.expand_path('../../../', __FILE__))}/(.*)").freeze
101
+
102
+ INFO_SUPPLEMENT = ' level=%s pid=%s'.freeze
103
+ DEBUG_SUPPLEMENT = ' at=%s'.freeze
104
+
105
+ def initialize(logger = Logger.new('/dev/null'))
106
+ raise ArgumentError, 'logger not specified' unless logger
107
+ super
108
+ end
109
+
110
+ Logger::Severity.constants.each do |severity|
111
+ define_method l = severity.downcase do |msg|
112
+ __getobj__().send(l, supplement(msg, l))
113
+ end
114
+ end
115
+
116
+ private
117
+
118
+ def supplement(msg, level)
119
+ msg << sprintf(INFO_SUPPLEMENT, level, Process.pid)
120
+ if level == :debug && l = caller_location
121
+ msg << sprintf(DEBUG_SUPPLEMENT, l.dump)
122
+ end
123
+ msg
124
+ end
125
+
126
+ def caller_location
127
+ if caller && caller.find {|l| l !~ LOCATE_CALLER_LOCATION && l =~ CALLER_LOCATION }
128
+ Regexp.last_match(1)
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
@@ -1,223 +1,187 @@
1
- require 'honeybadger/payload'
2
- require 'socket'
1
+ require 'json'
2
+ require 'securerandom'
3
+ require 'forwardable'
4
+ require 'ostruct'
5
+
6
+ require 'honeybadger/version'
7
+ require 'honeybadger/backtrace'
8
+ require 'honeybadger/util/stats'
9
+ require 'honeybadger/util/sanitizer'
10
+ require 'honeybadger/util/request_sanitizer'
11
+ require 'honeybadger/rack/request_hash'
3
12
 
4
13
  module Honeybadger
5
- class Notice
6
- # The exception that caused this notice, if any
7
- attr_reader :exception
14
+ NOTIFIER = {
15
+ name: 'honeybadger-ruby'.freeze,
16
+ url: 'https://github.com/honeybadger-io/honeybadger-ruby'.freeze,
17
+ version: VERSION,
18
+ language: 'ruby'.freeze
19
+ }.freeze
8
20
 
9
- # The backtrace from the given exception or hash.
10
- attr_reader :backtrace
21
+ # Internal: Substitution for gem root in backtrace lines.
22
+ GEM_ROOT = '[GEM_ROOT]'.freeze
11
23
 
12
- # Custom fingerprint for error, used to group similar errors together (optional)
13
- attr_reader :fingerprint
24
+ # Internal: Substitution for project root in backtrace lines.
25
+ PROJECT_ROOT = '[PROJECT_ROOT]'.freeze
14
26
 
15
- # The name of the class of error (such as RuntimeError)
16
- attr_reader :error_class
27
+ # Internal: Empty String (used for equality comparisons and assignment)
28
+ STRING_EMPTY = ''.freeze
17
29
 
18
- # Excerpt from source file
19
- attr_reader :source_extract
30
+ # Internal: Matches lines beginning with ./
31
+ RELATIVE_ROOT = Regexp.new('^\.\/').freeze
20
32
 
21
- # The number of lines of context to include before and after source excerpt
22
- attr_reader :source_extract_radius
23
-
24
- # The name of the server environment (such as "production")
25
- attr_reader :environment_name
26
-
27
- # CGI variables such as HTTP_METHOD
28
- attr_reader :cgi_data
29
-
30
- # The message from the exception, or a general description of the error
31
- attr_reader :error_message
32
-
33
- # See Configuration#send_request_session
34
- attr_reader :send_request_session
35
-
36
- # See Configuration#backtrace_filters
37
- attr_reader :backtrace_filters
38
-
39
- # See Configuration#params_filters
40
- attr_reader :params_filters
33
+ class Notice
34
+ extend Forwardable
41
35
 
42
- # A hash of parameters from the query string or post body.
43
- attr_reader :parameters
44
- alias_method :params, :parameters
36
+ # Public: The unique ID of this notice which can be used to reference the
37
+ # error in Honeybadger.
38
+ attr_reader :id
45
39
 
46
- # The component (if any) which was used in this request (usually the controller)
47
- attr_reader :component
48
- alias_method :controller, :component
40
+ # Public: The exception that caused this notice, if any.
41
+ attr_reader :exception
49
42
 
50
- # The action (if any) that was called in this request
51
- attr_reader :action
43
+ # Public: The backtrace from the given exception or hash.
44
+ attr_reader :backtrace
52
45
 
53
- # A hash of session data from the request
54
- attr_reader :session_data
46
+ # Public: Custom fingerprint for error, used to group similar errors together.
47
+ attr_reader :fingerprint
55
48
 
56
- # Additional contextual information (custom data)
57
- attr_reader :context
49
+ # Public: The name of the class of error. (example: RuntimeError)
50
+ attr_reader :error_class
58
51
 
59
- # The path to the project that caused the error (usually Rails.root)
60
- attr_reader :project_root
52
+ # Public: The message from the exception, or a general description of the error.
53
+ attr_reader :error_message
61
54
 
62
- # The URL at which the error occurred (if any)
63
- attr_reader :url
55
+ # Public: Excerpt from source file.
56
+ attr_reader :source
64
57
 
65
- # See Configuration#ignore
66
- attr_reader :ignore
58
+ # Public: CGI variables such as HTTP_METHOD.
59
+ def_delegator :@request, :cgi_data
67
60
 
68
- # See Configuration#ignore_by_filters
69
- attr_reader :ignore_by_filters
61
+ # Public: A hash of parameters from the query string or post body.
62
+ def_delegator :@request, :params
63
+ alias_method :parameters, :params
70
64
 
71
- # The name of the notifier library sending this notice, such as "Honeybadger Notifier"
72
- attr_reader :notifier_name
65
+ # Public: The component (if any) which was used in this request. (usually the controller)
66
+ def_delegator :@request, :component
67
+ alias_method :controller, :component
73
68
 
74
- # The version number of the notifier library sending this notice, such as "2.1.3"
75
- attr_reader :notifier_version
69
+ # Public: The action (if any) that was called in this request.
70
+ def_delegator :@request, :action
76
71
 
77
- # A URL for more information about the notifier library sending this notice
78
- attr_reader :notifier_url
72
+ # Public: A hash of session data from the request.
73
+ def_delegator :@request, :session
79
74
 
80
- # The host name where this error occurred (if any)
81
- attr_reader :hostname
75
+ # Public: The URL at which the error occurred (if any).
76
+ def_delegator :@request, :url
82
77
 
83
- # System stats
84
- attr_reader :stats
78
+ # Public: Local variables are extracted from first frame of backtrace.
79
+ attr_reader :local_variables
85
80
 
86
- # The api_key to use when sending notice (optional)
87
- attr_reader :api_key
81
+ # Internal: Cache project path substitutions for backtrace lines.
82
+ PROJECT_ROOT_CACHE = {}
88
83
 
89
- # Local variables are extracted from first frame of backtrace
90
- attr_reader :local_variables
84
+ # Internal: Cache gem path substitutions for backtrace lines.
85
+ GEM_ROOT_CACHE = {}
91
86
 
92
- # Additional features to enable/disable
93
- attr_reader :features
94
-
95
- def initialize(args)
96
- self.args = args
97
- self.features = args[:features] || {}
98
- self.exception = args[:exception]
99
- self.project_root = args[:project_root]
100
-
101
- self.notifier_name = args[:notifier_name]
102
- self.notifier_version = args[:notifier_version]
103
- self.notifier_url = args[:notifier_url]
104
-
105
- self.ignore = args[:ignore] || []
106
- self.ignore_by_filters = args[:ignore_by_filters] || []
107
- self.backtrace_filters = args[:backtrace_filters] || []
108
- self.params_filters = args[:params_filters] || []
109
- self.parameters = args[:parameters] ||
110
- action_dispatch_params ||
111
- rack_env(:params) ||
112
- {}
113
- self.component = args[:component] || args[:controller] || parameters['controller']
114
- self.action = args[:action] || parameters['action']
115
-
116
- self.environment_name = args[:environment_name]
117
- self.cgi_data = args[:cgi_data] || args[:rack_env]
118
- self.backtrace = Backtrace.parse(exception_attribute(:backtrace, caller), :filters => self.backtrace_filters)
119
- self.fingerprint = hashed_fingerprint
120
- self.error_class = exception_attribute(:error_class) {|exception| exception.class.name }
121
- self.error_message = trim_size(1024) do
87
+ # Internal: A list of backtrace filters to run all the time.
88
+ BACKTRACE_FILTERS = [
89
+ lambda { |line|
90
+ return line unless defined?(Gem)
91
+ GEM_ROOT_CACHE[line] ||= Gem.path.reduce(line) do |line, path|
92
+ line.sub(path, GEM_ROOT)
93
+ end
94
+ },
95
+ lambda { |line, config|
96
+ return line unless config
97
+ c = (PROJECT_ROOT_CACHE[config[:root]] ||= {})
98
+ return c[line] if c.has_key?(line)
99
+ c[line] ||= if (root = config[:root].to_s) != STRING_EMPTY
100
+ line.sub(root, PROJECT_ROOT)
101
+ else
102
+ line
103
+ end
104
+ },
105
+ lambda { |line| line.sub(RELATIVE_ROOT, STRING_EMPTY) },
106
+ lambda { |line| line if line !~ %r{lib/honeybadger} }
107
+ ].freeze
108
+
109
+ def initialize(config, opts = {})
110
+ @now = Time.now.utc
111
+ @id = SecureRandom.uuid
112
+
113
+ @opts = opts
114
+ @config = config
115
+
116
+ @exception = opts[:exception]
117
+ @error_class = exception_attribute(:error_class) {|exception| exception.class.name }
118
+ @error_message = trim_size(1024) do
122
119
  exception_attribute(:error_message, 'Notification') do |exception|
123
120
  "#{exception.class.name}: #{exception.message}"
124
121
  end
125
122
  end
123
+ @backtrace = Backtrace.parse(
124
+ exception_attribute(:backtrace, caller),
125
+ filters: construct_backtrace_filters(opts),
126
+ config: config
127
+ )
128
+ @source = extract_source_from_backtrace(@backtrace, config, opts)
129
+ @fingerprint = construct_fingerprint(opts)
126
130
 
127
- self.url = args[:url] || rack_env(:url)
128
- self.hostname = local_hostname
129
- self.stats = Stats.all
130
- self.api_key = args[:api_key]
131
-
132
- self.source_extract_radius = args[:source_extract_radius] || 2
133
- self.source_extract = extract_source_from_backtrace
131
+ @sanitizer = Util::Sanitizer.new(filters: config.params_filters)
132
+ @request_sanitizer = Util::RequestSanitizer.new(@sanitizer)
133
+ @request = OpenStruct.new(construct_request_hash(config.request, opts, @request_sanitizer))
134
+ @context = construct_context_hash(opts, @sanitizer)
134
135
 
135
- self.local_variables = send_local_variables? ? local_variables_from_exception(exception) : {}
136
+ @stats = Util::Stats.all
136
137
 
137
- self.send_request_session = args[:send_request_session].nil? ? true : args[:send_request_session]
138
-
139
- find_session_data
140
- also_use_rack_params_filters
141
- set_context
142
- clean_rack_request_data
143
- end
138
+ @local_variables = send_local_variables?(config) ? local_variables_from_exception(exception, config) : {}
144
139
 
145
- # Deprecated. Remove in 2.0.
146
- def deliver
147
- return false unless Honeybadger.sender
148
- Honeybadger.sender.send_to_honeybadger(self)
140
+ @api_key = opts[:api_key] || config[:api_key]
149
141
  end
150
142
 
151
- # Public: Template used to create JSON payload
143
+ # Internal: Template used to create JSON payload
152
144
  #
153
- # Returns JSON representation of notice
154
- def as_json(options = {})
155
- Payload.new({
156
- :api_key => api_key,
157
- :notifier => {
158
- :name => notifier_name,
159
- :url => notifier_url,
160
- :version => notifier_version,
161
- :language => 'ruby'
145
+ # Returns Hash JSON representation of notice
146
+ def as_json(*args)
147
+ {
148
+ api_key: api_key,
149
+ notifier: NOTIFIER,
150
+ error: {
151
+ token: id,
152
+ class: error_class,
153
+ message: error_message,
154
+ backtrace: backtrace,
155
+ source: source,
156
+ fingerprint: fingerprint
162
157
  },
163
- :error => {
164
- :class => error_class,
165
- :message => error_message,
166
- :backtrace => backtrace,
167
- :source => source_extract,
168
- :fingerprint => fingerprint
158
+ request: {
159
+ url: url,
160
+ component: component,
161
+ action: action,
162
+ params: params,
163
+ session: session,
164
+ cgi_data: cgi_data,
165
+ context: context,
166
+ local_variables: local_variables
169
167
  },
170
- :request => {
171
- :url => url,
172
- :component => component,
173
- :action => action,
174
- :params => parameters,
175
- :session => session_data,
176
- :cgi_data => cgi_data,
177
- :context => context,
178
- :local_variables => local_variables
179
- },
180
- :server => {
181
- :project_root => project_root,
182
- :environment_name => environment_name,
183
- :hostname => hostname,
184
- :stats => stats
168
+ server: {
169
+ project_root: config[:root],
170
+ environment_name: config[:env],
171
+ hostname: config[:hostname],
172
+ stats: stats,
173
+ time: now
185
174
  }
186
- }, :filters => params_filters)
175
+ }
187
176
  end
188
177
 
189
178
  # Public: Creates JSON
190
179
  #
191
- # Returns valid JSON representation of notice
180
+ # Returns valid JSON representation of Notice
192
181
  def to_json(*a)
193
182
  as_json.to_json(*a)
194
183
  end
195
184
 
196
- # Public: Determines if error class should be ignored
197
- #
198
- # ignored_class_name - The name of the ignored class. May be a
199
- # string or regexp (optional)
200
- #
201
- # Returns true/false with an argument, otherwise a Proc object
202
- def ignore_by_class?(ignored_class = nil)
203
- @ignore_by_class ||= Proc.new do |ignored_class|
204
- case error_class
205
- when (ignored_class.respond_to?(:name) ? ignored_class.name : ignored_class)
206
- true
207
- else
208
- exception && ignored_class.is_a?(Class) && exception.class < ignored_class
209
- end
210
- end
211
-
212
- ignored_class ? @ignore_by_class.call(ignored_class) : @ignore_by_class
213
- end
214
-
215
- # Public: Determines if this notice should be ignored
216
- def ignore?
217
- ignore.any?(&ignore_by_class?) ||
218
- ignore_by_filters.any? {|filter| filter.call(self) }
219
- end
220
-
221
185
  # Public: Allows properties to be accessed using a hash-like syntax
222
186
  #
223
187
  # method - The given key for an attribute
@@ -236,20 +200,19 @@ module Honeybadger
236
200
  end
237
201
  end
238
202
 
239
- private
203
+ # Internal: Determines if this notice should be ignored
204
+ def ignore?
205
+ config[:'exceptions.ignore'].any?(&ignore_by_class?) or
206
+ opts[:callbacks] &&
207
+ opts[:callbacks].exception_filter &&
208
+ opts[:callbacks].exception_filter.call(self)
209
+ end
240
210
 
241
- attr_writer :exception, :backtrace, :fingerprint, :error_class,
242
- :error_message, :backtrace_filters, :parameters, :params_filters,
243
- :environment_filters, :session_data, :project_root, :url, :ignore,
244
- :ignore_by_filters, :notifier_name, :notifier_url, :notifier_version,
245
- :component, :action, :cgi_data, :environment_name, :hostname, :stats,
246
- :context, :source_extract, :source_extract_radius, :send_request_session,
247
- :api_key, :features, :local_variables
211
+ private
248
212
 
249
- # Private: Arguments given in the initializer
250
- attr_accessor :args
213
+ attr_reader :config, :opts, :context, :stats, :api_key, :now
251
214
 
252
- # Internal: Gets a property named "attribute" of an exception, either from
215
+ # Gets a property named "attribute" of an exception, either from
253
216
  # the #args hash or actual exception (in order of precidence)
254
217
  #
255
218
  # attribute - A Symbol existing as a key in #args and/or attribute on
@@ -260,10 +223,10 @@ module Honeybadger
260
223
  #
261
224
  # Returns attribute value from args or exception, otherwise default
262
225
  def exception_attribute(attribute, default = nil, &block)
263
- args[attribute] || (exception && from_exception(attribute, &block)) || default
226
+ opts[attribute] || (opts[:exception] && from_exception(attribute, &block)) || default
264
227
  end
265
228
 
266
- # Private: Gets a property named +attribute+ from an exception.
229
+ # Gets a property named +attribute+ from an exception.
267
230
  #
268
231
  # If a block is given, it will be used when getting the property from an
269
232
  # exception. The block should accept and exception and return the value for
@@ -272,141 +235,141 @@ module Honeybadger
272
235
  # If no block is given, a method with the same name as +attribute+ will be
273
236
  # invoked for the value.
274
237
  def from_exception(attribute)
238
+ return unless opts[:exception]
239
+
275
240
  if block_given?
276
- yield(exception)
241
+ yield(opts[:exception])
277
242
  else
278
- exception.send(attribute)
243
+ opts[:exception].send(attribute)
279
244
  end
280
245
  end
281
246
 
282
- def clean_rack_request_data
283
- if cgi_data
284
- self.cgi_data = cgi_data.reject {|k,_| k == 'QUERY_STRING' || !k.match(/\A[A-Z_]+\Z/) }
247
+ # Internal: Determines if error class should be ignored
248
+ #
249
+ # ignored_class_name - The name of the ignored class. May be a
250
+ # string or regexp (optional)
251
+ #
252
+ # Returns true/false with an argument, otherwise a Proc object
253
+ def ignore_by_class?(ignored_class = nil)
254
+ @ignore_by_class ||= Proc.new do |ignored_class|
255
+ case error_class
256
+ when (ignored_class.respond_to?(:name) ? ignored_class.name : ignored_class)
257
+ true
258
+ else
259
+ exception && ignored_class.is_a?(Class) && exception.class < ignored_class
260
+ end
285
261
  end
262
+
263
+ ignored_class ? @ignore_by_class.call(ignored_class) : @ignore_by_class
286
264
  end
287
265
 
288
- def fingerprint_from_args
289
- if args[:fingerprint].respond_to?(:call)
290
- args[:fingerprint].call(self)
291
- else
292
- args[:fingerprint]
293
- end
266
+ # Limit size of string to bytes
267
+ #
268
+ # input - The String to be trimmed.
269
+ # bytes - The Integer bytes to trim.
270
+ # block - An optional block used in place of input.
271
+ #
272
+ # Examples
273
+ #
274
+ # trimmed = trim_size("Honeybadger doesn't care", 3)
275
+ #
276
+ # trimmed = trim_size(3) do
277
+ # "Honeybadger doesn't care"
278
+ # end
279
+ #
280
+ # Returns trimmed String
281
+ def trim_size(*args, &block)
282
+ input, bytes = args.first, args.last
283
+ input = yield if block_given?
284
+ input = input.dup
285
+ input = input[0...bytes] if input.respond_to?(:size) && input.size > bytes
286
+ input
294
287
  end
295
288
 
296
- def hashed_fingerprint
297
- fingerprint = fingerprint_from_args
298
- if fingerprint && fingerprint.respond_to?(:to_s)
299
- Digest::SHA1.hexdigest(fingerprint.to_s)
289
+ def construct_backtrace_filters(opts)
290
+ [
291
+ opts[:callbacks] ? opts[:callbacks].backtrace_filter : nil
292
+ ].compact | BACKTRACE_FILTERS
293
+ end
294
+
295
+ def construct_request_hash(rack_request, opts, sanitizer)
296
+ defaults = {
297
+ url: opts[:url],
298
+ component: opts[:component] || opts[:controller],
299
+ action: opts[:action],
300
+ params: opts[:params] || opts[:parameters] || {},
301
+ session: opts[:session] || {},
302
+ cgi_data: opts[:cgi_data] || {}
303
+ }
304
+
305
+ request = {}
306
+ request.merge!(Rack::RequestHash.new(rack_request)) if rack_request
307
+
308
+ defaults.each_pair do |k,v|
309
+ request[k] = v if opts.has_key?(k) || !request.has_key?(k)
300
310
  end
311
+
312
+ request[:session] = request[:session][:data] if request[:session][:data]
313
+
314
+ sanitizer.sanitize(request)
315
+ end
316
+
317
+ def construct_context_hash(opts, sanitizer)
318
+ context = {}
319
+ context.merge!(Thread.current[:__honeybadger_context]) if Thread.current[:__honeybadger_context]
320
+ context.merge!(opts[:context]) if opts[:context]
321
+ context.empty? ? nil : sanitizer.sanitize(context)
301
322
  end
302
323
 
303
- def extract_source_from_backtrace
324
+ def extract_source_from_backtrace(backtrace, config, opts)
304
325
  if backtrace.lines.empty?
305
326
  nil
306
327
  else
307
328
  # ActionView::Template::Error has its own source_extract method.
308
329
  # If present, use that instead.
309
- if exception.respond_to?(:source_extract)
330
+ if opts[:exception].respond_to?(:source_extract)
310
331
  Hash[exception.source_extract.split("\n").map do |line|
311
332
  parts = line.split(': ')
312
333
  [parts[0].strip, parts[1] || '']
313
334
  end]
314
335
  elsif backtrace.application_lines.any?
315
- backtrace.application_lines.first.source(source_extract_radius)
336
+ backtrace.application_lines.first.source(config[:'exceptions.source_radius'])
316
337
  else
317
- backtrace.lines.first.source(source_extract_radius)
338
+ backtrace.lines.first.source(config[:'exceptions.source_radius'])
318
339
  end
319
340
  end
320
341
  end
321
342
 
322
- def find_session_data
323
- if send_request_session
324
- self.session_data = args[:session_data] || args[:session] || rack_session || {}
325
- self.session_data = session_data[:data] if session_data[:data]
326
- end
327
- rescue => e
328
- # Rails raises ArgumentError when `config.secret_token` is missing, and
329
- # ActionDispatch::Session::SessionRestoreError when the session can't be
330
- # restored.
331
- self.session_data = { :error => "Failed to access session data -- #{e.message}" }
332
- end
333
-
334
- def set_context
335
- self.context = {}
336
- self.context.merge!(Thread.current[:honeybadger_context]) if Thread.current[:honeybadger_context]
337
- self.context.merge!(args[:context]) if args[:context]
338
- self.context = nil if context.empty?
339
- end
340
-
341
- def rack_env(method)
342
- rack_request.send(method) if rack_request
343
- rescue => e
344
- { :error => "Failed to call #{method} on Rack::Request -- #{e.message}" }
345
- end
343
+ def fingerprint_from_opts(opts)
344
+ callback = opts[:fingerprint]
345
+ callback ||= opts[:callbacks] && opts[:callbacks].exception_fingerprint
346
346
 
347
- def rack_request
348
- @rack_request ||= if args[:rack_env]
349
- ::Rack::Request.new(args[:rack_env])
347
+ if callback.respond_to?(:call)
348
+ callback.call(self)
349
+ else
350
+ callback
350
351
  end
351
352
  end
352
353
 
353
- def action_dispatch_params
354
- args[:rack_env]['action_dispatch.request.parameters'] if args[:rack_env]
355
- end
356
-
357
- def rack_session
358
- rack_env(:session).to_hash if args[:rack_env]
359
- end
360
-
361
- # Private: (Rails 3+) Adds params filters to filter list
362
- #
363
- # Returns nothing
364
- def also_use_rack_params_filters
365
- if cgi_data
366
- @params_filters ||= []
367
- @params_filters += cgi_data['action_dispatch.parameter_filter'] || []
354
+ def construct_fingerprint(opts)
355
+ fingerprint = fingerprint_from_opts(opts)
356
+ if fingerprint && fingerprint.respond_to?(:to_s)
357
+ Digest::SHA1.hexdigest(fingerprint.to_s)
368
358
  end
369
359
  end
370
360
 
371
- def local_hostname
372
- args[:hostname] || Socket.gethostname
373
- end
374
-
375
- # Internal: Limit size of string to bytes
376
- #
377
- # input - The String to be trimmed.
378
- # bytes - The Integer bytes to trim.
379
- # block - An optional block used in place of input.
380
- #
381
- # Examples
382
- #
383
- # trimmed = trim_size("Honeybadger doesn't care", 3)
384
- #
385
- # trimmed = trim_size(3) do
386
- # "Honeybadger doesn't care"
387
- # end
388
- #
389
- # Returns trimmed String
390
- def trim_size(*args, &block)
391
- input, bytes = args.first, args.last
392
- input = yield if block_given?
393
- input = input.dup
394
- input = input[0...bytes] if input.respond_to?(:size) && input.size > bytes
395
- input
396
- end
397
-
398
361
  # Internal: Fetch local variables from first frame of backtrace.
399
362
  #
400
363
  # exception - The Exception containing the bindings stack.
401
364
  #
402
365
  # Returns a Hash of local variables
403
- def local_variables_from_exception(exception)
366
+ def local_variables_from_exception(exception, config)
404
367
  return {} unless Exception === exception
405
368
  return {} unless exception.respond_to?(:__honeybadger_bindings_stack)
406
369
  return {} if exception.__honeybadger_bindings_stack.empty?
407
370
 
408
- if project_root
409
- binding = exception.__honeybadger_bindings_stack.find { |b| b.eval('__FILE__') =~ /^#{Regexp.escape(project_root.to_s)}/ }
371
+ if config[:root]
372
+ binding = exception.__honeybadger_bindings_stack.find { |b| b.eval('__FILE__') =~ /^#{Regexp.escape(config[:root].to_s)}/ }
410
373
  end
411
374
 
412
375
  binding ||= exception.__honeybadger_bindings_stack[0]
@@ -418,8 +381,8 @@ module Honeybadger
418
381
  # Internal: Should local variables be sent?
419
382
  #
420
383
  # Returns true to send local_variables
421
- def send_local_variables?
422
- args[:send_local_variables] && features['local_variables']
384
+ def send_local_variables?(config)
385
+ config[:'exceptions.local_variables']
423
386
  end
424
387
  end
425
388
  end