netuitive_rails_agent 0.10.0 → 1.0.1

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 (32) hide show
  1. data/README.md +18 -8
  2. data/config/agent.yml +36 -1
  3. data/lib/netuitive_rails_agent.rb +43 -17
  4. data/lib/netuitive_rails_agent/action_controller.rb +97 -0
  5. data/lib/netuitive_rails_agent/action_mailer.rb +29 -0
  6. data/lib/netuitive_rails_agent/action_view.rb +25 -0
  7. data/lib/netuitive_rails_agent/active_job.rb +25 -0
  8. data/lib/netuitive_rails_agent/active_record.rb +26 -0
  9. data/lib/netuitive_rails_agent/active_support.rb +46 -0
  10. data/lib/netuitive_rails_agent/api_interaction.rb +43 -0
  11. data/lib/netuitive_rails_agent/config_manager.rb +124 -0
  12. data/lib/netuitive_rails_agent/controller_utils.rb +17 -0
  13. data/lib/netuitive_rails_agent/error_tracker.rb +33 -0
  14. data/lib/netuitive_rails_agent/error_utils.rb +41 -0
  15. data/lib/netuitive_rails_agent/gc.rb +33 -0
  16. data/lib/netuitive_rails_agent/netuitive_logger.rb +49 -0
  17. data/lib/netuitive_rails_agent/objectspace.rb +19 -0
  18. data/lib/netuitive_rails_agent/request_data.rb +50 -0
  19. data/lib/netuitive_rails_agent/scheduler.rb +44 -0
  20. data/lib/netuitive_rails_agent/sidekiq.rb +67 -0
  21. metadata +23 -17
  22. data/lib/netuitive/action_controller.rb +0 -81
  23. data/lib/netuitive/action_mailer.rb +0 -22
  24. data/lib/netuitive/action_view.rb +0 -18
  25. data/lib/netuitive/active_job.rb +0 -18
  26. data/lib/netuitive/active_record.rb +0 -19
  27. data/lib/netuitive/active_support.rb +0 -39
  28. data/lib/netuitive/gc.rb +0 -23
  29. data/lib/netuitive/netuitive_rails_logger.rb +0 -24
  30. data/lib/netuitive/objectspace.rb +0 -13
  31. data/lib/netuitive/rails_config_manager.rb +0 -29
  32. data/lib/netuitive/scheduler.rb +0 -23
