asklytics-influxdb-rails 1.0.0.beta3

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 (69) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +78 -0
  5. data/.travis.yml +37 -0
  6. data/CHANGELOG.md +127 -0
  7. data/Gemfile +9 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +291 -0
  10. data/Rakefile +34 -0
  11. data/config.ru +7 -0
  12. data/gemfiles/Gemfile.rails-4.2.x +7 -0
  13. data/gemfiles/Gemfile.rails-5.0.x +7 -0
  14. data/gemfiles/Gemfile.rails-5.1.x +7 -0
  15. data/gemfiles/Gemfile.rails-5.2.x +7 -0
  16. data/influxdb-rails.gemspec +52 -0
  17. data/lib/httplog.rb +8 -0
  18. data/lib/influxdb-rails.rb +134 -0
  19. data/lib/influxdb/rails/air_traffic_controller.rb +41 -0
  20. data/lib/influxdb/rails/backtrace.rb +44 -0
  21. data/lib/influxdb/rails/configuration.rb +263 -0
  22. data/lib/influxdb/rails/context.rb +42 -0
  23. data/lib/influxdb/rails/exception_presenter.rb +94 -0
  24. data/lib/influxdb/rails/httplog/adapters/ethon.rb +62 -0
  25. data/lib/influxdb/rails/httplog/adapters/excon.rb +67 -0
  26. data/lib/influxdb/rails/httplog/adapters/http.rb +64 -0
  27. data/lib/influxdb/rails/httplog/adapters/httpclient.rb +76 -0
  28. data/lib/influxdb/rails/httplog/adapters/net_http.rb +53 -0
  29. data/lib/influxdb/rails/httplog/adapters/patron.rb +44 -0
  30. data/lib/influxdb/rails/httplog/helpers/al_helper.rb +12 -0
  31. data/lib/influxdb/rails/httplog/http_configuration.rb +55 -0
  32. data/lib/influxdb/rails/httplog/http_log.rb +332 -0
  33. data/lib/influxdb/rails/instrumentation.rb +34 -0
  34. data/lib/influxdb/rails/logger.rb +16 -0
  35. data/lib/influxdb/rails/middleware/hijack_render_exception.rb +16 -0
  36. data/lib/influxdb/rails/middleware/hijack_rescue_action_everywhere.rb +31 -0
  37. data/lib/influxdb/rails/middleware/render_subscriber.rb +28 -0
  38. data/lib/influxdb/rails/middleware/request_subscriber.rb +69 -0
  39. data/lib/influxdb/rails/middleware/simple_subscriber.rb +71 -0
  40. data/lib/influxdb/rails/middleware/sql_subscriber.rb +35 -0
  41. data/lib/influxdb/rails/middleware/subscriber.rb +44 -0
  42. data/lib/influxdb/rails/rack.rb +39 -0
  43. data/lib/influxdb/rails/railtie.rb +52 -0
  44. data/lib/influxdb/rails/sql/normalizer.rb +27 -0
  45. data/lib/influxdb/rails/sql/query.rb +32 -0
  46. data/lib/influxdb/rails/version.rb +5 -0
  47. data/lib/rails/generators/influxdb/influxdb_generator.rb +15 -0
  48. data/lib/rails/generators/influxdb/templates/initializer.rb +11 -0
  49. data/spec/controllers/widgets_controller_spec.rb +15 -0
  50. data/spec/integration/exceptions_spec.rb +37 -0
  51. data/spec/integration/integration_helper.rb +1 -0
  52. data/spec/integration/metrics_spec.rb +28 -0
  53. data/spec/shared_examples/tags.rb +42 -0
  54. data/spec/spec_helper.rb +31 -0
  55. data/spec/support/rails4/app.rb +44 -0
  56. data/spec/support/rails5/app.rb +44 -0
  57. data/spec/support/views/widgets/_item.html.erb +1 -0
  58. data/spec/support/views/widgets/index.html.erb +5 -0
  59. data/spec/unit/backtrace_spec.rb +85 -0
  60. data/spec/unit/configuration_spec.rb +125 -0
  61. data/spec/unit/context_spec.rb +40 -0
  62. data/spec/unit/exception_presenter_spec.rb +23 -0
  63. data/spec/unit/influxdb_rails_spec.rb +78 -0
  64. data/spec/unit/middleware/render_subscriber_spec.rb +92 -0
  65. data/spec/unit/middleware/request_subscriber_spec.rb +91 -0
  66. data/spec/unit/middleware/sql_subscriber_spec.rb +81 -0
  67. data/spec/unit/sql/normalizer_spec.rb +15 -0
  68. data/spec/unit/sql/query_spec.rb +29 -0
  69. metadata +487 -0
