rnotifier 0.0.6 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 50c10d0bf67e250e5ca27a71467daca164c6bdb3
4
+ data.tar.gz: 7326f599dc1a0d6bdf85a51253bcf3c0931f3e96
5
+ SHA512:
6
+ metadata.gz: 94f5b55b64acfff14967da646858a232457cbd0008dbf29092b95b2c0d9b273e7420a1c087a25eacc85ef186c871bd8c88cda0cc8b8eb3263eeb053695823302
7
+ data.tar.gz: 8fcfb420fd001c7a3769bcf2148b586f41ed97ed6f47553a926e1e2fb4304d63936d25274e224b4c8d96cd41049588193a75a8d822a79b5aec5d26d2ddef6cf1
data/Gemfile CHANGED
@@ -1,6 +1,4 @@
1
- source 'http://rubygems.org'
1
+ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in rnotifier.gemspec
4
- gem 'rake'
5
- gem 'json'
6
4
  gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Jiren Patel
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,48 +1,39 @@
1
- Rnotifier
2
- =========
1
+ # Rnotifier
3
2
 
4
- Rails 3 Exception catcher gem
3
+ Exception catcher for rack base applications.
5
4
 
6
- Rails application for exception catching : [git@rnotifier.git](https://github.com/joshsoftware/rnotifier)
5
+ ## Installation
7
6
 
8
- Usage
9
- -----
7
+ Add this line to your application's Gemfile:
10
8
 
11
- Login to [rnotifier.heroku.com](http://rnotifier.heroku.com)
9
+ gem 'rnotifier'
12
10
 
13
- Create Project and you will get api key.
11
+ And then execute:
14
12
 
15
- In your rails app
13
+ $ bundle
16
14
 
17
- rails g rnotifier -k "API-KEY"
15
+ Or install it yourself as:
18
16
 
19
- Configuration "config/initializers/rnotifier.rb"
20
- ------------------------------------------------
17
+ $ gem install rnotifier
21
18
 
22
- **Exception recipients email addresses**
19
+ ## Usage
23
20
 
24
- ```ruby
25
- c.exception_recipients = %w{jiren@joshsoftware.com example@example.com}
26
- ```
21
+ rnotifier install 'API-KEY' # This will create 'config/rnotifier.yaml' file.
27
22
 
28
- **Set exception notification server. Default is 'http://rnotifier.heroku.com'**
23
+ ### Config file options
29
24
 
30
- ```ruby
31
- c.notification_server = "http://rnotifier.heroku.com"
32
- ```
25
+ - environments: development #Default is production
26
+ - capture_code: true #Default false
27
+ - api_host: 'http://yourapp.com' #Default http://rnotifier.com
33
28
 
34
- Sample configuration
35
- --------------------
29
+ ## Contributing
36
30
 
37
- ```ruby
38
- Rnotifier::Notifier.config do |c|
39
- c.api_key = 'API-KEY'
40
- c.exception_recipients = %w{example@example.com}
41
- c.notification_server = "http://rnotifier.heroku.com"
42
- end
43
- ```
44
-
45
- Contributing
46
- ------------
47
- Please send me a pull request so that this can be improved.
31
+ 1. Fork it
32
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
33
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
34
+ 4. Push to the branch (`git push origin my-new-feature`)
35
+ 5. Create new Pull Request
48
36
 
37
+ License
38
+ -------
39
+ This is released under the MIT license.
data/Rakefile CHANGED
@@ -1,2 +1 @@
1
- #!/usr/bin/env rake
2
1
  require "bundler/gem_tasks"
data/bin/rnotifier ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ ARGV[0] = 'help' if ARGV.empty?
4
+
5
+ case ARGV[0]
6
+ when 'help'
7
+ puts %Q{
8
+ ---- HELP ----
9
+ test #Test rnotifier configuration is correct and send test exception.
10
+
11
+ install api-key [environments] #Create config/rnotifier.yaml with api_key and enabled for supplied environments.environments are optionbal, default is production.
12
+ }
13
+ when 'test'
14
+ require 'rnotifier'
15
+
16
+ Rnotofier::Config.load_config('config/rnotifier.yml')
17
+ if Rnotofier::Config.valid?
18
+ #NOTE: Add test code
19
+ else
20
+ puts 'Configuration is invalid. Check api key'
21
+ end
22
+ when 'install'
23
+ if (api_key = ARGV[1]).nil?
24
+ puts "'api-key' is required. Use like i.e rnotifier install api-key [environments]"
25
+ else
26
+ environments = ARGV[2..-1]
27
+ Dir.mkdir('config') unless Dir.exists?('config')
28
+
29
+ File.open('config/rnotifier.yaml', 'w') do |f|
30
+ f.puts("apikey: #{api_key}\n")
31
+ f.puts("environments: #{environments.join(', ')}") unless environments.empty?
32
+ f.puts("capture_code: false")
33
+ f.close
34
+ end
35
+
36
+ puts "Rnotofier configuration written in config/rnotifier.yaml"
37
+ end
38
+ end
@@ -6,9 +6,6 @@ class RnotifierGenerator < Rails::Generators::Base
6
6
  def add_config
7
7
  if options[:api_key]
8
8
  template 'initializer.rb', 'config/initializers/rnotifier.rb'
9
- p "******* INFO *************"
10
- p "Set exception recipients email address in 'config/initializers/rnotifier.rb'"
11
- p "i.e. c.exception_recipients => %w{user1@example.com, user2@example.com} "
12
9
  else
13
10
  p 'Set option --api-key or -k.'
14
11
  end
@@ -1,6 +1,4 @@
1
1
  Rnotifier::Notifier.config do |c|
2
2
  c.api_key = <%= api_key %>
3
- c.exception_recipients = %w{user1@example.com user2@example.com}
4
- c.notification_server = "http://rnotifier.heroku.com"
5
3
  end
6
4
 
@@ -0,0 +1,85 @@
1
+ module Rnotifier
2
+ class Config
3
+ DEFAULT = {
4
+ :api_host => 'http://api.rnotifier.com',
5
+ :api_version => 'v1',
6
+ :notify_path => 'exception',
7
+ :ignore_env => ['development', 'test'],
8
+ :http_open_timeout => 2,
9
+ :http_read_timeout => 4
10
+ }
11
+
12
+ CLIENT = "RN-RUBY-GEM:#{Rnotifier::VERSION}"
13
+
14
+ class << self
15
+ attr_accessor :api_key, :notification_path, :environments, :current_env,
16
+ :valid, :app_env, :api_host, :ignore_exceptions, :capture_code
17
+
18
+ def [](val)
19
+ DEFAULT[val]
20
+ end
21
+
22
+ def init
23
+ Rlogger.init
24
+
25
+ self.valid = false
26
+ self.current_env = ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
27
+ self.environments ||= []
28
+
29
+ if self.environments.is_a?(String) || self.environments.is_a?(Symbol)
30
+ self.environments = self.environments.to_s.split(',')
31
+ end
32
+
33
+ #Return if config environments not include current env
34
+ return if !self.environments.empty? && !self.environments.include?(self.current_env)
35
+
36
+ #Check for ignore env
37
+ if DEFAULT[:ignore_env].include?(self.current_env) && !self.environments.include?(self.current_env)
38
+ return
39
+ end
40
+
41
+ if self.api_key.nil? and !ENV['RNOTIFIER_API_KEY'].nil?
42
+ self.api_key = ENV['RNOTIFIER_API_KEY']
43
+ end
44
+
45
+ return if self.api_key.to_s.length == 0
46
+
47
+ self.api_host ||= DEFAULT[:api_host]
48
+ self.notification_path = '/' + [DEFAULT[:api_version], DEFAULT[:notify_path], self.api_key].join('/')
49
+ self.app_env = get_app_env
50
+ self.ignore_exceptions = self.ignore_exceptions.split(',') if self.ignore_exceptions.is_a?(String)
51
+
52
+ self.valid = true
53
+ end
54
+
55
+ def valid?
56
+ self.valid
57
+ end
58
+
59
+ def get_app_env
60
+ {
61
+ :env => self.current_env,
62
+ :pid => $$,
63
+ :host => (Socket.gethostname rescue ''),
64
+ :user_name => ENV['USER'] || ENV['USERNAME'],
65
+ :program_name => $PROGRAM_NAME,
66
+ :app_root => self.app_root,
67
+ :language => {
68
+ :name => 'ruby',
69
+ :version => (RUBY_VERSION rescue ''),
70
+ :patch_level => (RUBY_PATCHLEVEL rescue ''),
71
+ :platform => (RUBY_PLATFORM rescue ''),
72
+ :release_date => (RUBY_RELEASE_DATE rescue ''),
73
+ :ruby_path => Gem.ruby,
74
+ :gem_path => Gem.path
75
+ }
76
+ }
77
+ end
78
+
79
+ def app_root
80
+ (defined?(Rails) && Rails.respond_to?(:root)) ? Rails.root.to_s : Dir.pwd
81
+ end
82
+
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,31 @@
1
+ module Rnotifier
2
+ class ExceptionCode
3
+ class << self
4
+
5
+ def get(backtrace)
6
+ return unless backtrace
7
+ filename, line, method = (backtrace.find{|l| l =~ /^#{Config.app_env[:app_root]}/} || backtrace[0]).split(':')
8
+ self.find(filename, line.to_i, 3)
9
+ end
10
+
11
+ def find(filename, line_no, wrap_size = 1)
12
+ s_range = [line_no - wrap_size, 1].max - 1
13
+ e_range = line_no + wrap_size - 1
14
+ #s_range, e_range = [ (line_no - wrap_size) > 0 ? line_no - wrap_size : 0, line_no + wrap_size]
15
+ code = [s_range]
16
+
17
+ begin
18
+ File.open(filename) do |f|
19
+ f.each_with_index do |line, i|
20
+ code << line if i >= s_range && i <= e_range
21
+ break if i > e_range
22
+ end
23
+ end
24
+ rescue Exception => e
25
+ end
26
+ code
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,98 @@
1
+ module Rnotifier
2
+ class ExceptionData
3
+ attr_reader :env, :request, :exception, :options
4
+
5
+ def initialize(exception, env, options = {})
6
+ @exception = exception
7
+ @options = options
8
+ if options[:type] == :rack
9
+ @request = Rack::Request.new(env)
10
+ else
11
+ @env = env
12
+ end
13
+ end
14
+
15
+ def notify
16
+ return unless Config.valid?
17
+ return if Config.ignore_exceptions && Config.ignore_exceptions.include?(exception.class.to_s)
18
+
19
+
20
+ begin
21
+ data = if options[:type] == :rack
22
+ self.rack_exception_data
23
+ else
24
+ {:exception => self.exception_data, :extra => self.env }
25
+ end
26
+
27
+ data[:exception][:code] = ExceptionCode.get(data[:exception][:backtrace]) if Config.capture_code
28
+ data[:context_data] = Thread.current[:rnotifier_context] if Thread.current[:rnotifier_context]
29
+ data[:data_from] = options[:type]
30
+ data[:rnotifier_client] = Config::CLIENT
31
+
32
+ Notifier.send(data)
33
+ rescue Exception => e
34
+ Rlogger.error("[NOTIFY] #{e.message}")
35
+ Rlogger.error("[NOTIFY] #{e.backtrace}")
36
+ ensure
37
+ Rnotifier.clear_context
38
+ end
39
+ end
40
+
41
+ def rack_exception_data
42
+ data = {
43
+ :occurred_at => Time.now.to_s,
44
+ :app_env => Rnotifier::Config.app_env,
45
+ :exception => self.exception_data
46
+ }
47
+ data[:request] = {
48
+ :url => request.url,
49
+ :referer_url => request.referer,
50
+ :ip => request.ip,
51
+ :http_method => "#{request.request_method}#{' # XHR' if request.xhr?}",
52
+ :params => filtered_params,
53
+ :headers => self.headers,
54
+ :session => request.session
55
+ }
56
+
57
+ data
58
+ end
59
+
60
+ def exception_data
61
+ {
62
+ :class_name => exception.class.to_s,
63
+ :message => exception.message,
64
+ :backtrace => exception.backtrace,
65
+ :fingerprint => (self.fingerprint rescue nil)
66
+ }
67
+ end
68
+
69
+ def fingerprint
70
+ #data[:fingerprint] = Digest::MD5.hexdigest("#{exception.message.gsub(/#<\w*:\w*>/, '')}#{data[:fingerprint]}")
71
+
72
+ if exception.backtrace && !exception.backtrace.empty?
73
+ Digest::MD5.hexdigest(exception.backtrace.join)
74
+ end
75
+ end
76
+
77
+ def filtered_params
78
+ if rp = request.env['action_dispatch.parameter_filter']
79
+ ParameterFilter.filter(request.env['action_dispatch.request.parameters'] || request.params, rp)
80
+ else
81
+ ParameterFilter.default_filter(request.params)
82
+ end
83
+ end
84
+
85
+ HEADER_REGX = /^HTTP_/
86
+
87
+ def headers
88
+ headers = {}
89
+ request.env.each do |k, v|
90
+ headers[k.sub(HEADER_REGX, '').downcase] = v if k =~ HEADER_REGX
91
+ end
92
+ headers['cookie'] = headers['cookie'].sub(/_session=\S+/, '_session=[FILTERED]') if headers['cookie']
93
+ headers
94
+ end
95
+
96
+
97
+ end
98
+ end
@@ -1,58 +1,24 @@
1
1
  module Rnotifier
2
- class Notifier
2
+ class Notifier
3
+ class << self
3
4
 
4
- class << self
5
- def config(&block)
6
- yield(Rnotifier::Configuration)
7
- Configuration.validate_config rescue Rlogger.error('[CONFIG] Error from notification server.')
8
-
9
- #Initialize Rails Exception handler
10
- Rnotifier::RailsException.initialize #if Configuration.is_valid
11
-
12
- Configuration.filter_params ||= []
13
-
14
- if Configuration.filter_params.empty?
15
- Configuration.filter_params.concat(Configuration::FILTER_PARAMS)
16
- end
17
-
18
- Configuration.freeze
19
- end
20
-
21
- def send_exception(exception_data)
22
- exception_data[:params] = filter_params(exception_data[:params])
23
- exception_data[:occurred_at] = Time.now
24
-
25
- t = Thread.new do
26
- begin
27
- response = Util.post(Configuration[:notify_path], {:exception_data => exception_data})
28
-
29
- Rlogger.error("[SEND] #{response['message']}") unless response['notify']
30
- rescue Exception => e
31
- Rlogger.error("[SERVER] #{e.message}")
32
- Rlogger.error("[SERVER] #{e.backtrace}")
33
- end
34
- send_email_notification(exception_data)
35
- end
36
- end
37
-
38
- def send_email_notification(exception_data)
39
- begin
40
- EmailNotifier.exception_notify(exception_data).deliver
41
- rescue Exception => e
42
- Rlogger.error("[MAILER] #{e.message}")
43
- Rlogger.error("[MAILER] #{e.backtrace}")
5
+ def connection
6
+ @connection ||= Faraday.new(:url => Rnotifier::Config.api_host) do |faraday|
7
+ faraday.adapter Faraday.default_adapter
44
8
  end
45
9
  end
46
10
 
47
- def filter_params(params = {})
48
- return if params.empty?
49
-
50
- Configuration.filter_params.each do |key|
51
- params[key] = '[FILTERED]' if params.has_key?(key)
52
- end
53
- params
54
- end
11
+ def send(data)
12
+ response = self.connection.post do |req|
13
+ req.url Rnotifier::Config.notification_path
14
+ req.headers['Content-Type'] = 'application/json'
15
+ req.options[:timeout] = Rnotifier::Config[:http_open_timeout]
16
+ req.options[:open_timeout] = Rnotifier::Config[:http_read_timeout]
17
+ req.body = MultiJson.dump(data)
18
+ end
55
19
 
20
+ Rlogger.error("[RNOTIFIER SERVER] Response Status:#{response.status}") unless response.status == 200
21
+ end
56
22
  end
57
- end
23
+ end
58
24
  end
@@ -0,0 +1,78 @@
1
+ module Rnotifier
2
+ class ParameterFilter
3
+
4
+ DEFAULT_FIELDS = [:password, :password_confirmation, :authorization, :secret, :passwd]
5
+ FILTERED = '[FILTERED]'
6
+
7
+ def self.filter(params, filters)
8
+ @filter ||= ParameterFilter.new(filters)
9
+ @filter.filter(params)
10
+ end
11
+
12
+ def self.default_filter(params)
13
+ @default_filter ||= ParameterFilter.new(DEFAULT_FIELDS)
14
+ @default_filter.filter(params)
15
+ end
16
+
17
+ def initialize(filters)
18
+ @filters = filters
19
+ end
20
+
21
+ def filter(params)
22
+ if @filters && !@filters.empty?
23
+ compiled_filter.call(params)
24
+ else
25
+ params
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def compiled_filter
32
+ @compiled_filter ||= begin
33
+ regexps, blocks = compile_filter
34
+
35
+ lambda do |original_params|
36
+ filtered_params = {}
37
+
38
+ original_params.each do |key, value|
39
+ if regexps.find { |r| key =~ r }
40
+ value = FILTERED
41
+ elsif value.is_a?(Hash)
42
+ value = filter(value)
43
+ elsif value.is_a?(Array)
44
+ value = value.map { |v| v.is_a?(Hash) ? filter(v) : v }
45
+ elsif blocks
46
+ key = key.dup
47
+ value = value.dup rescue value
48
+ blocks.each { |b| b.call(key, value) }
49
+ end
50
+
51
+ filtered_params[key] = value
52
+ end
53
+
54
+ filtered_params
55
+ end
56
+ end
57
+ end
58
+
59
+ def compile_filter
60
+ strings, regexps, blocks = [], [], []
61
+
62
+ @filters.each do |item|
63
+ case item
64
+ when NilClass
65
+ when Proc
66
+ blocks << item
67
+ when Regexp
68
+ regexps << item
69
+ else
70
+ strings << item.to_s
71
+ end
72
+ end
73
+
74
+ regexps << Regexp.new(strings.join('|'), true) unless strings.empty?
75
+ [regexps, blocks]
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,26 @@
1
+ module Rnotifier
2
+ class RackMiddleware
3
+
4
+ def initialize(app, config_file = nil)
5
+ @app = app
6
+ Rnotifier.load_config(config_file) if config_file
7
+ end
8
+
9
+ def call(env)
10
+ begin
11
+ response = @app.call(env)
12
+ rescue Exception => e
13
+ Rnotifier::ExceptionData.new(e, env, {:type => :rack}).notify
14
+ env['rnotifier.notify'] = true
15
+ raise e
16
+ end
17
+
18
+ if e = (env['rack.exception'] || env['sinatra.error'])
19
+ Rnotifier::ExceptionData.new(e, env, {:type => :rack}).notify
20
+ env['rnotifier.notify'] = true
21
+ end
22
+
23
+ response
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ module Rnotifier
2
+ class Railtie < Rails::Railtie
3
+
4
+ initializer 'rnotifier.initialize' do |app|
5
+
6
+ file = File.join(Rails.root, 'config', 'rnotifier.yaml')
7
+ File.exist?(file) ? Rnotifier.load_config(file) : Rnotifier::Config.init
8
+
9
+ if Rnotifier::Config.valid?
10
+ if defined?(ActionDispatch::DebugExceptions)
11
+ app.middleware.insert_after ActionDispatch::DebugExceptions, Rnotifier::RackMiddleware
12
+ elsif defined?(ActionDispatch::ShowExceptions)
13
+ app.middleware.insert_after ActionDispatch::ShowExceptions, Rnotifier::RackMiddleware
14
+ else
15
+ app.middleware.use Rnotifier::RackMiddleware
16
+ end
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -1,26 +1,28 @@
1
1
  module Rnotifier
2
2
  class Rlogger
3
-
4
- LOG_TITLE = '[RNOTIFIER]'
5
-
3
+ TAG = '[RNOTIFIER]'
6
4
  class << self
7
5
 
8
- def info(msg)
9
- logger.info("#{LOG_TITLE}#{msg}")
10
- end
11
-
12
- def error(msg)
13
- logger.error("#{LOG_TITLE}#{msg}")
14
- end
15
-
16
- def warn(msg)
17
- logger.warn("#{LOG_TITLE}#{msg}")
18
- end
6
+ ['info', 'error', 'warn'].each do |level|
7
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
8
+ def #{level}(msg)
9
+ logger.#{level}("#{TAG} \#{msg}")
10
+ end
11
+ METHOD
12
+ end
13
+
14
+ def init
15
+ @logger = if defined?(Rails) && Rails.respond_to?(:logger)
16
+ Rails.logger
17
+ else
18
+ require 'logger' unless defined?(Logger)
19
+ Logger.new($stdout)
20
+ end
21
+ end
19
22
 
20
- private
21
- def logger
22
- @rlogger ||= (defined?(Rails.logger) ? Rails.logger : Logger.new(STDERR))
23
- end
23
+ def logger
24
+ @logger
25
+ end
24
26
 
25
27
  end
26
28
  end
@@ -1,3 +1,3 @@
1
1
  module Rnotifier
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.8"
3
3
  end