@@ -0,0 +1,17 @@
1
+ module NetuitiveRailsAgent
2
+ module ControllerUtils
3
+ def netuitive_action_name
4
+ return controller.action_name if defined? controller # rails 3.1.X
5
+ action_name # rails 4.X
6
+ end
7
+
8
+ def netuitive_controller_name
9
+ self.class.name
10
+ end
11
+
12
+ def netuitive_request_uri
13
+ return request.fullpath if defined? request.fullpath # rails 3
14
+ request.original_fullpath # rails >3.2
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,33 @@
1
+ module NetuitiveRailsAgent
2
+ module ErrorTrackerHook
3
+ extend ActiveSupport::Concern
4
+
5
+ include NetuitiveRailsAgent::ControllerUtils
6
+
7
+ include NetuitiveRailsAgent::ErrorUtils
8
+
9
+ included do
10
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug 'ErrorTracker included'
11
+ rescue_from Exception do |exception|
12
+ begin
13
+ tags = {
14
+ URI: netuitive_request_uri,
15
+ Controller: netuitive_controller_name,
16
+ Action: netuitive_action_name
17
+ }
18
+ metrics = ['action_controller.errors']
19
+ if netuitive_controller_name
20
+ metrics << "action_controller.#{netuitive_controller_name}.errors"
21
+ if netuitive_action_name
22
+ metrics << "action_controller.#{netuitive_controller_name}.#{netuitive_action_name}.errors"
23
+ end
24
+ end
25
+ handle_error(exception, metrics, tags)
26
+ rescue => e
27
+ NetuitiveRailsAgent::NetuitiveLogger.log.error "exception during controller error tracking: message:#{e.message} backtrace:#{e.backtrace}"
28
+ end
29
+ raise exception
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,41 @@
1
+ module NetuitiveRailsAgent
2
+ module ErrorUtils
3
+ attr_accessor :interaction
4
+
5
+ def ignored_error?(exception)
6
+ unless NetuitiveRailsAgent::ConfigManager.ignored_errors.empty?
7
+ NetuitiveRailsAgent::ConfigManager.ignored_errors.each do |name|
8
+ if name.include? '^'
9
+ name.tr!('^', '')
10
+ exception.class.ancestors.each do |ancestor|
11
+ return true if name.casecmp(ancestor.name).zero?
12
+ end
13
+ elsif name.casecmp(exception.class.name).zero?
14
+ return true
15
+ end
16
+ end
17
+ end
18
+ false
19
+ end
20
+
21
+ def handle_error(exception, metrics = [], tags = {})
22
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug "received error: #{exception}"
23
+ unless ignored_error?(exception)
24
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug "#{exception} wasn't ignored"
25
+ if NetuitiveRailsAgent::ConfigManager.capture_errors
26
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug "sending error: #{exception}"
27
+ @interaction = NetuitiveRailsAgent::ApiInteraction.new unless @interaction
28
+ @interaction.exception_event(exception, exception.class, tags)
29
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug 'sent error'
30
+ end
31
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug 'sending error metrics'
32
+ metrics.each do |metric|
33
+ @interaction.aggregate_metric(metric.to_s, 1)
34
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug "sent error metric with name: #{metric}"
35
+ end
36
+ end
37
+ rescue => e
38
+ NetuitiveRailsAgent::NetuitiveLogger.log.error "exception during error tracking: message:#{e.message} backtrace:#{e.backtrace}"
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,33 @@
1
+ module NetuitiveRailsAgent
2
+ class GCStatsCollector
3
+ attr_reader :interaction
4
+ def initialize(interaction)
5
+ @interaction = interaction
6
+ end
7
+
8
+ def collect
9
+ unless GC::Profiler.enabled?
10
+ NetuitiveRailsAgent::NetuitiveLogger.log.warn 'gc profiler not enabled'
11
+ return
12
+ end
13
+ begin
14
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug 'collecting gc metrics'
15
+ GC.stat.each do |key, value|
16
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug "GC stat key: #{key}"
17
+ if (key.to_s == 'total_allocated_object') || (key.to_s == 'total_freed_object') || (key.to_s == 'count')
18
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug "sending aggregateCounterMetric GC.stat.#{key}"
19
+ interaction.aggregate_counter_metric("GC.stat.#{key}", value)
20
+ else
21
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug "sending aggregateMetric GC.stat.#{key}"
22
+ interaction.aggregate_metric("GC.stat.#{key}", value)
23
+ end
24
+ end
25
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug 'sending aggregateCounterMetric GC.profiler.total_time'
26
+ interaction.aggregate_counter_metric('GC.profiler.total_time', GC::Profiler.total_time)
27
+ rescue => e
28
+ NetuitiveRailsAgent::NetuitiveLogger.log.error "exception during gc collection: message:#{e.message} backtrace:#{e.backtrace}"
29
+ end
30
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug 'finished collecting gc metrics'
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,49 @@
1
+ module NetuitiveRailsAgent
2
+ class CheaterLogger
3
+ attr_accessor :level
4
+ def debug(message)
5
+ end
6
+
7
+ def error(message)
8
+ end
9
+
10
+ def info(message)
11
+ end
12
+ end
13
+
14
+ class NetuitiveLogger
15
+ class << self
16
+ attr_accessor :log
17
+ def setup
18
+ file = NetuitiveRailsAgent::ConfigManager.property('logLocation', 'NETUITIVE_RAILS_LOG_LOCATION', "#{File.expand_path('../../..', __FILE__)}/log/netuitive.log")
19
+ age = NetuitiveRailsAgent::ConfigManager.property('logAge', 'NETUITIVE_RAILS_LOG_AGE', 'daily')
20
+ age = format_age(age)
21
+ size = NetuitiveRailsAgent::ConfigManager.property('logSize', 'NETUITIVE_RAILS_LOG_SIZE', '1000000')
22
+ size = format_size(size)
23
+ NetuitiveRailsAgent::NetuitiveLogger.log = Logger.new(file, age, size.to_i)
24
+ rescue => e
25
+ puts 'netuitive unable to open log file'
26
+ puts e.message
27
+ NetuitiveRailsAgent::NetuitiveLogger.log = NetuitiveRailsAgent::CheaterLogger.new
28
+ end
29
+
30
+ def format_age(age)
31
+ return 'daily' if age.nil?
32
+ begin
33
+ Integer(age)
34
+ rescue
35
+ age
36
+ end
37
+ end
38
+
39
+ def format_size(size)
40
+ return 1_000_000 if size.nil?
41
+ begin
42
+ Integer(size)
43
+ rescue
44
+ size
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,19 @@
1
+ module NetuitiveRailsAgent
2
+ class ObjectSpaceStatsCollector
3
+ attr_reader :interaction
4
+ def initialize(interaction)
5
+ @interaction = interaction
6
+ end
7
+
8
+ def collect
9
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug 'collecting object space metrics'
10
+ ObjectSpace.count_objects.each do |key, value|
11
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug "ObjectSpace.count_objects.#{key}"
12
+ interaction.aggregate_metric("ObjectSpace.count_objects.#{key}", value)
13
+ end
14
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug 'finished collecting object space metrics'
15
+ rescue => e
16
+ NetuitiveRailsAgent::NetuitiveLogger.log.error "exception during object space collection: message:#{e.message} backtrace:#{e.backtrace}"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,50 @@
1
+ module NetuitiveRailsAgent
2
+ module RequestDataHook
3
+ include NetuitiveRailsAgent::ControllerUtils
4
+
5
+ HEADERS_NAMES = [
6
+ 'HTTP_X_REQUEST_START'.freeze,
7
+ 'HTTP_X_QUEUE_START'.freeze,
8
+ 'HTTP_X_MIDDLEWARE_START'.freeze
9
+ ].freeze
10
+
11
+ def self.header_start_time(headers)
12
+ if headers
13
+ start_time = nil
14
+ HEADERS_NAMES.each do |header_name|
15
+ header = headers[header_name]
16
+ next if header.nil?
17
+ match = /\d+(\.\d{1,2})?/.match(header)
18
+ if match.nil?
19
+ NetuitiveRailsAgent::NetuitiveLogger.log.error "queue time header value #{header} is not recognized"
20
+ next
21
+ end
22
+ header_time = match.to_s.to_f / NetuitiveRailsAgent::ConfigManager.queue_time_divisor
23
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug "queue header_time: #{header_time}"
24
+ start_time = start_time.nil? || header_time < start_time ? header_time : start_time
25
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug "queue start_time: #{start_time}"
26
+ end
27
+ return (Time.now.to_f - start_time) * 1000.0
28
+ end
29
+ nil
30
+ end
31
+
32
+ def netuitive_request_hook
33
+ return unless request
34
+ begin
35
+ queue_time = NetuitiveRailsAgent::RequestDataHook.header_start_time(request.headers)
36
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug "queue_time: #{queue_time}"
37
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug 'sending queue_time metrics'
38
+ NetuitiveRubyAPI.add_sample('action_controller.request.queue_time', queue_time)
39
+ return unless netuitive_controller_name
40
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug "sending queue_time metrics with netuitive_controller_name #{netuitive_controller_name}"
41
+ NetuitiveRubyAPI.add_sample("action_controller.#{netuitive_controller_name}.request.queue_time", queue_time)
42
+ return unless netuitive_action_name
43
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug "sending queue_time metrics with netuitive_action_name #{netuitive_action_name}"
44
+ NetuitiveRubyAPI.add_sample("action_controller.#{netuitive_controller_name}.#{netuitive_action_name}.request.queue_time", queue_time)
45
+ rescue => e
46
+ NetuitiveRailsAgent::NetuitiveLogger.log.error "exception during request tracking: message:#{e.message} backtrace:#{e.backtrace}"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,44 @@
1
+ module NetuitiveRailsAgent
2
+ class Scheduler
3
+ attr_reader :gc_stats_collector
4
+ attr_reader :object_space_collector
5
+ attr_reader :interaction
6
+ def initialize(interaction)
7
+ @gc_stats_collector = NetuitiveRailsAgent::GCStatsCollector.new(interaction)
8
+ @object_space_collector = NetuitiveRailsAgent::ObjectSpaceStatsCollector.new(interaction)
9
+ @interaction = interaction
10
+ end
11
+
12
+ def start_schedule
13
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug 'starting schedule'
14
+ Thread.new do
15
+ loop do
16
+ begin
17
+ collect_metrics
18
+ sleep_time = interval
19
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug "scheduler sleeping for: #{sleep_time}"
20
+ sleep(sleep_time)
21
+ rescue => e
22
+ NetuitiveRailsAgent::NetuitiveLogger.log.error "error during schedule: #{e.message}, backtrace: #{e.backtrace}"
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ def interval
29
+ interval = interaction.interval
30
+ interval = 60 if interval.nil?
31
+ interval
32
+ end
33
+
34
+ def collect_metrics
35
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug 'collecting schedule metrics'
36
+ begin
37
+ gc_stats_collector.collect if NetuitiveRailsAgent::ConfigManager.gc_enabled
38
+ object_space_collector.collect if NetuitiveRailsAgent::ConfigManager.object_space_enabled
39
+ rescue => e
40
+ NetuitiveRailsAgent::NetuitiveLogger.log.error "unable to collect schedule metrics: message:#{e.message} backtrace:#{e.backtrace}"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,67 @@
1
+ module NetuitiveRailsAgent
2
+ class SidekiqTracker
3
+ def setup
4
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug 'turning on sidekiq tracking'
5
+ require 'sidekiq'
6
+ Sidekiq.configure_server do |config|
7
+ config.error_handlers << proc { |ex, ctx_hash| NetuitiveRailsAgent::SidekiqTracker::ErrorTracker.new.call(ex, ctx_hash) }
8
+ config.server_middleware do |chain|
9
+ chain.add NetuitiveRailsAgent::SidekiqTracker::ChainTracker
10
+ end
11
+ end
12
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug 'sidekiq tracking installed'
13
+ end
14
+
15
+ class ChainTracker
16
+ attr_accessor :interaction
17
+ def initialize
18
+ @interaction = NetuitiveRailsAgent::ApiInteraction.new
19
+ end
20
+
21
+ def call(worker, item, queue)
22
+ begin
23
+ klass = item['wrapped'.freeze] || worker.class.to_s
24
+ queue = item['queue']
25
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug "sidekiq job tracked. queue: #{queue}, class: #{klass}"
26
+ interaction.aggregate_metric("sidekiq.#{klass}.job.count", 1)
27
+ interaction.aggregate_metric("sidekiq.#{queue}.job.count", 1)
28
+ interaction.aggregate_metric("sidekiq.#{queue}.#{klass}.job.count", 1)
29
+ rescue => e
30
+ NetuitiveRailsAgent::NetuitiveLogger.log.error "exception during sidekiq chain tracking: message:#{e.message} backtrace:#{e.backtrace}"
31
+ end
32
+ yield
33
+ end
34
+ end
35
+
36
+ class ErrorTracker
37
+ include NetuitiveRailsAgent::ErrorUtils
38
+
39
+ def initialize
40
+ @interaction = NetuitiveRailsAgent::ApiInteraction.new
41
+ end
42
+
43
+ def call(exception, ctx_hash)
44
+ NetuitiveRailsAgent::NetuitiveLogger.log.debug "sidekiq error tracked. context: #{ctx_hash[:context]}"
45
+ tags = {
46
+ sidekiq: 'true',
47
+ context: ctx_hash[:context]
48
+ }
49
+ metrics = ['sidekiq.errors']
50
+ job = ctx_hash[:job]
51
+ unless job.nil?
52
+ if defined? job.item
53
+ item = job.item
54
+ klass = item['wrapped'.freeze] || worker.class.to_s
55
+ queue = item['queue']
56
+ metrics << "sidekiq.#{queue}.#{klass}.error.count"
57
+ metrics << "sidekiq.#{queue}.error.count"
58
+ end
59
+ end
60
+
61
+ handle_error(exception, metrics, tags)
62
+ rescue => e
63
+ NetuitiveRailsAgent::NetuitiveLogger.log.error "exception during sidekiq error tracking: message:#{e.message} backtrace:#{e.backtrace}"
64
+ end
65
+ end
66
+ end
67
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: netuitive_rails_agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 1.0.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-01-13 00:00:00.000000000 Z
12
+ date: 2016-10-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: netuitive_ruby_api
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ! '>='
20
20
  - !ruby/object:Gem::Version
