influxdb-rails 1.0.0 → 1.0.1.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +11 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +1 -1
  5. data/.travis.yml +4 -7
  6. data/CHANGELOG.md +17 -0
  7. data/README.md +47 -5
  8. data/Rakefile +0 -6
  9. data/gemfiles/Gemfile.rails-5.0.x +2 -0
  10. data/gemfiles/Gemfile.rails-6.0.x +10 -0
  11. data/influxdb-rails.gemspec +5 -3
  12. data/lib/influxdb-rails.rb +11 -0
  13. data/lib/influxdb/rails/configuration.rb +8 -12
  14. data/lib/influxdb/rails/context.rb +6 -40
  15. data/lib/influxdb/rails/helpers/rspec_matchers.rb +48 -0
  16. data/lib/influxdb/rails/metric.rb +39 -0
  17. data/lib/influxdb/rails/middleware/active_job_subscriber.rb +67 -0
  18. data/lib/influxdb/rails/middleware/active_record_subscriber.rb +26 -0
  19. data/lib/influxdb/rails/middleware/block_instrumentation_subscriber.rb +24 -0
  20. data/lib/influxdb/rails/middleware/render_subscriber.rb +15 -16
  21. data/lib/influxdb/rails/middleware/request_subscriber.rb +16 -21
  22. data/lib/influxdb/rails/middleware/sql_subscriber.rb +18 -18
  23. data/lib/influxdb/rails/middleware/subscriber.rb +40 -27
  24. data/lib/influxdb/rails/railtie.rb +15 -18
  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/sample-dashboard/README.md +1 -1
  30. data/spec/requests/action_controller_metrics_spec.rb +83 -0
  31. data/spec/requests/action_view_collection_metrics_spec.rb +66 -0
  32. data/spec/requests/action_view_partial_metrics_spec.rb +62 -0
  33. data/spec/requests/action_view_template_metrics_spec.rb +62 -0
  34. data/spec/requests/active_job_enqueue_metrics_spec.rb +65 -0
  35. data/spec/requests/active_job_perform_metrics_spec.rb +68 -0
  36. data/spec/requests/active_job_perform_start_metrics_spec.rb +68 -0
  37. data/spec/requests/active_record_instantiation_metrics_spec.rb +65 -0
  38. data/spec/requests/active_record_sql_metrics_spec.rb +103 -0
  39. data/spec/requests/block_inistrumentation_spec.rb +64 -0
  40. data/spec/requests/context_spec.rb +27 -0
  41. data/spec/requests/logger_spec.rb +10 -0
  42. data/spec/spec_helper.rb +10 -4
  43. data/spec/support/broken_client.rb +11 -0
  44. data/spec/support/rails5/app.rb +32 -10
  45. data/spec/support/rails6/app.rb +70 -0
  46. data/spec/support/views/{widgets → metrics}/_item.html.erb +0 -0
  47. data/spec/support/views/{widgets → metrics}/index.html.erb +0 -0
  48. data/spec/support/views/metrics/show.html.erb +4 -0
  49. data/spec/unit/block_instrumentation_spec.rb +18 -0
  50. metadata +87 -37
  51. data/gemfiles/Gemfile.rails-4.2.x +0 -7
  52. data/lib/influxdb/rails/instrumentation.rb +0 -34
  53. data/lib/influxdb/rails/middleware/simple_subscriber.rb +0 -33
  54. data/spec/controllers/widgets_controller_spec.rb +0 -15
  55. data/spec/integration/integration_helper.rb +0 -1
  56. data/spec/integration/metrics_spec.rb +0 -27
  57. data/spec/shared_examples/data.rb +0 -61
  58. data/spec/support/rails4/app.rb +0 -48
  59. data/spec/unit/context_spec.rb +0 -40
  60. data/spec/unit/middleware/render_subscriber_spec.rb +0 -96
  61. data/spec/unit/middleware/request_subscriber_spec.rb +0 -103
  62. data/spec/unit/middleware/sql_subscriber_spec.rb +0 -108
@@ -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,31 +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:
7
- def short_hook_name
8
- return "render_template" if hook_name.include?("render_template")
9
- return "render_partial" if hook_name.include?("render_partial")
10
- return "render_collection" if hook_name.include?("render_collection")
11
- end
12
-
6
+ class RenderSubscriber < Subscriber # :nodoc:
13
7
  private
14
8
 
15
- def values(started, finished, payload)
16
- super(started, finished, payload).merge(
9
+ def values
10
+ {
11
+ value: duration,
17
12
  count: payload[:count],
18
- cache_hits: payload[:cache_hits]
19
- ).reject { |_, value| value.nil? }
13
+ cache_hits: payload[:cache_hits],
14
+ }
20
15
  end
