influx_reporter 1.0.0

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.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +51 -0
  5. data/.travis.yml +42 -0
  6. data/.yardopts +3 -0
  7. data/Dockerfile +9 -0
  8. data/Gemfile +7 -0
  9. data/HISTORY.md +1 -0
  10. data/LICENSE +14 -0
  11. data/README.md +189 -0
  12. data/Rakefile +27 -0
  13. data/gemfiles/Gemfile.base +29 -0
  14. data/gemfiles/Gemfile.rails-3.2.x +4 -0
  15. data/gemfiles/Gemfile.rails-4.0.x +4 -0
  16. data/gemfiles/Gemfile.rails-4.1.x +4 -0
  17. data/gemfiles/Gemfile.rails-4.2.x +5 -0
  18. data/gemfiles/Gemfile.rails-5.0.x +5 -0
  19. data/gemfiles/Gemfile.rails-HEAD +7 -0
  20. data/influx_reporter.gemspec +26 -0
  21. data/lib/influx_reporter.rb +142 -0
  22. data/lib/influx_reporter/client.rb +306 -0
  23. data/lib/influx_reporter/configuration.rb +88 -0
  24. data/lib/influx_reporter/data_builders.rb +18 -0
  25. data/lib/influx_reporter/data_builders/error.rb +52 -0
  26. data/lib/influx_reporter/data_builders/transactions.rb +120 -0
  27. data/lib/influx_reporter/error.rb +7 -0
  28. data/lib/influx_reporter/error_message.rb +85 -0
  29. data/lib/influx_reporter/error_message/exception.rb +14 -0
  30. data/lib/influx_reporter/error_message/http.rb +73 -0
  31. data/lib/influx_reporter/error_message/stacktrace.rb +76 -0
  32. data/lib/influx_reporter/error_message/user.rb +25 -0
  33. data/lib/influx_reporter/filter.rb +66 -0
  34. data/lib/influx_reporter/http_client.rb +140 -0
  35. data/lib/influx_reporter/influx_db_client.rb +74 -0
  36. data/lib/influx_reporter/injections.rb +89 -0
  37. data/lib/influx_reporter/injections/json.rb +21 -0
  38. data/lib/influx_reporter/injections/net_http.rb +50 -0
  39. data/lib/influx_reporter/injections/redis.rb +25 -0
  40. data/lib/influx_reporter/injections/sequel.rb +37 -0
  41. data/lib/influx_reporter/injections/sinatra.rb +59 -0
  42. data/lib/influx_reporter/integration/delayed_job.rb +30 -0
  43. data/lib/influx_reporter/integration/rails/inject_exceptions_catcher.rb +25 -0
  44. data/lib/influx_reporter/integration/railtie.rb +56 -0
  45. data/lib/influx_reporter/integration/resque.rb +18 -0
  46. data/lib/influx_reporter/integration/sidekiq.rb +130 -0
  47. data/lib/influx_reporter/line_cache.rb +21 -0
  48. data/lib/influx_reporter/logging.rb +39 -0
  49. data/lib/influx_reporter/middleware.rb +63 -0
  50. data/lib/influx_reporter/normalizers.rb +65 -0
  51. data/lib/influx_reporter/normalizers/action_controller.rb +34 -0
  52. data/lib/influx_reporter/normalizers/action_view.rb +72 -0
  53. data/lib/influx_reporter/normalizers/active_record.rb +45 -0
  54. data/lib/influx_reporter/sql_summarizer.rb +31 -0
  55. data/lib/influx_reporter/subscriber.rb +80 -0
  56. data/lib/influx_reporter/tasks.rb +28 -0
  57. data/lib/influx_reporter/trace.rb +48 -0
  58. data/lib/influx_reporter/trace_helpers.rb +31 -0
  59. data/lib/influx_reporter/transaction.rb +114 -0
  60. data/lib/influx_reporter/util.rb +18 -0
  61. data/lib/influx_reporter/util/constantize.rb +56 -0
  62. data/lib/influx_reporter/util/inspector.rb +72 -0
  63. data/lib/influx_reporter/util/timestamp.rb +11 -0
  64. data/lib/influx_reporter/version.rb +5 -0
  65. data/lib/influx_reporter/worker.rb +56 -0
  66. data/spec/influx_reporter/client_spec.rb +264 -0
  67. data/spec/influx_reporter/configuration_spec.rb +42 -0
  68. data/spec/influx_reporter/data_builders/error_spec.rb +40 -0
  69. data/spec/influx_reporter/data_builders/transactions_spec.rb +48 -0
  70. data/spec/influx_reporter/error_message/exception_spec.rb +22 -0
  71. data/spec/influx_reporter/error_message/http_spec.rb +55 -0
  72. data/spec/influx_reporter/error_message/stacktrace_spec.rb +56 -0
  73. data/spec/influx_reporter/error_message/user_spec.rb +26 -0
  74. data/spec/influx_reporter/error_message_spec.rb +102 -0
  75. data/spec/influx_reporter/filter_spec.rb +33 -0
  76. data/spec/influx_reporter/injections/net_http_spec.rb +35 -0
  77. data/spec/influx_reporter/injections/sequel_spec.rb +33 -0
  78. data/spec/influx_reporter/injections/sinatra_spec.rb +13 -0
  79. data/spec/influx_reporter/injections_spec.rb +50 -0
  80. data/spec/influx_reporter/integration/delayed_job_spec.rb +37 -0
  81. data/spec/influx_reporter/integration/json_spec.rb +41 -0
  82. data/spec/influx_reporter/integration/rails_spec.rb +92 -0
  83. data/spec/influx_reporter/integration/redis_spec.rb +20 -0
  84. data/spec/influx_reporter/integration/resque_spec.rb +42 -0
  85. data/spec/influx_reporter/integration/sidekiq_spec.rb +40 -0
  86. data/spec/influx_reporter/integration/sinatra_spec.rb +99 -0
  87. data/spec/influx_reporter/line_cache_spec.rb +38 -0
  88. data/spec/influx_reporter/logging_spec.rb +49 -0
  89. data/spec/influx_reporter/middleware_spec.rb +32 -0
  90. data/spec/influx_reporter/normalizers/action_controller_spec.rb +37 -0
  91. data/spec/influx_reporter/normalizers/action_view_spec.rb +78 -0
  92. data/spec/influx_reporter/normalizers/active_record_spec.rb +70 -0
  93. data/spec/influx_reporter/normalizers_spec.rb +16 -0
  94. data/spec/influx_reporter/sql_summarizer_spec.rb +35 -0
  95. data/spec/influx_reporter/subscriber_spec.rb +83 -0
  96. data/spec/influx_reporter/trace_spec.rb +43 -0
  97. data/spec/influx_reporter/transaction_spec.rb +98 -0
  98. data/spec/influx_reporter/util/inspector_spec.rb +41 -0
  99. data/spec/influx_reporter/util_spec.rb +20 -0
  100. data/spec/influx_reporter/worker_spec.rb +54 -0
  101. data/spec/influx_reporter_spec.rb +50 -0
  102. data/spec/spec_helper.rb +86 -0
  103. metadata +188 -0
@@ -0,0 +1,264 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ module InfluxReporter
6
+ RSpec.describe Client do
7
+ let(:config) { Configuration.new influx_db: { host: :localhost }}
8
+
9
+ describe '.start!' do
10
+ it "set's up an instance and only one" do
11
+ first_instance = Client.start! config
12
+ expect(Client.inst).to_not be_nil
13
+ expect(Client.start!(config)).to be first_instance
14
+ end
15
+ end
16
+
17
+ describe '.stop!' do
18
+ it 'kills the instance but flushes before' do
19
+ Client.start! config
20
+ Client.inst.submit_transaction Transaction.new(Client.inst, 'Test').done(200)
21
+ Client.stop!
22
+ expect(WebMock).to have_requested(:post, %r{\/write})
23
+ expect(Client.inst).to be_nil
24
+ end
25
+ end
26
+
27
+ context 'without worker spec setting', start_without_worker: true do
28
+ it "doesn't start a Worker" do
29
+ expect(Thread).to_not receive(:new)
30
+ Client.start! config
31
+ expect(Client.inst).to_not be_nil
32
+ end
33
+ end
34
+
35
+ context 'with a running client', start_without_worker: true do
36
+ subject { Client.inst }
37
+
38
+ describe '#transaction' do
39
+ it 'returns a new transaction and sets it as current' do
40
+ transaction = subject.transaction 'Test'
41
+ expect(transaction).to_not be_nil
42
+ expect(subject.current_transaction).to be transaction
43
+ end
44
+ it 'returns the current transaction if present' do
45
+ transaction = subject.transaction 'Test'
46
+ expect(subject.transaction('Test')).to eq transaction
47
+ end
48
+ context 'with a block' do
49
+ it 'yields transaction' do
50
+ blck = ->(*args) {}
51
+ allow(blck).to receive(:call)
52
+ subject.transaction('Test') { |t| blck.call(t) }
53
+ expect(blck).to have_received(:call).with(Transaction)
54
+ end
55
+ it 'returns transaction' do
56
+ result = subject.transaction('Test') { "DON'T RETURN ME" }
57
+ expect(result).to be_a Transaction
58
+ end
59
+ end
60
+ end
61
+
62
+ describe '#trace' do
63
+ it 'delegates to current transaction' do
64
+ subject.current_transaction = double('transaction', trace: true)
65
+ subject.trace 1, 2, 3
66
+ expect(subject.current_transaction).to have_received(:trace).with(1, 2, 3)
67
+ subject.current_transaction = nil
68
+ end
69
+
70
+ it 'ignores when outside transaction' do
71
+ blk = proc {}
72
+ allow(blk).to receive(:call)
73
+ subject.trace { blk.call }
74
+ expect(blk).to have_received(:call)
75
+ end
76
+ end
77
+
78
+ describe '#submit_transaction' do
79
+ it "doesn't send right away" do
80
+ transaction = Transaction.new(subject, 'test')
81
+
82
+ subject.submit_transaction transaction
83
+
84
+ expect(subject.queue.length).to be 0
85
+ expect(WebMock).to_not have_requested(:post, %r{/transactions/$})
86
+ end
87
+
88
+ it "sends if it's long enough ago that we sent last" do
89
+ transaction = Transaction.new(subject, 'test')
90
+ subject.instance_variable_set :@last_sent_transactions, Time.now.utc - 61
91
+
92
+ subject.submit_transaction transaction
93
+
94
+ expect(subject.queue.length).to be 1
95
+ expect(subject.queue.pop).to be_a Worker::PostRequest
96
+ end
97
+
98
+ it 'sends if interval is disabled' do
99
+ transaction = Transaction.new(subject, 'test')
100
+ subject.config.transaction_post_interval = nil
101
+ subject.submit_transaction transaction
102
+ expect(subject.queue.length).to be 1
103
+ end
104
+ end
105
+
106
+ describe '#set_context' do
107
+ it 'sets context for future errors' do
108
+ subject.set_context(tags: { additional_information: 'remember me' })
109
+
110
+ exception = Exception.new('BOOM')
111
+ subject.report exception
112
+
113
+ expect(subject.queue.length).to be 1
114
+ expect(subject.queue.pop.data[:tags][:additional_information]).to eq('remember me')
115
+ end
116
+ end
117
+
118
+ describe '#with_context' do
119
+ it 'sets context for future errors' do
120
+ subject.with_context(tags: { additional_information: 'remember me' }) do
121
+ exception = Exception.new('BOOM')
122
+ subject.report exception
123
+ end
124
+
125
+ expect(subject.queue.length).to be 1
126
+ expect(subject.queue.pop.data[:tags][:additional_information]).to eq('remember me')
127
+ end
128
+
129
+ it 'supports nested contexts' do
130
+ subject.with_context(values: { info: 'a' }) do
131
+ subject.with_context(values: { more_info: 'b' }) do
132
+ exception = Exception.new('BOOM')
133
+ subject.report exception
134
+ end
135
+ end
136
+
137
+ expect(subject.queue.length).to be 1
138
+ data = subject.queue.pop.data
139
+ expect(data[:values][:info]).to eq('a')
140
+ expect(data[:values][:more_info]).to eq('b')
141
+ end
142
+
143
+ it 'restores context for future errors' do
144
+ subject.set_context(values: { info: 'hello' })
145
+
146
+ subject.with_context(values: { additional_information: 'remember me' }) do
147
+ end
148
+
149
+ exception = Exception.new('BOOM')
150
+ subject.report exception
151
+
152
+ expect(subject.queue.length).to be 1
153
+ data = subject.queue.pop.data
154
+ expect(data[:values][:info]).to eq('hello')
155
+ expect(data[:values][:additional_information]).not_to be
156
+ end
157
+
158
+ it 'returns what is yielded' do
159
+ result = subject.with_context(additional_information: 'remember me') do
160
+ 42
161
+ end
162
+
163
+ expect(result).to be 42
164
+ end
165
+ end
166
+
167
+ describe '#report' do
168
+ it 'builds and posts an exception' do
169
+ exception = Exception.new('BOOM')
170
+
171
+ subject.report exception
172
+
173
+ expect(subject.queue.length).to be 1
174
+ expect(subject.queue.pop).to be_a Worker::PostRequest
175
+ end
176
+
177
+ it 'skips nil exceptions' do
178
+ subject.report nil
179
+ expect(WebMock).to_not have_requested(:post, %r{/errors/$})
180
+ end
181
+ end
182
+
183
+ describe '#report_message' do
184
+ it 'builds and posts an exception' do
185
+ subject.report_message 'Massage message'
186
+
187
+ expect(subject.queue.length).to be 1
188
+ expect(subject.queue.pop).to be_a Worker::PostRequest
189
+ end
190
+ end
191
+
192
+ describe '#capture' do
193
+ it 'captures exceptions and sends them off then raises them again' do
194
+ exception = Exception.new('BOOM')
195
+
196
+ expect do
197
+ subject.capture do
198
+ raise exception
199
+ end
200
+ end.to raise_exception(Exception)
201
+
202
+ expect(subject.queue.length).to be 1
203
+ expect(subject.queue.pop).to be_a Worker::PostRequest
204
+ end
205
+ end
206
+ end
207
+
208
+ context 'with performance disabled' do
209
+ subject do
210
+ InfluxReporter::Client.inst
211
+ end
212
+
213
+ before do
214
+ config.disable_performance = true
215
+ InfluxReporter.start! config
216
+ end
217
+ after { InfluxReporter.stop! }
218
+
219
+ describe '#transaction' do
220
+ it 'yields' do
221
+ block = -> {}
222
+ expect(block).to receive(:call)
223
+ Client.inst.transaction('Test') { block.call }
224
+ end
225
+ it 'returns nil' do
226
+ expect(Client.inst.transaction('Test')).to be_nil
227
+ end
228
+ end
229
+
230
+ describe '#trace' do
231
+ it 'yields' do
232
+ block = -> {}
233
+ expect(block).to receive(:call)
234
+ Client.inst.trace('Test', 'trace') { block.call }
235
+ end
236
+ it 'returns nil' do
237
+ expect(Client.inst.trace('Test', 'test')).to be_nil
238
+ end
239
+ end
240
+ end
241
+
242
+ context 'with errors disabled' do
243
+ subject do
244
+ InfluxReporter::Client.inst
245
+ end
246
+
247
+ before do
248
+ config.disable_errors = true
249
+ InfluxReporter.start! config
250
+ end
251
+ after { InfluxReporter.stop! }
252
+
253
+ describe '#report' do
254
+ it "doesn't do anything" do
255
+ exception = Exception.new('BOOM')
256
+
257
+ Client.inst.report exception
258
+
259
+ expect(Client.inst.queue.length).to be 0
260
+ end
261
+ end
262
+ end
263
+ end
264
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ module InfluxReporter
6
+ RSpec.describe Configuration do
7
+ it 'has defaults' do
8
+ conf = Configuration.new
9
+ expect(conf.timeout).to be 100
10
+ expect(conf.filter_parameters).to eq [/(authorization|password|passwd|secret)/i]
11
+ end
12
+
13
+ it 'overwrites defaults when config given' do
14
+ conf = Configuration.new(filter_parameters: [:secret])
15
+ expect(conf.timeout).to be 100
16
+ expect(conf.filter_parameters).to eq [:secret]
17
+ end
18
+
19
+ it 'can initialize with a hash' do
20
+ conf = Configuration.new timeout: 1000
21
+ expect(conf.timeout).to be 1000
22
+ end
23
+
24
+ it 'yields itself to a given block' do
25
+ conf = Configuration.new do |c|
26
+ c.timeout = 1000
27
+ end
28
+ expect(conf.timeout).to be 1000
29
+ end
30
+
31
+ describe '#validate' do
32
+ let(:auth_opts) { { database: 'endpoints' } }
33
+ it 'is true when all auth options are set' do
34
+ expect(Configuration.new(auth_opts).validate!).to be true
35
+ end
36
+
37
+ it 'is true' do
38
+ expect(Configuration.new(auth_opts).validate!).to be true
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ module InfluxReporter
6
+ module DataBuilders
7
+ RSpec.describe Error do
8
+ let(:config) { Configuration.new }
9
+
10
+ subject do
11
+ Error.new config
12
+ end
13
+
14
+ def real_exception
15
+ 1 / 0
16
+ rescue ZeroDivisionError => e
17
+ e
18
+ end
19
+
20
+ describe '#build' do
21
+ it 'builds an error dict from an exception' do
22
+ error_message = ErrorMessage.from_exception config, real_exception
23
+ example = {
24
+ series: 'errors',
25
+ values: {
26
+ message: 'ZeroDivisionError: divided by 0',
27
+ culprit: "influx_reporter/data_builders/error_spec.rb:15:in `/'"
28
+ },
29
+ tags: {
30
+ level: :error,
31
+ excpetion: 'ZeroDivisionError'
32
+ },
33
+ timestamp: an_instance_of(Integer)
34
+ }
35
+ expect(subject.build(error_message)).to match(example)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ module InfluxReporter
6
+ module DataBuilders
7
+ RSpec.describe Transactions, mock_time: true, start_without_worker: true do
8
+ describe '#build' do
9
+ subject do
10
+ transaction1 = Transaction.new(nil, 'endpoint', 'special.kind')
11
+ transaction2 = Transaction.new(nil, 'endpoint', 'special.kind')
12
+ transaction3 = Transaction.new(nil, 'endpoint', 'special.kind')
13
+ travel 100
14
+ transaction1.done 200
15
+ transaction2.done 200
16
+ transaction3.done 500
17
+
18
+ transaction4 = InfluxReporter.transaction('endpoint', 'special.kind.foo') do
19
+ travel 100
20
+ InfluxReporter.trace 'things' do
21
+ travel 100
22
+ end
23
+ InfluxReporter.trace 'things', 'db.custom.sql' do
24
+ travel 100
25
+ end
26
+ end.done(500)
27
+
28
+ transactions = [transaction1, transaction2, transaction3, transaction4]
29
+
30
+ DataBuilders::Transactions.new(Configuration.new).build transactions
31
+ end
32
+
33
+ it 'should create proper series names' do
34
+ data = subject
35
+ expect(data.length).to be 6
36
+ expect(data.map { |t| t[:series] }.uniq).to eq %w[special.kind trace.code trace.db]
37
+ end
38
+
39
+ it 'combines transactions by result' do
40
+ data = subject
41
+ expect(data.length).to be 6
42
+ expect(data.map { |t| t[:tags][:result] }.compact).to eq [200, 200, 500, 500]
43
+ expect(data.map { |t| t[:values][:duration] }.flatten).to eq [100.0, 100.0, 100.0, 300.0, 100.0, 100.0]
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ module InfluxReporter
6
+ RSpec.describe ErrorMessage::Exception do
7
+ class ::Thing
8
+ class Error < StandardError
9
+ end
10
+ end
11
+
12
+ describe '.from' do
13
+ it 'initializes an object from an actual exception' do
14
+ exception = Thing::Error.new 'BOOM'
15
+ obj = ErrorMessage::Exception.from(exception)
16
+ expect(obj.type).to eq 'Thing::Error'
17
+ expect(obj.value).to eq 'BOOM'
18
+ expect(obj.module).to eq 'Thing'
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ module InfluxReporter
6
+ RSpec.describe ErrorMessage::HTTP do
7
+ describe '.from_rack_env' do
8
+ let(:config) { Configuration.new }
9
+
10
+ it 'initializes with a rack env' do
11
+ filter = Filter.new config
12
+
13
+ env = Rack::MockRequest.env_for '/nested/path?a=1&password=SECRET', 'HTTP_COOKIE' => 'user_id=1',
14
+ 'REMOTE_ADDR' => '1.2.3.4',
15
+ 'HTTP_USER_AGENT' => 'test-agent 1.2/3',
16
+ 'HTTP_FOO' => 'bar'
17
+
18
+ http = ErrorMessage::HTTP.from_rack_env env, filter: filter
19
+
20
+ expect(http.url).to eq 'http://example.org/nested/path'
21
+ expect(http.method).to eq 'GET'
22
+ expect(http.query_string).to eq 'a=1&password=[FILTERED]'
23
+ expect(http.cookies).to eq('user_id=1')
24
+ expect(http.remote_host).to eq '1.2.3.4'
25
+ expect(http.http_host).to eq 'example.org:80'
26
+ expect(http.user_agent).to eq 'test-agent 1.2/3'
27
+
28
+ expect(http.headers).to eq('Cookie' => 'user_id=1',
29
+ 'User-Agent' => 'test-agent 1.2/3',
30
+ 'Foo' => 'bar')
31
+ expect(http.env).to eq(env.reject do |k, _v|
32
+ k.match(/(^HTTP_|[a-z])/) # starting with HTTP_ or lower case
33
+ end)
34
+ end
35
+
36
+ it 'adds form data' do
37
+ env = Rack::MockRequest.env_for '/', 'REQUEST_METHOD' => 'POST',
38
+ input: 'thing=hotdog&accept=1'
39
+ http = ErrorMessage::HTTP.from_rack_env env
40
+
41
+ expect(http.data).to eq('thing' => 'hotdog',
42
+ 'accept' => '1')
43
+ end
44
+
45
+ it 'adds body' do
46
+ env = Rack::MockRequest.env_for '/', 'REQUEST_METHOD' => 'POST',
47
+ 'CONTENT_TYPE' => 'application/json',
48
+ input: { thing: 'hotdog' }.to_json
49
+ http = ErrorMessage::HTTP.from_rack_env env
50
+
51
+ expect(http.data).to eq('{"thing":"hotdog"}')
52
+ end
53
+ end
54
+ end
55
+ end