friendlyfashion-graylog2_exceptions 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
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: