error_stalker 0.0.12
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/.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
|