ramon 0.1.1 → 0.2.0

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/init.rb CHANGED
@@ -1 +1,7 @@
1
+ # Load the exception handler
2
+ # Aplicable only for Rails apps
1
3
  require 'ramon'
4
+
5
+ require File.join('amon', 'integration', 'rails')
6
+
7
+ Rails.configuration.middleware.use "Rack::RailsAmonException"
@@ -5,12 +5,19 @@ end
5
5
  require_local "version"
6
6
  require_local "logger"
7
7
  require_local "remote"
8
- require_local "exception_data"
8
+ require_local "catcher"
9
+ require_local "exception_data"
10
+ require_local "controller_exception_data"
11
+ require_local "enviroment_data"
9
12
 
10
13
  module Ramon
11
14
  def self.log(message, level=nil)
12
15
  log_hash = Log.log(message, level)
13
16
  Remote.post('log', log_hash)
14
17
  end
18
+
19
+ def self.post(type, data)
20
+ Remote.post(type, data)
21
+ end
15
22
  end
16
23
 
@@ -0,0 +1,20 @@
1
+ module Ramon
2
+ class Catcher
3
+ class << self
4
+
5
+ def handle_with_controller(exception, controller=nil, request=nil)
6
+ data = ControllerExceptionData.new(exception, controller, request)
7
+ Ramon.post('exception', data)
8
+ end
9
+
10
+ def handle_with_rack(exception, environment, request)
11
+ data = RackExceptionData.new(exception, environment, request)
12
+ end
13
+
14
+ def handle(exception, name=nil)
15
+ data = ExceptionData.new(exception, name)
16
+ end
17
+
18
+ end
19
+ end # class end
20
+ end # module end
@@ -1,44 +1,44 @@
1
1
  require 'json'
2
2
 
3
3
  module Ramon
4
- class Config
5
- class ConfigurationException < StandardError; end
6
-
7
- class << self
8
- DEFAULTS = {
9
- :host => 'http://127.0.0.1',
10
- :port => 2464
11
- }
4
+ class Config
5
+ class ConfigurationException < StandardError; end
6
+
7
+ class << self
8
+ DEFAULTS = {
9
+ :host => 'http://127.0.0.1',
10
+ :port => 2464
11
+ }
12
12
 
13
- def load
14
- config_file ||= "/etc/amon.conf"
13
+ def load
14
+ config_file ||= "/etc/amon.conf"
15
15
 
16
- if File.file?(config_file)
17
- begin
18
- f = File.read(config_file)
19
- config = JSON.parse(f)
16
+ if File.file?(config_file)
17
+ begin
18
+ f = File.read(config_file)
19
+ config = JSON.parse(f)
20
20
 
21
- @app_key = config['application_key'] unless config['application_key'].nil?
22
- @port = config['web_app']['port'].to_i unless config['web_app']['port'].nil?
23
- @host = config['web_app']['host'].to_s unless config['web_app']['host'].nil?
21
+ @app_key = config['application_key'] unless config['application_key'].nil?
22
+ @port = config['web_app']['port'].to_i unless config['web_app']['port'].nil?
23
+ @host = config['web_app']['host'].to_s unless config['web_app']['host'].nil?
24
24
 
25
- rescue Exception => e
26
- raise ConfigurationException.new("Unable to load configuration file: #{config_file}")
27
- end
28
- else
29
- puts "Amon::Config.load - /etc/amon.conf not found"
30
- end
25
+ rescue Exception => e
26
+ raise ConfigurationException.new("Unable to load configuration file: #{config_file}")
27
+ end
28
+ else
29
+ puts "Amon::Config.load - /etc/amon.conf not found"
31
30
  end
31
+ end
32
32
 
33
- def port
34
- @port ||= DEFAULTS[:port]
35
- end
36
-
37
- def host
38
- @host ||= DEFAULTS[:host]
39
- end
33
+ def port
34
+ @port ||= DEFAULTS[:port]
35
+ end
36
+
37
+ def host
38
+ @host ||= DEFAULTS[:host]
39
+ end
40
40
 
41
- end # self end
42
- load
43
- end # Config end
41
+ end # self end
42
+ load
43
+ end # Config end
44
44
  end # Module end
