exception_no 0.0.1
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.
- checksums.yaml +15 -0
- data/.gitignore +1 -0
- data/Makefile +4 -0
- data/exception_no.gemspec +16 -0
- data/lib/exception_no.rb +81 -0
- data/test/exception_no.rb +67 -0
- data/test/middleware.rb +66 -0
- data/test/prelude.rb +62 -0
- metadata +80 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZDg1YTk5NDNkNmRhNTA4NGVmNzE1ZTU3YzcwZDM3MGRlOWUyZWFiZg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
Yjc4Mzk0NjQzZWFjNjI4MWRjMDQ1OTA2Y2FiOGY3MWNjZjA2MjAyNA==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
YjQ1NGJmNjY1YWM2ZDc1OGJkNjMzYWQzZGZjNmJjNTE2YmNlZDBmMzdjYTgy
|
10
|
+
NDM1M2EzNmJkOWZiOTU0MzRkNWZlZDQyNTExZmU0NzY3MGJjMDgzYmJiYzMw
|
11
|
+
NTA0NDI5MjFmYWZiNjRjMDExZjZhMjRmMGZmNDkzZjA2MmYwZTI=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MjVhNzkxY2E2ZDFhY2UzNzUzMjJkMTIzNmY0MmZmY2NmNDdjOTU4ODVmOGRm
|
14
|
+
NDYwYjU1NTgyMmZlNWYwNGJhMmIwNTFlZDRiODk0M2FjMTVjOTQ1M2E1N2E0
|
15
|
+
ODU4ZGQxM2FmNTczNGE4NjI3NzY4MzRkM2M5Y2MzYTJlNTAzODg=
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
.gs
|
data/Makefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.expand_path("lib/exception_no", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "exception_no"
|
5
|
+
s.version = ExceptionNo::VERSION
|
6
|
+
s.summary = "Truly basic exception notification."
|
7
|
+
s.authors = ["Educabilia", "Damian Janowski"]
|
8
|
+
s.email = ["opensource@educabilia.com", "djanowski@dimaion.com"]
|
9
|
+
s.homepage = "https://github.com/djanowski/exception_no"
|
10
|
+
|
11
|
+
s.files = `git ls-files`.split("\n")
|
12
|
+
s.test_files = `git ls-files -- {test}/*`.split("\n")
|
13
|
+
|
14
|
+
s.add_development_dependency "mini-smtp-server"
|
15
|
+
s.add_development_dependency "cutest"
|
16
|
+
end
|
data/lib/exception_no.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require "net/smtp"
|
2
|
+
require "erb"
|
3
|
+
|
4
|
+
class ExceptionNo
|
5
|
+
VERSION = "0.0.1"
|
6
|
+
|
7
|
+
attr_accessor :backtrace_filter
|
8
|
+
|
9
|
+
def initialize(config = {})
|
10
|
+
@config = config
|
11
|
+
@template = ERB.new(TEMPLATE)
|
12
|
+
|
13
|
+
@backtrace_filter = -> line { true }
|
14
|
+
end
|
15
|
+
|
16
|
+
def _notify(exception, options = {})
|
17
|
+
body = @template.result(binding)
|
18
|
+
|
19
|
+
Net::SMTP.start(@config.fetch(:host), @config.fetch(:port, 25)) do |smtp|
|
20
|
+
smtp.send_message(body, @config.fetch(:from), @config.fetch(:to))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def notify(exception, options = {})
|
25
|
+
begin
|
26
|
+
_notify(exception, options)
|
27
|
+
rescue => notification_error
|
28
|
+
$stderr.write("*** FAILED SENDING ERROR NOTIFICATION\n")
|
29
|
+
$stderr.write("*** #{notification_error.class}: #{notification_error}\n")
|
30
|
+
$stderr.write("*** #{exception.class}: #{exception.message}\n")
|
31
|
+
|
32
|
+
exception.backtrace.each do |line|
|
33
|
+
$stderr.write("*** #{line}\n")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
TEMPLATE = (<<-'EMAIL').gsub(/^ {2}/, '')
|
39
|
+
From: <%= @config[:from_alias] %> <<%= @config[:from] %>>
|
40
|
+
To: <<%= @config[:to] %>>
|
41
|
+
Subject: <%= exception.class %>: <%= exception.message.split.join(" ") %>
|
42
|
+
|
43
|
+
<%= options[:body] %>
|
44
|
+
|
45
|
+
<%= "~" * 80 %>
|
46
|
+
|
47
|
+
A <%= exception.class.to_s %> occured: <%= exception.to_s %>
|
48
|
+
|
49
|
+
<%= exception.backtrace.select { |line| @backtrace_filter.call(line) }.join("\n") if exception.backtrace %>
|
50
|
+
EMAIL
|
51
|
+
|
52
|
+
class Middleware
|
53
|
+
def initialize(app, config = {})
|
54
|
+
@app = app
|
55
|
+
@notifier = ExceptionNo.new(config)
|
56
|
+
end
|
57
|
+
|
58
|
+
def call(env)
|
59
|
+
begin
|
60
|
+
@app.call(env)
|
61
|
+
rescue Exception => e
|
62
|
+
@notifier.notify(e, body: extract_env(env))
|
63
|
+
|
64
|
+
raise e
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def extract_env(env)
|
69
|
+
req = Rack::Request.new(env)
|
70
|
+
|
71
|
+
parts = []
|
72
|
+
|
73
|
+
parts << "#{req.request_method} #{req.url}"
|
74
|
+
parts << "User-Agent: #{req.user_agent}" if req.user_agent
|
75
|
+
parts << "Referrer: #{req.referrer}" if req.referrer
|
76
|
+
parts << "Cookie: #{req.env["HTTP_COOKIE"]}" if req.cookies.size > 0
|
77
|
+
|
78
|
+
parts.join("\n")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require_relative "prelude"
|
2
|
+
|
3
|
+
setup do
|
4
|
+
ExceptionNo.new(
|
5
|
+
host: "127.0.0.1",
|
6
|
+
port: 2525,
|
7
|
+
to: "root@localhost",
|
8
|
+
from: "service@localhost"
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
test "deliver exception notification" do |notifier|
|
13
|
+
ex = ArgumentError.new("Really bad argument")
|
14
|
+
|
15
|
+
notifier.notify(ex)
|
16
|
+
|
17
|
+
email = $smtp.outbox.last
|
18
|
+
|
19
|
+
assert_equal email[:to], "<root@localhost>"
|
20
|
+
assert_equal email[:from], "<service@localhost>"
|
21
|
+
|
22
|
+
headers, body = parse_email(email[:data])
|
23
|
+
|
24
|
+
assert_equal headers["Subject"], "ArgumentError: Really bad argument"
|
25
|
+
assert body.include?("A ArgumentError occured: Really bad argument")
|
26
|
+
end
|
27
|
+
|
28
|
+
test "exception messages with multiple lines" do |notifier|
|
29
|
+
notifier.notify(ArgumentError.new("A really\nbad\nargument"))
|
30
|
+
|
31
|
+
headers, body = parse_email($smtp.outbox.last[:data])
|
32
|
+
|
33
|
+
assert_equal headers["Subject"], "ArgumentError: A really bad argument"
|
34
|
+
end
|
35
|
+
|
36
|
+
test "includes backtrace information" do |notifier|
|
37
|
+
begin
|
38
|
+
raise ArgumentError, "A bad argument"
|
39
|
+
rescue Exception => ex
|
40
|
+
notifier.notify(ex)
|
41
|
+
end
|
42
|
+
|
43
|
+
headers, body = parse_email($smtp.outbox.last[:data])
|
44
|
+
|
45
|
+
assert body.include?(__FILE__)
|
46
|
+
assert body.include?(Gem.path.first)
|
47
|
+
end
|
48
|
+
|
49
|
+
test "allows to filter the backtrace" do |notifier|
|
50
|
+
notifier.backtrace_filter = -> line do
|
51
|
+
!line.include?(Gem.path.first)
|
52
|
+
end
|
53
|
+
|
54
|
+
begin
|
55
|
+
raise ArgumentError, "A bad argument"
|
56
|
+
rescue Exception => ex
|
57
|
+
notifier.notify(ex)
|
58
|
+
end
|
59
|
+
|
60
|
+
headers, body = parse_email($smtp.outbox.last[:data])
|
61
|
+
|
62
|
+
assert body.include?(__FILE__)
|
63
|
+
assert !body.include?(Gem.path.first)
|
64
|
+
end
|
65
|
+
|
66
|
+
$smtp.stop
|
67
|
+
$smtp.join
|
data/test/middleware.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require_relative "prelude"
|
2
|
+
|
3
|
+
require "rack"
|
4
|
+
|
5
|
+
setup do
|
6
|
+
Rack::Builder.new do |builder|
|
7
|
+
builder.use ExceptionNo::Middleware,
|
8
|
+
host: "127.0.0.1",
|
9
|
+
port: 2525,
|
10
|
+
to: "root@localhost",
|
11
|
+
from: "service@localhost"
|
12
|
+
|
13
|
+
builder.run(-> env { 1 / 0 })
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
test "re-raises exceptions" do |app|
|
18
|
+
assert_raise(ZeroDivisionError) { app.call(Rack::MockRequest.env_for("/")) }
|
19
|
+
end
|
20
|
+
|
21
|
+
test "extracts interesting stuff from the request" do |app|
|
22
|
+
env = Rack::MockRequest.env_for(
|
23
|
+
"/baz",
|
24
|
+
"HTTP_USER_AGENT" => "Mozilla/4.0 (compatible)",
|
25
|
+
"HTTP_REFERER" => "/other",
|
26
|
+
"HTTP_COOKIE" => "foo=bar",
|
27
|
+
)
|
28
|
+
|
29
|
+
begin
|
30
|
+
app.call(env)
|
31
|
+
rescue ZeroDivisionError
|
32
|
+
end
|
33
|
+
|
34
|
+
headers, body = parse_email($smtp.outbox.last[:data])
|
35
|
+
|
36
|
+
assert_equal headers["Subject"], "ZeroDivisionError: divided by 0"
|
37
|
+
assert body.include?("GET http://example.org/baz")
|
38
|
+
assert body.include?("User-Agent: Mozilla/4.0 (compatible)")
|
39
|
+
assert body.include?("Referrer: /other")
|
40
|
+
assert body.include?("Cookie: foo=bar")
|
41
|
+
end
|
42
|
+
|
43
|
+
test "doesn't raise when the notification fails" do |app|
|
44
|
+
app = Rack::Builder.new do |builder|
|
45
|
+
builder.use ExceptionNo::Middleware,
|
46
|
+
host: "127.0.0.1",
|
47
|
+
port: 2526,
|
48
|
+
to: "root@localhost",
|
49
|
+
from: "service@localhost"
|
50
|
+
|
51
|
+
builder.run(-> env { 1 / 0 })
|
52
|
+
end
|
53
|
+
|
54
|
+
env = Rack::MockRequest.env_for(
|
55
|
+
"/baz",
|
56
|
+
"HTTP_USER_AGENT" => "Mozilla/4.0 (compatible)",
|
57
|
+
"HTTP_REFERER" => "/other",
|
58
|
+
"HTTP_COOKIE" => "foo=bar",
|
59
|
+
)
|
60
|
+
|
61
|
+
assert_raise(ZeroDivisionError) do
|
62
|
+
capture_stderr do
|
63
|
+
app.call(env)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/test/prelude.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require "cutest"
|
2
|
+
require "mini-smtp-server"
|
3
|
+
|
4
|
+
require_relative "../lib/exception_no"
|
5
|
+
|
6
|
+
class SMTPServer < MiniSmtpServer
|
7
|
+
def outbox
|
8
|
+
@outbox ||= []
|
9
|
+
end
|
10
|
+
|
11
|
+
def new_message_event(message)
|
12
|
+
outbox << message
|
13
|
+
true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse_email(raw)
|
18
|
+
headers = {}
|
19
|
+
body = []
|
20
|
+
reading_body = false
|
21
|
+
|
22
|
+
raw.each_line do |line|
|
23
|
+
if !reading_body && line == "\r\n"
|
24
|
+
reading_body = true
|
25
|
+
next
|
26
|
+
end
|
27
|
+
|
28
|
+
if reading_body
|
29
|
+
body << line
|
30
|
+
else
|
31
|
+
key, value = line.split(": ", 2)
|
32
|
+
headers[key] = value && value.chomp
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
body.pop
|
37
|
+
|
38
|
+
return headers, body.join("").chomp
|
39
|
+
end
|
40
|
+
|
41
|
+
def capture_stderr
|
42
|
+
old, $stderr = $stderr, StringIO.new
|
43
|
+
|
44
|
+
begin
|
45
|
+
yield
|
46
|
+
ensure
|
47
|
+
$stderr = old
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
$smtp = SMTPServer.new(2525, "127.0.0.1")
|
52
|
+
$smtp.start
|
53
|
+
|
54
|
+
until SMTPServer.in_service?(2525)
|
55
|
+
end
|
56
|
+
|
57
|
+
at_exit do
|
58
|
+
if $smtp
|
59
|
+
$smtp.stop
|
60
|
+
$smtp.join
|
61
|
+
end
|
62
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: exception_no
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Educabilia
|
8
|
+
- Damian Janowski
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-09-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: mini-smtp-server
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ! '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ! '>='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: cutest
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ! '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ! '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
description:
|
43
|
+
email:
|
44
|
+
- opensource@educabilia.com
|
45
|
+
- djanowski@dimaion.com
|
46
|
+
executables: []
|
47
|
+
extensions: []
|
48
|
+
extra_rdoc_files: []
|
49
|
+
files:
|
50
|
+
- .gitignore
|
51
|
+
- Makefile
|
52
|
+
- exception_no.gemspec
|
53
|
+
- lib/exception_no.rb
|
54
|
+
- test/exception_no.rb
|
55
|
+
- test/middleware.rb
|
56
|
+
- test/prelude.rb
|
57
|
+
homepage: https://github.com/djanowski/exception_no
|
58
|
+
licenses: []
|
59
|
+
metadata: {}
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options: []
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ! '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 2.0.0
|
77
|
+
signing_key:
|
78
|
+
specification_version: 4
|
79
|
+
summary: Truly basic exception notification.
|
80
|
+
test_files: []
|