crashlog 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|