sfalma 0.7
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/bin/sfalma +76 -0
- data/init.rb +27 -0
- data/install.rb +19 -0
- data/lib/sfalma/alert_data.rb +15 -0
- data/lib/sfalma/application_environment.rb +79 -0
- data/lib/sfalma/catcher.rb +26 -0
- data/lib/sfalma/config.rb +93 -0
- data/lib/sfalma/controller_exception_data.rb +69 -0
- data/lib/sfalma/exception_data.rb +146 -0
- data/lib/sfalma/integration/alerter.rb +11 -0
- data/lib/sfalma/integration/dj.rb +12 -0
- data/lib/sfalma/integration/rack.rb +28 -0
- data/lib/sfalma/integration/rack_rails.rb +26 -0
- data/lib/sfalma/integration/rails.rb +27 -0
- data/lib/sfalma/integration/sinatra.rb +6 -0
- data/lib/sfalma/integration/tester.rb +20 -0
- data/lib/sfalma/log_factory.rb +39 -0
- data/lib/sfalma/monkeypatches.rb +10 -0
- data/lib/sfalma/rack_exception_data.rb +29 -0
- data/lib/sfalma/railtie.rb +20 -0
- data/lib/sfalma/remote.rb +59 -0
- data/lib/sfalma/startup.rb +14 -0
- data/lib/sfalma/vcs.rb +45 -0
- data/lib/sfalma/version.rb +3 -0
- data/lib/sfalma.rb +71 -0
- data/lib/tasks/sfalma_tasks.rake +11 -0
- data/rails/init.rb +1 -0
- data/sfalma.gemspec +18 -0
- data/spec/bin/ginger +54 -0
- data/spec/dj_integration_spec.rb +29 -0
- data/spec/fixtures/favicon.png +0 -0
- data/spec/fixtures/sfalma.yml +10 -0
- data/spec/fixtures/sfalma_disabled.yml +4 -0
- data/spec/ginger_scenarios.rb +33 -0
- data/spec/rack_integration_spec.rb +21 -0
- data/spec/rails_integration_spec.rb +95 -0
- data/spec/rails_rack_integration_spec.rb +29 -0
- data/spec/sfalma/alert_exception_data_spec.rb +11 -0
- data/spec/sfalma/catcher_spec.rb +13 -0
- data/spec/sfalma/config_spec.rb +64 -0
- data/spec/sfalma/controller_exception_data_spec.rb +26 -0
- data/spec/sfalma/exception_data_spec.rb +198 -0
- data/spec/sfalma/monkeypatches_spec.rb +11 -0
- data/spec/sfalma/rack_exception_data_spec.rb +87 -0
- data/spec/sfalma/remote_spec.rb +31 -0
- data/spec/sfalma/startup_spec.rb +15 -0
- data/spec/sfalma_rescue_spec.rb +74 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/standalone_spec.rb +9 -0
- metadata +111 -0
data/bin/sfalma
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
args = ARGV.dup
|
4
|
+
ARGV.clear
|
5
|
+
command = args.shift.strip rescue 'help'
|
6
|
+
|
7
|
+
case command
|
8
|
+
when 'help'
|
9
|
+
puts <<USAGE
|
10
|
+
help # Show this usage.
|
11
|
+
test # Send a test exception to Sfalma.
|
12
|
+
alert <message> # Send a message to Sfalma. Exits 0 for success, 1 for config error, and 2 for report failure
|
13
|
+
install <api_key> # Create config/sfalma.yml with your api_key. Overrites existing one.
|
14
|
+
install <api_key> <environment> # Create config/sfalma.yml with your api_key and enabled for a specific environment. Overrites existing config file.
|
15
|
+
install <api_key> <environment>,<environment> # Create config/sfalma.yml with your api_key enabled for multiple environments (comma seperated). Overrites existing config file.
|
16
|
+
|
17
|
+
USAGE
|
18
|
+
when 'test'
|
19
|
+
if defined?(RAILS_ROOT)
|
20
|
+
puts "Loading Rails environment."
|
21
|
+
require(File.join('config', 'boot'))
|
22
|
+
require(File.join(RAILS_ROOT, 'config', 'environment')) if defined?(RAILS_ROOT)
|
23
|
+
require "sfalma/integration/tester"
|
24
|
+
else
|
25
|
+
require 'json'
|
26
|
+
require 'sfalma'
|
27
|
+
require "sfalma/integration/tester"
|
28
|
+
Sfalma::Config.load('config/sfalma.yml')
|
29
|
+
end
|
30
|
+
if Sfalma::Config.api_key
|
31
|
+
Sfalma::Integration.test
|
32
|
+
else
|
33
|
+
puts 'API key not configured'
|
34
|
+
end
|
35
|
+
when 'alert'
|
36
|
+
require "sfalma"
|
37
|
+
require "sfalma/alert_data"
|
38
|
+
require "sfalma/integration/alerter"
|
39
|
+
Sfalma::Config.load('config/sfalma.yml')
|
40
|
+
if Sfalma::Config.api_key
|
41
|
+
exit(Sfalma::Integration.alert(args[0]) ? 0 : 2)
|
42
|
+
else
|
43
|
+
puts 'API key not configured. Put sfalma.yml in current directory or /config'
|
44
|
+
exit(1)
|
45
|
+
end
|
46
|
+
when 'install'
|
47
|
+
api_key = args[0]
|
48
|
+
environments = args[1]
|
49
|
+
|
50
|
+
if (api_key.nil?)
|
51
|
+
puts 'Missing required paramater <api-key>. Check your app configuration at http://sfalma.com.'
|
52
|
+
else
|
53
|
+
if (defined?(RAILS_ROOT) && !File.file?('config/environment.rb'))
|
54
|
+
puts "Run this command from the root of your rails application." and exit
|
55
|
+
else
|
56
|
+
Dir.mkdir('config') unless File.exists?('config')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
config_file = File.open('config/sfalma.yml', 'w')
|
60
|
+
config_file.puts("api-key: #{api_key}\n")
|
61
|
+
|
62
|
+
unless(environments.nil?)
|
63
|
+
environments.split(',').each do |envo|
|
64
|
+
config_file.puts("#{envo}:\n")
|
65
|
+
config_file.puts(" enabled: true\n")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
config_file.close
|
70
|
+
puts "Config file written as config/sfalma.yml.";
|
71
|
+
puts "----------------------------------------------";
|
72
|
+
File.open('config/sfalma.yml').readlines.each do |line|
|
73
|
+
puts line
|
74
|
+
end
|
75
|
+
puts "----------------------------------------------";
|
76
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'sfalma'
|
2
|
+
begin
|
3
|
+
|
4
|
+
if (Rails::VERSION::MAJOR < 3)
|
5
|
+
Sfalma::Config.load(File.join(RAILS_ROOT, "/config/sfalma.yml"))
|
6
|
+
if Sfalma::Config.should_send_to_api?
|
7
|
+
Sfalma.logger.info("Loading Sfalma #{Sfalma::VERSION} for #{Rails::VERSION::STRING}")
|
8
|
+
require File.join('sfalma', 'integration', 'rails')
|
9
|
+
require File.join('sfalma', 'integration', 'dj') if defined?(Delayed::Job)
|
10
|
+
end
|
11
|
+
else
|
12
|
+
Sfalma::Config.load(File.join(Rails.root, "/config/sfalma.yml"))
|
13
|
+
|
14
|
+
if Sfalma::Config.should_send_to_api?
|
15
|
+
Sfalma.logger.info("Loading Sfalma #{Sfalma::VERSION} for #{Rails::VERSION::STRING}")
|
16
|
+
Rails.configuration.middleware.use "Rack::RailsSfalma"
|
17
|
+
require File.join('sfalma', 'integration', 'dj') if defined?(Delayed::Job)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Sfalma::Startup.announce
|
22
|
+
rescue => e
|
23
|
+
STDERR.puts "Problem starting Sfalma Plugin. Your app will run as normal. #{e.message}"
|
24
|
+
Sfalma.logger.error(e.message)
|
25
|
+
Sfalma.logger.error(e.backtrace)
|
26
|
+
end
|
27
|
+
|
data/install.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# This is the post install hook for when Sfalma is installed as a plugin.
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
# puts IO.read(File.join(File.dirname(__FILE__), 'README'))
|
5
|
+
|
6
|
+
config_file = File.expand_path("#{File.dirname(__FILE__)}/../../../config/sfalma.yml")
|
7
|
+
example_config_file = "#{File.dirname(__FILE__)}/sfalma.yml"
|
8
|
+
|
9
|
+
if File::exists? config_file
|
10
|
+
puts "Sfalma config file already exists. Please ensure it is up-to-date with the current format."
|
11
|
+
puts "See #{example_config_file}"
|
12
|
+
else
|
13
|
+
puts "Installing default Sfalma config."
|
14
|
+
puts " From #{example_config_file}"
|
15
|
+
puts "For sfalma to work you need to configure your API key."
|
16
|
+
puts " See #{example_config_file}"
|
17
|
+
puts "If you don't have an API key, get one at http://www.sfalma.com/."
|
18
|
+
FileUtils.copy example_config_file, config_file
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Sfalma
|
2
|
+
class AlertData < ExceptionData
|
3
|
+
# Overwrite backtrace, since it is irrelevant
|
4
|
+
def extra_data
|
5
|
+
{
|
6
|
+
'exception' => {
|
7
|
+
'exception_class' => @exception.class.to_s,
|
8
|
+
'message' => @exception.message,
|
9
|
+
'backtrace' => "",
|
10
|
+
'occurred_at' => Time.now
|
11
|
+
}
|
12
|
+
}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
|
3
|
+
module Sfalma
|
4
|
+
class ApplicationEnvironment
|
5
|
+
def self.to_hash(framework)
|
6
|
+
env = extract_environment(ENV)
|
7
|
+
{
|
8
|
+
'client' => {
|
9
|
+
'name' => Sfalma::CLIENT_NAME,
|
10
|
+
'version' => Sfalma::VERSION,
|
11
|
+
'protocol_version' => Sfalma::PROTOCOL_VERSION
|
12
|
+
},
|
13
|
+
'application_environment' => {
|
14
|
+
'environment' => environment,
|
15
|
+
'host' => get_hostname,
|
16
|
+
'run_as_user' => get_username,
|
17
|
+
'app_home' => (application_root.to_s.respond_to?(:force_encoding) ? application_root.to_s.force_encoding("UTF-8") : application_root),
|
18
|
+
'gem_home' => env.fetch('GEM_HOME',''),
|
19
|
+
'language' => 'ruby',
|
20
|
+
'language_version' => language_version_string,
|
21
|
+
'framework' => framework,
|
22
|
+
'libraries_loaded' => libraries_loaded
|
23
|
+
}
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.to_hash_basic(framework)
|
28
|
+
{
|
29
|
+
'client' => {
|
30
|
+
'name' => Sfalma::CLIENT_NAME,
|
31
|
+
'version' => Sfalma::VERSION,
|
32
|
+
'protocol_version' => Sfalma::PROTOCOL_VERSION
|
33
|
+
},
|
34
|
+
'application_environment' => {
|
35
|
+
'environment' => environment,
|
36
|
+
'app_home' => (application_root.to_s.respond_to?(:force_encoding) ? application_root.to_s.force_encoding("UTF-8") : application_root),
|
37
|
+
'gem_home' => extract_environment(ENV).fetch('GEM_HOME','')
|
38
|
+
}
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.environment
|
43
|
+
Config.application_environment
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.application_root
|
47
|
+
Config.application_root
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.extract_environment(env)
|
51
|
+
env.reject do |k, v|
|
52
|
+
(k =~ /^HTTP_/) || Sfalma::ENVIRONMENT_FILTER.include?(k)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.get_hostname
|
57
|
+
require 'socket' unless defined?(Socket)
|
58
|
+
Socket.gethostname
|
59
|
+
rescue
|
60
|
+
'UNKNOWN'
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.language_version_string
|
64
|
+
"#{RUBY_VERSION rescue '?.?.?'} p#{RUBY_PATCHLEVEL rescue '???'} #{RUBY_RELEASE_DATE rescue '????-??-??'} #{RUBY_PLATFORM rescue '????'}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.get_username
|
68
|
+
ENV['LOGNAME'] || ENV['USER'] || ENV['USERNAME'] || ENV['APACHE_RUN_USER'] || 'UNKNOWN'
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.libraries_loaded
|
72
|
+
begin
|
73
|
+
return Hash[*Gem.loaded_specs.map{|name, gem_specification| [name, gem_specification.version.to_s]}.flatten]
|
74
|
+
rescue
|
75
|
+
end
|
76
|
+
{}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Sfalma
|
2
|
+
class Catcher
|
3
|
+
class << self
|
4
|
+
def handle_with_controller(exception, controller=nil, request=nil)
|
5
|
+
if Config.should_send_to_api?
|
6
|
+
data = ControllerExceptionData.new(exception, controller, request)
|
7
|
+
Remote.error(data)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def handle_with_rack(exception, environment, request)
|
12
|
+
if Config.should_send_to_api?
|
13
|
+
data = RackExceptionData.new(exception, environment, request)
|
14
|
+
Remote.error(data)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def handle(exception, name=nil)
|
19
|
+
if Config.should_send_to_api?
|
20
|
+
data = ExceptionData.new(exception, name)
|
21
|
+
Remote.error(data)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Sfalma
|
4
|
+
class Config
|
5
|
+
class ConfigurationException < StandardError; end
|
6
|
+
|
7
|
+
class << self
|
8
|
+
DEFAULTS = {
|
9
|
+
:ssl => false,
|
10
|
+
:remote_host_http => 'http://www.sfalma.com',
|
11
|
+
:http_open_timeout => 2,
|
12
|
+
:http_read_timeout => 4,
|
13
|
+
:disabled_by_default => %w(development test)
|
14
|
+
}
|
15
|
+
|
16
|
+
attr_accessor :api_key, :enabled
|
17
|
+
attr_accessor :http_proxy_host, :http_proxy_port, :http_proxy_username, :http_proxy_password
|
18
|
+
attr_writer :ssl
|
19
|
+
|
20
|
+
def load(config_file=nil)
|
21
|
+
if (config_file && File.file?(config_file))
|
22
|
+
begin
|
23
|
+
config = YAML::load_file(config_file)
|
24
|
+
env_config = config[application_environment] || {}
|
25
|
+
@api_key = config['api-key'] || env_config['api-key']
|
26
|
+
|
27
|
+
@http_proxy_host = config['http-proxy-host']
|
28
|
+
@http_proxy_port = config['http-proxy-port']
|
29
|
+
@http_proxy_username = config['http-proxy-username']
|
30
|
+
@http_proxy_password = config['http-proxy-password']
|
31
|
+
@http_open_timeout = config['http-open-timeout']
|
32
|
+
@http_read_timeout = config['http-read-timeout']
|
33
|
+
|
34
|
+
@ssl = config['ssl'] || env_config['ssl']
|
35
|
+
@enabled = env_config['enabled']
|
36
|
+
@remote_port = config['remote-port'].to_i unless config['remote-port'].nil?
|
37
|
+
@remote_host = config['remote-host'] unless config['remote-host'].nil?
|
38
|
+
# VCS
|
39
|
+
@vcs = config['vcs'] unless config['vcs'].nil?
|
40
|
+
rescue Exception => e
|
41
|
+
raise ConfigurationException.new("Unable to load configuration #{config_file} for environment #{application_environment} : #{e.message}")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def vcs
|
47
|
+
@vcs ||= nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def api_key
|
51
|
+
return @api_key unless @api_key.nil?
|
52
|
+
@api_key ||= ENV['EXCEPTIONAL_API_KEY'] unless ENV['EXCEPTIONAL_API_KEY'].nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
def application_environment
|
56
|
+
ENV['RACK_ENV'] || ENV['RAILS_ENV']|| 'development'
|
57
|
+
end
|
58
|
+
|
59
|
+
def should_send_to_api?
|
60
|
+
return @enabled unless @enabled.nil?
|
61
|
+
@enabled = true#!(DEFAULTS[:disabled_by_default].include?(application_environment))
|
62
|
+
end
|
63
|
+
|
64
|
+
def application_root
|
65
|
+
(defined?(Rails) && Rails.respond_to?(:root)) ? Rails.root : Dir.pwd
|
66
|
+
end
|
67
|
+
|
68
|
+
def ssl?
|
69
|
+
@ssl ||= DEFAULTS[:ssl]
|
70
|
+
end
|
71
|
+
|
72
|
+
def remote_host
|
73
|
+
@remote_host ||= DEFAULTS[:remote_host_http]
|
74
|
+
end
|
75
|
+
|
76
|
+
def remote_port
|
77
|
+
@remote_port =80 #||= ssl? ? 443 : 80, we wait for naked domain SSL on GAE
|
78
|
+
end
|
79
|
+
|
80
|
+
def reset
|
81
|
+
@enabled = @ssl = @remote_host = @remote_port = @api_key = nil
|
82
|
+
end
|
83
|
+
|
84
|
+
def http_open_timeout
|
85
|
+
@http_open_timeout ||= DEFAULTS[:http_open_timeout]
|
86
|
+
end
|
87
|
+
|
88
|
+
def http_read_timeout
|
89
|
+
@http_read_timeout ||= DEFAULTS[:http_read_timeout]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
|
3
|
+
module Sfalma
|
4
|
+
class ControllerExceptionData < ExceptionData
|
5
|
+
def initialize(exception, controller=nil, request=nil)
|
6
|
+
super(exception)
|
7
|
+
@request = request
|
8
|
+
@controller = controller
|
9
|
+
end
|
10
|
+
|
11
|
+
def framework
|
12
|
+
"rails"
|
13
|
+
end
|
14
|
+
|
15
|
+
def extra_stuff
|
16
|
+
return {} if @request.nil?
|
17
|
+
|
18
|
+
{
|
19
|
+
'request' => {
|
20
|
+
'url' => (@request.respond_to?(:url) ? @request.url : "#{@request.protocol}#{@request.host}#{@request.request_uri}"),
|
21
|
+
'controller' => @controller.class.to_s,
|
22
|
+
'action' => action_name,
|
23
|
+
'parameters' => filter_paramaters(@request.respond_to?(:parameters) ? @request.parameters : @request.params),
|
24
|
+
'request_method' => @request.request_method.to_s,
|
25
|
+
'remote_ip' => (@request.respond_to?(:remote_ip) ? @request.remote_ip : @request.ip),
|
26
|
+
'headers' => extract_http_headers(@request.env),
|
27
|
+
'session' => self.class.sanitize_session(@request)
|
28
|
+
}
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def action_name
|
33
|
+
action_name = ''
|
34
|
+
if @controller.respond_to?(:action_name)
|
35
|
+
action_name = @controller.action_name.to_s
|
36
|
+
else
|
37
|
+
action_name = (@request.respond_to?(:parameters) ? @request.parameters['action'] : @request.params['action'])
|
38
|
+
end
|
39
|
+
action_name
|
40
|
+
end
|
41
|
+
|
42
|
+
def filter_hash(keys_to_filter, hash)
|
43
|
+
if keys_to_filter.is_a?(Array) && !keys_to_filter.empty?
|
44
|
+
hash.each do |key, value|
|
45
|
+
if value.respond_to?(:to_hash)
|
46
|
+
filter_hash(keys_to_filter, hash[key])
|
47
|
+
elsif key_match?(key, keys_to_filter)
|
48
|
+
hash[key] = "[FILTERED]"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
hash
|
53
|
+
end
|
54
|
+
|
55
|
+
def key_match?(key, keys_to_filter)
|
56
|
+
keys_to_filter.map {|k| k.to_s}.include?(key.to_s)
|
57
|
+
end
|
58
|
+
|
59
|
+
def filter_paramaters(hash)
|
60
|
+
if @request.respond_to?(:env) && @request.env["action_dispatch.parameter_filter"]
|
61
|
+
filter_hash(@request.env["action_dispatch.parameter_filter"], hash)
|
62
|
+
elsif @controller.respond_to?(:filter_parameters)
|
63
|
+
@controller.send(:filter_parameters, hash)
|
64
|
+
else
|
65
|
+
hash
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module Sfalma
|
5
|
+
class ExceptionData
|
6
|
+
|
7
|
+
def initialize(exception, name=nil)
|
8
|
+
@exception = exception
|
9
|
+
@name = name
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_hash
|
13
|
+
# Do not send the environment for every exception
|
14
|
+
hash = ::Sfalma::ApplicationEnvironment.to_hash_basic(framework)
|
15
|
+
app_home = hash['application_environment']['app_home']
|
16
|
+
gem_home = hash['application_environment']['gem_home']
|
17
|
+
line_and_number = where(@exception.backtrace, app_home, gem_home)
|
18
|
+
|
19
|
+
hash.merge!({
|
20
|
+
'exception' => {
|
21
|
+
'klass' => @exception.class.to_s,
|
22
|
+
'message' => @exception.message,
|
23
|
+
'backtrace' => normalize(@exception.backtrace, app_home, gem_home),
|
24
|
+
'occurred_at' => Time.now.utc.iso8601,
|
25
|
+
'where' => line_and_number
|
26
|
+
}
|
27
|
+
})
|
28
|
+
hash.merge!(extra_stuff)
|
29
|
+
hash.merge!(context_stuff)
|
30
|
+
#hash.merge!(::Sfalma::VCS(line_and_number, app_home).to_hash)
|
31
|
+
self.class.sanitize_hash(hash)
|
32
|
+
end
|
33
|
+
|
34
|
+
def normalize(trace, app_home, gem_home)
|
35
|
+
trace.map do |line|
|
36
|
+
if !line.index(app_home).nil?
|
37
|
+
line.gsub!(app_home,'[APP_HOME]')
|
38
|
+
elsif !line.index(gem_home).nil?
|
39
|
+
line.gsub!(gem_home, '[GEM_HOME]')
|
40
|
+
end
|
41
|
+
line
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def where(trace, app_path, gem_path)
|
46
|
+
line_and_number = ''
|
47
|
+
trace.each do |line|
|
48
|
+
if !line.index(app_path).nil?
|
49
|
+
line_and_number = line[0,line.rindex(':')]
|
50
|
+
puts line
|
51
|
+
break
|
52
|
+
end
|
53
|
+
end
|
54
|
+
if line_and_number == ''
|
55
|
+
line_and_number = trace[0][0,trace[0].rindex(':')]
|
56
|
+
end
|
57
|
+
line_and_number.gsub!(app_path,'').gsub!(gem_path,'') rescue line_and_number
|
58
|
+
line_and_number
|
59
|
+
end
|
60
|
+
|
61
|
+
def extra_stuff
|
62
|
+
{ 'rescue_block' => { 'name' => @name} }
|
63
|
+
end
|
64
|
+
|
65
|
+
def context_stuff
|
66
|
+
context = Thread.current[:exceptional_context]
|
67
|
+
(context.nil? || context.empty?) ? {} : {'context' => context}
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_json
|
71
|
+
begin
|
72
|
+
to_hash.to_json
|
73
|
+
rescue NoMethodError
|
74
|
+
begin
|
75
|
+
require 'json'
|
76
|
+
return to_hash.to_json
|
77
|
+
rescue StandardError => e
|
78
|
+
Sfalma.logger.error(e.message)
|
79
|
+
Sfalma.logger.error(e.backtrace)
|
80
|
+
raise StandardError.new("You need a json gem/library installed to send errors to Sfalma (Object.to_json not defined). \nInstall json_pure, yajl-ruby, json-jruby, or the c-based json gem")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def framework
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
|
89
|
+
def uniqueness_hash
|
90
|
+
return nil if (@exception.backtrace.nil? || @exception.backtrace.empty?)
|
91
|
+
# in case we have the same exception class at the same line but caused by different method
|
92
|
+
@exception.backtrace.push(@exception.message)
|
93
|
+
traces = @exception.backtrace.collect{ |line|
|
94
|
+
line if line.scan(/_run__\d+__process_action__\d+__callbacks/).size<1
|
95
|
+
}.compact
|
96
|
+
Digest::MD5.hexdigest(traces.join)
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.sanitize_hash(hash)
|
100
|
+
case hash
|
101
|
+
when Hash
|
102
|
+
hash.inject({}) do |result, (key, value)|
|
103
|
+
result.update(key => sanitize_hash(value))
|
104
|
+
end
|
105
|
+
when Array
|
106
|
+
hash.collect{|value| sanitize_hash(value)}
|
107
|
+
when Fixnum, String, Bignum
|
108
|
+
hash
|
109
|
+
else
|
110
|
+
hash.to_s
|
111
|
+
end
|
112
|
+
rescue Exception => e
|
113
|
+
Sfalma.logger.error(hash)
|
114
|
+
Sfalma.logger.error(e.message)
|
115
|
+
Sfalma.logger.error(e.backtrace)
|
116
|
+
{}
|
117
|
+
end
|
118
|
+
|
119
|
+
def extract_http_headers(env)
|
120
|
+
headers = {}
|
121
|
+
env.select{|k, v| k =~ /^HTTP_/}.each do |name, value|
|
122
|
+
proper_name = name.sub(/^HTTP_/, '').split('_').map{|upper_case| upper_case.capitalize}.join('-')
|
123
|
+
headers[proper_name] = value
|
124
|
+
end
|
125
|
+
unless headers['Cookie'].nil?
|
126
|
+
headers['Cookie'] = headers['Cookie'].sub(/_session=\S+/, '_session=[FILTERED]')
|
127
|
+
end
|
128
|
+
headers
|
129
|
+
end
|
130
|
+
|
131
|
+
def self.sanitize_session(request)
|
132
|
+
session_hash = {'session_id' => "", 'data' => {}}
|
133
|
+
|
134
|
+
if request.respond_to?(:session)
|
135
|
+
session = request.session
|
136
|
+
session_hash['session_id'] = request.session_options ? request.session_options[:id] : nil
|
137
|
+
session_hash['session_id'] ||= session.respond_to?(:session_id) ? session.session_id : session.instance_variable_get("@session_id")
|
138
|
+
session_hash['data'] = session.respond_to?(:to_hash) ? session.to_hash : session.instance_variable_get("@data") || {}
|
139
|
+
session_hash['session_id'] ||= session_hash['data'][:session_id]
|
140
|
+
session_hash['data'].delete(:session_id)
|
141
|
+
end
|
142
|
+
|
143
|
+
self.sanitize_hash(session_hash)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
begin
|
2
|
+
class Delayed::Job
|
3
|
+
def log_exception_with_sfalma(e)
|
4
|
+
Sfalma.handle(e, "Delayed::Job #{self.name}")
|
5
|
+
log_exception_without_sfalma(e)
|
6
|
+
Sfalma.context.clear!
|
7
|
+
end
|
8
|
+
alias_method_chain :log_exception, :sfalma
|
9
|
+
Sfalma.logger.info "DJ integration enabled"
|
10
|
+
end
|
11
|
+
rescue
|
12
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rack'
|
3
|
+
|
4
|
+
module Rack
|
5
|
+
class Sfalma
|
6
|
+
|
7
|
+
def initialize(app, api_key = nil)
|
8
|
+
@app = app
|
9
|
+
if api_key.nil?
|
10
|
+
sfalma_config = "config/sfalma.yml"
|
11
|
+
::Sfalma::Config.load(sfalma_config)
|
12
|
+
else
|
13
|
+
::Sfalma.configure(api_key)
|
14
|
+
::Sfalma::Config.enabled = true
|
15
|
+
::Sfalma.logger.info "Enabling Sfalma for Rack"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def call(env)
|
20
|
+
begin
|
21
|
+
status, headers, body = @app.call(env)
|
22
|
+
rescue Exception => e
|
23
|
+
::Sfalma::Catcher.handle_with_rack(e,env, Rack::Request.new(env))
|
24
|
+
raise(e)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rack'
|
3
|
+
|
4
|
+
module Rack
|
5
|
+
class RailsSfalma
|
6
|
+
|
7
|
+
def initialize(app)
|
8
|
+
@app = app
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
begin
|
13
|
+
body = @app.call(env)
|
14
|
+
rescue Exception => e
|
15
|
+
::Sfalma::Catcher.handle_with_controller(e,env['action_controller.instance'], Rack::Request.new(env))
|
16
|
+
raise
|
17
|
+
end
|
18
|
+
|
19
|
+
if env['rack.exception']
|
20
|
+
::Sfalma::Catcher.handle_with_controller(env['rack.exception'],env['action_controller.instance'], Rack::Request.new(env))
|
21
|
+
end
|
22
|
+
|
23
|
+
body
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# force Rails < 2.0 to use quote keys as per the JSON standard...
|
2
|
+
if defined?(ActiveSupport) && defined?(ActiveSupport::JSON) && ActiveSupport::JSON.respond_to?(:unquote_hash_key_identifiers)
|
3
|
+
ActiveSupport::JSON.unquote_hash_key_identifiers = false
|
4
|
+
end
|
5
|
+
|
6
|
+
if defined? ActionController
|
7
|
+
module ActionController
|
8
|
+
class Base
|
9
|
+
def rescue_action_with_exceptional(exception)
|
10
|
+
unless exception_handled_by_rescue_from?(exception)
|
11
|
+
Sfalma::Catcher.handle_with_controller(exception, self, request)
|
12
|
+
Sfalma.context.clear!
|
13
|
+
end
|
14
|
+
rescue_action_without_exceptional exception
|
15
|
+
end
|
16
|
+
|
17
|
+
alias_method :rescue_action_without_exceptional, :rescue_action
|
18
|
+
alias_method :rescue_action, :rescue_action_with_exceptional
|
19
|
+
protected :rescue_action
|
20
|
+
|
21
|
+
private
|
22
|
+
def exception_handled_by_rescue_from?(exception)
|
23
|
+
respond_to?(:handler_for_rescue) && handler_for_rescue(exception)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|