@@ -0,0 +1,59 @@
1
+ require 'digest/md5'
2
+
3
+ module Ramon
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 additional_data
16
+ return {} if @request.nil?
17
+ {
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_paramaters(@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
+ end
30
+
31
+ def filter_hash(keys_to_filter, hash)
32
+ if keys_to_filter.is_a?(Array) && !keys_to_filter.empty?
33
+ hash.each do |key, value|
34
+ if value.respond_to?(:to_hash)
35
+ filter_hash(keys_to_filter, hash[key])
36
+ elsif key_match?(key, keys_to_filter)
37
+ hash[key] = "[FILTERED]"
38
+ end
39
+ end
40
+ end
41
+ hash
42
+ end
43
+
44
+ def key_match?(key, keys_to_filter)
45
+ keys_to_filter.map {|k| k.to_s}.include?(key.to_s)
46
+ end
47
+
48
+ def filter_paramaters(hash)
49
+ if @request.respond_to?(:env) && @request.env["action_dispatch.parameter_filter"]
50
+ filter_hash(@request.env["action_dispatch.parameter_filter"], hash)
51
+ elsif @controller.respond_to?(:filter_parameters)
52
+ @controller.send(:filter_parameters, hash)
53
+ else
54
+ hash
55
+ end
56
+ end
57
+
58
+ end # class end
59
+ end # module end
@@ -0,0 +1,39 @@
1
+ require 'digest/md5'
2
+
3
+ module Ramon
4
+ class ApplicationEnvironment
5
+ def self.to_hash(framework)
6
+ {
7
+ 'language' => 'ruby',
8
+ 'language_version' => language_version_string,
9
+ 'framework' => framework,
10
+ #'libraries_loaded' => libraries_loaded
11
+ }
12
+ end
13
+
14
+
15
+ def self.get_hostname
16
+ require 'socket' unless defined?(Socket)
17
+ Socket.gethostname
18
+ rescue
19
+ 'UNKNOWN'
20
+ end
21
+
22
+ def self.language_version_string
23
+ "#{RUBY_VERSION rescue '?.?.?'} p#{RUBY_PATCHLEVEL rescue '???'} #{RUBY_RELEASE_DATE rescue '????-??-??'} #{RUBY_PLATFORM rescue '????'}"
24
+ end
25
+
26
+ def self.get_username
27
+ ENV['LOGNAME'] || ENV['USER'] || ENV['USERNAME'] || ENV['APACHE_RUN_USER'] || 'UNKNOWN'
28
+ end
29
+
30
+ def self.libraries_loaded
31
+ begin
32
+ return Hash[*Gem.loaded_specs.map{|name, gem_specification| [name, gem_specification.version.to_s]}.flatten]
33
+ rescue
34
+ end
35
+ {}
36
+ end
37
+
38
+ end # class end
39
+ end # module end
@@ -0,0 +1,99 @@
1
+ module Ramon
2
+ class ExceptionData
3
+
4
+ def initialize(exception, name=nil)
5
+ @exception = exception
6
+ @name = name
7
+ end
8
+
9
+ def to_hash
10
+ hash = {}
11
+ # We need the url before the main exception info
12
+ hash['data'] = additional_data
13
+
14
+ hash.merge!({
15
+ 'exception_class' => @exception.class.to_s,
16
+ 'message' => @exception.message,
17
+ 'backtrace' => @exception.backtrace,
18
+ 'url' => hash['data']['request']['url']
19
+ })
20
+
21
+ hash['data'].merge!(ApplicationEnvironment.to_hash(framework))
22
+ hash['data'].merge!(context_stuff)
23
+ hash['data'].merge!(extra_stuff)
24
+ self.class.sanitize_hash(hash)
25
+ end
26
+
27
+ def extra_stuff
28
+ if @name
29
+ {'name' => @name}
30
+ else
31
+ {}
32
+ end
33
+ end
34
+
35
+ def context_stuff
36
+ context = Thread.current[:exceptional_context]
37
+ (context.nil? || context.empty?) ? {} : {'context' => context}
38
+ end
39
+
40
+ def framework
41
+ nil
42
+ end
43
+
44
+
45
+ def self.sanitize_hash(hash)
46
+
47
+ case hash
48
+ when Hash
49
+ hash.inject({}) do |result, (key, value)|
50
+ result.update(key => sanitize_hash(value))
51
+ end
52
+ when Array
53
+ hash.collect{|value| sanitize_hash(value)}
54
+ when Fixnum, String, Bignum
55
+ hash
56
+ else
57
+ hash.to_s
58
+ end
59
+ rescue Exception => e
60
+ {}
61
+ end
62
+
63
+ def extract_http_headers(env)
64
+ headers = {}
65
+ env.select{|k, v| k =~ /^HTTP_/}.each do |name, value|
66
+ proper_name = name.sub(/^HTTP_/, '').split('_').map{|upper_case| upper_case.capitalize}.join('-')
67
+ headers[proper_name] = value
68
+ end
69
+ unless headers['Cookie'].nil?
70
+ headers['Cookie'] = headers['Cookie'].sub(/_session=\S+/, '_session=[FILTERED]')
71
+ end
72
+ headers
73
+ end
74
+
75
+ def self.sanitize_session(request)
76
+
77
+ session_hash = {'session_id' => "", 'data' => {}}
78
+
79
+ if request.respond_to?(:session)
80
+
81
+ session = request.session
82
+ session_hash['session_id'] = request.session_options ? request.session_options[:id] : nil
83
+ session_hash['session_id'] ||= session.respond_to?(:session_id) ? session.session_id : session.instance_variable_get("@session_id")
84
+ session_hash['data'] = session.respond_to?(:to_hash) ? session.to_hash : session.instance_variable_get("@data") || {}
85
+ session_hash['session_id'] ||= session_hash['data'][:session_id]
86
+ session_hash['data'].delete(:session_id)
87
+ end
88
+
89
+ # Don't return the session hash if there is nothing in it
90
+ if session_hash['session_id'].nil? && session_hash['data'].empty?
91
+ {}
92
+ else
93
+ self.sanitize_hash(session_hash)
94
+ end
95
+
96
+ end
97
+
98
+ end # class end
99
+ end # module end
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'rack'
3
+
4
+ module Rack
5
+ class RailsAmonException
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
+ Ramon::Catcher.handle_with_controller(e,env['action_controller.instance'], Rack::Request.new(env))
16
+ raise
17
+ end
18
+
19
+ body
20
+ end
21
+ end
22
+ end
@@ -1,13 +1,13 @@
1
1
  module Ramon
2
- class Log
3
- def self.log(message, level)
4
- level ||= 'notset'
5
- log = {"message" => message, "level" => level}
6
-
7
- log
8
- end
2
+ class Log
3
+ def self.log(message, level)
4
+ level ||= 'notset'
5
+ log = {"message" => message, "level" => level}
6
+
7
+ log
9
8
  end
10
- end
9
+ end # class end
10
+ end # module end
11
11
 
12
12
 
13
13
 
@@ -4,20 +4,20 @@ require 'zlib'
4
4
  require "#{File.dirname(__FILE__)}/config"
5
5
 
6
6
  module Ramon
7
- class Remote
8
- def self.post(type, data)
7
+ class Remote
8
+ def self.post(type, data)
9
9
 
10
- if type == 'log':
11
- @url = '/api/log'
12
- else
13
- @url = '/api/exception'
14
- end
15
-
16
- req = Net::HTTP::Post.new(@url, initheader = {'Content-Type' =>'application/json'})
17
- #req.body = Zlib::Deflate.deflate(data.to_json, Zlib::BEST_SPEED)
18
- req.body = data.to_json
19
- response = Net::HTTP.new(Config::host, Config::port).start {|http| http.request(req) }
20
- #puts "Response #{response.code} #{response.message}: #{response.body}"
10
+ if type == 'log':
11
+ @url = '/api/log'
12
+ else
13
+ @url = '/api/exception'
21
14
  end
22
- end # class end
15
+
16
+ req = Net::HTTP::Post.new(@url, initheader = {'Content-Type' =>'application/json'})
17
+ #req.body = Zlib::Deflate.deflate(data.to_json, Zlib::BEST_SPEED)
18
+ req.body = data.to_json
19
+ response = Net::HTTP.new(Config::host, Config::port).start {|http| http.request(req) }
20
+ #puts "Response #{response.code} #{response.message}: #{response.body}"
21
+ end
22
+ end # class end
23
23
  end # module end
@@ -1,3 +1,3 @@
1
1
  module Ramon
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__) , '../init.rb')
@@ -1,15 +1,14 @@
1
1
  # -*- encoding: utf-8 -*-