21
- version: 0.9.0
21
+ version: 1.0.1
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
- version: 0.9.0
29
+ version: 1.0.1
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: netuitived
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -34,7 +34,7 @@ dependencies:
34
34
  requirements:
35
35
  - - ! '>='
36
36
  - !ruby/object:Gem::Version
37
- version: 0.10.0
37
+ version: 1.0.1
38
38
  type: :development
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
@@ -42,7 +42,7 @@ dependencies:
42
42
  requirements:
43
43
  - - ! '>='
44
44
  - !ruby/object:Gem::Version
45
- version: 0.10.0
45
+ version: 1.0.1
46
46
  description: Automatically generates Rails metrics for submission to Netuitive's API
47
47
  email: jking@netuitive.com
48
48
  executables: []
@@ -50,17 +50,23 @@ extensions: []
50
50
  extra_rdoc_files: []
51
51
  files:
52
52
  - lib/netuitive_rails_agent.rb
53
- - lib/netuitive/action_mailer.rb
54
- - lib/netuitive/objectspace.rb
55
- - lib/netuitive/netuitive_rails_logger.rb
56
- - lib/netuitive/action_view.rb
57
- - lib/netuitive/scheduler.rb
58
- - lib/netuitive/active_job.rb
59
- - lib/netuitive/rails_config_manager.rb
60
- - lib/netuitive/action_controller.rb
61
- - lib/netuitive/active_record.rb
62
- - lib/netuitive/gc.rb
63
- - lib/netuitive/active_support.rb
53
+ - lib/netuitive_rails_agent/error_utils.rb
54
+ - lib/netuitive_rails_agent/action_mailer.rb
55
+ - lib/netuitive_rails_agent/objectspace.rb
56
+ - lib/netuitive_rails_agent/action_view.rb
57
+ - lib/netuitive_rails_agent/netuitive_logger.rb
58
+ - lib/netuitive_rails_agent/scheduler.rb
59
+ - lib/netuitive_rails_agent/active_job.rb
60
+ - lib/netuitive_rails_agent/request_data.rb
61
+ - lib/netuitive_rails_agent/config_manager.rb
62
+ - lib/netuitive_rails_agent/action_controller.rb
63
+ - lib/netuitive_rails_agent/active_record.rb
64
+ - lib/netuitive_rails_agent/controller_utils.rb
65
+ - lib/netuitive_rails_agent/gc.rb
66
+ - lib/netuitive_rails_agent/sidekiq.rb
67
+ - lib/netuitive_rails_agent/error_tracker.rb
68
+ - lib/netuitive_rails_agent/active_support.rb
69
+ - lib/netuitive_rails_agent/api_interaction.rb
64
70
  - config/agent.yml
65
71
  - ./LICENSE
66
72
  - log/netuitive.log