@@ -0,0 +1,40 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe InfluxDB::Rails::Context do
4
+ subject { described_class.new }
5
+
6
+ describe "#controller" do
7
+ it "does set and get" do
8
+ subject.controller = "Controller"
9
+ expect(subject.controller).to eq("Controller")
10
+ end
11
+ end
12
+
13
+ describe "#action" do
14
+ it "does get and set" do
15
+ subject.action = "action"
16
+ expect(subject.action).to eq("action")
17
+ end
18
+ end
19
+
20
+ describe "#location" do
21
+ before do
22
+ subject.controller = "Controller"
23
+ subject.action = "action"
24
+ end
25
+
26
+ it { expect(subject.location).to eq("Controller#action") }
27
+ end
28
+
29
+ describe "#reset" do
30
+ before do
31
+ subject.controller = "Controller"
32
+ subject.action = "action"
33
+ end
34
+
35
+ it "does reset the location" do
36
+ subject.reset
37
+ expect(subject.location).to be_empty
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,23 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe InfluxDB::Rails::ExceptionPresenter do
4
+ before do
5
+ begin
6
+ 1 / 0
7
+ rescue ZeroDivisionError => e
8
+ @exception = e
9
+ end
10
+ end
11
+
12
+ describe ".new" do
13
+ it "should create a new ExceptionPresenter" do
14
+ exception_presenter = InfluxDB::Rails::ExceptionPresenter.new(@exception)
15
+ expect(exception_presenter).to be_a(InfluxDB::Rails::ExceptionPresenter)
16
+ end
17
+
18
+ it "should accept an exception as a parameter" do
19
+ exception_presenter = InfluxDB::Rails::ExceptionPresenter.new(@exception)
20
+ expect(exception_presenter).not_to be_nil
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,78 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe InfluxDB::Rails do
4
+ before do
5
+ InfluxDB::Rails.configure do |config|
6
+ config.application_name = "my-rails-app"
7
+ config.ignored_environments = []
8
+ config.time_precision = "ms"
9
+ end
10
+ end
11
+
12
+ describe ".current_timestamp" do
13
+ let(:timestamp) { 1_513_009_229_111 }
14
+
15
+ it "should return the current timestamp in the configured precision" do
16
+ expect(Process).to receive(:clock_gettime)
17
+ .with(Process::CLOCK_REALTIME, :millisecond)
18
+ .and_return(timestamp)
19
+
20
+ expect(InfluxDB::Rails.current_timestamp).to eq(timestamp)
21
+ end
22
+ end
23
+
24
+ describe ".ignorable_exception?" do
25
+ it "should be true for exception types specified in the configuration" do
26
+ class DummyException < RuntimeError; end
27
+ exception = DummyException.new
28
+
29
+ InfluxDB::Rails.configure do |config|
30
+ config.ignored_exceptions << "DummyException"
31
+ end
32
+
33
+ expect(InfluxDB::Rails.ignorable_exception?(exception)).to be_truthy
34
+ end
35
+
36
+ it "should be true for exception types specified in the configuration" do
37
+ exception = ActionController::RoutingError.new("foo")
38
+ expect(InfluxDB::Rails.ignorable_exception?(exception)).to be_truthy
39
+ end
40
+
41
+ it "should be false for valid exceptions" do
42
+ exception = ZeroDivisionError.new
43
+ expect(InfluxDB::Rails.ignorable_exception?(exception)).to be_falsey
44
+ end
45
+ end
46
+
47
+ describe "rescue" do
48
+ it "should transmit an exception when passed" do
49
+ expect(InfluxDB::Rails.client).to receive(:write_point)
50
+
51
+ InfluxDB::Rails.rescue do
52
+ raise ArgumentError, "wrong"
53
+ end
54
+ end
55
+
56
+ it "should also raise the exception when in an ignored environment" do
57
+ InfluxDB::Rails.configure do |config|
58
+ config.ignored_environments = %w[development test]
59
+ end
60
+
61
+ expect do
62
+ InfluxDB::Rails.rescue do
63
+ raise ArgumentError, "wrong"
64
+ end
65
+ end.to raise_error(ArgumentError)
66
+ end
67
+ end
68
+
69
+ describe "rescue_and_reraise" do
70
+ it "should transmit an exception when passed" do
71
+ expect(InfluxDB::Rails.client).to receive(:write_point)
72
+
73
+ expect do
74
+ InfluxDB::Rails.rescue_and_reraise { raise ArgumentError, "wrong" }
75
+ end.to raise_error(ArgumentError)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,92 @@
1
+ require "spec_helper"
2
+ require "shared_examples/tags"
3
+
4
+ RSpec.describe InfluxDB::Rails::Middleware::RenderSubscriber do
5
+ let(:config) { InfluxDB::Rails::Configuration.new }
6
+ let(:logger) { double(:logger) }
7
+
8
+ before do
9
+ allow(config).to receive(:application_name).and_return("my-rails-app")
10
+ allow(config).to receive(:ignored_environments).and_return([])
11
+ allow(config).to receive(:time_precision).and_return("ms")
12
+ end
13
+
14
+ describe ".call" do
15
+ let(:start) { Time.at(1_517_567_368) }
16
+ let(:finish) { Time.at(1_517_567_370) }
17
+ let(:series_name) { "series_name" }
18
+ let(:payload) { { identifier: "index.html", count: 43, cache_hits: 42 } }
19
+ let(:data) do
20
+ {
21
+ values: {
22
+ value: 2000,
23
+ count: 43,
24
+ cache_hits: 42
25
+ },
26
+ tags: {
27
+ filename: "index.html",
28
+ location: "Foo#bar",
29
+ },
30
+ timestamp: 1_517_567_370_000
31
+ }
32
+ end
33
+
34
+ subject { described_class.new(config, series_name) }
35
+
36
+ before do
37
+ InfluxDB::Rails.current.controller = "Foo"
38
+ InfluxDB::Rails.current.action = "bar"
39
+ end
40
+
41
+ after do
42
+ InfluxDB::Rails.current.reset
43
+ end
44
+
45
+ context "successfully" do
46
+ it "writes to InfluxDB" do
47
+ expect_any_instance_of(InfluxDB::Client).to receive(:write_point).with(
48
+ series_name, data
49
+ )
50
+ subject.call("name", start, finish, "id", payload)
51
+ end
52
+
53
+ it_behaves_like "with additional tags", ["series_name"]
54
+
55
+ context "with an empty value" do
56
+ before do
57
+ payload[:count] = nil
58
+ data[:values].delete(:count)
59
+ end
60
+
61
+ it "does not write empty value" do
62
+ expect_any_instance_of(InfluxDB::Client).to receive(:write_point).with(
63
+ series_name, data
64
+ )
65
+ subject.call("name", start, finish, "id", payload)
66
+ end
67
+ end
68
+
69
+ context "disabled" do
70
+ subject { described_class.new(config, nil) }
71
+
72
+ it "does not write a data point" do
73
+ expect_any_instance_of(InfluxDB::Client).not_to receive(:write_point)
74
+ subject.call("name", start, finish, "id", payload)
75
+ end
76
+ end
77
+ end
78
+
79
+ context "unsuccessfully" do
80
+ before do
81
+ allow(config).to receive(:logger).and_return(logger)
82
+ InfluxDB::Rails.configuration = config
83
+ end
84
+
85
+ it "does log exceptions" do
86
+ allow_any_instance_of(InfluxDB::Client).to receive(:write_point).and_raise("boom")
87
+ expect(logger).to receive(:error).with(/boom/)
88
+ subject.call("name", start, finish, "id", payload)
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,91 @@
1
+ require "spec_helper"
2
+ require "shared_examples/tags"
3
+
4
+ RSpec.describe InfluxDB::Rails::Middleware::RequestSubscriber do
5
+ let(:config) { InfluxDB::Rails::Configuration.new }
6
+
7
+ before do
8
+ allow(config).to receive(:time_precision).and_return("ms")
9
+ end
10
+
11
+ subject { described_class.new(config) }
12
+
13
+ describe "#call" do
14
+ let(:start) { Time.at(1_517_567_368) }
15
+ let(:finish) { Time.at(1_517_567_370) }
16
+ let(:payload) { { view_runtime: 2, db_runtime: 2, controller: "MyController", action: "show", method: "GET", format: "*/*", status: 200 } }
17
+ let(:data) do
18
+ {
19
+ values: {
20
+ value: 2
21
+ },
22
+ tags: {
23
+ method: "MyController#show",
24
+ status: 200,
25
+ format: "*/*",
26
+ http_method: "GET",
27
+ server: Socket.gethostname,
28
+ app_name: "my-rails-app",
29
+ },
30
+ timestamp: 1_517_567_370_000
31
+ }
32
+ end
33
+
34
+ context "application_name is set" do
35
+ before do
36
+ allow(config).to receive(:application_name).and_return("my-rails-app")
37
+ end
38
+
39
+ it "sends metrics with taggings and timestamps" do
40
+ expect_any_instance_of(InfluxDB::Client).to receive(:write_point).with(
41
+ "rails.controller", data.merge(values: { value: 2000 })
42
+ )
43
+ expect_any_instance_of(InfluxDB::Client).to receive(:write_point).with("rails.view", data)
44
+ expect_any_instance_of(InfluxDB::Client).to receive(:write_point).with("rails.db", data)
45
+
46
+ subject.call("unused", start, finish, "unused", payload)
47
+ end
48
+
49
+ it_behaves_like "with additional tags", ["rails.controller", "rails.view", "rails.db"]
50
+ end
51
+
52
+ context "application_name is nil" do
53
+ before do
54
+ allow(config).to receive(:application_name).and_return(nil)
55
+ end
56
+
57
+ it "does not add the app_name tag to metrics" do
58
+ tags = {
59
+ method: "MyController#show",
60
+ status: 200,
61
+ format: "*/*",
62
+ http_method: "GET",
63
+ server: Socket.gethostname,
64
+ }
65
+
66
+ expect_any_instance_of(InfluxDB::Client).to receive(:write_point).with(
67
+ "rails.controller", data.merge(values: { value: 2000 }, tags: tags)
68
+ )
69
+ expect_any_instance_of(InfluxDB::Client).to receive(:write_point).with("rails.view", data.merge(tags: tags))
70
+ expect_any_instance_of(InfluxDB::Client).to receive(:write_point).with("rails.db", data.merge(tags: tags))
71
+
72
+ subject.call("unused", start, finish, "unused", payload)
73
+ end
74
+ end
75
+
76
+ context "not successfull" do
77
+ let(:logger) { double(:logger) }
78
+
79
+ before do
80
+ allow(config).to receive(:logger).and_return(logger)
81
+ InfluxDB::Rails.configuration = config
82
+ end
83
+
84
+ it "does log an error" do
85
+ allow_any_instance_of(InfluxDB::Client).to receive(:write_point).and_raise("boom")
86
+ expect(logger).to receive(:error).with(/boom/)
87
+ subject.call("name", start, finish, "id", payload)
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,81 @@
1
+ require "spec_helper"
2
+ require "shared_examples/tags"
3
+
4
+ RSpec.describe InfluxDB::Rails::Middleware::SqlSubscriber do
5
+ let(:config) { InfluxDB::Rails::Configuration.new }
6
+ let(:logger) { double(:logger) }
7
+
8
+ before do
9
+ allow(config).to receive(:application_name).and_return("my-rails-app")
10
+ allow(config).to receive(:ignored_environments).and_return([])
11
+ allow(config).to receive(:time_precision).and_return("ms")
12
+ end
13
+
14
+ describe ".call" do
15
+ let(:start) { Time.at(1_517_567_368) }
16
+ let(:finish) { Time.at(1_517_567_370) }
17
+ let(:series_name) { "rails.sql" }
18
+ let(:payload) { { sql: "SELECT * FROM POSTS WHERE id = 1", name: "Post Load", binds: %w[1 2 3] } }
19
+ let(:data) do
20
+ {
21
+ values: {
22
+ value: 2000,
23
+ sql: "SELECT * FROM POSTS WHERE id = xxx"
24
+ },
25
+ tags: {
26
+ location: "Foo#bar",
27
+ operation: "SELECT",
28
+ class_name: "Post",
29
+ name: "Post Load",
30
+ },
31
+ timestamp: 1_517_567_370_000
32
+ }
33
+ end
34
+
35
+ subject { described_class.new(config, series_name) }
36
+
37
+ before do
38
+ InfluxDB::Rails.current.controller = "Foo"
39
+ InfluxDB::Rails.current.action = "bar"
40
+ end
41
+
42
+ after do
43
+ InfluxDB::Rails.current.reset
44
+ end
45
+
46
+ context "successfully" do
47
+ it "writes to InfluxDB" do
48
+ expect_any_instance_of(InfluxDB::Client).to receive(:write_point).with(
49
+ series_name, data
50
+ )
51
+ subject.call("name", start, finish, "id", payload)
52
+ end
53
+
54
+ context "with not relevant queries" do
55
+ before do
56
+ payload[:sql] = "SHOW FULL FIELDS FROM `users`"
57
+ end
58
+
59
+ it "does not write to InfluxDB" do
60
+ expect_any_instance_of(InfluxDB::Client).not_to receive(:write_point)
61
+ subject.call("name", start, finish, "id", payload)
62
+ end
63
+ end
64
+
65
+ it_behaves_like "with additional tags", ["rails.sql"]
66
+ end
67
+
68
+ context "unsuccessfully" do
69
+ before do
70
+ allow(config).to receive(:logger).and_return(logger)
71
+ InfluxDB::Rails.configuration = config
72
+ end
73
+
74
+ it "does log exceptions" do
75
+ allow_any_instance_of(InfluxDB::Client).to receive(:write_point).and_raise("boom")
76
+ expect(logger).to receive(:error).with(/boom/)
77
+ subject.call("name", start, finish, "id", payload)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,15 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe InfluxDB::Rails::Sql::Normalizer do
4
+ describe "#perform" do
5
+ it { expect(described_class.new("SELECT * FROM posts WHERE id = 1").perform).to eq("SELECT * FROM posts WHERE id = xxx") }
6
+ it { expect(described_class.new("SELECT * FROM posts WHERE id = 1".freeze).perform).to eq("SELECT * FROM posts WHERE id = xxx") }
7
+ it { expect(described_class.new("SELECT * FROM posts LIMIT 10").perform).to eq("SELECT * FROM posts LIMIT xxx") }
8
+ it { expect(described_class.new("SELECT * FROM posts OFFSET 10").perform).to eq("SELECT * FROM posts OFFSET xxx") }
9
+ it { expect(described_class.new("SELECT * FROM posts WHERE name LIKE '%foobar%'").perform).to eq("SELECT * FROM posts WHERE name LIKE xxx") }
10
+ it { expect(described_class.new("SELECT * FROM posts WHERE id IN (1,2,3)").perform).to eq("SELECT * FROM posts WHERE id IN (xxx)") }
11
+ it { expect(described_class.new("SELECT * FROM products WHERE price BETWEEN 10 AND 20").perform).to eq("SELECT * FROM products WHERE price BETWEEN xxx AND xxx") }
12
+ it { expect(described_class.new("INSERT INTO products (title, price) VALUES ('Computer', 100)").perform).to eq("INSERT INTO products (title, price) VALUES (xxx)") }
13
+ it { expect(described_class.new(" SELECT * FROM POSTS ").perform).to eq("SELECT * FROM POSTS") }
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe InfluxDB::Rails::Sql::Query do
4
+ let(:payload) do
5
+ {
6
+ sql: "select * from users where user_id = 42;",
7
+ name: "User Load",
8
+ }
9
+ end
10
+ subject { described_class.new(payload) }
11
+
12
+ describe "#class_name" do
13
+ it { expect(subject.class_name).to eq("User") }
14
+ end
15
+
16
+ describe "#operation" do
17
+ it { expect(subject.operation).to eq("SELECT") }
18
+ end
19
+
20
+ describe "#track?" do
21
+ it { expect(described_class.new(sql: "INSERT").track?).to be true }
22
+ it { expect(described_class.new(sql: "UPDATE").track?).to be true }
23
+ it { expect(described_class.new(sql: "SELECT").track?).to be true }
24
+ it { expect(described_class.new(sql: "DELETE").track?).to be true }
25
+ it { expect(described_class.new(sql: "SCHEMA").track?).to be false }
26
+ it { expect(described_class.new(sql: "BEGIN").track?).to be false }
27
+ it { expect(described_class.new(sql: "SELECT", name: "SCHEMA").track?).to be false }
28
+ end
29
+ end