2
- $:.push File.expand_path("../lib", __FILE__)
3
- require "ramon/version"
2
+ require File.expand_path('../lib/ramon/version', __FILE__)
4
3
 
5
4
  Gem::Specification.new do |s|
6
5
  s.name = "ramon"
7
6
  s.version = Ramon::VERSION
8
7
  s.authors = ["martinrusev"]
9
- s.email = ["martinrusev@zoho.com"]
8
+ s.email = ["martin@amon.cx"]
10
9
  s.homepage = "http://amon.cx"
11
10
  s.summary = %q{Ruby binding for Amon}
12
- s.description = %q{Amon client that provides logging and exception handling}
11
+ s.description = %q{Amon client for Ruby that provides logging and exception handling for web applications}
13
12
 
14
13
  s.rubyforge_project = "ramon"
15
14
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ramon
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
9
- - 1
10
- version: 0.1.1
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - martinrusev
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-11-09 00:00:00 +00:00
18
+ date: 2011-11-14 00:00:00 +00:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -32,9 +32,9 @@ dependencies:
32
32
  version: "0"
33
33
  type: :runtime
34
34
  version_requirements: *id001
35
- description: Amon client that provides logging and exception handling
35
+ description: Amon client for Ruby that provides logging and exception handling for web applications
36
36
  email:
37
- - martinrusev@zoho.com
37
+ - martin@amon.cx
38
38
  executables: []
39
39
 
40
40
  extensions: []
@@ -47,11 +47,16 @@ files:
47
47
  - Rakefile
48
48
  - init.rb
49
49
  - lib/ramon.rb
50
+ - lib/ramon/catcher.rb
50
51
  - lib/ramon/config.rb
52
+ - lib/ramon/controller_exception_data.rb
53
+ - lib/ramon/enviroment_data.rb
51
54
  - lib/ramon/exception_data.rb
55
+ - lib/ramon/integration/rails.rb
52
56
  - lib/ramon/logger.rb
53
57
  - lib/ramon/remote.rb
54
58
  - lib/ramon/version.rb
59
+ - rails/init.rb
55
60
  - ramon.gemspec
56
61
  has_rdoc: true
57
62
  homepage: http://amon.cx