influxdb-rails 1.0.0.beta2 → 1.0.1.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +11 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +6 -1
  5. data/.travis.yml +5 -12
  6. data/CHANGELOG.md +47 -12
  7. data/README.md +299 -140
  8. data/gemfiles/Gemfile.rails-6.0.x +10 -0
  9. data/influxdb-rails.gemspec +18 -9
  10. data/lib/influxdb-rails.rb +33 -77
  11. data/lib/influxdb/rails/configuration.rb +99 -194
  12. data/lib/influxdb/rails/context.rb +26 -0
  13. data/lib/influxdb/rails/helpers/rspec_matchers.rb +48 -0
  14. data/lib/influxdb/rails/metric.rb +39 -0
  15. data/lib/influxdb/rails/middleware/action_mailer_subscriber.rb +22 -0
  16. data/lib/influxdb/rails/middleware/active_job_subscriber.rb +67 -0
  17. data/lib/influxdb/rails/middleware/active_record_subscriber.rb +26 -0
  18. data/lib/influxdb/rails/middleware/block_instrumentation_subscriber.rb +24 -0
  19. data/lib/influxdb/rails/middleware/render_subscriber.rb +18 -6
  20. data/lib/influxdb/rails/middleware/request_subscriber.rb +24 -31
  21. data/lib/influxdb/rails/middleware/sql_subscriber.rb +18 -13
  22. data/lib/influxdb/rails/middleware/subscriber.rb +45 -15
  23. data/lib/influxdb/rails/railtie.rb +31 -35
  24. data/lib/influxdb/rails/sql/query.rb +5 -3
  25. data/lib/influxdb/rails/tags.rb +33 -0
  26. data/lib/influxdb/rails/test_client.rb +13 -0
  27. data/lib/influxdb/rails/values.rb +24 -0
  28. data/lib/influxdb/rails/version.rb +1 -1
  29. data/lib/rails/generators/influxdb/influxdb_generator.rb +1 -1
  30. data/lib/rails/generators/influxdb/templates/initializer.rb +39 -9
  31. data/sample-dashboard/Dockerfile +24 -0
  32. data/sample-dashboard/README.md +74 -0
  33. data/sample-dashboard/Rakefile +9 -0
  34. data/sample-dashboard/Ruby On Rails Performance (per Action).json +1576 -0
  35. data/sample-dashboard/Ruby On Rails Performance (per Request).json +1053 -0
  36. data/sample-dashboard/Ruby On Rails Performance.json +2041 -0
  37. data/sample-dashboard/docker-compose.yml +34 -0
  38. data/sample-dashboard/provisioning/grafana-dashboards.yml +12 -0
  39. data/sample-dashboard/provisioning/grafana-datasource.yml +10 -0
  40. data/sample-dashboard/provisioning/performance-action.json +1576 -0
  41. data/sample-dashboard/provisioning/performance-request.json +1053 -0
  42. data/sample-dashboard/provisioning/performance.json +2041 -0
  43. data/spec/requests/action_controller_metrics_spec.rb +83 -0
  44. data/spec/requests/action_mailer_deliver_metrics_spec.rb +49 -0
  45. data/spec/requests/action_view_collection_metrics_spec.rb +66 -0
  46. data/spec/requests/action_view_partial_metrics_spec.rb +62 -0
  47. data/spec/requests/action_view_template_metrics_spec.rb +62 -0
  48. data/spec/requests/active_job_enqueue_metrics_spec.rb +65 -0
  49. data/spec/requests/active_job_perform_metrics_spec.rb +68 -0
  50. data/spec/requests/active_job_perform_start_metrics_spec.rb +68 -0
  51. data/spec/requests/active_record_instantiation_metrics_spec.rb +65 -0
  52. data/spec/requests/active_record_sql_metrics_spec.rb +103 -0
  53. data/spec/requests/block_inistrumentation_spec.rb +64 -0
  54. data/spec/requests/context_spec.rb +27 -0
  55. data/spec/requests/logger_spec.rb +10 -0
  56. data/spec/spec_helper.rb +13 -4
  57. data/spec/support/broken_client.rb +11 -0
  58. data/spec/support/rails5/app.rb +49 -9
  59. data/spec/support/rails6/app.rb +84 -0
  60. data/spec/support/views/layouts/mailer.txt.erb +1 -0
  61. data/spec/support/views/{widgets → metrics}/_item.html.erb +0 -0
  62. data/spec/support/views/{widgets → metrics}/index.html.erb +0 -0
  63. data/spec/support/views/metrics/show.html.erb +4 -0
  64. data/spec/unit/block_instrumentation_spec.rb +18 -0
  65. data/spec/unit/configuration_spec.rb +47 -65
  66. data/spec/unit/sql/query_spec.rb +1 -0
  67. metadata +142 -57
  68. data/gemfiles/Gemfile.rails-4.2.x +0 -7
  69. data/gemfiles/Gemfile.rails-5.0.x +0 -7
  70. data/gemfiles/Gemfile.rails-5.1.x +0 -7
  71. data/lib/influxdb/rails/air_traffic_controller.rb +0 -41
  72. data/lib/influxdb/rails/backtrace.rb +0 -44
  73. data/lib/influxdb/rails/exception_presenter.rb +0 -94
  74. data/lib/influxdb/rails/instrumentation.rb +0 -34
  75. data/lib/influxdb/rails/logger.rb +0 -16
  76. data/lib/influxdb/rails/middleware/hijack_render_exception.rb +0 -16
  77. data/lib/influxdb/rails/middleware/hijack_rescue_action_everywhere.rb +0 -31
  78. data/lib/influxdb/rails/middleware/simple_subscriber.rb +0 -46
  79. data/lib/influxdb/rails/rack.rb +0 -24
  80. data/spec/controllers/widgets_controller_spec.rb +0 -15
  81. data/spec/integration/exceptions_spec.rb +0 -37
  82. data/spec/integration/integration_helper.rb +0 -1
  83. data/spec/integration/metrics_spec.rb +0 -28
  84. data/spec/support/rails4/app.rb +0 -44
  85. data/spec/unit/backtrace_spec.rb +0 -85
  86. data/spec/unit/exception_presenter_spec.rb +0 -23
  87. data/spec/unit/influxdb_rails_spec.rb +0 -78
  88. data/spec/unit/middleware/render_subscriber_spec.rb +0 -90
  89. data/spec/unit/middleware/request_subscriber_spec.rb +0 -106
  90. data/spec/unit/middleware/sql_subscriber_spec.rb +0 -79
