error_stalker 0.0.12
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/Gemfile +30 -0
- data/Gemfile.lock +76 -0
- data/README.rdoc +3 -0
- data/Rakefile +19 -0
- data/bin/create_indexes +14 -0
- data/bin/error_stalker_server +16 -0
- data/error_stalker.gemspec +21 -0
- data/lib/error_stalker.rb +4 -0
- data/lib/error_stalker/backend.rb +14 -0
- data/lib/error_stalker/backend/base.rb +14 -0
- data/lib/error_stalker/backend/in_memory.rb +25 -0
- data/lib/error_stalker/backend/log_file.rb +33 -0
- data/lib/error_stalker/backend/server.rb +41 -0
- data/lib/error_stalker/client.rb +62 -0
- data/lib/error_stalker/exception_group.rb +29 -0
- data/lib/error_stalker/exception_report.rb +116 -0
- data/lib/error_stalker/plugin.rb +42 -0
- data/lib/error_stalker/plugin/base.rb +24 -0
- data/lib/error_stalker/plugin/email_sender.rb +60 -0
- data/lib/error_stalker/plugin/lighthouse_reporter.rb +95 -0
- data/lib/error_stalker/plugin/views/exception_email.erb +18 -0
- data/lib/error_stalker/plugin/views/report.erb +18 -0
- data/lib/error_stalker/server.rb +152 -0
- data/lib/error_stalker/server/public/exception_logger.css +173 -0
- data/lib/error_stalker/server/public/grid.css +338 -0
- data/lib/error_stalker/server/public/images/background.png +0 -0
- data/lib/error_stalker/server/public/jquery-1.4.4.min.js +167 -0
- data/lib/error_stalker/server/views/_exception_message.erb +1 -0
- data/lib/error_stalker/server/views/_exception_table.erb +34 -0
- data/lib/error_stalker/server/views/index.erb +31 -0
- data/lib/error_stalker/server/views/layout.erb +18 -0
- data/lib/error_stalker/server/views/search.erb +41 -0
- data/lib/error_stalker/server/views/show.erb +32 -0
- data/lib/error_stalker/server/views/similar.erb +6 -0
- data/lib/error_stalker/sinatra_link_renderer.rb +25 -0
- data/lib/error_stalker/store.rb +11 -0
- data/lib/error_stalker/store/base.rb +75 -0
- data/lib/error_stalker/store/in_memory.rb +109 -0
- data/lib/error_stalker/store/mongoid.rb +318 -0
- data/lib/error_stalker/version.rb +4 -0
- data/test/test_helper.rb +8 -0
- data/test/unit/backend/base_test.rb +9 -0
- data/test/unit/backend/in_memory_test.rb +22 -0
- data/test/unit/backend/log_file_test.rb +25 -0
- data/test/unit/client_test.rb +67 -0
- data/test/unit/exception_report_test.rb +24 -0
- data/test/unit/plugins/email_sender_test.rb +12 -0
- data/test/unit/server_test.rb +141 -0
- data/test/unit/stores/in_memory_test.rb +58 -0
- metadata +109 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in error_stalker.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
group :server do
|
7
|
+
gem 'sinatra', '~>1.1.2'
|
8
|
+
gem 'vegas', '~>0.1.8'
|
9
|
+
gem 'thin', '~>1.2.7'
|
10
|
+
end
|
11
|
+
|
12
|
+
group :mongoid_store do
|
13
|
+
gem 'mongoid', '2.0.0.beta.20'
|
14
|
+
end
|
15
|
+
|
16
|
+
group :test do
|
17
|
+
gem 'rack-test', '~>0.5.7'
|
18
|
+
gem 'mocha', '~>0.9.10'
|
19
|
+
end
|
20
|
+
|
21
|
+
group :lighthouse_reporter do
|
22
|
+
gem 'lighthouse-api', '2.0'
|
23
|
+
gem 'addressable', '~>2.2.2'
|
24
|
+
end
|
25
|
+
|
26
|
+
group :email_sender do
|
27
|
+
gem 'mail', '~>2.2.15'
|
28
|
+
end
|
29
|
+
|
30
|
+
gem 'json', '1.4.6', :platforms => :ruby_18
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
error_stalker (0.0.12)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
activemodel (3.0.3)
|
10
|
+
activesupport (= 3.0.3)
|
11
|
+
builder (~> 2.1.2)
|
12
|
+
i18n (~> 0.4)
|
13
|
+
activeresource (3.0.3)
|
14
|
+
activemodel (= 3.0.3)
|
15
|
+
activesupport (= 3.0.3)
|
16
|
+
activesupport (3.0.3)
|
17
|
+
addressable (2.2.2)
|
18
|
+
bson (1.2.0)
|
19
|
+
builder (2.1.2)
|
20
|
+
daemons (1.1.0)
|
21
|
+
eventmachine (0.12.10)
|
22
|
+
i18n (0.5.0)
|
23
|
+
json (1.4.6)
|
24
|
+
lighthouse-api (2.0)
|
25
|
+
activeresource (>= 3.0.0)
|
26
|
+
activesupport (>= 3.0.0)
|
27
|
+
mail (2.2.15)
|
28
|
+
activesupport (>= 2.3.6)
|
29
|
+
i18n (>= 0.4.0)
|
30
|
+
mime-types (~> 1.16)
|
31
|
+
treetop (~> 1.4.8)
|
32
|
+
mime-types (1.16)
|
33
|
+
mocha (0.9.10)
|
34
|
+
rake
|
35
|
+
mongo (1.2.0)
|
36
|
+
bson (>= 1.2.0)
|
37
|
+
mongoid (2.0.0.beta.20)
|
38
|
+
activemodel (~> 3.0)
|
39
|
+
mongo (~> 1.1)
|
40
|
+
tzinfo (~> 0.3.22)
|
41
|
+
will_paginate (~> 3.0.pre)
|
42
|
+
polyglot (0.3.1)
|
43
|
+
rack (1.2.1)
|
44
|
+
rack-test (0.5.7)
|
45
|
+
rack (>= 1.0)
|
46
|
+
rake (0.8.7)
|
47
|
+
sinatra (1.1.2)
|
48
|
+
rack (~> 1.1)
|
49
|
+
tilt (~> 1.2)
|
50
|
+
thin (1.2.7)
|
51
|
+
daemons (>= 1.0.9)
|
52
|
+
eventmachine (>= 0.12.6)
|
53
|
+
rack (>= 1.0.0)
|
54
|
+
tilt (1.2.1)
|
55
|
+
treetop (1.4.9)
|
56
|
+
polyglot (>= 0.3.1)
|
57
|
+
tzinfo (0.3.24)
|
58
|
+
vegas (0.1.8)
|
59
|
+
rack (>= 1.0.0)
|
60
|
+
will_paginate (3.0.pre2)
|
61
|
+
|
62
|
+
PLATFORMS
|
63
|
+
ruby
|
64
|
+
|
65
|
+
DEPENDENCIES
|
66
|
+
addressable (~> 2.2.2)
|
67
|
+
error_stalker!
|
68
|
+
json (= 1.4.6)
|
69
|
+
lighthouse-api (= 2.0)
|
70
|
+
mail (~> 2.2.15)
|
71
|
+
mocha (~> 0.9.10)
|
72
|
+
mongoid (= 2.0.0.beta.20)
|
73
|
+
rack-test (~> 0.5.7)
|
74
|
+
sinatra (~> 1.1.2)
|
75
|
+
thin (~> 1.2.7)
|
76
|
+
vegas (~> 0.1.8)
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
Bundler::GemHelper.install_tasks
|
5
|
+
|
6
|
+
task :default => :test
|
7
|
+
task :build => :test
|
8
|
+
|
9
|
+
Rake::TestTask.new do |t|
|
10
|
+
t.libs << "test"
|
11
|
+
t.test_files = FileList['test/**/*_test.rb']
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
Rake::RDocTask.new do |rd|
|
16
|
+
rd.main = "README.rdoc"
|
17
|
+
rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
|
18
|
+
rd.rdoc_dir = 'doc'
|
19
|
+
end
|
data/bin/create_indexes
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.expand_path('../lib/error_stalker/server', File.dirname(__FILE__))
|
4
|
+
|
5
|
+
ENV['RACK_ENV'] ||= 'development'
|
6
|
+
|
7
|
+
if ARGV.length != 1
|
8
|
+
puts "Usage: create_indexes </path/to/error_stalker/config.yml>"
|
9
|
+
exit(1)
|
10
|
+
end
|
11
|
+
|
12
|
+
ErrorStalker::Server.configuration = YAML.load(File.read(ARGV[0]))[ENV['RACK_ENV']]
|
13
|
+
store = ErrorStalker::Server.store
|
14
|
+
store.create_indexes if store.respond_to?(:create_indexes)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.expand_path('../lib/error_stalker/server', File.dirname(__FILE__))
|
4
|
+
require 'vegas'
|
5
|
+
ENV['RACK_ENV'] ||= 'development'
|
6
|
+
|
7
|
+
Vegas::Runner.new(ErrorStalker::Server, 'error_stalker_server', {
|
8
|
+
:foreground => true,
|
9
|
+
:before_run => lambda {|v|
|
10
|
+
config_file = ENV['ERROR_STALKER_CONFIG'] || v.args.first
|
11
|
+
if config_file
|
12
|
+
params = YAML.load(File.read(config_file))[ENV['RACK_ENV']]
|
13
|
+
ErrorStalker::Server.configuration = params
|
14
|
+
end
|
15
|
+
}
|
16
|
+
})
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "error_stalker/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "error_stalker"
|
7
|
+
s.version = ErrorStalker::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Justin Weiss"]
|
10
|
+
s.email = ["jweiss@avvo.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Logs exceptions to a pluggable backend and/or a pluggable store}
|
13
|
+
s.description = %q{Logs exceptions to a pluggable backend. Also provides a server for centralized exception logging using a pluggable data store.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "error_stalker"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# ErrorStalker backends are objects representing a place that
|
2
|
+
# ErrorStalker::Client sends exceptions to. A backend simply needs to
|
3
|
+
# inherit from ErrorStalker::Backend::Base and override the +report+
|
4
|
+
# method, after which they can be set using the
|
5
|
+
# ErrorStalker::Client.backend attribute.
|
6
|
+
#
|
7
|
+
# The default backend is an ErrorStalker::Backend::LogFile instance,
|
8
|
+
# which logs exception data to a file.
|
9
|
+
module ErrorStalker::Backend
|
10
|
+
autoload :Base, 'error_stalker/backend/base'
|
11
|
+
autoload :InMemory, 'error_stalker/backend/in_memory'
|
12
|
+
autoload :LogFile, 'error_stalker/backend/log_file'
|
13
|
+
autoload :Server, 'error_stalker/backend/server'
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ErrorStalker::Backend
|
2
|
+
|
3
|
+
# The base class for exception logger backends. All backends should
|
4
|
+
# inherit from this class and implement the +report+ method.
|
5
|
+
class Base
|
6
|
+
|
7
|
+
# Store an exception report into this backend. Subclasses should
|
8
|
+
# override this method. +exception_report+ is an
|
9
|
+
# ErrorStalker::ExceptionReport instance.
|
10
|
+
def report(exception_report)
|
11
|
+
raise NotImplementedError, "This method must be overridden in subclasses"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Provides an in-memory backend that stores all exception reports in
|
2
|
+
# an array. This is mostly useful for tests, and probably shouldn't be
|
3
|
+
# used in real code.
|
4
|
+
class ErrorStalker::Backend::InMemory < ErrorStalker::Backend::Base
|
5
|
+
|
6
|
+
# A list of exceptions stored in this backend.
|
7
|
+
attr_reader :exceptions
|
8
|
+
|
9
|
+
# Create a new instance of this backend, with an empty exception
|
10
|
+
# list.
|
11
|
+
def initialize
|
12
|
+
clear
|
13
|
+
end
|
14
|
+
|
15
|
+
# Stores exception_report in the exceptions list.
|
16
|
+
def report(exception_report)
|
17
|
+
@exceptions << exception_report
|
18
|
+
end
|
19
|
+
|
20
|
+
# Clears the exception list. Pretty useful in a test +setup+ method!
|
21
|
+
def clear
|
22
|
+
@exceptions = []
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
# A backend that logs all exception reports to a file. This is
|
4
|
+
# probably what you want to be running in development mode, and is
|
5
|
+
# also the default backend.
|
6
|
+
class ErrorStalker::Backend::LogFile < ErrorStalker::Backend::Base
|
7
|
+
|
8
|
+
# The name of the file containing the exception reports
|
9
|
+
attr_reader :filename
|
10
|
+
|
11
|
+
# Creates a new LogFile backend that will log exceptions to +filename+
|
12
|
+
def initialize(filename)
|
13
|
+
@filename = filename
|
14
|
+
end
|
15
|
+
|
16
|
+
# Writes the information contained in +exception_report+ to the log
|
17
|
+
# file specified when this backend was initialized.
|
18
|
+
def report(exception_report)
|
19
|
+
File.open(filename, 'a') do |file|
|
20
|
+
file.puts "Machine: #{exception_report.machine}"
|
21
|
+
file.puts "Application: #{exception_report.application}"
|
22
|
+
file.puts "Timestamp: #{exception_report.timestamp}"
|
23
|
+
file.puts "Type: #{exception_report.type}"
|
24
|
+
file.puts "Exception: #{exception_report.exception}"
|
25
|
+
file.puts "Data: #{YAML.dump(exception_report.data)}"
|
26
|
+
if exception_report.backtrace
|
27
|
+
file.puts "Stack trace:"
|
28
|
+
file.puts exception_report.backtrace.join("\n")
|
29
|
+
end
|
30
|
+
file.puts
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
# Stores reported exceptions to a central ErrorStalker server (a Rack
|
5
|
+
# server pointing to an ErrorStalker::Server instance). The most
|
6
|
+
# complicated ErrorStalker backend, it is also the most powerful. This
|
7
|
+
# is probably what you want to be using in production.
|
8
|
+
class ErrorStalker::Backend::Server < ErrorStalker::Backend::Base
|
9
|
+
|
10
|
+
# The hostname of the ErrorStalker server
|
11
|
+
attr_accessor :host
|
12
|
+
|
13
|
+
# The ErrorStalker server's port
|
14
|
+
attr_accessor :port
|
15
|
+
|
16
|
+
# http or https
|
17
|
+
attr_accessor :protocol
|
18
|
+
|
19
|
+
# The path of the ErrorStalker server, if applicable
|
20
|
+
attr_accessor :path
|
21
|
+
|
22
|
+
# Creates a new Server backend instance that will report exceptions
|
23
|
+
# to a centralized ErrorStalker server.
|
24
|
+
def initialize(params = {})
|
25
|
+
@protocol = params[:protocol] || 'http://'
|
26
|
+
@host = params[:host] || 'localhost'
|
27
|
+
@port = params[:port] || '5678'
|
28
|
+
@path = params[:path] || ''
|
29
|
+
end
|
30
|
+
|
31
|
+
# Reports +exception_report+ to a central ErrorStalker server.
|
32
|
+
def report(exception_report)
|
33
|
+
req = Net::HTTP::Post.new("#{path}/report.json")
|
34
|
+
req["content-type"] = "application/json"
|
35
|
+
req.body = exception_report.to_json
|
36
|
+
http = Net::HTTP.new(host, port)
|
37
|
+
http.read_timeout = 10
|
38
|
+
res = http.start { |http| http.request(req) }
|
39
|
+
res
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'error_stalker/exception_report'
|
2
|
+
require 'error_stalker/backend'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
# The error_stalker client enables you to log exception data to a
|
6
|
+
# backend. The class method ErrorStalker::Client.report is usually the
|
7
|
+
# method you want to use out of this class, although for those who
|
8
|
+
# like block syntax, this class also provides a +report_exceptions+
|
9
|
+
# method that reports all exceptions raised inside a block.
|
10
|
+
class ErrorStalker::Client
|
11
|
+
|
12
|
+
# Sets the backend the client will use to report exceptions to
|
13
|
+
# +new_backend+, an ErrorStalker::Backend instance. By default,
|
14
|
+
# exceptions are logged using ErrorStalker::Backend::LogFileBackend.
|
15
|
+
def self.backend=(new_backend)
|
16
|
+
@backend = new_backend
|
17
|
+
end
|
18
|
+
|
19
|
+
# Report an exception to the exception logging backend.
|
20
|
+
#
|
21
|
+
# * application_name: A tag representing the name of the app that
|
22
|
+
# this exception occurred in. This is used for advanced filtering
|
23
|
+
# on the server, if the server backend is used.
|
24
|
+
#
|
25
|
+
# * exception: The exception object that was thrown. This can also
|
26
|
+
# be a string, but you won't get information like the backtrace if
|
27
|
+
# you don't pass an actual exception subclass.
|
28
|
+
#
|
29
|
+
# * extra_data: A hash of additional data to log with the
|
30
|
+
# exception. Depending on which backend the server uses, this may
|
31
|
+
# or may not be indexable.
|
32
|
+
def self.report(application_name, exception, extra_data = {})
|
33
|
+
begin
|
34
|
+
@backend.report(ErrorStalker::ExceptionReport.new(:application => application_name, :exception => exception, :data => extra_data))
|
35
|
+
rescue Exception => e # keep going if this fails
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Calls +report+ on all exceptions raised in the provided block of
|
40
|
+
# code. +options+ can be:
|
41
|
+
#
|
42
|
+
# [:reraise] if true, reraise exceptions caught in this
|
43
|
+
# block. Defaults to true.
|
44
|
+
def self.report_exceptions(application_name, options = {})
|
45
|
+
options = {:reraise => true}.merge(options)
|
46
|
+
begin
|
47
|
+
yield
|
48
|
+
rescue => e
|
49
|
+
report(application_name, e)
|
50
|
+
if options[:reraise]
|
51
|
+
raise e
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
logfile = File.join(Dir.tmpdir, "exceptions.log")
|
58
|
+
|
59
|
+
# let's put this in a better place if we're using rails
|
60
|
+
logfile = Rails.root + 'log/exceptions.log' if defined?(Rails)
|
61
|
+
|
62
|
+
ErrorStalker::Client.backend = ErrorStalker::Backend::LogFile.new(logfile)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Container for information about a group of exceptions. This is used
|
2
|
+
# so that we don't get overwhelmed by duplicate exceptions. Some
|
3
|
+
# Exceptional::Stores may subclass and override this class, but the attributes
|
4
|
+
# called out here should always be valid.
|
5
|
+
class ErrorStalker::ExceptionGroup
|
6
|
+
# The number of times this exception has been reported
|
7
|
+
attr_accessor :count
|
8
|
+
|
9
|
+
# The unique identifier of this group. All similar exceptions should
|
10
|
+
# generate the same digest.
|
11
|
+
attr_accessor :digest
|
12
|
+
|
13
|
+
# The list of machines that have seen this exception
|
14
|
+
attr_accessor :machines
|
15
|
+
|
16
|
+
# The first time this exception occurred
|
17
|
+
attr_accessor :first_timestamp
|
18
|
+
|
19
|
+
# The most recent time this exception occurred
|
20
|
+
attr_accessor :most_recent_timestamp
|
21
|
+
|
22
|
+
# The most recent ExceptionReport instance belonging to this group
|
23
|
+
attr_accessor :most_recent_report
|
24
|
+
|
25
|
+
def type
|
26
|
+
most_recent_report.type unless most_recent_report.nil?
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'digest/md5'
|
3
|
+
|
4
|
+
# An ExceptionReport contains all of the information we have on an
|
5
|
+
# exception, which can then be transformed into whatever format is
|
6
|
+
# needed for further investigation. Some data stores may override this
|
7
|
+
# class, but they should be able to be treated as instances of this
|
8
|
+
# class regardless.
|
9
|
+
class ErrorStalker::ExceptionReport
|
10
|
+
|
11
|
+
# The name of the application that caused this exception.
|
12
|
+
attr_reader :application
|
13
|
+
|
14
|
+
# The name of the machine that raised this exception.
|
15
|
+
attr_reader :machine
|
16
|
+
|
17
|
+
# The time that this exception occurred
|
18
|
+
attr_reader :timestamp
|
19
|
+
|
20
|
+
# The class name of +exception+
|
21
|
+
attr_reader :type
|
22
|
+
|
23
|
+
# The exception object (or string message) this report represents
|
24
|
+
attr_reader :exception
|
25
|
+
|
26
|
+
# A hash of extra data logged along with this exception.
|
27
|
+
attr_reader :data
|
28
|
+
|
29
|
+
# The backtrace corresponding to this exception. Should be an array
|
30
|
+
# of strings, each referring to a single stack frame.
|
31
|
+
attr_reader :backtrace
|
32
|
+
|
33
|
+
# A unique identifier for this exception
|
34
|
+
attr_accessor :id
|
35
|
+
|
36
|
+
# Build a new ExceptionReport. <tt>params[:application]</tt> is a
|
37
|
+
# string identifying the application or component the exception was
|
38
|
+
# sent from, <tt>params[:exception]</tt> is the exception object you
|
39
|
+
# want to report (or a string error message), and
|
40
|
+
# <tt>params[:data]</tt> is any extra arbitrary data you want to log
|
41
|
+
# along with this report.
|
42
|
+
def initialize(params = {})
|
43
|
+
params = symbolize_keys(params)
|
44
|
+
@id = params[:id]
|
45
|
+
@application = params[:application]
|
46
|
+
@machine = params[:machine] || machine_name
|
47
|
+
@timestamp = params[:timestamp] || Time.now
|
48
|
+
@type = params[:type] || params[:exception].class.name
|
49
|
+
|
50
|
+
if params[:exception].is_a?(Exception)
|
51
|
+
@exception = params[:exception].to_s
|
52
|
+
else
|
53
|
+
@exception = params[:exception]
|
54
|
+
end
|
55
|
+
|
56
|
+
@data = params[:data]
|
57
|
+
|
58
|
+
if params[:backtrace]
|
59
|
+
@backtrace = params[:backtrace]
|
60
|
+
else
|
61
|
+
@backtrace = params[:exception].backtrace if params[:exception].is_a?(Exception)
|
62
|
+
end
|
63
|
+
|
64
|
+
@digest = params[:digest] if params[:digest]
|
65
|
+
end
|
66
|
+
|
67
|
+
# The number of characters in this exception's stacktrace that
|
68
|
+
# should be used to uniquify this exception. Exceptions raised from
|
69
|
+
# the same place should have the same stacktrace, up to
|
70
|
+
# +STACK_DIGEST_LENGTH+ characters.
|
71
|
+
STACK_DIGEST_LENGTH = 4096
|
72
|
+
|
73
|
+
# Generate a 'mostly-unique' hash code for this exception, that
|
74
|
+
# should be the same for similar exceptions and different for
|
75
|
+
# different exceptions. This is used to group similar exceptions
|
76
|
+
# together.
|
77
|
+
def digest
|
78
|
+
@digest ||= Digest::MD5.hexdigest((backtrace ? backtrace.to_s[0,STACK_DIGEST_LENGTH] : exception.to_s) + type.to_s)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Serialize this object to json, so we can send it over the wire.
|
82
|
+
def to_json
|
83
|
+
{
|
84
|
+
:application => application,
|
85
|
+
:machine => machine,
|
86
|
+
:timestamp => timestamp,
|
87
|
+
:type => type,
|
88
|
+
:exception => exception,
|
89
|
+
:data => data,
|
90
|
+
:backtrace => backtrace
|
91
|
+
}.to_json
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
# Shamelessly stolen from rails. Converts the keys in +hash+ from
|
97
|
+
# strings to symbols.
|
98
|
+
def symbolize_keys(hash)
|
99
|
+
hash.inject({}) do |options, (key, value)|
|
100
|
+
options[(key.to_sym rescue key) || key] = value
|
101
|
+
options
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Determine the name of this machine. Should work on Windows, Linux,
|
106
|
+
# and Mac OS X, but only tested on Debian, Ubuntu, and Mac OS X.
|
107
|
+
def machine_name
|
108
|
+
machine_name = 'unknown'
|
109
|
+
if RUBY_PLATFORM =~ /win32/
|
110
|
+
machine_name = ENV['COMPUTERNAME']
|
111
|
+
elsif RUBY_PLATFORM =~ /linux/ || RUBY_PLATFORM =~ /darwin/
|
112
|
+
machine_name = `/bin/hostname`.chomp
|
113
|
+
end
|
114
|
+
machine_name
|
115
|
+
end
|
116
|
+
end
|