appygram 0.1.4 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/README.markdown +55 -0
- data/Rakefile +1 -0
- data/appygram.gemspec +21 -13
- data/lib/appygram/version.rb +1 -1
- data/lib/appygram.rb +37 -61
- metadata +17 -48
- data/init.rb +0 -39
- data/lib/appygram/alert_data.rb +0 -15
- data/lib/appygram/application_environment.rb +0 -55
- data/lib/appygram/catcher.rb +0 -34
- data/lib/appygram/config.rb +0 -87
- data/lib/appygram/controller_exception_data.rb +0 -75
- data/lib/appygram/exception_data.rb +0 -108
- data/lib/appygram/integration/alerter.rb +0 -11
- data/lib/appygram/integration/debug_exceptions.rb +0 -16
- data/lib/appygram/integration/dj.rb +0 -15
- data/lib/appygram/integration/rack.rb +0 -28
- data/lib/appygram/integration/rack_rails.rb +0 -26
- data/lib/appygram/integration/rails.rb +0 -27
- data/lib/appygram/integration/sinatra.rb +0 -6
- data/lib/appygram/log_factory.rb +0 -39
- data/lib/appygram/monkeypatches.rb +0 -10
- data/lib/appygram/rack_exception_data.rb +0 -29
- data/lib/appygram/railtie.rb +0 -23
- data/lib/appygram/remote.rb +0 -48
- data/lib/appygram/startup.rb +0 -14
- data/rails/init.rb +0 -1
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Appygram <http://www.appygram.com>
|
2
|
+
|
3
|
+
Appygram is a simple service to handle messages from your web
|
4
|
+
or mobile app. This gem provides communication with the Appygram
|
5
|
+
service via a simple API. Useful features higher in the stack
|
6
|
+
are found in gems that depend on this one, e.g. appygram-rails,
|
7
|
+
which turns uncaught Rails exceptions into appygrams.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
1. Add gem entry to Gemfile
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'appygram'
|
15
|
+
```
|
16
|
+
|
17
|
+
2. Run <code>bundle install</code>
|
18
|
+
|
19
|
+
3. Configure your API Key in an initializer, e.g. config/appygram.rb
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
Appygram.configure :api_key => 'your_api_key'
|
23
|
+
```
|
24
|
+
|
25
|
+
using a valid API key for your app provided by Appygram
|
26
|
+
|
27
|
+
## Invocation
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
require 'appygram'
|
31
|
+
|
32
|
+
Appygram.send :topic => 'Test', :message => 'This is a test.'
|
33
|
+
```
|
34
|
+
|
35
|
+
## Supported fields in an appygram
|
36
|
+
|
37
|
+
* topic
|
38
|
+
* subject
|
39
|
+
* message
|
40
|
+
* name
|
41
|
+
* email
|
42
|
+
* phone
|
43
|
+
* platform ('web' by default for this connector)
|
44
|
+
* software ('appygram.rb [VERSION]' by default for this connector)
|
45
|
+
|
46
|
+
## Rules for improvement
|
47
|
+
|
48
|
+
* This gem must not have other dependencies.
|
49
|
+
* This gem may take advantage of other gems (e.g. async i/o,
|
50
|
+
background queueing, http pooling) if detected, and the advantage
|
51
|
+
is related directly to communication with the service
|
52
|
+
* Effort should be made to retain parity with other fully supported
|
53
|
+
connectors
|
54
|
+
|
55
|
+
Copyright © 2012 Solertium
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/appygram.gemspec
CHANGED
@@ -1,16 +1,24 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "appygram/version"
|
3
4
|
|
4
|
-
Gem::Specification.new do |
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "appygram"
|
7
|
+
s.version = Appygram::VERSION
|
8
|
+
s.authors = ["rfc2616"]
|
9
|
+
s.email = ["heittman.rob@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/anythinglabs/appygram.rb"
|
11
|
+
s.summary = %q{Communicate with the Appygram message routing service}
|
12
|
+
s.description = %q{Discovers topics and sends messages}
|
13
|
+
|
14
|
+
s.rubyforge_project = "appygram"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
# s.add_development_dependency "rspec"
|
23
|
+
# s.add_runtime_dependency "rest-client"
|
16
24
|
end
|
data/lib/appygram/version.rb
CHANGED
data/lib/appygram.rb
CHANGED
@@ -1,71 +1,47 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require '
|
4
|
-
require 'appygram/catcher'
|
5
|
-
require 'appygram/startup'
|
6
|
-
require 'appygram/log_factory'
|
7
|
-
require 'appygram/config'
|
8
|
-
require 'appygram/application_environment'
|
9
|
-
require 'appygram/exception_data'
|
10
|
-
require 'appygram/controller_exception_data'
|
11
|
-
require 'appygram/rack_exception_data'
|
12
|
-
require 'appygram/alert_data'
|
13
|
-
require 'appygram/remote'
|
14
|
-
require 'appygram/integration/rack'
|
15
|
-
require 'appygram/integration/rack_rails'
|
16
|
-
require 'appygram/integration/alerter'
|
17
|
-
require 'appygram/version'
|
18
|
-
require 'appygram/integration/debug_exceptions'
|
19
|
-
|
20
|
-
require 'appygram/railtie' if defined?(Rails::Railtie)
|
1
|
+
require "appygram/version"
|
2
|
+
require 'net/http'
|
3
|
+
require 'net/https'
|
21
4
|
|
22
5
|
module Appygram
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
::Appygram::
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.configure(api_key)
|
32
|
-
Appygram::Config.api_key = api_key
|
6
|
+
def self.configure(params)
|
7
|
+
Appygram::Config.api_key = params[:api_key]
|
8
|
+
endpoint = params[:endpoint] || 'https://appygram.herokuapp.com/appygrams'
|
9
|
+
Appygram::Config.endpoint = URI endpoint
|
10
|
+
Appygram::Config.platform = params[:platform] || 'web'
|
11
|
+
Appygram::Config.software = params[:software] || "appygram.rb #{Appygram::VERSION}"
|
33
12
|
end
|
34
13
|
|
35
|
-
def self.
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
14
|
+
def self.send(params)
|
15
|
+
pc = params.clone
|
16
|
+
pc[:api_key] = Appygram::Config.api_key
|
17
|
+
pc[:platform] = Appygram::Config.platform unless pc[:platform]
|
18
|
+
pc[:software] = Appygram::Config.software unless pc[:software]
|
19
|
+
url = Appygram::Config.endpoint
|
20
|
+
req = Net::HTTP::Post.new url.request_uri
|
21
|
+
req.form_data = pc
|
22
|
+
session = Net::HTTP.new(url.hostname, url.port)
|
23
|
+
session.use_ssl = true
|
24
|
+
session.start {|http|
|
25
|
+
http.request(req)
|
26
|
+
}
|
48
27
|
end
|
49
28
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
29
|
+
class Config
|
30
|
+
class << self
|
31
|
+
attr_accessor :api_key, :endpoint, :software, :platform
|
32
|
+
def api_key
|
33
|
+
return @api_key unless @api_key.nil?
|
34
|
+
end
|
35
|
+
def endpoint
|
36
|
+
return @endpoint unless @endpoint.nil?
|
37
|
+
end
|
38
|
+
def software
|
39
|
+
return @software unless @software.nil?
|
40
|
+
end
|
41
|
+
def platform
|
42
|
+
return @platform unless @platform.nil?
|
43
|
+
end
|
59
44
|
end
|
60
45
|
end
|
61
46
|
|
62
|
-
def self.clear!
|
63
|
-
Thread.current[:appygram_context] = nil
|
64
|
-
end
|
65
|
-
|
66
|
-
def self.context(hash = {})
|
67
|
-
Thread.current[:appygram_context] ||= {}
|
68
|
-
Thread.current[:appygram_context].merge!(hash)
|
69
|
-
self
|
70
|
-
end
|
71
47
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: appygram
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,52 +9,23 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
13
|
-
dependencies:
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ! '>='
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: '0'
|
22
|
-
type: :runtime
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: *70149039687960
|
25
|
-
description: Appygram is the Ruby gem for communicating with http://appygram.com (hosted
|
26
|
-
messaging service). It supports an exception reporting mechanism similar to http://exceptional.io,
|
27
|
-
and this Ruby gem, forked from the Exceptional gem, emulates Exceptional functionality.
|
28
|
-
email: heittman.rob@gmail.com
|
12
|
+
date: 2012-09-13 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Discovers topics and sends messages
|
15
|
+
email:
|
16
|
+
- heittman.rob@gmail.com
|
29
17
|
executables: []
|
30
18
|
extensions: []
|
31
19
|
extra_rdoc_files: []
|
32
20
|
files:
|
33
|
-
-
|
34
|
-
-
|
35
|
-
-
|
36
|
-
-
|
37
|
-
- lib/appygram/controller_exception_data.rb
|
38
|
-
- lib/appygram/exception_data.rb
|
39
|
-
- lib/appygram/integration/alerter.rb
|
40
|
-
- lib/appygram/integration/debug_exceptions.rb
|
41
|
-
- lib/appygram/integration/dj.rb
|
42
|
-
- lib/appygram/integration/rack.rb
|
43
|
-
- lib/appygram/integration/rack_rails.rb
|
44
|
-
- lib/appygram/integration/rails.rb
|
45
|
-
- lib/appygram/integration/sinatra.rb
|
46
|
-
- lib/appygram/log_factory.rb
|
47
|
-
- lib/appygram/monkeypatches.rb
|
48
|
-
- lib/appygram/rack_exception_data.rb
|
49
|
-
- lib/appygram/railtie.rb
|
50
|
-
- lib/appygram/remote.rb
|
51
|
-
- lib/appygram/startup.rb
|
52
|
-
- lib/appygram/version.rb
|
53
|
-
- lib/appygram.rb
|
54
|
-
- rails/init.rb
|
55
|
-
- init.rb
|
21
|
+
- .gitignore
|
22
|
+
- Gemfile
|
23
|
+
- README.markdown
|
24
|
+
- Rakefile
|
56
25
|
- appygram.gemspec
|
57
|
-
|
26
|
+
- lib/appygram.rb
|
27
|
+
- lib/appygram/version.rb
|
28
|
+
homepage: https://github.com/anythinglabs/appygram.rb
|
58
29
|
licenses: []
|
59
30
|
post_install_message:
|
60
31
|
rdoc_options: []
|
@@ -72,12 +43,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
43
|
- - ! '>='
|
73
44
|
- !ruby/object:Gem::Version
|
74
45
|
version: '0'
|
75
|
-
requirements:
|
76
|
-
|
77
|
-
|
78
|
-
rubygems_version: 1.8.17
|
46
|
+
requirements: []
|
47
|
+
rubyforge_project: appygram
|
48
|
+
rubygems_version: 1.8.15
|
79
49
|
signing_key:
|
80
50
|
specification_version: 3
|
81
|
-
summary:
|
51
|
+
summary: Communicate with the Appygram message routing service
|
82
52
|
test_files: []
|
83
|
-
has_rdoc:
|
data/init.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'appygram'
|
2
|
-
|
3
|
-
# If old plugin still installed then we don't want to install this one.
|
4
|
-
# In production environments we should continue to work as before, but in development/test we should
|
5
|
-
# advise how to correct the problem and exit
|
6
|
-
if (defined?(Appygram::VERSION::STRING) rescue nil) && %w(development test).include?(RAILS_ENV)
|
7
|
-
message = %Q(
|
8
|
-
***********************************************************************
|
9
|
-
You seem to still have an old version of the Appygram plugin installed.
|
10
|
-
Remove it from /vendor/plugins and try again.
|
11
|
-
***********************************************************************
|
12
|
-
)
|
13
|
-
puts message
|
14
|
-
exit -1
|
15
|
-
else
|
16
|
-
begin
|
17
|
-
|
18
|
-
if (Rails::VERSION::MAJOR < 3)
|
19
|
-
Appygram::Config.load(File.join(RAILS_ROOT, "/config/Appygram.yml"))
|
20
|
-
if Appygram::Config.should_send_to_api?
|
21
|
-
Appygram.logger.info("Loading Appygram #{Appygram::VERSION} for #{Rails::VERSION::STRING}")
|
22
|
-
require File.join('Appygram', 'integration', 'rails')
|
23
|
-
require File.join('Appygram', 'integration', 'dj') if defined?(Delayed::Job)
|
24
|
-
end
|
25
|
-
else
|
26
|
-
Appygram::Config.load(File.join(Rails.root, "/config/Appygram.yml"))
|
27
|
-
|
28
|
-
if Appygram::Config.should_send_to_api?
|
29
|
-
Appygram.logger.info("Loading Appygram #{Appygram::VERSION} for #{Rails::VERSION::STRING}")
|
30
|
-
Rails.configuration.middleware.use "Rack::RailsAppygram"
|
31
|
-
require File.join('Appygram', 'integration', 'dj') if defined?(Delayed::Job)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
rescue => e
|
35
|
-
STDERR.puts "Problem starting Appygram Plugin. Your app will run as normal. #{e.message}"
|
36
|
-
Appygram.logger.error(e.message)
|
37
|
-
Appygram.logger.error(e.backtrace)
|
38
|
-
end
|
39
|
-
end
|
data/lib/appygram/alert_data.rb
DELETED
@@ -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
|
data/lib/appygram/catcher.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
module Appygram
|
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
|
-
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
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
data/lib/appygram/config.rb
DELETED
@@ -1,87 +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 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
|
-
rescue Exception => e
|
39
|
-
raise ConfigurationException.new("Unable to load configuration #{config_file} for environment #{application_environment} : #{e.message}")
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def api_key
|
45
|
-
return @api_key unless @api_key.nil?
|
46
|
-
@api_key ||= ENV['EXCEPTIONAL_API_KEY'] unless ENV['EXCEPTIONAL_API_KEY'].nil?
|
47
|
-
end
|
48
|
-
|
49
|
-
def application_environment
|
50
|
-
ENV['RACK_ENV'] || ENV['RAILS_ENV']|| 'development'
|
51
|
-
end
|
52
|
-
|
53
|
-
def should_send_to_api?
|
54
|
-
return @enabled unless @enabled.nil?
|
55
|
-
@enabled = !(DEFAULTS[:disabled_by_default].include?(application_environment))
|
56
|
-
end
|
57
|
-
|
58
|
-
def application_root
|
59
|
-
(defined?(Rails) && Rails.respond_to?(:root)) ? Rails.root : Dir.pwd
|
60
|
-
end
|
61
|
-
|
62
|
-
def ssl?
|
63
|
-
@ssl ||= DEFAULTS[:ssl]
|
64
|
-
end
|
65
|
-
|
66
|
-
def remote_host
|
67
|
-
@remote_host ||= DEFAULTS[:remote_host_http]
|
68
|
-
end
|
69
|
-
|
70
|
-
def remote_port
|
71
|
-
@remote_port ||= ssl? ? 443 : 80
|
72
|
-
end
|
73
|
-
|
74
|
-
def reset
|
75
|
-
@enabled = @ssl = @remote_host = @remote_port = @api_key = nil
|
76
|
-
end
|
77
|
-
|
78
|
-
def http_open_timeout
|
79
|
-
@http_open_timeout ||= DEFAULTS[:http_open_timeout]
|
80
|
-
end
|
81
|
-
|
82
|
-
def http_read_timeout
|
83
|
-
@http_read_timeout ||= DEFAULTS[:http_read_timeout]
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
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,15 +0,0 @@
|
|
1
|
-
if Delayed::Worker.method_defined? :handle_failed_job
|
2
|
-
class Delayed::Worker
|
3
|
-
def handle_failed_job_with_appygram(job, e)
|
4
|
-
Appygram.handle(e, "Delayed::Job #{job.name}")
|
5
|
-
handle_failed_job_without_appygram(job, e)
|
6
|
-
Appygram.context.clear!
|
7
|
-
end
|
8
|
-
alias_method_chain :handle_failed_job, :appygram
|
9
|
-
Appygram.logger.info "DJ integration enabled"
|
10
|
-
end
|
11
|
-
else
|
12
|
-
message = "\n\n\nThe Appygram gem does not support Delayed Job 1.8.4 or earlier.\n\n\n"
|
13
|
-
STDERR.puts(message)
|
14
|
-
Appygram.logger.error(message)
|
15
|
-
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,26 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'rack'
|
3
|
-
|
4
|
-
module Rack
|
5
|
-
class RailsAppygram
|
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
|
-
::Appygram::Catcher.handle_with_controller(e,env['action_controller.instance'], Rack::Request.new(env))
|
16
|
-
raise
|
17
|
-
end
|
18
|
-
|
19
|
-
if env['rack.exception']
|
20
|
-
::Appygram::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
|
@@ -1,27 +0,0 @@
|
|
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_appygram(exception)
|
10
|
-
unless exception_handled_by_rescue_from?(exception)
|
11
|
-
Appygram::Catcher.handle_with_controller(exception, self, request)
|
12
|
-
Appygram.context.clear!
|
13
|
-
end
|
14
|
-
rescue_action_without_appygram exception
|
15
|
-
end
|
16
|
-
|
17
|
-
alias_method :rescue_action_without_appygram, :rescue_action
|
18
|
-
alias_method :rescue_action, :rescue_action_with_appygram
|
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
|
data/lib/appygram/log_factory.rb
DELETED
@@ -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
|
data/lib/appygram/railtie.rb
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
require 'appygram'
|
2
|
-
require 'rails'
|
3
|
-
|
4
|
-
module Appygram
|
5
|
-
class Railtie < Rails::Railtie
|
6
|
-
|
7
|
-
initializer "appygram.middleware" do |app|
|
8
|
-
|
9
|
-
config_file = File.join(Rails.root, "/config/appygram.yml")
|
10
|
-
Appygram::Config.load config_file if File.exist?(config_file)
|
11
|
-
# On Heroku config is loaded via the ENV so no need to load it from the file
|
12
|
-
|
13
|
-
if Appygram::Config.should_send_to_api?
|
14
|
-
Appygram.logger.info("Loading Appygram #{Appygram::VERSION} for #{Rails::VERSION::STRING}")
|
15
|
-
if defined?(ActionDispatch::DebugExceptions)
|
16
|
-
ActionDispatch::DebugExceptions.send(:include,Appygram::DebugExceptions)
|
17
|
-
else
|
18
|
-
app.config.middleware.use "Rack::RailsAppygram"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
data/lib/appygram/remote.rb
DELETED
@@ -1,48 +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
|
-
call_remote(url, exception_data.to_json)
|
18
|
-
end
|
19
|
-
|
20
|
-
def call_remote(url, data)
|
21
|
-
config = Appygram::Config
|
22
|
-
optional_proxy = Net::HTTP::Proxy(config.http_proxy_host,
|
23
|
-
config.http_proxy_port,
|
24
|
-
config.http_proxy_username,
|
25
|
-
config.http_proxy_password)
|
26
|
-
client = optional_proxy.new(config.remote_host, config.remote_port)
|
27
|
-
client.open_timeout = config.http_open_timeout
|
28
|
-
client.read_timeout = config.http_read_timeout
|
29
|
-
client.use_ssl = config.ssl?
|
30
|
-
client.verify_mode = OpenSSL::SSL::VERIFY_NONE if config.ssl?
|
31
|
-
begin
|
32
|
-
response = client.post(url, data)
|
33
|
-
case response
|
34
|
-
when Net::HTTPSuccess
|
35
|
-
Appygram.logger.info( "#{url} - #{response.message}")
|
36
|
-
return true
|
37
|
-
else
|
38
|
-
Appygram.logger.error("#{url} - #{response.code} - #{response.message}")
|
39
|
-
end
|
40
|
-
rescue Exception => e
|
41
|
-
Appygram.logger.error('Problem notifying Appygram about the error')
|
42
|
-
Appygram.logger.error(e)
|
43
|
-
end
|
44
|
-
nil
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
data/lib/appygram/startup.rb
DELETED
@@ -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 (/config/appygram.yml)'
|
9
|
-
end
|
10
|
-
Remote.startup_announce(::Appygram::ApplicationEnvironment.to_hash('rails'))
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
data/rails/init.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__) , '../init.rb')
|