crashlog 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/.gitignore +2 -1
  2. data/.rspec +1 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +10 -0
  5. data/Gemfile.lock +161 -0
  6. data/INSTALL +22 -0
  7. data/README.md +14 -4
  8. data/Rakefile +13 -0
  9. data/crashlog.gemspec +12 -3
  10. data/generators/crashlog/templates/initializer.rb +6 -0
  11. data/install.rb +2 -0
  12. data/lib/crash_log/backtrace/line.rb +105 -0
  13. data/lib/crash_log/backtrace/line_cache.rb +23 -0
  14. data/lib/crash_log/backtrace.rb +66 -0
  15. data/lib/crash_log/configuration.bak.rb +199 -0
  16. data/lib/crash_log/configuration.rb +188 -0
  17. data/lib/crash_log/logging.rb +51 -0
  18. data/lib/crash_log/payload.rb +157 -0
  19. data/lib/crash_log/rack.rb +47 -0
  20. data/lib/crash_log/rails/action_controller_rescue.rb +32 -0
  21. data/lib/crash_log/rails/controller_methods.rb +45 -0
  22. data/lib/crash_log/rails/middleware/debug_exception_catcher.rb +43 -0
  23. data/lib/crash_log/rails.rb +32 -0
  24. data/lib/crash_log/railtie.rb +41 -0
  25. data/lib/crash_log/reporter.rb +105 -0
  26. data/lib/crash_log/system_information.rb +64 -0
  27. data/lib/crash_log/templates/payload.rabl +7 -0
  28. data/lib/crash_log/version.rb +1 -1
  29. data/lib/crash_log.rb +118 -0
  30. data/lib/faraday/request/hmac_authentication.rb +73 -0
  31. data/lib/rails/generators/crashlog/crashlog_generator.rb +42 -0
  32. data/rails/init.rb +1 -0
  33. data/spec/crash_log/backtrace_spec.rb +79 -0
  34. data/spec/crash_log/initializer_spec.rb +53 -0
  35. data/spec/crash_log/payload_spec.rb +124 -0
  36. data/spec/crash_log/reporter_spec.rb +179 -0
  37. data/spec/crash_log_spec.rb +153 -0
  38. data/spec/dummy/README.rdoc +261 -0
  39. data/spec/dummy/Rakefile +7 -0
  40. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  41. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  42. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  43. data/spec/dummy/app/controllers/break_controller.rb +10 -0
  44. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  45. data/spec/dummy/app/mailers/.gitkeep +0 -0
  46. data/spec/dummy/app/models/.gitkeep +0 -0
  47. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  48. data/spec/dummy/config/application.rb +59 -0
  49. data/spec/dummy/config/boot.rb +10 -0
  50. data/spec/dummy/config/database.yml +44 -0
  51. data/spec/dummy/config/environment.rb +5 -0
  52. data/spec/dummy/config/environments/development.rb +37 -0
  53. data/spec/dummy/config/environments/production.rb +67 -0
  54. data/spec/dummy/config/environments/test.rb +37 -0
  55. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  56. data/spec/dummy/config/initializers/crashlog.rb +6 -0
  57. data/spec/dummy/config/initializers/inflections.rb +15 -0
  58. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  59. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  60. data/spec/dummy/config/initializers/session_store.rb +8 -0
  61. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  62. data/spec/dummy/config/routes.rb +6 -0
  63. data/spec/dummy/config.ru +4 -0
  64. data/spec/dummy/lib/assets/.gitkeep +0 -0
  65. data/spec/dummy/log/.gitkeep +0 -0
  66. data/spec/dummy/public/404.html +26 -0
  67. data/spec/dummy/public/422.html +26 -0
  68. data/spec/dummy/public/500.html +25 -0
  69. data/spec/dummy/public/favicon.ico +0 -0
  70. data/spec/dummy/script/rails +6 -0
  71. data/spec/requests/rack_spec.rb +29 -0
  72. data/spec/requests/rails_controller_rescue_spec.rb +46 -0
  73. data/spec/spec_helper.rb +23 -0
  74. data/spec/support/doing.rb +1 -0
  75. data/spec/support/dummy_app.rb +13 -0
  76. data/spec/support/hash_ext.rb +7 -0
  77. metadata +197 -7
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+ # require 'crash_log/rails'
3
+
4
+ describe "Initializer" do
5
+
6
+ let(:logger) { stub("Logger") }
7
+ let(:other_logger) { stub("OtherLogger") }
8
+
9
+ # before(:all) do
10
+ # load_dummy_app
11
+ # end
12
+
13
+ describe 'auto configure logger' do
14
+ before do
15
+ load_dummy_app
16
+ # unless defined?(Rails)
17
+ # module Rails
18
+ # end
19
+ # end
20
+ Rails.stub(:logger).and_return(logger)
21
+ logger.stub(:error)
22
+ other_logger.stub(:error)
23
+ end
24
+
25
+
26
+ it 'detects presence of Rails logger' do
27
+ pending
28
+ # CrashLog::Rails.__send__(:initialize)
29
+ CrashLog.logger.should be(logger)
30
+ end
31
+
32
+ it "allows overriding of the logger if already assigned" do
33
+ pending
34
+ # unless defined?(::Rails)
35
+ # module Rails
36
+ # end
37
+ # end
38
+
39
+ Rails.stub(:logger).and_return(logger)
40
+
41
+ CrashLog.logger.should_not == logger
42
+ # CrashLog::Rails.initialize
43
+ CrashLog.logger.should == logger
44
+
45
+ CrashLog.configure do |config|
46
+ config.logger = other_logger
47
+ end
48
+
49
+ CrashLog.logger.should == other_logger
50
+ end
51
+ end
52
+
53
+ end
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+ require 'json_spec'
3
+
4
+ describe CrashLog::Payload do
5
+ include JsonSpec
6
+
7
+ let(:configuration) do
8
+ stub('configuration').tap do |config|
9
+ config.stub(:[])
10
+ end
11
+ end
12
+
13
+ subject { CrashLog::Payload.build(raised_error, configuration) }
14
+
15
+ let(:raised_error) do
16
+ begin
17
+ raise RuntimeError, "This broke"
18
+ rescue RuntimeError => e
19
+ e
20
+ end
21
+ end
22
+
23
+ describe '#add_context' do
24
+ it 'user_data should be empty' do
25
+ subject.context.should be_empty
26
+ end
27
+
28
+ it 'merges in new user data' do
29
+ data = {:email => "user@example.com"}
30
+ subject.add_context(data)
31
+ subject.context.should == data
32
+ end
33
+ end
34
+
35
+ describe '#add_session_data' do
36
+ it 'is empty by default' do
37
+ subject.environment[:session].should be_nil
38
+ end
39
+
40
+ it 'allows merging in data' do
41
+ data = {:path => '/problematic/path'}
42
+ subject.add_session_data(data)
43
+ subject.environment[:session].should == data
44
+ end
45
+
46
+ it 'allows adding more data' do
47
+ data_1 = {:path => '/problematic/path'}
48
+ data_2 = {:count => 42}
49
+ subject.add_session_data(data_1)
50
+ subject.add_session_data(data_2)
51
+
52
+ subject.environment[:session][:path].should == data_1[:path]
53
+ subject.environment[:session][:count].should == data_2[:count]
54
+ end
55
+ end
56
+
57
+ describe 'private.unwrap_exception' do
58
+ it 'unwraps exception objects' do
59
+ subject.__send__(:unwrap_exception, raised_error).should == raised_error
60
+ end
61
+ end
62
+
63
+ describe '#body' do
64
+
65
+ describe 'notifier' do
66
+ it 'has name' do
67
+ subject.body.to_json.should have_json_path('notifier/name')
68
+ end
69
+
70
+ it 'has version' do
71
+ subject.body.to_json.should have_json_path('notifier/version')
72
+ end
73
+ end
74
+
75
+ describe 'exception' do
76
+
77
+ it 'has class_name' do
78
+ subject.body.to_json.should have_json_path('event/type')
79
+ end
80
+
81
+ it 'has message' do
82
+ subject.body.to_json.should have_json_path('event/message')
83
+ end
84
+
85
+ describe 'backtrace' do
86
+ it 'has line number' do
87
+ subject.body.to_json.should have_json_path('backtrace/0/number')
88
+ end
89
+
90
+ it 'has integer as line number' do
91
+ subject.body.to_json.should have_json_type(Integer).at_path('backtrace/0/number')
92
+ end
93
+
94
+ it 'has filename' do
95
+ subject.body.to_json.should have_json_path('backtrace/0/file')
96
+ end
97
+
98
+ it 'has method' do
99
+ subject.body.to_json.should have_json_path('backtrace/0/method')
100
+ end
101
+ end
102
+
103
+ it 'has backtrace' do
104
+ subject.body.to_json.should have_json_path('backtrace/0')
105
+ end
106
+
107
+ it 'has timestamp' do
108
+ subject.body.to_json.should have_json_path('event/timestamp')
109
+ end
110
+ end
111
+
112
+ describe 'session' do
113
+
114
+ end
115
+
116
+ describe 'user_data' do
117
+ it 'has first key provided by user' do
118
+ pending
119
+ # subject.add_user_data({:email => "user@example.com"})
120
+ # subject.body.to_json.should have_json_path('user_data/email')
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,179 @@
1
+ require 'spec_helper'
2
+
3
+ describe CrashLog::Reporter do
4
+ let(:uuid) { UUID.generate }
5
+
6
+ let(:config) {
7
+ CrashLog::Configuration.new.tap do |config|
8
+ config.project_id = 'PROJECT_ID'
9
+ config.api_key = 'API_KEY'
10
+ # config.adapter = test_adapter
11
+ config.scheme = 'http'
12
+ end
13
+ }
14
+
15
+ # let(:config) { stub("Configuration", {
16
+ # :host => "io.crashlog.io",
17
+ # :scheme => "https",
18
+ # :port => 443,
19
+ # :endpoint => '/notify',
20
+ # :announce => true,
21
+ # :announce_endpoint => '/announce',
22
+ # :dry_run => false,
23
+ # :api_key => 'API_TOKEN',
24
+ # :project_id => "PROJECT_ID",
25
+ # :http_read_timeout => 1,
26
+ # :http_open_timeout => 1
27
+ # }).tap do |config|
28
+ # config.stub(:adapter).and_return(:test, &test_adapter)
29
+ # end
30
+ # }
31
+
32
+ let(:test_adapter) {
33
+ lambda { |stub|
34
+ stub.post('/events') do |env|
35
+ [201, {}, env[:request_headers]]
36
+ end
37
+ }
38
+ }
39
+
40
+ subject { CrashLog::Reporter.new(config) }
41
+
42
+ let(:positive_response) do
43
+ {:result_url => "https://crashlog.io/collect/#{uuid}"}
44
+ end
45
+
46
+ let(:announce_response) do
47
+ {:application => "CrashLog Test"}
48
+ end
49
+
50
+ let(:positive_response_json) { positive_response.to_json }
51
+ let(:announce_json) { announce_response.to_json }
52
+
53
+ let(:payload) {
54
+ {}
55
+ }
56
+
57
+ before do
58
+ CrashLog.stub(:report_for_duty!)
59
+ end
60
+
61
+ it 'should not be doing a dry run' do
62
+ subject.should_not be_dry_run
63
+ end
64
+
65
+ describe '#notify' do
66
+ before do
67
+ test_connection = Faraday.new(:url => subject.url) do |builder|
68
+ builder.adapter :test, stubs
69
+ builder.request :hmac_authentication, 'PROJECT_ID', 'SECRET', {:service_id => 'CrashLog'}
70
+ builder.request :url_encoded
71
+ end
72
+
73
+ subject.stub(:connection).and_return(test_connection)
74
+ end
75
+
76
+ let!(:stubs) do
77
+ Faraday::Adapter::Test::Stubs.new do |stub|
78
+ stub.post('/events') { [200, {}, positive_response_json] }
79
+ end
80
+ end
81
+
82
+ after do
83
+ #stubs.verify_stubbed_calls
84
+ end
85
+
86
+ it 'makes a post request' do
87
+ subject.send(:connection).should_receive(:post).once
88
+ subject.notify(payload)
89
+ end
90
+
91
+ it 'authenticates request with HMAC' do
92
+ subject.notify(payload).should be_true
93
+ subject.response.env[:request_headers]['Authorization'].should ==
94
+ CrashLog::AuthHMAC.new({}, {:service_id => 'CrashLog'}).authorization(subject.response.env, 'PROJECT_ID', 'SECRET')
95
+ stubs.verify_stubbed_calls
96
+ end
97
+
98
+ it 'sends a serialized payload to crashlog.io' do
99
+ subject.notify(payload).should be_true
100
+
101
+ stubs.verify_stubbed_calls
102
+ end
103
+
104
+ it 'captures result body' do
105
+ subject.notify(payload).should be_true
106
+ subject.result.should == positive_response
107
+ end
108
+ end
109
+
110
+ describe '#announce' do
111
+ let!(:stubs) do
112
+ Faraday::Adapter::Test::Stubs.new do |stub|
113
+ stub.post('/announce') { [201, {}, announce_json] }
114
+ end
115
+ end
116
+
117
+ before do
118
+ test_connection = Faraday.new(:url => subject.url) do |builder|
119
+ builder.adapter :test, stubs
120
+ end
121
+
122
+ subject.stub(:connection).and_return(test_connection)
123
+ end
124
+
125
+ it 'sends an identification payload to CrashLog'
126
+
127
+ it 'responds with an application name' do
128
+ subject.announce.should === 'CrashLog Test'
129
+ stubs.verify_stubbed_calls
130
+ end
131
+ end
132
+
133
+ describe 'url' do
134
+ it 'constructs url from configuration' do
135
+ subject.url.to_s.should == 'http://stdin.crashlog.io'
136
+ end
137
+
138
+ it 'sends HMACAuth header'
139
+
140
+ # # config.stub(:adapter).and_return(:test, &test_adapter)
141
+
142
+ # # .and_return(:test).and_yield do |stub|
143
+ # # stub.post('/notify') do |env|
144
+ # # [200, {}, env[:request_headers]]
145
+ # # end
146
+ # # end
147
+
148
+ # # test_connection = Faraday.new(:url => subject.url) do |faraday|
149
+ # # faraday.adapter :test do |stub|
150
+ # # stub.post('/notify') do |env|
151
+ # # [200, {}, env[:request_headers]]
152
+ # # end
153
+ # # end
154
+ # # end
155
+
156
+ # # #faraday.request :url_encoded
157
+ # # faraday.request :token_auth, "config.api_key"
158
+ # # #faraday.response :logger
159
+ # # # faraday.token_auth config.api_key
160
+ # # faraday.options[:timeout] = 1
161
+ # # faraday.options[:open_timeout] = 1
162
+ # # faraday.ssl[:verify] = false
163
+ # # end
164
+
165
+ # # subject.stub(:connection).and_return(test_connection)
166
+ # # subject.connection.adapter(:test) do |stub|
167
+ # # stub.post('/notify') do |env|
168
+ # # [200, {}, env[:request_headers]]
169
+ # # end
170
+ # # end
171
+
172
+ # subject.notify(payload).should be_true
173
+
174
+ # subject.response.should == 'Token token=""'
175
+
176
+ # stubs.verify_stubbed_calls
177
+ # end
178
+ end
179
+ end
@@ -0,0 +1,153 @@
1
+ require 'spec_helper'
2
+
3
+ describe CrashLog do
4
+ def set_public_env
5
+ CrashLog.configure { |config| config.stage = 'production' }
6
+ end
7
+
8
+ def set_development_env
9
+ CrashLog.configure { |config| config.stage = 'development' }
10
+ end
11
+
12
+ let(:raised_error) do
13
+ begin
14
+ raise RuntimeError, "This broke"
15
+ rescue RuntimeError => e
16
+ e
17
+ end
18
+ end
19
+
20
+ after do
21
+ CrashLog.instance_variable_set("@configuration", nil)
22
+ end
23
+
24
+ let(:user) do
25
+ stub('User', :email => 'user@example.com')
26
+ end
27
+
28
+ describe '.notify' do
29
+ it 'does not send if not live' do
30
+ CrashLog::Reporter.any_instance.should_receive(:notify).never
31
+
32
+ CrashLog.stub(:live?).and_return(false)
33
+ CrashLog.notify(raised_error)
34
+ end
35
+
36
+ it 'handles being passed an exception object' do
37
+ CrashLog::Reporter.any_instance.should_receive(:notify).once
38
+
39
+ CrashLog.stub(:live?).and_return(true)
40
+ CrashLog.notify(raised_error)
41
+ end
42
+
43
+ it 'handles being passed an exception object and context' do
44
+ payload = {}
45
+
46
+ CrashLog::Reporter.any_instance.stub(:notify)
47
+ #should_receive(:notify).with(payload).once
48
+ CrashLog.stub(:live?).and_return(true)
49
+
50
+ context = {:current_user => user}
51
+ CrashLog.notify(raised_error, context)
52
+ end
53
+
54
+ it 'handles being passed a hash'
55
+ it 'handles being passed a string'
56
+ end
57
+
58
+ describe '.logger' do
59
+ it 'detects Rails.logger'
60
+ it 'defaults to STDOUT'
61
+ end
62
+
63
+ describe '.configuration' do
64
+ after do
65
+ CrashLog.instance_variable_set("@configuration", nil)
66
+ end
67
+
68
+ it 'handles being configured with a block' do
69
+ logger = stub("Logger")
70
+ logger.stub(:error)
71
+
72
+ CrashLog.configuration.logger.should be_nil
73
+ CrashLog.configure do |config|
74
+ config.logger = logger
75
+ end
76
+ CrashLog.configuration.logger.should == logger
77
+ end
78
+
79
+ it 'handles directly configuring attributes' do
80
+ logger = stub("Logger")
81
+ CrashLog.configuration.logger.should be_nil
82
+ CrashLog.configuration.logger = logger
83
+ CrashLog.configuration.logger.should == logger
84
+ end
85
+
86
+ it 'accepts api_key' do
87
+ key = stub("THIS IS AN API KEY")
88
+ CrashLog.configuration.api_key.should be_nil
89
+ CrashLog.configuration.api_key = key
90
+ CrashLog.configuration.api_key.should == key
91
+ end
92
+ end
93
+
94
+ describe '.ready' do
95
+ it 'logs an ready message' do
96
+ CrashLog::Reporter.any_instance.stub(:announce).and_return("Test Application")
97
+ logger = stub('Logger')
98
+ logger.should_receive(:info).
99
+ with("** [CrashLog] Initialized and ready to handle exceptions for Test Application")
100
+
101
+ CrashLog.stub(:logger).and_return(logger)
102
+ CrashLog.report_for_duty!
103
+ end
104
+ end
105
+
106
+ describe '.live?' do
107
+ it 'is live if current stage is included in release stages' do
108
+ CrashLog.configure do |c|
109
+ c.release_stages = ['test']
110
+ c.stage = 'test'
111
+ end
112
+
113
+ CrashLog.should be_live
114
+ end
115
+
116
+ it 'is not live if current stage is not included in release stages' do
117
+ CrashLog.configure do |c|
118
+ c.release_stages = ['production']
119
+ c.stage = 'test'
120
+ end
121
+
122
+ CrashLog.should_not be_live
123
+ end
124
+
125
+ it 'handles irregular stage names' do
126
+ CrashLog.configure do |c|
127
+ c.release_stages = ['test']
128
+ c.stage = 'Test'
129
+ end
130
+
131
+ CrashLog.configuration.stage.should === 'test'
132
+ CrashLog.should be_live
133
+ end
134
+ end
135
+
136
+ describe '#ignored?' do
137
+ it 'returns true if current exception is on ignored list' do
138
+ CrashLog.ignored?(RuntimeError.new).should be_false
139
+ end
140
+
141
+ it 'ignores ActiveRecord::RecordNotFound' do
142
+ # load_dummy_app
143
+ unless defined?(ActiveRecord)
144
+ module ActiveRecord
145
+ class RecordNotFound < RuntimeError
146
+ end
147
+ end
148
+ end
149
+
150
+ CrashLog.ignored?(ActiveRecord::RecordNotFound).should be_true
151
+ end
152
+ end
153
+ end