crashlog 0.0.1 → 0.0.2
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.
- data/.gitignore +2 -1
- data/.rspec +1 -0
- data/.travis.yml +11 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +161 -0
- data/INSTALL +22 -0
- data/README.md +14 -4
- data/Rakefile +13 -0
- data/crashlog.gemspec +12 -3
- data/generators/crashlog/templates/initializer.rb +6 -0
- data/install.rb +2 -0
- data/lib/crash_log/backtrace/line.rb +105 -0
- data/lib/crash_log/backtrace/line_cache.rb +23 -0
- data/lib/crash_log/backtrace.rb +66 -0
- data/lib/crash_log/configuration.bak.rb +199 -0
- data/lib/crash_log/configuration.rb +188 -0
- data/lib/crash_log/logging.rb +51 -0
- data/lib/crash_log/payload.rb +157 -0
- data/lib/crash_log/rack.rb +47 -0
- data/lib/crash_log/rails/action_controller_rescue.rb +32 -0
- data/lib/crash_log/rails/controller_methods.rb +45 -0
- data/lib/crash_log/rails/middleware/debug_exception_catcher.rb +43 -0
- data/lib/crash_log/rails.rb +32 -0
- data/lib/crash_log/railtie.rb +41 -0
- data/lib/crash_log/reporter.rb +105 -0
- data/lib/crash_log/system_information.rb +64 -0
- data/lib/crash_log/templates/payload.rabl +7 -0
- data/lib/crash_log/version.rb +1 -1
- data/lib/crash_log.rb +118 -0
- data/lib/faraday/request/hmac_authentication.rb +73 -0
- data/lib/rails/generators/crashlog/crashlog_generator.rb +42 -0
- data/rails/init.rb +1 -0
- data/spec/crash_log/backtrace_spec.rb +79 -0
- data/spec/crash_log/initializer_spec.rb +53 -0
- data/spec/crash_log/payload_spec.rb +124 -0
- data/spec/crash_log/reporter_spec.rb +179 -0
- data/spec/crash_log_spec.rb +153 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/controllers/break_controller.rb +10 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config/application.rb +59 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +44 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/crashlog.rb +6 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/routes.rb +6 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/requests/rack_spec.rb +29 -0
- data/spec/requests/rails_controller_rescue_spec.rb +46 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/doing.rb +1 -0
- data/spec/support/dummy_app.rb +13 -0
- data/spec/support/hash_ext.rb +7 -0
- metadata +197 -7
@@ -0,0 +1,199 @@
|
|
1
|
+
module CrashLog
|
2
|
+
class Configuration
|
3
|
+
|
4
|
+
DEFAULT_PARAMS_FILTERS = %w(password password_confirmation).freeze
|
5
|
+
|
6
|
+
DEFAULT_BACKTRACE_FILTERS = [
|
7
|
+
lambda { |line|
|
8
|
+
if defined?(CrashLog.configuration.project_root) &&
|
9
|
+
CrashLog.configuration.project_root.to_s != ''
|
10
|
+
line.sub(/#{CrashLog.configuration.project_root}/, "[PROJECT_ROOT]")
|
11
|
+
else
|
12
|
+
line
|
13
|
+
end
|
14
|
+
},
|
15
|
+
lambda { |line| line.gsub(/^\.\//, "") },
|
16
|
+
lambda { |line|
|
17
|
+
if defined?(Gem)
|
18
|
+
Gem.path.inject(line) do |line, path|
|
19
|
+
line.gsub(/#{path}/, "[GEM_ROOT]")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
},
|
23
|
+
lambda { |line| line if line !~ %r{lib/crash_log} }
|
24
|
+
].freeze
|
25
|
+
|
26
|
+
ENVIRONMENT_FILTERS = []
|
27
|
+
|
28
|
+
IGNORE_DEFAULT = ['ActiveRecord::RecordNotFound',
|
29
|
+
'ActionController::RoutingError',
|
30
|
+
'ActionController::InvalidAuthenticityToken',
|
31
|
+
'CGI::Session::CookieStore::TamperedWithCookie',
|
32
|
+
'ActionController::UnknownAction',
|
33
|
+
'AbstractController::ActionNotFound',
|
34
|
+
'Mongoid::Errors::DocumentNotFound']
|
35
|
+
|
36
|
+
# The default logging device
|
37
|
+
#
|
38
|
+
# This will be set to Rails.logger automatically if using Rails,
|
39
|
+
# otherwise it defaults to STDOUT.
|
40
|
+
attr_accessor :logger
|
41
|
+
|
42
|
+
# The API key to authenticate this project with CrashLog
|
43
|
+
#
|
44
|
+
# Get this from your projects configuration page within http://CrashLog.io
|
45
|
+
attr_accessor :api_key
|
46
|
+
|
47
|
+
# Stages (environments) which we consider to be in a production environment
|
48
|
+
# and thus you want to be sent notifications for.
|
49
|
+
attr_accessor :release_stages
|
50
|
+
|
51
|
+
# The name of the current stage
|
52
|
+
attr_reader :stage
|
53
|
+
|
54
|
+
# Project Root directory
|
55
|
+
attr_accessor :project_root
|
56
|
+
alias :root :project_root
|
57
|
+
alias :root= :project_root=
|
58
|
+
|
59
|
+
# If set, this will serialize the object returned by sending this key to
|
60
|
+
# the controller context. You can use this to send user data CrashLog to
|
61
|
+
# correlate errors with users to give you more advanced data about directly
|
62
|
+
# who was affected.
|
63
|
+
#
|
64
|
+
# All user data is stored encrypted for security and always remains your
|
65
|
+
# property.
|
66
|
+
attr_accessor :user_context_key
|
67
|
+
|
68
|
+
# Reporter configuration options
|
69
|
+
#
|
70
|
+
# Host to send exceptions to. Default: crashlog.io
|
71
|
+
attr_accessor :host
|
72
|
+
|
73
|
+
# Port to use for http connections. 80 or 443 by default.
|
74
|
+
attr_writer :port
|
75
|
+
|
76
|
+
def port
|
77
|
+
if @port
|
78
|
+
@port
|
79
|
+
else
|
80
|
+
secure? ? 443 : 80
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# HTTP transfer scheme, default: https
|
85
|
+
attr_accessor :scheme
|
86
|
+
|
87
|
+
# API endpoint to context for notifications. Default /notify
|
88
|
+
attr_accessor :endpoint
|
89
|
+
|
90
|
+
# The faraday adapter to use to make the connection.
|
91
|
+
#
|
92
|
+
# Possible values are:
|
93
|
+
# - :test
|
94
|
+
# - :net_http
|
95
|
+
# - :net_http_persistent
|
96
|
+
# - :typhoeus
|
97
|
+
# - :patron
|
98
|
+
# - :em_synchrony
|
99
|
+
# - :em_http
|
100
|
+
# - :excon
|
101
|
+
# - :rack
|
102
|
+
# - :httpclient
|
103
|
+
#
|
104
|
+
# Possible performance gains can be made by using the em based adapters if
|
105
|
+
# your application supports this architecture.
|
106
|
+
#
|
107
|
+
# Default: net_http
|
108
|
+
attr_accessor :adapter
|
109
|
+
|
110
|
+
# Reader for Array of ignored error class names
|
111
|
+
attr_reader :ignore
|
112
|
+
|
113
|
+
attr_reader :announce_endpoint, :announce
|
114
|
+
|
115
|
+
# Send context lines for backtrace.
|
116
|
+
#
|
117
|
+
# Takes an integer of the number of lines, set to false or nil to disable.
|
118
|
+
attr_accessor :context_lines
|
119
|
+
|
120
|
+
# Environment variables to discard from ENV.
|
121
|
+
attr_accessor :environment_filters
|
122
|
+
|
123
|
+
# Framework name
|
124
|
+
attr_accessor :framework
|
125
|
+
|
126
|
+
attr_accessor :dry_run
|
127
|
+
|
128
|
+
attr_accessor :http_read_timeout, :http_open_timeout
|
129
|
+
|
130
|
+
def initialize
|
131
|
+
@dry_run = false
|
132
|
+
@secure = true
|
133
|
+
@use_system_ssl_cert_chain= false
|
134
|
+
@host = 'crashlog.io'
|
135
|
+
@http_open_timeout = 5
|
136
|
+
@http_read_timeout = 2
|
137
|
+
@adapter = :net_http
|
138
|
+
@params_filters = DEFAULT_PARAMS_FILTERS.dup
|
139
|
+
@backtrace_filters = DEFAULT_BACKTRACE_FILTERS.dup
|
140
|
+
@environment_filters = ENVIRONMENT_FILTERS.dup
|
141
|
+
@ignore_by_filters = []
|
142
|
+
@ignore = IGNORE_DEFAULT.dup
|
143
|
+
@release_stages = %w(production staging)
|
144
|
+
@notifier_version = CrashLog::VERSION
|
145
|
+
@notifier_url = 'https://github.com/ivanvanderbyl/crashlog'
|
146
|
+
@framework = 'Standalone'
|
147
|
+
@stage = 'development'
|
148
|
+
@host = 'stdin.crashlog.io'
|
149
|
+
@port = nil
|
150
|
+
@scheme = port.eql?(443) ? 'https' : 'http'
|
151
|
+
@endpoint = '/notify'
|
152
|
+
@announce = true
|
153
|
+
@announce_endpoint = '/announce'
|
154
|
+
@context_lines = 5
|
155
|
+
@framework = 'Standalone'
|
156
|
+
end
|
157
|
+
|
158
|
+
def release_stage?
|
159
|
+
@release_stages.include?(stage)
|
160
|
+
end
|
161
|
+
|
162
|
+
def stage=(name)
|
163
|
+
@stage = name.downcase.strip
|
164
|
+
end
|
165
|
+
|
166
|
+
# Is this configuration valid for sending exceptions to CrashLog
|
167
|
+
#
|
168
|
+
# Returns true if all required keys are provided, otherwise false
|
169
|
+
def valid?
|
170
|
+
[:api_key, :host, :port].all? do |key|
|
171
|
+
!__send__(key).nil?
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def ignored?(exception)
|
176
|
+
ignore.include?(error_class(exception))
|
177
|
+
end
|
178
|
+
|
179
|
+
def secure?
|
180
|
+
scheme == 'https'
|
181
|
+
end
|
182
|
+
|
183
|
+
# Hash like accessor
|
184
|
+
def [](key)
|
185
|
+
if self.respond_to?(key)
|
186
|
+
self.__send__(key)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
def error_class(exception)
|
193
|
+
# The "Class" check is for some strange exceptions like Timeout::Error
|
194
|
+
# which throw the error class instead of an instance
|
195
|
+
(exception.is_a? Class) ? exception.name : exception.class.name
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'hashr'
|
2
|
+
module CrashLog
|
3
|
+
class Configuration < Hashr
|
4
|
+
DEFAULT_PARAMS_FILTERS = %w(password password_confirmation).freeze
|
5
|
+
|
6
|
+
DEFAULT_BACKTRACE_FILTERS = [
|
7
|
+
lambda { |line|
|
8
|
+
if defined?(CrashLog.configuration.root) &&
|
9
|
+
CrashLog.configuration.root.to_s != ''
|
10
|
+
line.sub(/#{CrashLog.configuration.root}/, "[PROJECT_ROOT]")
|
11
|
+
else
|
12
|
+
line
|
13
|
+
end
|
14
|
+
},
|
15
|
+
lambda { |line| line.gsub(/^\.\//, "") },
|
16
|
+
lambda { |line|
|
17
|
+
if defined?(Gem)
|
18
|
+
Gem.path.inject(line) do |line, path|
|
19
|
+
line.gsub(/#{path}/, "[GEM_ROOT]")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
},
|
23
|
+
lambda { |line| line if line !~ %r{lib/crash_log} }
|
24
|
+
].freeze
|
25
|
+
|
26
|
+
IGNORE_DEFAULT = ['ActiveRecord::RecordNotFound',
|
27
|
+
'ActionController::RoutingError',
|
28
|
+
'ActionController::InvalidAuthenticityToken',
|
29
|
+
'CGI::Session::CookieStore::TamperedWithCookie',
|
30
|
+
'ActionController::UnknownAction',
|
31
|
+
'AbstractController::ActionNotFound',
|
32
|
+
'Mongoid::Errors::DocumentNotFound']
|
33
|
+
|
34
|
+
|
35
|
+
# The logger to use for internal messages
|
36
|
+
define :logger => nil,
|
37
|
+
|
38
|
+
# The API key to authenticate this project with CrashLog
|
39
|
+
#
|
40
|
+
# Get this from your projects configuration page within http://CrashLog.io
|
41
|
+
:api_key => nil,
|
42
|
+
:project_id => nil,
|
43
|
+
|
44
|
+
# Stages (environments) which we consider to be in a production environment
|
45
|
+
# and thus you want to be sent notifications for.
|
46
|
+
:release_stages => ['staging', 'production'],
|
47
|
+
|
48
|
+
# The name of the current stage
|
49
|
+
:stage => 'development',
|
50
|
+
|
51
|
+
# Project Root directory
|
52
|
+
:project_root => nil,
|
53
|
+
|
54
|
+
# If set, this will serialize the object returned by sending this key to
|
55
|
+
# the controller context. You can use this to send user data CrashLog to
|
56
|
+
# correlate errors with users to give you more advanced data about directly
|
57
|
+
# who was affected.
|
58
|
+
#
|
59
|
+
# All user data is stored encrypted for security and always remains your
|
60
|
+
# property.
|
61
|
+
:user_context_key => nil,
|
62
|
+
|
63
|
+
# Reporter configuration options
|
64
|
+
#
|
65
|
+
# Host to send exceptions to. Default: crashlog.io
|
66
|
+
:host => 'stdin.crashlog.io',
|
67
|
+
|
68
|
+
# Port to use for http connections. 80 or 443 by default.
|
69
|
+
# :port => 443,
|
70
|
+
|
71
|
+
# HTTP transfer scheme, default: https
|
72
|
+
:scheme => 'https',
|
73
|
+
|
74
|
+
# API endpoint to context for notifications. Default /events
|
75
|
+
:endpoint => '/events',
|
76
|
+
|
77
|
+
# The faraday adapter to use to make the connection.
|
78
|
+
#
|
79
|
+
# Possible values are:
|
80
|
+
# - :test
|
81
|
+
# - :net_http
|
82
|
+
# - :net_http_persistent
|
83
|
+
# - :typhoeus
|
84
|
+
# - :patron
|
85
|
+
# - :em_synchrony
|
86
|
+
# - :em_http
|
87
|
+
# - :excon
|
88
|
+
# - :rack
|
89
|
+
# - :httpclient
|
90
|
+
#
|
91
|
+
# Possible performance gains can be made by using the em based adapters if
|
92
|
+
# your application supports this architecture.
|
93
|
+
#
|
94
|
+
# Default: net_http
|
95
|
+
:adapter => :net_http,
|
96
|
+
|
97
|
+
# Timeout for actually sending the exeption payload
|
98
|
+
:http_read_timeout => 2,
|
99
|
+
|
100
|
+
# Timeout for connecting to CrashLog collector interface
|
101
|
+
:http_open_timeout => 5,
|
102
|
+
|
103
|
+
# Ignored error class names
|
104
|
+
:ignore => IGNORE_DEFAULT.dup,
|
105
|
+
|
106
|
+
# Endpoint used for announcing application launch
|
107
|
+
:announce_endpoint => '/announce',
|
108
|
+
:announce => true,
|
109
|
+
|
110
|
+
# Send context lines for backtrace.
|
111
|
+
#
|
112
|
+
# Takes an integer of the number of lines, set to nil to disable.
|
113
|
+
:context_lines => 5,
|
114
|
+
|
115
|
+
# Environment variables to discard from ENV.
|
116
|
+
:environment_filters => [],
|
117
|
+
|
118
|
+
# Framework name
|
119
|
+
:framework => 'Standalone',
|
120
|
+
|
121
|
+
# Run in dry mode (Doesn't actually send exceptions, used for testing)
|
122
|
+
:dry_run => false,
|
123
|
+
|
124
|
+
:backtrace_filters => DEFAULT_BACKTRACE_FILTERS.dup,
|
125
|
+
|
126
|
+
:params_filters => DEFAULT_PARAMS_FILTERS.dup,
|
127
|
+
|
128
|
+
# Internal
|
129
|
+
# Do not change unless you know what this does.
|
130
|
+
:service_name => 'CrashLog'
|
131
|
+
|
132
|
+
def root
|
133
|
+
fetch(:project_root)
|
134
|
+
end
|
135
|
+
|
136
|
+
def root=(string)
|
137
|
+
self[:project_root] = string
|
138
|
+
end
|
139
|
+
|
140
|
+
def port
|
141
|
+
if secure?
|
142
|
+
443
|
143
|
+
else
|
144
|
+
fetch(:port, 80)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Release stages are stages which send exceptions
|
149
|
+
def release_stage?
|
150
|
+
release_stages.include?(stage)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Set the current stage
|
154
|
+
def stage=(name)
|
155
|
+
self[:stage] = name.downcase.strip
|
156
|
+
end
|
157
|
+
|
158
|
+
# Is this configuration valid for sending exceptions to CrashLog
|
159
|
+
#
|
160
|
+
# Returns true if all required keys are provided, otherwise false
|
161
|
+
def valid?
|
162
|
+
[:api_key, :project_id, :host, :port].all? do |key|
|
163
|
+
!__send__(key).nil?
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def ignored?(exception)
|
168
|
+
ignore.include?(error_class(exception))
|
169
|
+
end
|
170
|
+
|
171
|
+
def secure?
|
172
|
+
fetch(:scheme, 'https') == 'https'
|
173
|
+
end
|
174
|
+
|
175
|
+
def notifier_version
|
176
|
+
CrashLog::VERSION
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def error_class(exception)
|
182
|
+
# The "Class" check is for some strange exceptions like Timeout::Error
|
183
|
+
# which throw the error class instead of an instance
|
184
|
+
(exception.is_a? Class) ? exception.name : exception.class.name
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
2
|
+
require 'active_support/core_ext/module/aliasing'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
module CrashLog
|
6
|
+
module Logging
|
7
|
+
ANSI = {
|
8
|
+
:red => 31,
|
9
|
+
:green => 32,
|
10
|
+
:yellow => 33,
|
11
|
+
:cyan => 36
|
12
|
+
}
|
13
|
+
|
14
|
+
def self.included(base)
|
15
|
+
base.__send__(:include, ClassMethods)
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
delegate :logger, :to => CrashLog
|
20
|
+
|
21
|
+
[:fatal, :error, :warn, :info, :debug].each do |level|
|
22
|
+
define_method(level) do |*args|
|
23
|
+
message, options = *args
|
24
|
+
message.chomp.split("\n").each do |line|
|
25
|
+
logger.send(level, prefix(line))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def prefix(string)
|
31
|
+
[CrashLog::LOG_PREFIX, string].join(' ')
|
32
|
+
end
|
33
|
+
|
34
|
+
def colorize(color, text)
|
35
|
+
"\e[#{ANSI[color]}m#{text}\e[0m"
|
36
|
+
end
|
37
|
+
|
38
|
+
def log_exception(exception)
|
39
|
+
logger.error("#{exception.class.name}: #{exception.message}")
|
40
|
+
exception.backtrace.each { |line| logger.error(line) } if exception.backtrace
|
41
|
+
rescue Exception => e
|
42
|
+
puts '--- FATAL ---'
|
43
|
+
puts 'an exception occured while logging an exception'
|
44
|
+
puts e.message, e.backtrace
|
45
|
+
puts exception.message, exception.backtrace
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'rabl'
|
2
|
+
|
3
|
+
module CrashLog
|
4
|
+
class Payload
|
5
|
+
include Logging
|
6
|
+
|
7
|
+
def self.build(exception, config, &block)
|
8
|
+
payload = new(exception, config)
|
9
|
+
yield(payload) if block_given?
|
10
|
+
payload
|
11
|
+
end
|
12
|
+
|
13
|
+
# Delivers this payload to CrashLog
|
14
|
+
#
|
15
|
+
# Captures any exceptions and logs them.
|
16
|
+
def deliver!
|
17
|
+
deliver
|
18
|
+
rescue Exception => e
|
19
|
+
error('Failed to deliver notification to CrashLog collector')
|
20
|
+
log_exception(e)
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :config, :backtrace_filters
|
24
|
+
|
25
|
+
def initialize(event_data, config)
|
26
|
+
@config = config || {}
|
27
|
+
@event_data = event_data
|
28
|
+
@context = {}
|
29
|
+
@environment = {}
|
30
|
+
@backtrace_filters = config[:backtrace_filters] || []
|
31
|
+
|
32
|
+
# Actually serialize the exception/event hash for transport
|
33
|
+
@event = serialize_event(event_data)
|
34
|
+
|
35
|
+
# add_environment_data(:system => SystemInformation.to_hash)
|
36
|
+
end
|
37
|
+
|
38
|
+
def deliver
|
39
|
+
Reporter.new(config).notify(self.body)
|
40
|
+
end
|
41
|
+
|
42
|
+
attr_reader :event, :backtrace, :exception_object, :environment, :context
|
43
|
+
|
44
|
+
def body
|
45
|
+
renderer.render
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_context(data)
|
49
|
+
(@context ||= {}).merge!(data) if data.is_a?(Hash)
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_user_data(data, value = nil)
|
53
|
+
if data.respond_to?(:keys)
|
54
|
+
@user_data.merge!(data)
|
55
|
+
elsif value && data.respond_to?(:to_sym)
|
56
|
+
@user_data[data.to_sym] = value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def add_session_data(data)
|
61
|
+
(@environment[:session] ||= {}).merge!(data) if data.is_a?(Hash)
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_environment_data(data)
|
65
|
+
@environment.merge!(data) if data.respond_to?(:keys)
|
66
|
+
end
|
67
|
+
|
68
|
+
# The canonical time this exception occurred.
|
69
|
+
#
|
70
|
+
# Other notifiers leave this to the collector to set, we however take time
|
71
|
+
# more seriously and use this figure internally to detect processing time
|
72
|
+
# irregularities.
|
73
|
+
#
|
74
|
+
# Returns UNIX UTC timestamp integer.
|
75
|
+
def timestamp
|
76
|
+
Time.now.utc.to_i
|
77
|
+
end
|
78
|
+
|
79
|
+
# Various meta data about this notifier gem
|
80
|
+
def notifier
|
81
|
+
{
|
82
|
+
:name => "crashlog",
|
83
|
+
:version => CrashLog::VERSION
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
# Returns the hostname of this machine
|
88
|
+
def hostname
|
89
|
+
SystemInformation.hostname
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def serialize_event(event_data)
|
95
|
+
if event_data.is_a?(Exception)
|
96
|
+
@backtrace = build_backtrace(event_data)
|
97
|
+
serialize_exception(event_data)
|
98
|
+
|
99
|
+
elsif event_data.is_a?(Hash)
|
100
|
+
event_data.merge(:timestamp => timestamp)
|
101
|
+
|
102
|
+
elsif event_data.respond_to?(:message) && event_data.respond_to?(:type)
|
103
|
+
ducktype_event(event_data)
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def ducktype_event(event_data)
|
109
|
+
{}.tap do |response|
|
110
|
+
response[:timestamp] = timestamp
|
111
|
+
response[:type] = event_data.type
|
112
|
+
response[:message] = event_data.message
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def serialize_exception(exception)
|
117
|
+
exception = unwrap_exception(exception)
|
118
|
+
# opts = opts.merge(:exception => exception) if exception.is_a?(Exception)
|
119
|
+
# opts = opts.merge(exception.to_hash) if exception.respond_to?(:to_hash)
|
120
|
+
|
121
|
+
{}.tap do |response|
|
122
|
+
response[:timestamp] = timestamp
|
123
|
+
response[:message] = exception.message
|
124
|
+
response[:type] = error_class(exception)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def build_backtrace(exception)
|
129
|
+
Backtrace.parse((exception.backtrace || caller),
|
130
|
+
:filters => @backtrace_filters).to_a
|
131
|
+
end
|
132
|
+
|
133
|
+
def error_class(exception)
|
134
|
+
# The "Class" check is for some strange exceptions like Timeout::Error
|
135
|
+
# which throw the error class instead of an instance
|
136
|
+
(exception.is_a? Class) ? exception.name : exception.class.name
|
137
|
+
end
|
138
|
+
|
139
|
+
def unwrap_exception(exception)
|
140
|
+
if exception.respond_to?(:original_exception)
|
141
|
+
exception.original_exception
|
142
|
+
elsif exception.respond_to?(:continued_exception)
|
143
|
+
exception.continued_exception
|
144
|
+
else
|
145
|
+
exception
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def renderer
|
150
|
+
Rabl::Renderer.new('payload', self, { :format => 'hash', :view_path => view_path })
|
151
|
+
end
|
152
|
+
|
153
|
+
def view_path
|
154
|
+
File.expand_path('../templates', __FILE__)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module CrashLog
|
2
|
+
# CrashLog Middleware for Rack applications. Any errors raised by the upstream
|
3
|
+
# application will be delivered to CrashLog and re-raised.
|
4
|
+
#
|
5
|
+
# Synopsis:
|
6
|
+
#
|
7
|
+
# require 'rack'
|
8
|
+
# require 'crashlog'
|
9
|
+
#
|
10
|
+
# CrashLog.configure do |config|
|
11
|
+
# config.api_key = 'project-api-key'
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# app = Rack::Builder.app do
|
15
|
+
# run lambda { |env| raise "Fully Racked" }
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# use CrashLog::Rack
|
19
|
+
# run app
|
20
|
+
#
|
21
|
+
# Use a standard CrashLog.configure call to configure your api key.
|
22
|
+
|
23
|
+
class Rack
|
24
|
+
def initialize(app)
|
25
|
+
@app = app
|
26
|
+
CrashLog.configuration.logger ||= Logger.new($stdout)
|
27
|
+
end
|
28
|
+
|
29
|
+
def call(env)
|
30
|
+
begin
|
31
|
+
response = @app.call(env)
|
32
|
+
rescue Exception => exception
|
33
|
+
error_id = CrashLog.notify(exception, :rack_env => env)
|
34
|
+
env['crash_log.error_id'] = error_id
|
35
|
+
raise
|
36
|
+
end
|
37
|
+
|
38
|
+
if env['rack.exception']
|
39
|
+
error_id = CrashLog.notify(env['rack.exception'], :rack_env => env)
|
40
|
+
env['crash_log.error_id'] = error_id
|
41
|
+
end
|
42
|
+
|
43
|
+
response
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module CrashLog
|
2
|
+
module Rails
|
3
|
+
|
4
|
+
# CrashLog controller integration
|
5
|
+
# Aliases method chain for Rails internal rescue action
|
6
|
+
module ActionControllerRescue
|
7
|
+
def self.included(base)
|
8
|
+
base.send(:alias_method, :rescue_action_in_public_without_crash_log, :rescue_action_in_public)
|
9
|
+
base.send(:alias_method, :rescue_action_in_public, :rescue_action_in_public_with_crash_log)
|
10
|
+
|
11
|
+
base.send(:alias_method, :rescue_action_locally_without_crash_log, :rescue_action_locally)
|
12
|
+
base.send(:alias_method, :rescue_action_locally, :rescue_action_locally_with_crash_log)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# crash_log_context is defined in controller_methods.rb
|
18
|
+
def rescue_action_in_public_with_crash_log(exception)
|
19
|
+
crash_log.auto_notify(exception, crash_log_context)
|
20
|
+
rescue_action_in_public_without_crash_log(exception)
|
21
|
+
end
|
22
|
+
|
23
|
+
# crash_log_context is defined in controller_methods.rb
|
24
|
+
def rescue_action_locally_with_crash_log(exception)
|
25
|
+
crash_log.auto_notify(exception, crash_log_context)
|
26
|
+
rescue_action_locally_without_crash_log(exception)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|