@@ -0,0 +1,26 @@
1
+ module InfluxDB
2
+ module Rails
3
+ class Context
4
+ def reset
5
+ Thread.current[:_influxdb_rails_tags] = {}
6
+ Thread.current[:_influxdb_rails_values] = {}
7
+ end
8
+
9
+ def tags
10
+ Thread.current[:_influxdb_rails_tags].to_h
11
+ end
12
+
13
+ def tags=(tags)
14
+ Thread.current[:_influxdb_rails_tags] = self.tags.merge(tags)
15
+ end
16
+
17
+ def values
18
+ Thread.current[:_influxdb_rails_values].to_h
19
+ end
20
+
21
+ def values=(values)
22
+ Thread.current[:_influxdb_rails_values] = self.values.merge(values)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,48 @@
1
+ require_relative "../test_client"
2
+ require "launchy"
3
+
4
+ module InfluxDB
5
+ module Rails
6
+ module Matchers
7
+ def expect_metric(name: "rails", **options)
8
+ expect(metrics).to include(
9
+ a_hash_including(options.merge(name: name))
10
+ )
11
+ end
12
+
13
+ def expect_no_metric(name: "rails", **options)
14
+ expect(metrics).not_to include(
15
+ a_hash_including(options.merge(name: name))
16
+ )
17
+ end
18
+
19
+ def save_and_open_metrics
20
+ dir = File.join(File.dirname(__FILE__), "..", "..", "tmp")
21
+ FileUtils.mkdir_p(dir)
22
+ file_path = File.join(dir, "metrics.json")
23
+ output = JSON.pretty_generate(metrics)
24
+ File.write(file_path, output, mode: "wb")
25
+ ::Launchy.open(file_path)
26
+ end
27
+
28
+ def metrics
29
+ TestClient.metrics
30
+ end
31
+
32
+ RSpec.configure do |config|
33
+ config.before :each do
34
+ InfluxDB::Rails.instance_variable_set :@configuration, nil
35
+ InfluxDB::Rails.configure
36
+
37
+ allow(InfluxDB::Rails).to receive(:client).and_return(InfluxDB::Rails::TestClient.new)
38
+ allow_any_instance_of(InfluxDB::Rails::Configuration)
39
+ .to receive(:ignored_environments).and_return(%w[development])
40
+
41
+ InfluxDB::Rails::TestClient.metrics.clear
42
+ end
43
+
44
+ config.include InfluxDB::Rails::Matchers
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,39 @@
1
+ require "influxdb/rails/values"
2
+ require "influxdb/rails/tags"
3
+
4
+ module InfluxDB
5
+ module Rails
6
+ class Metric
7
+ def initialize(configuration:, timestamp:, tags: {}, values: {})
8
+ @configuration = configuration
9
+ @timestamp = timestamp
10
+ @tags = tags
11
+ @values = values
12
+ end
13
+
14
+ def write
15
+ client.write_point configuration.measurement_name, options
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :configuration, :tags, :values, :timestamp
21
+
22
+ def options
23
+ {
24
+ values: Values.new(values: values).to_h,
25
+ tags: Tags.new(tags: tags, config: configuration).to_h,
26
+ timestamp: timestamp_with_precision,
27
+ }
28
+ end
29
+
30
+ def timestamp_with_precision
31
+ InfluxDB.convert_timestamp(timestamp.utc, configuration.client.time_precision)
32
+ end
33
+
34
+ def client
35
+ InfluxDB::Rails.client
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,22 @@
1
+ require "influxdb/rails/middleware/subscriber"
2
+
3
+ module InfluxDB
4
+ module Rails
5
+ module Middleware
6
+ class ActionMailerSubscriber < Subscriber # :nodoc:
7
+ private
8
+
9
+ def values
10
+ { value: 1 }
11
+ end
12
+
13
+ def tags
14
+ {
15
+ hook: "deliver",
16
+ mailer: payload[:mailer],
17
+ }
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,67 @@
1
+ require "influxdb/rails/middleware/subscriber"
2
+
3
+ module InfluxDB
4
+ module Rails
5
+ module Middleware
6
+ class ActiveJobSubscriber < Subscriber # :nodoc:
7
+ private
8
+
9
+ def values
10
+ {
11
+ value: value,
12
+ }
13
+ end
14
+
15
+ def tags
16
+ {
17
+ hook: short_hook_name,
18
+ state: job_state,
19
+ job: job.class.name,
20
+ queue: job.queue_name,
21
+ }
22
+ end
23
+
24
+ def job_state
25
+ return "failed" if failed?
26
+
27
+ case short_hook_name
28
+ when "enqueue"
29
+ "queued"
30
+ when "perform_start"
31
+ "running"
32
+ when "perform"
33
+ "succeeded"
34
+ end
35
+ end
36
+
37
+ def measure_performance?
38
+ short_hook_name == "perform"
39
+ end
40
+
41
+ def short_hook_name
42
+ @short_hook_name ||= fetch_short_hook_name
43
+ end
44
+
45
+ def fetch_short_hook_name
46
+ return "enqueue" if hook_name.include?("enqueue")
47
+ return "perform_start" if hook_name.include?("perform_start")
48
+ return "perform" if hook_name.include?("perform")
49
+ end
50
+
51
+ def job
52
+ @job ||= payload[:job]
53
+ end
54
+
55
+ def value
56
+ return duration if measure_performance?
57
+
58
+ 1
59
+ end
60
+
61
+ def failed?
62
+ payload[:exception_object]
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,26 @@
1
+ require "influxdb/rails/middleware/subscriber"
2
+ require "influxdb/rails/sql/query"
3
+
4
+ module InfluxDB
5
+ module Rails
6
+ module Middleware
7
+ class ActiveRecordSubscriber < Subscriber # :nodoc:
8
+ private
9
+
10
+ def values
11
+ {
12
+ value: duration,
13
+ record_count: payload[:record_count],
14
+ }
15
+ end
16
+
17
+ def tags
18
+ {
19
+ hook: "instantiation",
20
+ class_name: payload[:class_name],
21
+ }
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ require "influxdb/rails/middleware/subscriber"
2
+
3
+ module InfluxDB
4
+ module Rails
5
+ module Middleware
6
+ class BlockInstrumentationSubscriber < Subscriber
7
+ private
8
+
9
+ def values
10
+ {
11
+ value: duration,
12
+ }.merge(payload[:values].to_h)
13
+ end
14
+
15
+ def tags
16
+ {
17
+ hook: "block_instrumentation",
18
+ name: payload[:name],
19
+ }.merge(payload[:tags].to_h)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,18 +1,30 @@
1
- require "influxdb/rails/middleware/simple_subscriber"
1
+ require "influxdb/rails/middleware/subscriber"
2
2
 
