friendlyfashion-graylog2_exceptions 1.3.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.
data/README ADDED
@@ -0,0 +1,5 @@
1
+ Log all the Exceptions of your Rack application to a Graylog2 server.
2
+
3
+ Installation and configuration instructions in the Wiki.
4
+
5
+ More information about Graylog2: [[http://www.graylog2.org/]]
@@ -0,0 +1,42 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ task :default => :test
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |gem|
9
+ gem.name = "graylog2_exceptions"
10
+ gem.summary = 'Graylog2 exception notifier'
11
+ gem.description = 'A Rack middleware that sends every Exception as GELF message to your Graylog2 server'
12
+ gem.email = "lennart@socketfeed.com"
13
+ gem.homepage = "http://www.graylog2.org/"
14
+ gem.authors = "Lennart Koopmann"
15
+ gem.add_dependency "gelf", "= 1.1.3"
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ require 'rake/testtask'
24
+ Rake::TestTask.new(:test) do |test|
25
+ test.libs << 'lib' << 'test'
26
+ test.pattern = 'test/**/test_*.rb'
27
+ test.verbose = true
28
+ end
29
+
30
+ begin
31
+ require 'rcov/rcovtask'
32
+ Rcov::RcovTask.new do |test|
33
+ test.libs << 'test'
34
+ test.pattern = 'test/**/test_*.rb'
35
+ test.rcov_opts << '--exclude gem'
36
+ test.verbose = true
37
+ end
38
+ rescue LoadError
39
+ task :rcov do
40
+ abort "RCov is not available. In order to run rcov, you must: gem install rcov"
41
+ end
42
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.3.0
@@ -0,0 +1,48 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{friendlyfashion-graylog2_exceptions}
8
+ s.version = "1.3.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Laurynas Butkus", "Tomas Didziokas", "Justas Janauskas", "Edvinas Bartkus"]
12
+ s.email = ["laurynas.butkus@gmail.com", "tomas.did@gmail.com", "jjanauskas@gmail.com", "edvinas.bartkus@gmail.com"]
13
+ s.date = %q{2012-08-21}
14
+ s.description = %q{A Rack middleware that sends every Exception as GELF message to your Graylog2 server}
15
+ s.extra_rdoc_files = [
16
+ "README"
17
+ ]
18
+ s.files = [
19
+ "README",
20
+ "Rakefile",
21
+ "VERSION",
22
+ "graylog2_exceptions.gemspec",
23
+ "lib/graylog2_exceptions.rb",
24
+ "test/helper.rb",
25
+ "test/test_graylog2_exceptions.rb"
26
+ ]
27
+ s.homepage = %q{http://www.graylog2.org/}
28
+ s.require_paths = ["lib"]
29
+ s.rubygems_version = %q{1.3.7}
30
+ s.summary = %q{Graylog2 exception notifier}
31
+ s.test_files = [
32
+ "test/helper.rb",
33
+ "test/test_graylog2_exceptions.rb"
34
+ ]
35
+
36
+ if s.respond_to? :specification_version then
37
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
38
+ s.specification_version = 3
39
+
40
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
41
+ s.add_runtime_dependency(%q<gelf>, ["~> 1.3"])
42
+ else
43
+ s.add_dependency(%q<gelf>, ["~> 1.3"])
44
+ end
45
+ else
46
+ s.add_dependency(%q<gelf>, ["~> 1.3"])
47
+ end
48
+ end
@@ -0,0 +1,89 @@
1
+ require 'rubygems'
2
+ require 'gelf'
3
+ require 'socket'
4
+
5
+ class Graylog2Exceptions
6
+ attr_reader :args
7
+
8
+ def initialize(app, args = {})
9
+ standard_args = {
10
+ :hostname => "localhost",
11
+ :port => 12201,
12
+ :local_app_name => Socket::gethostname,
13
+ :facility => 'graylog2_exceptions',
14
+ :max_chunk_size => 'LAN',
15
+ :level => 3,
16
+ :host => nil,
17
+ :short_message => nil,
18
+ :full_message => nil,
19
+ :file => nil,
20
+ :line => nil
21
+ }
22
+
23
+ @args = standard_args.merge(args).reject {|k, v| v.nil? }
24
+ @extra_args = @args.reject {|k, v| standard_args.has_key?(k) }
25
+ @app = app
26
+ end
27
+
28
+ def call(env)
29
+ # Make thread safe
30
+ dup._call(env)
31
+ end
32
+
33
+ def _call(env)
34
+ begin
35
+ # Call the app we are monitoring
36
+ response = @app.call(env)
37
+ rescue => err
38
+ # An exception has been raised. Send to Graylog2!
39
+ send_to_graylog2(err, env)
40
+
41
+ # Raise the exception again to pass back to app.
42
+ raise
43
+ end
44
+
45
+ if env['rack.exception']
46
+ send_to_graylog2(env['rack.exception'], env)
47
+ end
48
+
49
+ response
50
+ end
51
+
52
+ def send_to_graylog2(err, env=nil)
53
+ begin
54
+ notifier = GELF::Notifier.new(@args[:hostname], @args[:port], @args[:max_chunk_size])
55
+ notifier.collect_file_and_line = false
56
+
57
+ opts = {
58
+ :short_message => err.message,
59
+ :facility => @args[:facility],
60
+ :level => @args[:level],
61
+ :host => @args[:local_app_name]
62
+ }
63
+
64
+ if err.backtrace && err.backtrace.size > 0
65
+ opts[:full_message] = "Backtrace:\n" + err.backtrace.join("\n")
66
+ opts[:file] = err.backtrace[0].split(":")[0]
67
+ opts[:line] = err.backtrace[0].split(":")[1]
68
+ end
69
+
70
+ if env and env.size > 0
71
+ opts[:full_message] ||= ""
72
+ opts[:full_message] << "\n\nEnvironment:\n"
73
+
74
+ env.each do |k, v|
75
+ begin
76
+ opts[:full_message] << "#{k}: #{v}\n"
77
+ #opts[:full_message] << "#{k}: #{v.inspect}\n"
78
+ rescue
79
+ end
80
+ end
81
+ end
82
+
83
+ notifier.notify!(opts.merge(@extra_args))
84
+ rescue Exception => i_err
85
+ puts "Graylog2 Exception logger. Could not send message: " + i_err.message
86
+ end
87
+ end
88
+
89
+ end
@@ -0,0 +1,6 @@
1
+ require 'test/unit'
2
+ require 'zlib'
3
+
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+ require 'graylog2_exceptions.rb'
@@ -0,0 +1,172 @@
1
+ require 'helper'
2
+ require 'logger'
3
+
4
+ class TestGraylog2Exceptions < Test::Unit::TestCase
5
+
6
+ # Exceptions raised in the app should be thrown back
7
+ # to the app after handling. Simulating this by giving
8
+ # a nil app and expecting the caused exceptions.
9
+ def test_should_rethrow_exception
10
+ c = Graylog2Exceptions.new(nil, {})
11
+ assert_raise NoMethodError do
12
+ c.call nil
13
+ end
14
+ end
15
+
16
+ def test_correct_parameters_when_custom_set
17
+ c = Graylog2Exceptions.new(nil, {:host => "localhost", :port => 1337, :max_chunk_size => 'WAN', :local_app_name => "yomama", :level => 1})
18
+
19
+ assert_equal "yomama", c.args[:local_app_name]
20
+ assert_equal "localhost", c.args[:hostname]
21
+ assert_equal 1337, c.args[:port]
22
+ assert_equal 'WAN', c.args[:max_chunk_size]
23
+ assert_equal 1, c.args[:level]
24
+ end
25
+
26
+ def test_add_custom_attributes_to_parameters
27
+ c = Graylog2Exceptions.new(nil, {:_app => "my_awesome_app", :_rails_env => "staging"})
28
+
29
+ assert_equal "my_awesome_app", c.args[:_app]
30
+ assert_equal "staging", c.args[:_rails_env]
31
+ end
32
+
33
+ def test_custom_attributes_dont_override_standard
34
+ ex = build_exception
35
+ c = Graylog2Exceptions.new(nil, {:line => 9999})
36
+ sent = Zlib::Inflate.inflate(c.send_to_graylog2(ex).join)
37
+ json = JSON.parse(sent)
38
+
39
+ assert 9999 != json["line"]
40
+ end
41
+
42
+ def test_correct_parameters_when_not_custom_set
43
+ c = Graylog2Exceptions.new(nil, {})
44
+
45
+ assert_equal Socket.gethostname, c.args[:local_app_name]
46
+ assert_equal "localhost", c.args[:hostname]
47
+ assert_equal 12201, c.args[:port]
48
+ assert_equal 'LAN', c.args[:max_chunk_size]
49
+ assert_equal 3, c.args[:level]
50
+ end
51
+
52
+ def test_send_exception_to_graylog2_without_custom_parameters
53
+ ex = build_exception
54
+ c = Graylog2Exceptions.new(nil, {})
55
+ sent = Zlib::Inflate.inflate(c.send_to_graylog2(ex).join)
56
+ json = JSON.parse(sent)
57
+
58
+ assert json["short_message"].include?('undefined method `klopfer!')
59
+ assert json["full_message"].include?('in `build_exception')
60
+ assert_equal 'graylog2_exceptions', json["facility"]
61
+ assert_equal 4, json["level"]
62
+ assert_equal Socket.gethostname, json["host"]
63
+ assert_equal ex.backtrace[0].split(":")[1], json["line"]
64
+ assert_equal ex.backtrace[0].split(":")[0], json["file"]
65
+ end
66
+
67
+ def test_send_exception_to_graylog2_with_custom_parameters
68
+ ex = build_exception
69
+
70
+ c = Graylog2Exceptions.new(nil, {:local_app_name => "machinexx", :level => 4, :facility => 'myfacility'})
71
+ sent = Zlib::Inflate.inflate(c.send_to_graylog2(ex).join)
72
+ json = JSON.parse(sent)
73
+
74
+ assert json["short_message"].include?('undefined method `klopfer!')
75
+ assert json["full_message"].include?('in `build_exception')
76
+ assert_equal 'myfacility', json["facility"]
77
+ assert_equal 3, json["level"]
78
+ assert_equal "machinexx", json["host"]
79
+ assert_equal ex.backtrace[0].split(":")[1], json["line"]
80
+ assert_equal ex.backtrace[0].split(":")[0], json["file"]
81
+ end
82
+
83
+ def test_send_exception_to_graylog2_with_custom_attributes
84
+ ex = build_exception
85
+
86
+ c = Graylog2Exceptions.new(nil, {
87
+ :local_app_name => "machinexx", :level => 4, :facility => 'myfacility',
88
+ :_app => "my_awesome_app", :_rails_env => "staging"
89
+ })
90
+ sent = Zlib::Inflate.inflate(c.send_to_graylog2(ex).join)
91
+ json = JSON.parse(sent)
92
+
93
+ assert json["short_message"].include?('undefined method `klopfer!')
94
+ assert json["full_message"].include?('in `build_exception')
95
+ assert_equal 'myfacility', json["facility"]
96
+ assert_equal 3, json["level"]
97
+ assert_equal "machinexx", json["host"]
98
+ assert_equal ex.backtrace[0].split(":")[1], json["line"]
99
+ assert_equal ex.backtrace[0].split(":")[0], json["file"]
100
+ assert_equal 'my_awesome_app', json["_app"]
101
+ assert_equal 'staging', json["_rails_env"]
102
+ end
103
+
104
+ def test_send_backtraceless_exception_to_graylog2
105
+ ex = Exception.new("bad")
106
+ c = Graylog2Exceptions.new(nil, {})
107
+ sent = Zlib::Inflate.inflate(c.send_to_graylog2(ex).join)
108
+ json = JSON.parse(sent)
109
+
110
+ assert json["short_message"].include?('bad')
111
+ assert json["full_message"].nil?
112
+ end
113
+
114
+ def test_send_rack_environment_to_graylog2
115
+ ex = build_exception
116
+ c = Graylog2Exceptions.new(nil, {})
117
+
118
+ sent = Zlib::Inflate.inflate(c.send_to_graylog2(ex).join)
119
+ json = JSON.parse(sent)
120
+ assert json.keys.none? {|k| k =~/^_env_/ }
121
+
122
+ sent = Zlib::Inflate.inflate(c.send_to_graylog2(ex, {}).join)
123
+ json = JSON.parse(sent)
124
+ assert json.keys.none? {|k| k =~/^_env_/ }
125
+
126
+ bad = Object.new
127
+ def bad.inspect; raise "bad"; end
128
+ data = {
129
+ "nil" => nil,
130
+ "str" => "bar",
131
+ "int" => 123,
132
+ "arr" => ["a", 2],
133
+ "hash" => {"a" => 1},
134
+ "obj" => Object.new,
135
+ "bad" => bad
136
+ }
137
+
138
+ sent = Zlib::Inflate.inflate(c.send_to_graylog2(ex, data).join)
139
+ json = JSON.parse(sent)
140
+ assert_equal('nil', json["_env_nil"])
141
+ assert_equal('"bar"', json["_env_str"])
142
+ assert_equal('123', json["_env_int"])
143
+ assert_equal('["a", 2]', json["_env_arr"])
144
+ assert_equal('{"a"=>1}', json["_env_hash"])
145
+ assert_match(/#<Object:.*>/, json["_env_obj"])
146
+ assert ! json.has_key?("_env_bad")
147
+ end
148
+
149
+ def test_invalid_port_detection
150
+ ex = build_exception
151
+
152
+ c = Graylog2Exceptions.new(nil, {:port => 0})
153
+
154
+ # send_to_graylog2 returns nil when nothing was sent
155
+ # the test is fine when the message is just not sent
156
+ # and there are no exceptions. the method informs
157
+ # the user via puts
158
+ assert_nil c.send_to_graylog2(ex)
159
+ end
160
+
161
+ private
162
+
163
+ # Returns a self-caused exception we can send.
164
+ def build_exception
165
+ begin
166
+ klopfer!
167
+ rescue => e
168
+ return e
169
+ end
170
+ end
171
+
172
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: friendlyfashion-graylog2_exceptions
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Laurynas Butkus
9
+ - Tomas Didziokas
10
+ - Justas Janauskas
11
+ - Edvinas Bartkus
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+ date: 2012-08-21 00:00:00.000000000 Z
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: gelf
19
+ requirement: !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: '1.3'
25
+ type: :runtime
26
+ prerelease: false
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: '1.3'
33
+ description: A Rack middleware that sends every Exception as GELF message to your
34
+ Graylog2 server
35
+ email:
36
+ - laurynas.butkus@gmail.com
37
+ - tomas.did@gmail.com
38
+ - jjanauskas@gmail.com
39
+ - edvinas.bartkus@gmail.com
40
+ executables: []
41
+ extensions: []
42
+ extra_rdoc_files:
43
+ - README
44
+ files:
45
+ - README
46
+ - Rakefile
47
+ - VERSION
48
+ - graylog2_exceptions.gemspec
49
+ - lib/graylog2_exceptions.rb
50
+ - test/helper.rb
51
+ - test/test_graylog2_exceptions.rb
52
+ homepage: http://www.graylog2.org/
53
+ licenses: []
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ requirements: []
71
+ rubyforge_project:
72
+ rubygems_version: 1.8.24
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: Graylog2 exception notifier
76
+ test_files:
77
+ - test/helper.rb
78
+ - test/test_graylog2_exceptions.rb
79
+ has_rdoc: