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,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ if defined?(Redis)
6
+ RSpec.describe 'Redis integration', start_without_worker: true do
7
+ let(:redis) { Redis.new }
8
+
9
+ describe '#call' do
10
+ it 'adds a trace to current transaction' do
11
+ transaction = InfluxReporter.transaction 'Redis' do
12
+ redis.lrange('some:where', 0, -1)
13
+ end
14
+
15
+ expect(transaction.traces.length).to be 2
16
+ expect(transaction.traces.last.signature).to eq 'lrange'
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ # :nocov:
6
+ begin
7
+ require 'resque'
8
+ rescue LoadError
9
+ puts 'Skipping Resque specs'
10
+ end
11
+ # :nocov:
12
+
13
+ if defined? Resque
14
+ RSpec.describe 'Resque integration', start_without_worker: true do
15
+ before do
16
+ # using fakeredis
17
+ Resque.redis = Redis.new
18
+
19
+ require 'resque/failure/multiple'
20
+ Resque::Failure::Multiple.classes = [InfluxReporter::Integration::Resque]
21
+ Resque::Failure.backend = Resque::Failure::Multiple
22
+ end
23
+
24
+ class MyWorker
25
+ @queue = :default
26
+
27
+ def self.perform(txt)
28
+ raise Exception, txt
29
+ end
30
+ end
31
+
32
+ it 'captures and reports exceptions' do
33
+ Resque.enqueue MyWorker, 'BOOM'
34
+
35
+ worker = Resque::Worker.new(:default)
36
+ job = worker.reserve
37
+ worker.perform job
38
+
39
+ expect(InfluxReporter::Client.inst.queue.length).to be 1
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ # :nocov:
6
+ begin
7
+ require 'sidekiq'
8
+ rescue LoadError
9
+ puts 'Skipping Sidekiq specs'
10
+ end
11
+ # :nocov:
12
+
13
+ if defined?(Sidekiq)
14
+ require 'sidekiq/testing'
15
+
16
+ Sidekiq::Testing.inline!
17
+ Sidekiq::Testing.server_middleware do |chain|
18
+ chain.add InfluxReporter::Integration::SidekiqException
19
+ end
20
+
21
+ RSpec.describe InfluxReporter::Integration::SidekiqException, start_without_worker: true do
22
+ class MyWorker
23
+ include Sidekiq::Worker
24
+
25
+ def perform(ex)
26
+ raise ex
27
+ end
28
+ end
29
+
30
+ it 'captures and reports exceptions to influx_reporter' do
31
+ exception = Exception.new('BOOM')
32
+
33
+ expect do
34
+ MyWorker.perform_async exception
35
+ end.to raise_error(Exception)
36
+
37
+ expect(InfluxReporter::Client.inst.queue.length).to be 1
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'sinatra'
5
+
6
+ module InfluxReporter
7
+ RSpec.describe 'sinatra integration' do
8
+ include Rack::Test::Methods
9
+
10
+ def config
11
+ @config ||= InfluxReporter::Configuration.new do |c|
12
+ c.disable_worker = true
13
+ end
14
+ end
15
+
16
+ around do |example|
17
+ InfluxReporter.start! config
18
+ example.call
19
+ InfluxReporter.stop!
20
+ end
21
+
22
+ class TestApp < ::Sinatra::Base
23
+ disable :show_exceptions
24
+ use InfluxReporter::Middleware
25
+
26
+ get '/' do
27
+ erb 'I am an inline template!'
28
+ end
29
+
30
+ template :tmpl do
31
+ 'I am a template!'
32
+ end
33
+
34
+ get '/tmpl' do
35
+ erb :tmpl
36
+ end
37
+ end
38
+
39
+ def app
40
+ TestApp
41
+ end
42
+
43
+ it 'wraps routes in transactions' do
44
+ get '/'
45
+
46
+ transaction = InfluxReporter::Client.inst.pending_transactions.last
47
+ expect(transaction.endpoint).to eq 'GET /'
48
+ end
49
+
50
+ it 'traces templates' do
51
+ get '/tmpl'
52
+
53
+ transaction = InfluxReporter::Client.inst.pending_transactions.last
54
+ expect(transaction.traces.last.signature).to eq 'tmpl'
55
+ end
56
+
57
+ it 'traces inline templates' do
58
+ get '/'
59
+
60
+ transaction = InfluxReporter::Client.inst.pending_transactions.last
61
+ expect(transaction.traces.last.signature).to eq 'Inline erb'
62
+ end
63
+ end
64
+
65
+ RSpec.describe 'sinatra integration without perfomance' do
66
+ include Rack::Test::Methods
67
+
68
+ def config
69
+ @config ||= InfluxReporter::Configuration.new do |c|
70
+ c.disable_worker = true
71
+ c.disable_performance = true
72
+ end
73
+ end
74
+
75
+ around do |example|
76
+ InfluxReporter.start! config
77
+ example.call
78
+ InfluxReporter.stop!
79
+ end
80
+
81
+ class TestApp < ::Sinatra::Base
82
+ disable :show_exceptions
83
+ use InfluxReporter::Middleware
84
+
85
+ get '/' do
86
+ erb 'I am an inline template!'
87
+ end
88
+ end
89
+
90
+ def app
91
+ TestApp
92
+ end
93
+
94
+ it 'wraps routes in transactions' do
95
+ get '/'
96
+ expect(InfluxReporter::Client.inst.pending_transactions.last).to be_nil
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ module InfluxReporter
6
+ RSpec.describe LineCache do
7
+ let(:path) { 'some/file.rb' }
8
+ let(:lines) { %w[these are the lines] }
9
+
10
+ describe '.all' do
11
+ it 'returns the lines of the file at path' do
12
+ allow(File).to receive(:readlines) { lines }
13
+ expect(LineCache.all(path)).to eq lines
14
+ end
15
+ it 'rescues any exception' do
16
+ allow(LineCache::CACHE).to receive(:[]) { nil }
17
+ allow(File).to receive(:readlines).and_raise 'BOOM'
18
+ expect(LineCache.all(path)).to eq []
19
+ end
20
+ it 'caches results' do
21
+ allow(File).to receive(:readlines) { lines }.once
22
+ LineCache.all path
23
+ LineCache.all path
24
+ end
25
+ end
26
+
27
+ describe '.find' do
28
+ it 'returns one line of the file at path' do
29
+ allow(File).to receive(:readlines) { lines }.once
30
+ allow(LineCache::CACHE).to receive(:[]) { nil }
31
+ expect(LineCache.find(path, 2)).to eq 'are'
32
+ end
33
+ it 'is nil when less than 1' do
34
+ expect(LineCache.find(path, 0)).to be_nil
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'influx_reporter'
5
+
6
+ module InfluxReporter
7
+ describe Logging do
8
+ class FakeLogger
9
+ def method_missing(name, *args)
10
+ @calls ||= []
11
+ @calls << [name, *args]
12
+ end
13
+ attr_reader :calls
14
+ end
15
+
16
+ let(:logger) { FakeLogger.new }
17
+ let(:config) { Struct.new(:logger).new(logger) }
18
+
19
+ before :each do
20
+ extend Logging
21
+ end
22
+
23
+ %w[fatal error info debug warn].map(&:to_sym).each do |level|
24
+ it "does #{level} with args" do
25
+ send level, 'msg'
26
+ expect(logger.calls.last).to eq [level, '** [InfluxReporter] msg']
27
+ end
28
+ it "does #{level} with block" do
29
+ blck = -> { 'msg' }
30
+ send level, &blck
31
+ expect(logger.calls.last).to eq [level, '** [InfluxReporter] msg']
32
+ end
33
+ end
34
+
35
+ context 'without a backend logger' do
36
+ before do
37
+ config.logger = nil
38
+ end
39
+
40
+ it 'should not error' do
41
+ fatal 'fatalmsg'
42
+ error 'errormsg'
43
+ warn 'warnmsg'
44
+ info 'infomsg'
45
+ debug 'debugmsg'
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'influx_reporter'
5
+
6
+ module InfluxReporter
7
+ describe Middleware, start_without_worker: true do
8
+ it 'surrounds the request in a transaction' do
9
+ app = Middleware.new(lambda do |_env|
10
+ [200, {}, ['']]
11
+ end)
12
+ status, _, body = app.call(Rack::MockRequest.env_for('/'))
13
+ body.close
14
+
15
+ expect(status).to eq 200
16
+ expect(InfluxReporter::Client.inst.pending_transactions.length).to be 1
17
+ expect(InfluxReporter::Client.inst.current_transaction).to be_nil
18
+ end
19
+
20
+ it 'submits on exceptions' do
21
+ app = Middleware.new(lambda do |_env|
22
+ raise Exception, 'BOOM'
23
+ end)
24
+
25
+ expect { app.call(Rack::MockRequest.env_for('/')) }.to raise_error(Exception)
26
+ expect(InfluxReporter::Client.inst.queue.length).to be 1
27
+ expect(InfluxReporter::Client.inst.current_transaction).to be_nil
28
+
29
+ expect(InfluxReporter::Client.inst.queue.length).to be 1
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ module InfluxReporter
6
+ RSpec.describe Normalizers::ActionController do
7
+ let(:config) { Configuration.new payload_tags: %i(geohash), payload_values: %i(uuid) }
8
+ let(:normalizers) { Normalizers.build config }
9
+
10
+ describe Normalizers::ActionController::ProcessAction do
11
+ subject do
12
+ normalizers.normalizer_for('process_action.action_controller')
13
+ end
14
+
15
+ it 'registers' do
16
+ expect(subject).to be_a Normalizers::ActionController::ProcessAction
17
+ end
18
+
19
+ describe '#normalize' do
20
+ it 'normalizes input and updates transaction' do
21
+ transaction = Transaction.new(nil, nil, nil)
22
+ uuid = SecureRandom.uuid
23
+
24
+ result = subject.normalize(transaction, 'process_action.action_controller',
25
+ controller: 'SomeController',
26
+ action: 'index',
27
+ uuid: uuid,
28
+ geohash: 'foo')
29
+
30
+ expect(transaction.endpoint).to eq 'SomeController#index'
31
+ expect(result).to match ['SomeController#index', 'app.controller.action', nil]
32
+ expect(transaction.root_trace.extra).to eq( tags: { geohash: 'foo' }, values: { uuid: uuid } )
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ module InfluxReporter
6
+ RSpec.describe Normalizers::ActionView do
7
+ let(:config) { Configuration.new view_paths: ['/var/www/app/views'] }
8
+ let(:normalizers) { Normalizers.build config }
9
+
10
+ shared_examples_for :a_render_normalizer do |key|
11
+ describe '#normalize' do
12
+ it 'normalizes an unknown template' do
13
+ expect(normalize(key, {})).to eq ['Unknown template', described_class.const_get(:KIND), nil]
14
+ end
15
+
16
+ it 'returns a local template' do
17
+ path = 'somewhere/local.html.erb'
18
+ expect(normalize(key, identifier: path)[0]).to eq 'somewhere/local.html.erb'
19
+ end
20
+
21
+ it 'looks up a template in config.view_paths' do
22
+ path = '/var/www/app/views/users/index.html.erb'
23
+ expect(normalize(key, identifier: path)[0]).to eq 'users/index.html.erb'
24
+ end
25
+
26
+ it 'truncates gem path' do
27
+ path = Gem.path[0] + '/some/template.html.erb'
28
+ expect(normalize(key, identifier: path)[0]).to eq '$GEM_PATH/some/template.html.erb'
29
+ end
30
+
31
+ it 'returns absolute if not found in known dirs' do
32
+ path = '/somewhere/else.html.erb'
33
+ expect(normalize(key, identifier: path)[0]).to eq 'Absolute path'
34
+ end
35
+
36
+ def normalize(key, payload)
37
+ subject.normalize nil, key, payload
38
+ end
39
+ end
40
+ end
41
+
42
+ describe Normalizers::ActionView::RenderTemplate do
43
+ subject do
44
+ normalizers.normalizer_for 'render_template.action_view'
45
+ end
46
+
47
+ it 'registers' do
48
+ expect(subject).to be_a Normalizers::ActionView::RenderTemplate
49
+ end
50
+
51
+ it_should_behave_like :a_render_normalizer
52
+ end
53
+
54
+ describe Normalizers::ActionView::RenderPartial do
55
+ subject do
56
+ normalizers.normalizer_for 'render_partial.action_view'
57
+ end
58
+
59
+ it 'registers' do
60
+ expect(subject).to be_a Normalizers::ActionView::RenderPartial
61
+ end
62
+
63
+ it_should_behave_like :a_render_normalizer
64
+ end
65
+
66
+ describe Normalizers::ActionView::RenderCollection do
67
+ subject do
68
+ normalizers.normalizer_for 'render_collection.action_view'
69
+ end
70
+
71
+ it 'registers' do
72
+ expect(subject).to be_a Normalizers::ActionView::RenderCollection
73
+ end
74
+
75
+ it_should_behave_like :a_render_normalizer
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ module InfluxReporter
6
+ RSpec.describe Normalizers::ActionView do
7
+ let(:config) { Configuration.new view_paths: ['/var/www/app/views'] }
8
+ let(:normalizers) { Normalizers.build config }
9
+
10
+ describe Normalizers::ActiveRecord::SQL do
11
+ subject { normalizers.normalizer_for 'sql.active_record' }
12
+
13
+ it 'registers' do
14
+ expect(subject).to be_a Normalizers::ActiveRecord::SQL
15
+ end
16
+
17
+ describe '#normalize' do
18
+ it 'skips SCHEMA queries' do
19
+ expect(normalize(name: 'SCHEMA')).to be :skip
20
+ end
21
+
22
+ it 'skips CACHE queries' do
23
+ expect(normalize(name: 'CACHE', sql: 'select * from tables')).to be :skip
24
+ end
25
+
26
+ it 'normalizes SELECT queries' do
27
+ sql = 'SELECT "hotdogs".* FROM "hotdogs" WHERE "hotdogs"."topping" = $1 LIMIT 1'
28
+ signature, kind, extra = normalize(name: 'Hotdogs load', sql: sql)
29
+ expect(signature).to eq 'SELECT FROM "hotdogs"'
30
+ expect(kind).to eq 'db.unknown.sql'
31
+ expect(extra[:values]).to eq sql: sql
32
+ end
33
+
34
+ it 'normalizes INSERT queries' do
35
+ sig, = normalize(name: 'Hotdogs create',
36
+ sql: 'insert into "hotdogs" (kind, topping) values ($1, $2)')
37
+ expect(sig).to eq 'INSERT INTO "hotdogs"'
38
+ end
39
+
40
+ it 'normalizes UPDATE queries' do
41
+ sig, = normalize(name: 'Hotdogs update',
42
+ sql: 'update "hotdogs" (topping) values ($1) where id=1')
43
+ expect(sig).to eq 'UPDATE "hotdogs"'
44
+ end
45
+
46
+ it 'normalizes DELETE queries' do
47
+ sig, = normalize(name: 'Hotdogs delete',
48
+ sql: 'delete from "hotdogs" where id=1')
49
+ expect(sig).to eq 'DELETE FROM "hotdogs"'
50
+ end
51
+
52
+ context 'inside AR' do
53
+ before do
54
+ module ::ActiveRecord; class Base; end; end unless defined? ActiveRecord
55
+ allow(::ActiveRecord::Base).to receive(:connection) { double(adapter_name: 'MySQL') }
56
+ end
57
+ it 'knows the ar adapter' do
58
+ _, kind, = normalize(name: 'Hotdogs load',
59
+ sql: 'select * from "hotdogs"')
60
+ expect(kind).to eq 'db.mysql.sql'
61
+ end
62
+ end
63
+
64
+ def normalize(payload)
65
+ subject.normalize nil, 'sql.active_record', payload
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end