3
3
  module InfluxDB
4
4
  module Rails
5
5
  module Middleware
6
- class RenderSubscriber < SimpleSubscriber # :nodoc:
6
+ class RenderSubscriber < Subscriber # :nodoc:
7
7
  private
8
8
 
9
- def tags(payload)
9
+ def values
10
10
  {
11
- location: location,
12
- filename: payload[:identifier],
11
+ value: duration,
13
12
  count: payload[:count],
14
13
  cache_hits: payload[:cache_hits],
15
- }.reject { |_, value| value.blank? }
14
+ }
15
+ end
16
+
17
+ def tags
18
+ {
19
+ hook: short_hook_name,
20
+ filename: payload[:identifier],
21
+ }
22
+ end
23
+
24
+ def short_hook_name
25
+ return "render_template" if hook_name.include?("render_template")
26
+ return "render_partial" if hook_name.include?("render_partial")
27
+ return "render_collection" if hook_name.include?("render_collection")
16
28
  end
17
29
  end
18
30
  end
@@ -4,46 +4,39 @@ module InfluxDB
4
4
  module Rails
5
5
  module Middleware
6
6
  class RequestSubscriber < Subscriber # :nodoc:
7
- def call(_name, start, finish, _id, payload) # rubocop:disable Metrics/MethodLength
8
- return unless enabled?
9
-
10
- ts = InfluxDB.convert_timestamp(finish.utc, configuration.time_precision)
11
- tags = tags(payload)
12
- begin
13
- series(payload, start, finish).each do |series_name, value|
14
- InfluxDB::Rails.client.write_point \
15
- series_name,
16
- values: { value: value },
17
- tags: tags,
18
- timestamp: ts
19
- end
20
- rescue StandardError => e
21
- log :error, "[InfluxDB::Rails] Unable to write points: #{e.message}"
22
- ensure
23
- Thread.current[:_influxdb_rails_controller] = nil
24
- Thread.current[:_influxdb_rails_action] = nil
25
- end
7
+ def write
8
+ super
9
+ ensure
10
+ InfluxDB::Rails.current.reset
26
11
  end
27
12
 
28
13
  private
29
14
 
30
- def series(payload, start, finish)
15
+ def tags
31
16
  {
32
- configuration.series_name_for_controller_runtimes => ((finish - start) * 1000).ceil,
33
- configuration.series_name_for_view_runtimes => (payload[:view_runtime] || 0).ceil,
34
- configuration.series_name_for_db_runtimes => (payload[:db_runtime] || 0).ceil,
35
- }
36
- end
37
-
38
- def tags(payload)
39
- configuration.tags_middleware.call({
40
17
  method: "#{payload[:controller]}##{payload[:action]}",
18
+ hook: "process_action",
41
19
  status: payload[:status],
42
20
  format: payload[:format],
43
21
  http_method: payload[:method],
44
- server: Socket.gethostname,
45
- app_name: configuration.application_name,
46
- }.reject { |_, value| value.nil? })
22
+ exception: payload[:exception]&.first,
23
+ }
24
+ end
25
+
26
+ def values
27
+ {
28
+ controller: duration,
29
+ view: (payload[:view_runtime] || 0).ceil,
30
+ db: (payload[:db_runtime] || 0).ceil,
31
+ started: started,
32
+ }
33
+ end
34
+
35
+ def started
36
+ InfluxDB.convert_timestamp(
37
+ start.utc,
38
+ configuration.client.time_precision
39
+ )
47
40
  end
48
41
  end
49
42
  end
@@ -1,31 +1,36 @@
1
- require "influxdb/rails/middleware/simple_subscriber"
1
+ require "influxdb/rails/middleware/subscriber"
2
2
  require "influxdb/rails/sql/query"
3
3
 
4
4
  module InfluxDB
5
5
  module Rails
6
6
  module Middleware
7
- class SqlSubscriber < SimpleSubscriber # :nodoc:
8
- def call(_name, started, finished, _unique_id, payload)
9
- return unless InfluxDB::Rails::Sql::Query.new(payload).track?
10
-
11
- super
12
- end
13
-
7
+ class SqlSubscriber < Subscriber # :nodoc:
14
8
  private
15
9
 
16
- def values(started, finished, payload)
17
- super.merge(sql: InfluxDB::Rails::Sql::Normalizer.new(payload[:sql]).perform)
10
+ def values
11
+ {
12
+ value: duration,
13
+ sql: InfluxDB::Rails::Sql::Normalizer.new(payload[:sql]).perform,
14
+ }
18
15
  end
19
16
 
20
- def tags(payload)
21
- query = InfluxDB::Rails::Sql::Query.new(payload)
17
+ def tags
22
18
  {
23
- location: location,
19
+ hook: "sql",
24
20
  operation: query.operation,
25
21
  class_name: query.class_name,
26
22
  name: query.name,
23
+ location: :raw,
27
24
  }
28
25
  end
26
+
27
+ def disabled?
28
+ super || !query.track?
29
+ end
30
+
31
+ def query
32
+ @query ||= InfluxDB::Rails::Sql::Query.new(payload)
33
+ end
29
34
  end
30
35
  end
31
36
  end
@@ -1,4 +1,4 @@
1
- require "influxdb/rails/logger"
1
+ require "influxdb/rails/metric"
2
2
 
3
3
  module InfluxDB
4
4
  module Rails
@@ -7,30 +7,60 @@ module InfluxDB
7
7
  # which are intended as ActiveSupport::Notifications.subscribe
8
8
  # consumers.
9
9
  class Subscriber
10
- include InfluxDB::Rails::Logger
10
+ def initialize(configuration:, hook_name:, start:, finish:, payload:)
11
+ @configuration = configuration
12
+ @hook_name = hook_name
13
+ @start = start
14
+ @finish = finish
15
+ @payload = payload
16
+ end
11
17
 
12
- attr_reader :configuration
18
+ def self.call(name, start, finish, _id, payload)
19
+ new(
20
+ configuration: InfluxDB::Rails.configuration,
21
+ start: start,
22
+ finish: finish,
23
+ payload: payload,
24
+ hook_name: name
25
+ ).write
26
+ end
13
27
 
14
- def initialize(configuration)
15
- @configuration = configuration
28
+ def write
29
+ return if disabled?
30
+
31
+ metric.write
32
+ rescue StandardError => e
33
+ ::Rails.logger.error("[InfluxDB::Rails] Unable to write points: #{e.message}")
16
34
  end
17
35
 
18
- def call(*)
36
+ private
37
+
38
+ attr_reader :configuration, :hook_name, :start, :finish, :payload
39
+
40
+ def metric
41
+ InfluxDB::Rails::Metric.new(
42
+ values: values,
43
+ tags: tags,
44
+ configuration: configuration,
45
+ timestamp: finish
46
+ )
47
+ end
48
+
49
+ def tags
19
50
  raise NotImplementedError, "must be implemented in subclass"
20
51
  end
21
52
 
22
- private
53
+ def values
54
+ raise NotImplementedError, "must be implemented in subclass"
55
+ end
23
56
 
24
- def enabled?
25
- configuration.instrumentation_enabled? &&
26
- !configuration.ignore_current_environment?
57
+ def duration
58
+ ((finish - start) * 1000).ceil
27
59
  end
28
60
 
29
- def location
30
- [
31
- Thread.current[:_influxdb_rails_controller],
32
- Thread.current[:_influxdb_rails_action],
33
- ].reject(&:blank?).join("#")
61
+ def disabled?
62
+ configuration.ignore_current_environment? ||
63
+ configuration.ignored_hooks.include?(hook_name)
34
64
  end
35
65
  end
36
66
  end