influxdb-rails 1.0.0.beta4 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +11 -0
  3. data/.github/workflows/rubocop.yml +18 -0
  4. data/.github/workflows/spec.yml +32 -0
  5. data/.gitignore +1 -0
  6. data/.rubocop.yml +35 -7
  7. data/CHANGELOG.md +40 -3
  8. data/README.md +125 -27
  9. data/Rakefile +0 -6
  10. data/gemfiles/Gemfile.rails-6.0.x +9 -0
  11. data/gemfiles/Gemfile.rails-6.1.x +9 -0
  12. data/influxdb-rails.gemspec +7 -4
  13. data/lib/influxdb-rails.rb +13 -2
  14. data/lib/influxdb/rails/configuration.rb +22 -13
  15. data/lib/influxdb/rails/context.rb +6 -40
  16. data/lib/influxdb/rails/helpers/rspec_matchers.rb +48 -0
  17. data/lib/influxdb/rails/metric.rb +39 -0
  18. data/lib/influxdb/rails/middleware/action_mailer_subscriber.rb +22 -0
  19. data/lib/influxdb/rails/middleware/active_job_subscriber.rb +67 -0
  20. data/lib/influxdb/rails/middleware/active_record_subscriber.rb +26 -0
  21. data/lib/influxdb/rails/middleware/block_instrumentation_subscriber.rb +24 -0
  22. data/lib/influxdb/rails/middleware/render_subscriber.rb +15 -16
  23. data/lib/influxdb/rails/middleware/request_subscriber.rb +16 -21
  24. data/lib/influxdb/rails/middleware/sql_subscriber.rb +18 -18
  25. data/lib/influxdb/rails/middleware/subscriber.rb +40 -27
  26. data/lib/influxdb/rails/railtie.rb +15 -18
  27. data/lib/influxdb/rails/sql/normalizer.rb +3 -3
  28. data/lib/influxdb/rails/tags.rb +33 -0
  29. data/lib/influxdb/rails/test_client.rb +13 -0
  30. data/lib/influxdb/rails/values.rb +24 -0
  31. data/lib/influxdb/rails/version.rb +1 -1
  32. data/lib/rails/generators/influxdb/templates/initializer.rb +4 -0
  33. data/sample-dashboard/Dockerfile +2 -3
  34. data/sample-dashboard/README.md +21 -28
  35. data/sample-dashboard/Rakefile +10 -4
  36. data/sample-dashboard/Ruby On Rails ActiveJob.json +600 -0
  37. data/sample-dashboard/Ruby On Rails Performance per Action.json +1330 -0
  38. data/sample-dashboard/{Ruby On Rails Performance (per Request).json → Ruby On Rails Performance per Request.json } +367 -217
  39. data/sample-dashboard/Ruby On Rails Performance.json +1348 -1110
  40. data/sample-dashboard/Ruby On Rails Requests.json +834 -0
  41. data/sample-dashboard/Ruby On Rails Slowlog by Action.json +278 -0
  42. data/sample-dashboard/Ruby On Rails Slowlog by Request.json +277 -0
  43. data/sample-dashboard/Ruby On Rails Slowlog by SQL.json +328 -0
  44. data/sample-dashboard/docker-compose.yml +7 -1
  45. data/sample-dashboard/provisioning/activejob.json +600 -0
  46. data/sample-dashboard/provisioning/performance-action.json +1330 -0
  47. data/sample-dashboard/provisioning/performance-request.json +361 -211
  48. data/sample-dashboard/provisioning/performance.json +1337 -1099
  49. data/sample-dashboard/provisioning/requests.json +834 -0
  50. data/sample-dashboard/provisioning/slowlog-action.json +278 -0
  51. data/sample-dashboard/provisioning/slowlog-requests.json +277 -0
  52. data/sample-dashboard/provisioning/slowlog-sql.json +328 -0
  53. data/spec/requests/action_controller_metrics_spec.rb +83 -0
  54. data/spec/requests/action_mailer_deliver_metrics_spec.rb +49 -0
  55. data/spec/requests/action_view_collection_metrics_spec.rb +66 -0
  56. data/spec/requests/action_view_partial_metrics_spec.rb +62 -0
  57. data/spec/requests/action_view_template_metrics_spec.rb +62 -0
  58. data/spec/requests/active_job_enqueue_metrics_spec.rb +65 -0
  59. data/spec/requests/active_job_perform_metrics_spec.rb +68 -0
  60. data/spec/requests/active_record_instantiation_metrics_spec.rb +65 -0
  61. data/spec/requests/active_record_sql_metrics_spec.rb +103 -0
  62. data/spec/requests/block_inistrumentation_spec.rb +64 -0
  63. data/spec/requests/context_spec.rb +27 -0
  64. data/spec/requests/logger_spec.rb +10 -0
  65. data/spec/spec_helper.rb +12 -4
  66. data/spec/support/broken_client.rb +11 -0
  67. data/spec/support/rails5/app.rb +48 -13
  68. data/spec/support/rails6/app.rb +83 -0
  69. data/spec/support/views/layouts/mailer.txt.erb +1 -0
  70. data/spec/support/views/{widgets → metrics}/_item.html.erb +0 -0
  71. data/spec/support/views/{widgets → metrics}/index.html.erb +0 -0
  72. data/spec/support/views/metrics/show.html.erb +4 -0
  73. data/spec/unit/block_instrumentation_spec.rb +18 -0
  74. metadata +124 -46
  75. data/.travis.yml +0 -37
  76. data/gemfiles/Gemfile.rails-4.2.x +0 -7
  77. data/gemfiles/Gemfile.rails-5.0.x +0 -7
  78. data/gemfiles/Gemfile.rails-5.1.x +0 -7
  79. data/lib/influxdb/rails/instrumentation.rb +0 -34
  80. data/lib/influxdb/rails/middleware/simple_subscriber.rb +0 -33
  81. data/spec/controllers/widgets_controller_spec.rb +0 -15
  82. data/spec/integration/integration_helper.rb +0 -1
  83. data/spec/integration/metrics_spec.rb +0 -27
  84. data/spec/shared_examples/data.rb +0 -61
  85. data/spec/support/rails4/app.rb +0 -48
  86. data/spec/unit/context_spec.rb +0 -40
  87. data/spec/unit/middleware/render_subscriber_spec.rb +0 -96
  88. data/spec/unit/middleware/request_subscriber_spec.rb +0 -103
  89. data/spec/unit/middleware/sql_subscriber_spec.rb +0 -108
