appygram-rails 0.9.5 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/appygram-rails.gemspec +4 -3
- data/lib/appygram-rails.rb +2 -95
- data/lib/appygram-rails/catcher.rb +4 -27
- data/lib/appygram-rails/integration/rack_rails.rb +2 -2
- data/lib/appygram-rails/version.rb +2 -2
- metadata +21 -20
- data/lib/appygram-rails/alert_data.rb +0 -15
- data/lib/appygram-rails/application_environment.rb +0 -55
- data/lib/appygram-rails/config.rb +0 -63
- data/lib/appygram-rails/controller_exception_data.rb +0 -75
- data/lib/appygram-rails/exception_data.rb +0 -108
- data/lib/appygram-rails/integration/alerter.rb +0 -11
- data/lib/appygram-rails/integration/debug_exceptions.rb +0 -16
- data/lib/appygram-rails/integration/rack.rb +0 -28
- data/lib/appygram-rails/log_factory.rb +0 -39
- data/lib/appygram-rails/monkeypatches.rb +0 -10
- data/lib/appygram-rails/rack_exception_data.rb +0 -29
- data/lib/appygram-rails/remote.rb +0 -52
- data/lib/appygram-rails/startup.rb +0 -14
data/appygram-rails.gemspec
CHANGED
@@ -5,10 +5,10 @@ require 'appygram-rails/version'
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |gem|
|
7
7
|
gem.name = %q{appygram-rails}
|
8
|
-
gem.version =
|
8
|
+
gem.version = AppygramRails::VERSION
|
9
9
|
gem.authors = ["rfc2616"]
|
10
10
|
gem.summary = %q{ appygram is a hosted service for sending messages from mobile/web apps }
|
11
|
-
gem.description = %q{appygram-rails sends exceptions
|
11
|
+
gem.description = %q{appygram-rails sends uncaught Rails exceptions as traces to the hosted messaging service at http://www.appygram.com}
|
12
12
|
gem.email = ['heittman.rob@gmail.com']
|
13
13
|
gem.homepage = 'http://www.appygram.com/'
|
14
14
|
|
@@ -17,5 +17,6 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
18
|
gem.require_paths = ["lib"]
|
19
19
|
|
20
|
-
gem.add_runtime_dependency '
|
20
|
+
gem.add_runtime_dependency 'appygram', '~> 1.0', '>= 1.0.3'
|
21
|
+
gem.add_runtime_dependency 'rack'
|
21
22
|
end
|
data/lib/appygram-rails.rb
CHANGED
@@ -1,106 +1,13 @@
|
|
1
|
-
require 'appygram-rails/monkeypatches'
|
2
1
|
require 'appygram-rails/catcher'
|
3
|
-
require 'appygram-rails/startup'
|
4
|
-
require 'appygram-rails/log_factory'
|
5
|
-
require 'appygram-rails/config'
|
6
|
-
require 'appygram-rails/application_environment'
|
7
|
-
require 'appygram-rails/exception_data'
|
8
|
-
require 'appygram-rails/controller_exception_data'
|
9
|
-
require 'appygram-rails/rack_exception_data'
|
10
|
-
require 'appygram-rails/alert_data'
|
11
|
-
require 'appygram-rails/remote'
|
12
|
-
require 'appygram-rails/integration/rack'
|
13
2
|
require 'appygram-rails/integration/rack_rails'
|
14
|
-
require 'appygram-rails/integration/alerter'
|
15
3
|
require 'appygram-rails/version'
|
16
|
-
require 'appygram-rails/integration/debug_exceptions'
|
17
4
|
|
18
|
-
module
|
19
|
-
|
20
|
-
PROTOCOL_VERSION = 5
|
21
|
-
CLIENT_NAME = 'appygram-rails'
|
22
|
-
ENVIRONMENT_FILTER = []
|
23
|
-
|
24
|
-
@@limit = 25
|
25
|
-
|
26
|
-
def self.restrict_duplicates(hash)
|
27
|
-
return false unless @@limit > 0
|
28
|
-
|
29
|
-
@@throttle ||= Hash.new
|
30
|
-
@@last_used ||= Date.today
|
31
|
-
if Date.today.yday != @@last_used.yday
|
32
|
-
@@last_used = Date.today
|
33
|
-
@@throttle = Hash.new
|
34
|
-
end
|
35
|
-
if @@throttle[hash]
|
36
|
-
@@throttle[hash] = @@throttle[hash] + 1
|
37
|
-
else
|
38
|
-
@@throttle[hash] = 1
|
39
|
-
end
|
40
|
-
if @@throttle[hash] > @@limit
|
41
|
-
return true
|
42
|
-
end
|
43
|
-
return false
|
44
|
-
end
|
45
|
-
|
46
|
-
def self.set_duplicate_limit(limit)
|
47
|
-
@@limit = limit
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.logger
|
51
|
-
::Appygram::LogFactory.logger
|
52
|
-
end
|
53
|
-
|
54
|
-
def self.configure(api_key)
|
55
|
-
Appygram::Config.api_key = api_key
|
56
|
-
end
|
57
|
-
|
58
|
-
def self.handle(exception, name=nil)
|
59
|
-
Appygram::Catcher.handle(exception, name)
|
60
|
-
end
|
61
|
-
|
62
|
-
def self.rescue(name=nil, context=nil, &block)
|
63
|
-
begin
|
64
|
-
self.context(context) unless context.nil?
|
65
|
-
block.call
|
66
|
-
rescue Exception => e
|
67
|
-
Appygram::Catcher.handle(e,name)
|
68
|
-
ensure
|
69
|
-
self.clear!
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def self.rescue_and_reraise(name=nil, context=nil, &block)
|
74
|
-
begin
|
75
|
-
self.context(context) unless context.nil?
|
76
|
-
block.call
|
77
|
-
rescue Exception => e
|
78
|
-
Appygram::Catcher.handle(e,name)
|
79
|
-
raise(e)
|
80
|
-
ensure
|
81
|
-
self.clear!
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def self.clear!
|
86
|
-
Thread.current[:appygram_context] = nil
|
87
|
-
end
|
88
|
-
|
89
|
-
def self.context(hash = {})
|
90
|
-
Thread.current[:appygram_context] ||= {}
|
91
|
-
Thread.current[:appygram_context].merge!(hash)
|
92
|
-
self
|
93
|
-
end
|
5
|
+
module AppygramRails
|
94
6
|
|
95
7
|
class Railtie < Rails::Railtie
|
96
8
|
|
97
9
|
initializer "appygram.middleware" do |app|
|
98
|
-
|
99
|
-
if defined?(ActionDispatch::DebugExceptions)
|
100
|
-
ActionDispatch::DebugExceptions.send(:include,Appygram::DebugExceptions)
|
101
|
-
else
|
102
|
-
app.config.middleware.use "Rack::RailsAppygram"
|
103
|
-
end
|
10
|
+
app.config.middleware.use "Rack::RailsAppygram"
|
104
11
|
end
|
105
12
|
|
106
13
|
end
|
@@ -1,33 +1,10 @@
|
|
1
|
-
|
1
|
+
require 'appygram'
|
2
|
+
|
3
|
+
module AppygramRails
|
2
4
|
class Catcher
|
3
5
|
class << self
|
4
6
|
def handle_with_controller(exception, controller=nil, request=nil)
|
5
|
-
|
6
|
-
data = ControllerExceptionData.new(exception, controller, request)
|
7
|
-
Remote.error(data)
|
8
|
-
else
|
9
|
-
raise exception
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
# unspeced
|
14
|
-
def handle_with_rack(exception, environment, request)
|
15
|
-
if Config.should_send_to_api?
|
16
|
-
data = RackExceptionData.new(exception, environment, request)
|
17
|
-
Remote.error(data)
|
18
|
-
else
|
19
|
-
raise exception
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
# unspeced
|
24
|
-
def handle(exception, name=nil)
|
25
|
-
if Config.should_send_to_api?
|
26
|
-
data = ExceptionData.new(exception, name)
|
27
|
-
Remote.error(data)
|
28
|
-
else
|
29
|
-
raise exception
|
30
|
-
end
|
7
|
+
Appygram.trace(exception)
|
31
8
|
end
|
32
9
|
end
|
33
10
|
end
|
@@ -12,12 +12,12 @@ module Rack
|
|
12
12
|
begin
|
13
13
|
body = @app.call(env)
|
14
14
|
rescue Exception => e
|
15
|
-
::
|
15
|
+
::AppygramRails::Catcher.handle_with_controller(e,env['action_controller.instance'], Rack::Request.new(env))
|
16
16
|
raise
|
17
17
|
end
|
18
18
|
|
19
19
|
if env['rack.exception']
|
20
|
-
::
|
20
|
+
::AppygramRails::Catcher.handle_with_controller(env['rack.exception'],env['action_controller.instance'], Rack::Request.new(env))
|
21
21
|
end
|
22
22
|
|
23
23
|
body
|
@@ -1,3 +1,3 @@
|
|
1
|
-
module
|
2
|
-
VERSION = '0.
|
1
|
+
module AppygramRails
|
2
|
+
VERSION = '1.0.1'
|
3
3
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: appygram-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,25 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-11-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
16
|
-
requirement: &
|
15
|
+
name: appygram
|
16
|
+
requirement: &20298560 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.0'
|
22
|
+
- - ! '>='
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 1.0.3
|
25
|
+
type: :runtime
|
26
|
+
prerelease: false
|
27
|
+
version_requirements: *20298560
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rack
|
30
|
+
requirement: &20297860 !ruby/object:Gem::Requirement
|
17
31
|
none: false
|
18
32
|
requirements:
|
19
33
|
- - ! '>='
|
@@ -21,9 +35,9 @@ dependencies:
|
|
21
35
|
version: '0'
|
22
36
|
type: :runtime
|
23
37
|
prerelease: false
|
24
|
-
version_requirements: *
|
25
|
-
description: appygram-rails sends exceptions
|
26
|
-
service at http://www.appygram.com
|
38
|
+
version_requirements: *20297860
|
39
|
+
description: appygram-rails sends uncaught Rails exceptions as traces to the hosted
|
40
|
+
messaging service at http://www.appygram.com
|
27
41
|
email:
|
28
42
|
- heittman.rob@gmail.com
|
29
43
|
executables: []
|
@@ -38,21 +52,8 @@ files:
|
|
38
52
|
- Rakefile
|
39
53
|
- appygram-rails.gemspec
|
40
54
|
- lib/appygram-rails.rb
|
41
|
-
- lib/appygram-rails/alert_data.rb
|
42
|
-
- lib/appygram-rails/application_environment.rb
|
43
55
|
- lib/appygram-rails/catcher.rb
|
44
|
-
- lib/appygram-rails/config.rb
|
45
|
-
- lib/appygram-rails/controller_exception_data.rb
|
46
|
-
- lib/appygram-rails/exception_data.rb
|
47
|
-
- lib/appygram-rails/integration/alerter.rb
|
48
|
-
- lib/appygram-rails/integration/debug_exceptions.rb
|
49
|
-
- lib/appygram-rails/integration/rack.rb
|
50
56
|
- lib/appygram-rails/integration/rack_rails.rb
|
51
|
-
- lib/appygram-rails/log_factory.rb
|
52
|
-
- lib/appygram-rails/monkeypatches.rb
|
53
|
-
- lib/appygram-rails/rack_exception_data.rb
|
54
|
-
- lib/appygram-rails/remote.rb
|
55
|
-
- lib/appygram-rails/startup.rb
|
56
57
|
- lib/appygram-rails/version.rb
|
57
58
|
homepage: http://www.appygram.com/
|
58
59
|
licenses: []
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module Appygram
|
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
|
@@ -1,55 +0,0 @@
|
|
1
|
-
require 'digest/md5'
|
2
|
-
|
3
|
-
module Appygram
|
4
|
-
class ApplicationEnvironment
|
5
|
-
def self.to_hash(framework)
|
6
|
-
{
|
7
|
-
'client' => {
|
8
|
-
'name' => Appygram::CLIENT_NAME,
|
9
|
-
'version' => Appygram::VERSION,
|
10
|
-
'protocol_version' => Appygram::PROTOCOL_VERSION
|
11
|
-
},
|
12
|
-
'application_environment' => {
|
13
|
-
'host' => get_hostname,
|
14
|
-
'run_as_user' => get_username,
|
15
|
-
'application_root_directory' => (application_root.to_s.respond_to?(:force_encoding) ? application_root.to_s.force_encoding("UTF-8") : application_root),
|
16
|
-
'language' => 'ruby',
|
17
|
-
'language_version' => language_version_string,
|
18
|
-
'framework' => framework,
|
19
|
-
'libraries_loaded' => libraries_loaded
|
20
|
-
}
|
21
|
-
}
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.environment
|
25
|
-
Config.application_environment
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.application_root
|
29
|
-
Config.application_root
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.get_hostname
|
33
|
-
require 'socket' unless defined?(Socket)
|
34
|
-
Socket.gethostname
|
35
|
-
rescue
|
36
|
-
'UNKNOWN'
|
37
|
-
end
|
38
|
-
|
39
|
-
def self.language_version_string
|
40
|
-
"#{RUBY_VERSION rescue '?.?.?'} p#{RUBY_PATCHLEVEL rescue '???'} #{RUBY_RELEASE_DATE rescue '????-??-??'} #{RUBY_PLATFORM rescue '????'}"
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.get_username
|
44
|
-
ENV['LOGNAME'] || ENV['USER'] || ENV['USERNAME'] || ENV['APACHE_RUN_USER'] || 'UNKNOWN'
|
45
|
-
end
|
46
|
-
|
47
|
-
def self.libraries_loaded
|
48
|
-
begin
|
49
|
-
return Hash[*Gem.loaded_specs.map{|name, gem_specification| [name, gem_specification.version.to_s]}.flatten]
|
50
|
-
rescue
|
51
|
-
end
|
52
|
-
{}
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
|
3
|
-
module Appygram
|
4
|
-
class Config
|
5
|
-
class ConfigurationException < StandardError; end
|
6
|
-
|
7
|
-
class << self
|
8
|
-
DEFAULTS = {
|
9
|
-
:ssl => false,
|
10
|
-
:remote_host_http => 'api.appygram.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 api_key
|
21
|
-
return @api_key unless @api_key.nil?
|
22
|
-
@api_key ||= ENV['EXCEPTIONAL_API_KEY'] unless ENV['EXCEPTIONAL_API_KEY'].nil?
|
23
|
-
end
|
24
|
-
|
25
|
-
def application_environment
|
26
|
-
ENV['RACK_ENV'] || ENV['RAILS_ENV']|| 'development'
|
27
|
-
end
|
28
|
-
|
29
|
-
def should_send_to_api?
|
30
|
-
return @enabled unless @enabled.nil?
|
31
|
-
@enabled = !(DEFAULTS[:disabled_by_default].include?(application_environment))
|
32
|
-
end
|
33
|
-
|
34
|
-
def application_root
|
35
|
-
(defined?(Rails) && Rails.respond_to?(:root)) ? Rails.root : Dir.pwd
|
36
|
-
end
|
37
|
-
|
38
|
-
def ssl?
|
39
|
-
@ssl ||= DEFAULTS[:ssl]
|
40
|
-
end
|
41
|
-
|
42
|
-
def remote_host
|
43
|
-
@remote_host ||= DEFAULTS[:remote_host_http]
|
44
|
-
end
|
45
|
-
|
46
|
-
def remote_port
|
47
|
-
@remote_port ||= ssl? ? 443 : 80
|
48
|
-
end
|
49
|
-
|
50
|
-
def reset
|
51
|
-
@enabled = @ssl = @remote_host = @remote_port = @api_key = nil
|
52
|
-
end
|
53
|
-
|
54
|
-
def http_open_timeout
|
55
|
-
@http_open_timeout ||= DEFAULTS[:http_open_timeout]
|
56
|
-
end
|
57
|
-
|
58
|
-
def http_read_timeout
|
59
|
-
@http_read_timeout ||= DEFAULTS[:http_read_timeout]
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
@@ -1,75 +0,0 @@
|
|
1
|
-
require 'digest/md5'
|
2
|
-
|
3
|
-
module Appygram
|
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
|
-
e = {
|
18
|
-
'request' => {
|
19
|
-
'url' => (@request.respond_to?(:url) ? @request.url : "#{@request.protocol}#{@request.host}#{@request.request_uri}"),
|
20
|
-
'controller' => @controller.class.to_s,
|
21
|
-
'action' => (@request.respond_to?(:parameters) ? @request.parameters['action'] : @request.params['action']),
|
22
|
-
'parameters' => filter_parameters(@request.respond_to?(:parameters) ? @request.parameters : @request.params),
|
23
|
-
'request_method' => @request.request_method.to_s,
|
24
|
-
'remote_ip' => (@request.respond_to?(:remote_ip) ? @request.remote_ip : @request.ip),
|
25
|
-
'headers' => extract_http_headers(@request.env),
|
26
|
-
'session' => self.class.sanitize_session(@request)
|
27
|
-
}
|
28
|
-
}
|
29
|
-
if @controller.respond_to?(:current_user)
|
30
|
-
cu = @controller.current_user
|
31
|
-
if cu and cu.email
|
32
|
-
e['request']['current_user'] = {
|
33
|
-
'id' => cu.id,
|
34
|
-
'email'=> cu.email
|
35
|
-
}
|
36
|
-
end
|
37
|
-
end
|
38
|
-
return e
|
39
|
-
end
|
40
|
-
|
41
|
-
def filter_hash(keys_to_filter, hash)
|
42
|
-
keys_to_filter.map! {|x| x.to_s}
|
43
|
-
if keys_to_filter.is_a?(Array) && !keys_to_filter.empty?
|
44
|
-
hash.each do |key, value|
|
45
|
-
if key_match?(key, keys_to_filter)
|
46
|
-
hash[key] = "[FILTERED]"
|
47
|
-
elsif value.respond_to?(:to_hash)
|
48
|
-
filter_hash(keys_to_filter, hash[key])
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
hash
|
53
|
-
end
|
54
|
-
|
55
|
-
# Closer alignment to latest filtered_params:
|
56
|
-
# https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/http/parameter_filter.rb
|
57
|
-
# https://github.com/exceptional/exceptional/issues/20
|
58
|
-
def key_match?(key, keys_to_filter)
|
59
|
-
keys_to_filter.any? { |k|
|
60
|
-
regexp = k.is_a?(Regexp)? k : Regexp.new(k, true)
|
61
|
-
key =~ regexp
|
62
|
-
}
|
63
|
-
end
|
64
|
-
|
65
|
-
def filter_parameters(hash)
|
66
|
-
if @request.respond_to?(:env) && @request.env["action_dispatch.parameter_filter"]
|
67
|
-
filter_hash(@request.env["action_dispatch.parameter_filter"], hash)
|
68
|
-
elsif @controller.respond_to?(:filter_parameters)
|
69
|
-
@controller.send(:filter_parameters, hash)
|
70
|
-
else
|
71
|
-
hash
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
@@ -1,108 +0,0 @@
|
|
1
|
-
require 'digest/md5'
|
2
|
-
require 'time'
|
3
|
-
|
4
|
-
module Appygram
|
5
|
-
class ExceptionData
|
6
|
-
|
7
|
-
def initialize(exception, name=nil)
|
8
|
-
@exception = exception
|
9
|
-
@name = name
|
10
|
-
end
|
11
|
-
|
12
|
-
def to_hash
|
13
|
-
hash = ::Appygram::ApplicationEnvironment.to_hash(framework)
|
14
|
-
hash.merge!({
|
15
|
-
'exception' => {
|
16
|
-
'exception_class' => @exception.class.to_s,
|
17
|
-
'message' => @exception.message,
|
18
|
-
'backtrace' => @exception.backtrace,
|
19
|
-
'occurred_at' => Time.now.utc.iso8601
|
20
|
-
}
|
21
|
-
})
|
22
|
-
hash.merge!(extra_stuff)
|
23
|
-
hash.merge!(context_stuff)
|
24
|
-
self.class.sanitize_hash(hash)
|
25
|
-
end
|
26
|
-
|
27
|
-
def extra_stuff
|
28
|
-
{ 'rescue_block' => { 'name' => @name} }
|
29
|
-
end
|
30
|
-
|
31
|
-
def context_stuff
|
32
|
-
context = Thread.current[:appygram_context]
|
33
|
-
(context.nil? || context.empty?) ? {} : {'context' => context}
|
34
|
-
end
|
35
|
-
|
36
|
-
def to_json
|
37
|
-
begin
|
38
|
-
to_hash.to_json
|
39
|
-
rescue NoMethodError
|
40
|
-
begin
|
41
|
-
require 'json'
|
42
|
-
return to_hash.to_json
|
43
|
-
rescue StandardError => e
|
44
|
-
Appygram.logger.error(e.message)
|
45
|
-
Appygram.logger.error(e.backtrace)
|
46
|
-
raise StandardError.new("You need a json gem/library installed to send errors to Appygram (Object.to_json not defined). \nInstall json_pure, yajl-ruby, json-jruby, or the c-based json gem")
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def framework
|
52
|
-
nil
|
53
|
-
end
|
54
|
-
|
55
|
-
def uniqueness_hash
|
56
|
-
return nil if (@exception.backtrace.nil? || @exception.backtrace.empty?)
|
57
|
-
Digest::MD5.hexdigest(@exception.backtrace.join)
|
58
|
-
end
|
59
|
-
|
60
|
-
def self.sanitize_hash(hash)
|
61
|
-
|
62
|
-
case hash
|
63
|
-
when Hash
|
64
|
-
hash.inject({}) do |result, (key, value)|
|
65
|
-
result.update(key => sanitize_hash(value))
|
66
|
-
end
|
67
|
-
when Array
|
68
|
-
hash.collect{|value| sanitize_hash(value)}
|
69
|
-
when Fixnum, String, Bignum
|
70
|
-
hash
|
71
|
-
else
|
72
|
-
hash.to_s
|
73
|
-
end
|
74
|
-
rescue Exception => e
|
75
|
-
Appygram.logger.error(hash)
|
76
|
-
Appygram.logger.error(e.message)
|
77
|
-
Appygram.logger.error(e.backtrace)
|
78
|
-
{}
|
79
|
-
end
|
80
|
-
|
81
|
-
def extract_http_headers(env)
|
82
|
-
headers = {}
|
83
|
-
env.select{|k, v| k =~ /^HTTP_/}.each do |name, value|
|
84
|
-
proper_name = name.sub(/^HTTP_/, '').split('_').map{|upper_case| upper_case.capitalize}.join('-')
|
85
|
-
headers[proper_name] = value
|
86
|
-
end
|
87
|
-
unless headers['Cookie'].nil?
|
88
|
-
headers['Cookie'] = headers['Cookie'].sub(/_session=\S+/, '_session=[FILTERED]')
|
89
|
-
end
|
90
|
-
headers
|
91
|
-
end
|
92
|
-
|
93
|
-
def self.sanitize_session(request)
|
94
|
-
session_hash = {'session_id' => "", 'data' => {}}
|
95
|
-
|
96
|
-
if request.respond_to?(:session)
|
97
|
-
session = request.session
|
98
|
-
session_hash['session_id'] = request.session_options ? request.session_options[:id] : nil
|
99
|
-
session_hash['session_id'] ||= session.respond_to?(:session_id) ? session.session_id : session.instance_variable_get("@session_id")
|
100
|
-
session_hash['data'] = session.respond_to?(:to_hash) ? session.to_hash : session.instance_variable_get("@data") || {}
|
101
|
-
session_hash['session_id'] ||= session_hash['data'][:session_id]
|
102
|
-
session_hash['data'].delete(:session_id)
|
103
|
-
end
|
104
|
-
|
105
|
-
self.sanitize_hash(session_hash)
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
module Appygram
|
2
|
-
module DebugExceptions
|
3
|
-
|
4
|
-
def self.included(base)
|
5
|
-
base.send(:alias_method_chain,:render_exception,:appygram)
|
6
|
-
end
|
7
|
-
|
8
|
-
def render_exception_with_appygram(env,exception)
|
9
|
-
::Appygram::Catcher.handle_with_controller(exception,
|
10
|
-
env['action_controller.instance'],
|
11
|
-
Rack::Request.new(env))
|
12
|
-
render_exception_without_appygram(env,exception)
|
13
|
-
end
|
14
|
-
|
15
|
-
end
|
16
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'rack'
|
3
|
-
|
4
|
-
module Rack
|
5
|
-
class Appygram
|
6
|
-
|
7
|
-
def initialize(app, api_key = nil)
|
8
|
-
@app = app
|
9
|
-
if api_key.nil?
|
10
|
-
appygram_config = "config/appygram.yml"
|
11
|
-
::Appygram::Config.load(appygram_config)
|
12
|
-
else
|
13
|
-
::Appygram.configure(api_key)
|
14
|
-
::Appygram::Config.enabled = true
|
15
|
-
::Appygram.logger.info "Enabling Appygram 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
|
-
::Appygram::Catcher.handle_with_rack(e,env, Rack::Request.new(env))
|
24
|
-
raise(e)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'logger'
|
2
|
-
|
3
|
-
module Appygram
|
4
|
-
class LogFactory
|
5
|
-
def self.logger
|
6
|
-
@logger ||= create_logger_with_fallback
|
7
|
-
end
|
8
|
-
|
9
|
-
private
|
10
|
-
def self.create_logger_with_fallback
|
11
|
-
begin
|
12
|
-
log_dir = File.join(Config.application_root, 'log')
|
13
|
-
Dir.mkdir(log_dir) unless File.directory?(log_dir)
|
14
|
-
log_path = File.join(log_dir, "/appygram.log")
|
15
|
-
log = Logger.new(log_path)
|
16
|
-
log.level = Logger::INFO
|
17
|
-
def log.format_message(severity, timestamp, progname, msg)
|
18
|
-
"[#{severity.upcase}] (#{[Kernel.caller[2].split('/').last]}) #{timestamp.utc.to_s} - #{msg2str(msg).gsub(/\n/, '').lstrip}\n"
|
19
|
-
end
|
20
|
-
def log.msg2str(msg)
|
21
|
-
case msg
|
22
|
-
when ::String
|
23
|
-
msg
|
24
|
-
when ::Exception
|
25
|
-
"#{ msg.message } (#{ msg.class }): " <<
|
26
|
-
(msg.backtrace || []).join(" | ")
|
27
|
-
else
|
28
|
-
msg.inspect
|
29
|
-
end
|
30
|
-
end
|
31
|
-
log
|
32
|
-
rescue
|
33
|
-
return Rails.logger if defined?(Rails) && defined?(Rails.logger)
|
34
|
-
return RAILS_DEFAULT_LOGGER if defined?(RAILS_DEFAULT_LOGGER)
|
35
|
-
return Logger.new(STDERR)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
require 'digest/md5'
|
2
|
-
|
3
|
-
module Appygram
|
4
|
-
class RackExceptionData < ExceptionData
|
5
|
-
def initialize(exception, environment, request)
|
6
|
-
super(exception)
|
7
|
-
@environment = environment
|
8
|
-
@request = request
|
9
|
-
end
|
10
|
-
|
11
|
-
def framework
|
12
|
-
"rack"
|
13
|
-
end
|
14
|
-
|
15
|
-
def extra_stuff
|
16
|
-
return {} if @request.nil?
|
17
|
-
{
|
18
|
-
'request' => {
|
19
|
-
'url' => "#{@request.url}",
|
20
|
-
'parameters' => @request.params,
|
21
|
-
'request_method' => @request.request_method.to_s,
|
22
|
-
'remote_ip' => @request.ip,
|
23
|
-
'headers' => extract_http_headers(@environment),
|
24
|
-
'session' => self.class.sanitize_session(@request)
|
25
|
-
}
|
26
|
-
}
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,52 +0,0 @@
|
|
1
|
-
require 'zlib'
|
2
|
-
require 'cgi'
|
3
|
-
require 'net/http'
|
4
|
-
require 'net/https'
|
5
|
-
require 'digest/md5'
|
6
|
-
|
7
|
-
module Appygram
|
8
|
-
class Remote
|
9
|
-
class << self
|
10
|
-
def startup_announce(startup_data)
|
11
|
-
#FIXME this is a noop based on an Exceptional behavior Appygram doesn't have
|
12
|
-
Appygram.logger.info 'Appygram remote exception forwarding started'
|
13
|
-
end
|
14
|
-
|
15
|
-
def error(exception_data)
|
16
|
-
url = "/api/exceptions?api_key=#{::Appygram::Config.api_key}"
|
17
|
-
if Appygram.restrict_duplicates exception_data.uniqueness_hash
|
18
|
-
Appygram.logger.debug "Duplicate notification suppressed."
|
19
|
-
else
|
20
|
-
call_remote(url, exception_data.to_json)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def call_remote(url, data)
|
25
|
-
config = Appygram::Config
|
26
|
-
optional_proxy = Net::HTTP::Proxy(config.http_proxy_host,
|
27
|
-
config.http_proxy_port,
|
28
|
-
config.http_proxy_username,
|
29
|
-
config.http_proxy_password)
|
30
|
-
client = optional_proxy.new(config.remote_host, config.remote_port)
|
31
|
-
client.open_timeout = config.http_open_timeout
|
32
|
-
client.read_timeout = config.http_read_timeout
|
33
|
-
client.use_ssl = config.ssl?
|
34
|
-
client.verify_mode = OpenSSL::SSL::VERIFY_NONE if config.ssl?
|
35
|
-
begin
|
36
|
-
response = client.post(url, data)
|
37
|
-
case response
|
38
|
-
when Net::HTTPSuccess
|
39
|
-
Appygram.logger.info( "#{url} - #{response.message}")
|
40
|
-
return true
|
41
|
-
else
|
42
|
-
Appygram.logger.error("#{url} - #{response.code} - #{response.message}")
|
43
|
-
end
|
44
|
-
rescue Exception => e
|
45
|
-
Appygram.logger.error('Problem notifying Appygram about the error')
|
46
|
-
Appygram.logger.error(e)
|
47
|
-
end
|
48
|
-
nil
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
module Appygram
|
2
|
-
class StartupException < StandardError;
|
3
|
-
end
|
4
|
-
class Startup
|
5
|
-
class << self
|
6
|
-
def announce
|
7
|
-
if Config.api_key.blank?
|
8
|
-
raise StartupException, 'API Key must be configured'
|
9
|
-
end
|
10
|
-
Remote.startup_announce(::Appygram::ApplicationEnvironment.to_hash('rails'))
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|