21
16
 
22
- def tags(payload)
23
- tags = {
24
- location: location,
17
+ def tags
18
+ {
25
19
  hook: short_hook_name,
26
20
  filename: payload[:identifier],
27
21
  }
28
- super(tags)
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")
29
28
  end
30
29
  end
31
30
  end
@@ -4,44 +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)
8
- return unless enabled?
9
-
10
- InfluxDB::Rails.client.write_point \
11
- configuration.measurement_name,
12
- values: values(start, finish, payload),
13
- tags: tags(payload),
14
- timestamp: timestamp(finish)
15
- rescue StandardError => e
16
- ::Rails.logger.error("[InfluxDB::Rails] Unable to write points: #{e.message}")
7
+ def write
8
+ super
17
9
  ensure
18
10
  InfluxDB::Rails.current.reset
19
11
  end
20
12
 
21
13
  private
22
14
 
23
- def tags(payload)
24
- tags = {
15
+ def tags
16
+ {
25
17
  method: "#{payload[:controller]}##{payload[:action]}",
26
18
  hook: "process_action",
27
19
  status: payload[:status],
28
20
  format: payload[:format],
29
21
  http_method: payload[:method],
30
- server: Socket.gethostname,
31
- app_name: configuration.application_name,
22
+ exception: payload[:exception]&.first,
32
23
  }
33
- super(tags)
34
24
  end
35
25
 
36
- def values(started, finished, payload)
26
+ def values
37
27
  {
38
- controller: ((finished - started) * 1000).ceil,
28
+ controller: duration,
39
29
  view: (payload[:view_runtime] || 0).ceil,
40
30
  db: (payload[:db_runtime] || 0).ceil,
41
- started: timestamp(started),
42
- }.merge(InfluxDB::Rails.current.values).reject do |_, value|
43
- value.nil? || value == ""
44
- end
31
+ started: started,
32
+ }
33
+ end
34
+
35
+ def started
36
+ InfluxDB.convert_timestamp(
37
+ start.utc,
38
+ configuration.client.time_precision
39
+ )
45
40
  end
46
41
  end
47
42
  end
@@ -1,35 +1,35 @@
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
- super if InfluxDB::Rails::Sql::Query.new(payload).track?
10
- end
11
-
7
+ class SqlSubscriber < Subscriber # :nodoc:
12
8
  private
13
9
 
14
- def values(started, finished, payload)
15
- super.merge(sql: InfluxDB::Rails::Sql::Normalizer.new(payload[:sql]).perform)
16
- end
17
-
18
- def location
19
- result = super
20
- result.empty? ? :raw : result
10
+ def values
11
+ {
12
+ value: duration,
13
+ sql: InfluxDB::Rails::Sql::Normalizer.new(payload[:sql]).perform,
14
+ }
21
15
  end
22
16
 
23
- def tags(payload)
24
- query = InfluxDB::Rails::Sql::Query.new(payload)
25
- tags = {
26
- location: location,
17
+ def tags
18
+ {
27
19
  hook: "sql",
28
20
  operation: query.operation,
29
21
  class_name: query.class_name,
30
22
  name: query.name,
23
+ location: :raw,
31
24
  }
32
- super(tags)
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
33
  end
34
34
  end
35
35
  end
@@ -1,3 +1,5 @@
1
+ require "influxdb/rails/metric"
2
+
1
3
  module InfluxDB
2
4
  module Rails
3
5
  module Middleware
@@ -5,49 +7,60 @@ module InfluxDB
5
7
  # which are intended as ActiveSupport::Notifications.subscribe
6
8
  # consumers.
7
9
  class Subscriber
8
- attr_reader :configuration
9
- attr_reader :hook_name
10
-
11
- def initialize(configuration, hook_name)
10
+ def initialize(configuration:, hook_name:, start:, finish:, payload:)
12
11
  @configuration = configuration
13
12
  @hook_name = hook_name
13
+ @start = start
14
+ @finish = finish
15
+ @payload = payload
14
16
  end
15
17
 
16
- def call(*)
17
- raise NotImplementedError, "must be implemented in subclass"
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
27
+
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}")
18
34
  end
19
35
 
20
36
  private
21
37
 
22
- def timestamp(time)
23
- InfluxDB.convert_timestamp(time.utc, client.time_precision)
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
+ )
24
47
  end
25
48
 
26
- def client
27
- @client = configuration.client
49
+ def tags
50
+ raise NotImplementedError, "must be implemented in subclass"
28
51
  end
29
52
 