data/.travis.yml DELETED
@@ -1,37 +0,0 @@
1
- sudo: required
2
- dist: trusty
3
- language: ruby
4
- before_install:
5
- - gem update --system --no-doc
6
- - gem install bundler --no-doc
7
- - gem update bundler --no-doc
8
- rvm:
9
- - ruby-head
10
- - "2.6"
11
- - "2.5"
12
- - "2.4"
13
- - "2.3"
14
- gemfile:
15
- - gemfiles/Gemfile.rails-5.2.x
16
- - gemfiles/Gemfile.rails-5.1.x
17
- - gemfiles/Gemfile.rails-5.0.x
18
- - gemfiles/Gemfile.rails-4.2.x
19
- env:
20
- - TEST_TASK=spec
21
- matrix:
22
- allow_failures:
23
- - rvm: ruby-head
24
- include:
25
- - { rvm: "2.6", gemfile: "Gemfile", env: [TEST_TASK=rubocop] }
26
- exclude:
27
- # Rails < 5 not on MRI 2.4+
28
- - { rvm: ruby-head, gemfile: "gemfiles/Gemfile.rails-4.2.x" }
29
- - { rvm: "2.6", gemfile: "gemfiles/Gemfile.rails-4.2.x" }
30
- - { rvm: "2.5", gemfile: "gemfiles/Gemfile.rails-4.2.x" }
31
- - { rvm: "2.4", gemfile: "gemfiles/Gemfile.rails-4.2.x" }
32
- addons:
33
- apt:
34
- packages:
35
- - haveged
36
- - libgmp-dev
37
- script: bundle exec rake $TEST_TASK
@@ -1,7 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gem 'actionpack', '~> 4.2.0'
4
- gem 'activesupport', '~> 4.2.0'
5
- gem 'activemodel', '~> 4.2.0'
6
-
7
- gemspec :path => '../'
@@ -1,7 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gem 'actionpack', '~> 5.0.0'
4
- gem 'activesupport', '~> 5.0.0'
5
- gem 'activemodel', '~> 5.0.0'
6
-
7
- gemspec :path => '../'
@@ -1,7 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- gem 'actionpack', '~> 5.1.0'
4
- gem 'activesupport', '~> 5.1.0'
5
- gem 'activemodel', '~> 5.1.0'
6
-
7
- gemspec :path => '../'
@@ -1,34 +0,0 @@
1
- module InfluxDB
2
- module Rails
3
- module Instrumentation
4
- def benchmark_for_instrumentation # rubocop:disable Metrics/MethodLength
5
- start = Time.now
6
- yield
7
-
8
- c = InfluxDB::Rails.configuration
9
- return if c.ignore_current_environment?
10
-
11
- InfluxDB::Rails.client.write_point \
12
- "instrumentation".freeze,
13
- values: {
14
- value: ((Time.now - start) * 1000).ceil,
15
- },
16
- tags: c.tags_middleware.call(
17
- method: "#{controller_name}##{action_name}",
18
- server: Socket.gethostname
19
- )
20
- end
21
-
22
- def self.included(base)
23
- base.extend(ClassMethods)
24
- end
25
-
26
- module ClassMethods
27
- def instrument(methods = [])
28
- methods = [methods] unless methods.is_a?(Array)
29
- around_filter :benchmark_for_instrumentation, only: methods
30
- end
31
- end
32
- end
33
- end
34
- end
@@ -1,33 +0,0 @@
1
- require "influxdb/rails/middleware/subscriber"
2
-
3
- module InfluxDB
4
- module Rails
5
- module Middleware
6
- # Subscriber acts as base class for different *Subscriber classes,
7
- # which are intended as ActiveSupport::Notifications.subscribe
8
- # consumers.
9
- class SimpleSubscriber < Subscriber
10
- def call(_name, started, finished, _id, payload)
11
- return unless enabled?
12
-
13
- InfluxDB::Rails.client.write_point \
14
- configuration.measurement_name,
15
- values: values(started, finished, payload),
16
- tags: tags(payload),
17
- timestamp: timestamp(finished)
18
- rescue StandardError => e
19
- ::Rails.logger.error("[InfluxDB::Rails] Unable to write points: #{e.message}")
20
- end
21
-
22
- private
23
-
24
- def values(started, finished, _payload)
25
- result = { value: ((finished - started) * 1000).ceil }
26
- result.merge(InfluxDB::Rails.current.values).reject do |_, value|
27
- value.nil? || value == ""
28
- end
29
- end
30
- end
31
- end
32
- end
33
- end
@@ -1,15 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.describe WidgetsController, type: :controller do
4
- describe "#new" do
5
- it "should raise an exception" do
6
- expect { get :new }.to raise_error(ZeroDivisionError)
7
- end
8
- end
9
-
10
- describe "#index" do
11
- it "should not raise an exception" do
12
- expect { get :index }.to_not raise_error
13
- end
14
- end
15
- end
@@ -1 +0,0 @@
1
- require File.dirname(__FILE__) + "/../spec_helper"
@@ -1,27 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + "/integration_helper")
2
-
3
- RSpec.describe "User visits widgets", type: :request do
4
- before do
5
- allow_any_instance_of(InfluxDB::Rails::Configuration).to receive(:ignored_environments).and_return(%w[development])
6
- end
7
-
8
- describe "in a normal request" do
9
- it "should result in attempts to write metrics via the client" do
10
- expect(InfluxDB::Rails.client).to receive(:write_point).exactly(5).times
11
- get "/widgets"
12
- end
13
-
14
- context "additional values" do
15
- it "should result in attempts to write metrics via the client" do
16
- allow_any_instance_of(ActionDispatch::Request).to receive(:request_id).and_return(:request_id)
17
- expect(InfluxDB::Rails.client).to receive(:write_point).with(
18
- "rails", a_hash_including(
19
- tags: a_hash_including(method: "WidgetsController#index", hook: "process_action"),
20
- values: a_hash_including(request_id: :request_id, key: :value)
21
- )
22
- )
23
- get "/widgets"
24
- end
25
- end
26
- end
27
- end
@@ -1,61 +0,0 @@
1
- require "spec_helper"
2
-
3
- RSpec.shared_examples_for "with additional data" do
4
- context "values" do
5
- let(:additional_values) do
6
- { another: :value }
7
- end
8
-
9
- after do
10
- InfluxDB::Rails.current.reset
11
- end
12
-
13
- it "does include the tags" do
14
- InfluxDB::Rails.current.values = additional_values
15
-
16
- expect_any_instance_of(InfluxDB::Client).to receive(:write_point).with(config.measurement_name, hash_including(values: hash_including(another: :value)))
17
-
18
- subject.call("unused", start, finish, "unused", payload)
19
- end
20
- end
21
-
22
- context "tags" do
23
- context "when tags_middleware is overwritten" do
24
- before do
25
- allow(config).to receive(:tags_middleware).and_return(tags_middleware)
26
- end
27
-
28
- let(:tags_middleware) { ->(tags) { tags.merge(static: "value", nil: nil, empty: "") } }
29
-
30
- it "processes tags throught the middleware" do
31
- tags = data[:tags].merge(static: "value")
32
-
33
- expect_any_instance_of(InfluxDB::Client).to receive(:write_point).with(config.measurement_name, include(tags: tags))
34
-
35
- subject.call("unused", start, finish, "unused", payload)
36
- end
37
- end
38
-
39
- context "when tags are set in the current context" do
40
- let(:input) do
41
- { another: :value, nil: nil, empty: "" }
42
- end
43
- let(:output) do
44
- { another: :value }
45
- end
46
-
47
- after do
48
- InfluxDB::Rails.current.reset
49
- end
50
-
51
- it "does include the tags" do
52
- InfluxDB::Rails.current.tags = input
53
- tags = data[:tags].merge(output)
54
-
55
- expect_any_instance_of(InfluxDB::Client).to receive(:write_point).with(config.measurement_name, include(tags: tags))
56
-
57
- subject.call("unused", start, finish, "unused", payload)
58
- end
59
- end
60
- end
61
- end
@@ -1,48 +0,0 @@
1
- require "action_controller/railtie"
2
- require "active_record"
3
-
4
- app = Class.new(Rails::Application)
5
- app.config.secret_key_base = "1234567890abcdef1234567890abcdef"
6
- app.config.secret_token = "1234567890abcdef1234567890abcdef"
7
- app.config.session_store :cookie_store, key: "_myapp_session"
8
- app.config.active_support.deprecation = :log
9
- app.config.eager_load = false
10
- app.config.root = __dir__
11
- Rails.backtrace_cleaner.remove_silencers!
12
- app.initialize!
13
-
14
- app.routes.draw do
15
- resources :widgets
16
- end
17
-
18
- InfluxDB::Rails.configure do |config|
19
- end
20
-
21
- ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
22
- ActiveRecord::Schema.define do
23
- create_table :widgets, force: true do |t|
24
- t.string :title
25
-
26
- t.timestamps
27
- end
28
- end
29
-
30
- class Widget < ActiveRecord::Base; end
31
- class ApplicationController < ActionController::Base; end
32
- class WidgetsController < ApplicationController
33
- prepend_view_path File.join(__dir__, "..", "views")
34
-
35
- before_action do
36
- InfluxDB::Rails.current.values = { key: :value }
37
- end
38
-
39
- def index
40
- Widget.create!(title: "test")
41
- end
42
-
43
- def new
44
- 1 / 0
45
- end
46
- end
47
-
48
- Object.const_set(:ApplicationHelper, Module.new)
@@ -1,40 +0,0 @@
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
@@ -1,96 +0,0 @@
1
- require "spec_helper"
2
- require "shared_examples/data"
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.client).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(:hook_name) { "render_partial.action_view" }
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
- hook: "render_partial",
30
- },
31
- timestamp: 1_517_567_370_000
32
- }
33
- end
34
-
35
- subject { described_class.new(config, hook_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
- config.measurement_name, data
50
- )
51
- subject.call("name", start, finish, "id", payload)
52
- end
53
-
54
- it_behaves_like "with additional data"
55
-
56
- context "with an empty value" do
57
- before do
58
- payload[:count] = nil
59
- data[:values].delete(:count)
60
- end
61
-
62
- it "does not write empty value" do
63
- expect_any_instance_of(InfluxDB::Client).to receive(:write_point).with(
64
- config.measurement_name, data
65
- )
66
- subject.call("name", start, finish, "id", payload)
67
- end
68
- end
69
-
70
- context "disabled" do
71
- before do
72
- allow(config).to receive(:ignored_hooks).and_return(["render_partial.action_view"])
73
- end
74
-
75
- subject { described_class.new(config, nil) }
76
-
77
- it "does not write a data point" do
78
- expect_any_instance_of(InfluxDB::Client).not_to receive(:write_point)
79
- subject.call("name", start, finish, "id", payload)
80
- end
81
- end
82
- end
83
-
84
- context "unsuccessfully" do
85
- before do
86
- InfluxDB::Rails.configuration = config
87
- end
88
-
89
- it "does log exceptions" do
90
- allow_any_instance_of(InfluxDB::Client).to receive(:write_point).and_raise("boom")
91
- expect(::Rails.logger).to receive(:error).with(/boom/)
92
- subject.call("name", start, finish, "id", payload)
93
- end
94
- end
95
- end
96
- end
@@ -1,103 +0,0 @@
1
- require "spec_helper"
2
- require "shared_examples/data"
3
-
4
- RSpec.describe InfluxDB::Rails::Middleware::RequestSubscriber do
5
- let(:config) { InfluxDB::Rails::Configuration.new }
6
-
7
- before do
8
- allow(config.client).to receive(:time_precision).and_return("ms")
9
- allow(config).to receive(:environment).and_return("production")
10
- end
11
-
12
- subject { described_class.new(config, "process_action.action_controller") }
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(:payload) { { view_runtime: 2, db_runtime: 2, controller: "MyController", action: "show", method: "GET", format: "*/*", status: 200 } }
18
- let(:data) do
19
- {
20
- values: {
21
- controller: 2,
22
- started: InfluxDB.convert_timestamp(start.utc, config.client.time_precision),
23
- },
24
- tags: {
25
- method: "MyController#show",
26
- hook: "process_action",
27
- status: 200,
28
- format: "*/*",
29
- http_method: "GET",
30
- server: Socket.gethostname,
31
- app_name: "my-rails-app",
32
- },
33
- timestamp: 1_517_567_370_000
34
- }
35
- end
36
-
37
- context "application_name is set" do
38
- before do
39
- allow(config).to receive(:application_name).and_return("my-rails-app")
40
- end
41
-
42
- it "sends metrics with taggings and timestamps" do
43
- expect_any_instance_of(InfluxDB::Client).to receive(:write_point).with(
44
- config.measurement_name, data.deep_merge(values: { controller: 2000, db: 2, view: 2 })
45
- )
46
-
47
- subject.call("unused", start, finish, "unused", payload)
48
- end
49
-
50
- it_behaves_like "with additional data", ["requests"]
51
- end
52
-
53
- context "application_name is nil" do
54
- let(:tags) do
55
- {
56
- method: "MyController#show",
57
- hook: "process_action",
58
- status: 200,
59
- format: "*/*",
60
- http_method: "GET",
61
- server: Socket.gethostname,
62
- }
63
- end
64
-
65
- before do
66
- allow(config).to receive(:application_name).and_return(nil)
67
- end
68
-
69
- it "does not add the app_name tag to metrics" do
70
- expect_any_instance_of(InfluxDB::Client).to receive(:write_point).with(
71
- config.measurement_name, data.merge(tags: tags).deep_merge(values: { controller: 2000, db: 2, view: 2 })
72
- )
73
-
74
- subject.call("unused", start, finish, "unused", payload)
75
- end
76
- end
77
-
78
- context "not successfull" do
79
- before do
80
- InfluxDB::Rails.configuration = config
81
- end
82
-
83
- it "does log an error" do
84
- allow_any_instance_of(InfluxDB::Client).to receive(:write_point).and_raise("boom")
85
- expect(::Rails.logger).to receive(:error).with(/boom/)
86
- subject.call("name", start, finish, "id", payload)
87
- end
88
- end
89
-
90
- context "disabled" do
91
- before do
92
- allow(config).to receive(:ignored_hooks).and_return(["process_action.action_controller"])
93
- end
94
-
95
- subject { described_class.new(config, "process_action.action_controller") }
96
-
97
- it "does not write a data point" do
98
- expect_any_instance_of(InfluxDB::Client).not_to receive(:write_point)
99
- subject.call("name", start, finish, "id", payload)
100
- end
101
- end
102
- end
103
- end