30
- def tags(tags)
31
- result = tags.merge(InfluxDB::Rails.current.tags)
32
- result = configuration.tags_middleware.call(result)
33
- result.reject! do |_, value|
34
- value.nil? || value == ""
35
- end
36
- result
53
+ def values
54
+ raise NotImplementedError, "must be implemented in subclass"
37
55
  end
38
56
 
39
- def enabled?
40
- configuration.instrumentation_enabled? &&
41
- !configuration.ignore_current_environment? &&
42
- !configuration.ignored_hooks.include?(hook_name)
57
+ def duration
58
+ ((finish - start) * 1000).ceil
43
59
  end
44
60
 
45
- def location
46
- current = InfluxDB::Rails.current
47
- [
48
- current.controller,
49
- current.action,
50
- ].reject(&:blank?).join("#")
61
+ def disabled?
62
+ configuration.ignore_current_environment? ||
63
+ configuration.ignored_hooks.include?(hook_name)
51
64
  end
52
65
  end
53
66
  end
@@ -11,38 +11,35 @@ module InfluxDB
11
11
  end
12
12
 
13
13
  ActiveSupport.on_load(:action_controller) do
14
- require "influxdb/rails/instrumentation"
15
- include InfluxDB::Rails::Instrumentation
16
-
17
14
  before_action do
18
15
  current = InfluxDB::Rails.current
19
- current.request_id = request.request_id if request.respond_to?(:request_id)
16
+ current.values = { request_id: request.request_id } if request.respond_to?(:request_id)
20
17
  end
21
18
  end
22
19
 
23
20
  cache = lambda do |_, _, _, _, payload|
24
21
  current = InfluxDB::Rails.current
25
- current.controller = payload[:controller]
26
- current.action = payload[:action]
22
+ location = [payload[:controller], payload[:action]].join("#")
23
+ current.tags = { location: location }
27
24
  end
28
25
  ActiveSupport::Notifications.subscribe "start_processing.action_controller", &cache
29
26
 
30
27
  {
31
- "process_action.action_controller" => Middleware::RequestSubscriber,
32
- "render_template.action_view" => Middleware::RenderSubscriber,
33
- "render_partial.action_view" => Middleware::RenderSubscriber,
34
- "render_collection.action_view" => Middleware::RenderSubscriber,
35
- "sql.active_record" => Middleware::SqlSubscriber,
36
- }.each do |hook_name, subscriber_class|
37
- subscribe_to(hook_name, subscriber_class)
28
+ "process_action.action_controller" => Middleware::RequestSubscriber,
29
+ "render_template.action_view" => Middleware::RenderSubscriber,
30
+ "render_partial.action_view" => Middleware::RenderSubscriber,
31
+ "render_collection.action_view" => Middleware::RenderSubscriber,
32
+ "sql.active_record" => Middleware::SqlSubscriber,
33
+ "instantiation.active_record" => Middleware::ActiveRecordSubscriber,
34
+ "enqueue.active_job" => Middleware::ActiveJobSubscriber,
35
+ "perform_start.active_job" => Middleware::ActiveJobSubscriber,
36
+ "perform.active_job" => Middleware::ActiveJobSubscriber,
37
+ "block_instrumentation.influxdb_rails" => Middleware::BlockInstrumentationSubscriber,
38
+ }.each do |hook_name, subscriber|
39
+ ActiveSupport::Notifications.subscribe(hook_name, subscriber)
38
40
  end
39
41
  end
40
42
  # rubocop:enable Metrics/BlockLength
41
-
42
- def subscribe_to(hook_name, subscriber_class)
43
- subscriber = subscriber_class.new(InfluxDB::Rails.configuration, hook_name)
44
- ActiveSupport::Notifications.subscribe hook_name, subscriber
45
- end
46
43
  end
47
44
  end
48
45
  end
@@ -0,0 +1,33 @@
1
+ module InfluxDB
2
+ module Rails
3
+ class Tags
4
+ def initialize(tags: {}, config:, additional_tags: InfluxDB::Rails.current.tags)
5
+ @tags = tags
6
+ @config = config
7
+ @additional_tags = additional_tags
8
+ end
9
+
10
+ def to_h
11
+ expanded_tags.reject do |_, value|
12
+ value.nil? || value == ""
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :additional_tags, :tags, :config
19
+
20
+ def expanded_tags
21
+ config.tags_middleware.call(tags.merge(default_tags))
22
+ end
23
+
24
+ def default_tags
25
+ {
26
+ server: Socket.gethostname,
27
+ app_name: config.application_name,
28
+ location: :raw,
29
+ }.merge(additional_tags)
30
+ end
31
+ end
32
+ end
33
+ end