influx_reporter 1.0.0

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 (103) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +51 -0
  5. data/.travis.yml +42 -0
  6. data/.yardopts +3 -0
  7. data/Dockerfile +9 -0
  8. data/Gemfile +7 -0
  9. data/HISTORY.md +1 -0
  10. data/LICENSE +14 -0
  11. data/README.md +189 -0
  12. data/Rakefile +27 -0
  13. data/gemfiles/Gemfile.base +29 -0
  14. data/gemfiles/Gemfile.rails-3.2.x +4 -0
  15. data/gemfiles/Gemfile.rails-4.0.x +4 -0
  16. data/gemfiles/Gemfile.rails-4.1.x +4 -0
  17. data/gemfiles/Gemfile.rails-4.2.x +5 -0
  18. data/gemfiles/Gemfile.rails-5.0.x +5 -0
  19. data/gemfiles/Gemfile.rails-HEAD +7 -0
  20. data/influx_reporter.gemspec +26 -0
  21. data/lib/influx_reporter.rb +142 -0
  22. data/lib/influx_reporter/client.rb +306 -0
  23. data/lib/influx_reporter/configuration.rb +88 -0
  24. data/lib/influx_reporter/data_builders.rb +18 -0
  25. data/lib/influx_reporter/data_builders/error.rb +52 -0
  26. data/lib/influx_reporter/data_builders/transactions.rb +120 -0
  27. data/lib/influx_reporter/error.rb +7 -0
  28. data/lib/influx_reporter/error_message.rb +85 -0
  29. data/lib/influx_reporter/error_message/exception.rb +14 -0
  30. data/lib/influx_reporter/error_message/http.rb +73 -0
  31. data/lib/influx_reporter/error_message/stacktrace.rb +76 -0
  32. data/lib/influx_reporter/error_message/user.rb +25 -0
  33. data/lib/influx_reporter/filter.rb +66 -0
  34. data/lib/influx_reporter/http_client.rb +140 -0
  35. data/lib/influx_reporter/influx_db_client.rb +74 -0
  36. data/lib/influx_reporter/injections.rb +89 -0
  37. data/lib/influx_reporter/injections/json.rb +21 -0
  38. data/lib/influx_reporter/injections/net_http.rb +50 -0
  39. data/lib/influx_reporter/injections/redis.rb +25 -0
  40. data/lib/influx_reporter/injections/sequel.rb +37 -0
  41. data/lib/influx_reporter/injections/sinatra.rb +59 -0
  42. data/lib/influx_reporter/integration/delayed_job.rb +30 -0
  43. data/lib/influx_reporter/integration/rails/inject_exceptions_catcher.rb +25 -0
  44. data/lib/influx_reporter/integration/railtie.rb +56 -0
  45. data/lib/influx_reporter/integration/resque.rb +18 -0
  46. data/lib/influx_reporter/integration/sidekiq.rb +130 -0
  47. data/lib/influx_reporter/line_cache.rb +21 -0
  48. data/lib/influx_reporter/logging.rb +39 -0
  49. data/lib/influx_reporter/middleware.rb +63 -0
  50. data/lib/influx_reporter/normalizers.rb +65 -0
  51. data/lib/influx_reporter/normalizers/action_controller.rb +34 -0
  52. data/lib/influx_reporter/normalizers/action_view.rb +72 -0
  53. data/lib/influx_reporter/normalizers/active_record.rb +45 -0
  54. data/lib/influx_reporter/sql_summarizer.rb +31 -0
  55. data/lib/influx_reporter/subscriber.rb +80 -0
  56. data/lib/influx_reporter/tasks.rb +28 -0
  57. data/lib/influx_reporter/trace.rb +48 -0
  58. data/lib/influx_reporter/trace_helpers.rb +31 -0
  59. data/lib/influx_reporter/transaction.rb +114 -0
  60. data/lib/influx_reporter/util.rb +18 -0
  61. data/lib/influx_reporter/util/constantize.rb +56 -0
  62. data/lib/influx_reporter/util/inspector.rb +72 -0
  63. data/lib/influx_reporter/util/timestamp.rb +11 -0
  64. data/lib/influx_reporter/version.rb +5 -0
  65. data/lib/influx_reporter/worker.rb +56 -0
  66. data/spec/influx_reporter/client_spec.rb +264 -0
  67. data/spec/influx_reporter/configuration_spec.rb +42 -0
  68. data/spec/influx_reporter/data_builders/error_spec.rb +40 -0
  69. data/spec/influx_reporter/data_builders/transactions_spec.rb +48 -0
  70. data/spec/influx_reporter/error_message/exception_spec.rb +22 -0
  71. data/spec/influx_reporter/error_message/http_spec.rb +55 -0
  72. data/spec/influx_reporter/error_message/stacktrace_spec.rb +56 -0
  73. data/spec/influx_reporter/error_message/user_spec.rb +26 -0
  74. data/spec/influx_reporter/error_message_spec.rb +102 -0
  75. data/spec/influx_reporter/filter_spec.rb +33 -0
  76. data/spec/influx_reporter/injections/net_http_spec.rb +35 -0
  77. data/spec/influx_reporter/injections/sequel_spec.rb +33 -0
  78. data/spec/influx_reporter/injections/sinatra_spec.rb +13 -0
  79. data/spec/influx_reporter/injections_spec.rb +50 -0
  80. data/spec/influx_reporter/integration/delayed_job_spec.rb +37 -0
  81. data/spec/influx_reporter/integration/json_spec.rb +41 -0
  82. data/spec/influx_reporter/integration/rails_spec.rb +92 -0
  83. data/spec/influx_reporter/integration/redis_spec.rb +20 -0
  84. data/spec/influx_reporter/integration/resque_spec.rb +42 -0
  85. data/spec/influx_reporter/integration/sidekiq_spec.rb +40 -0
  86. data/spec/influx_reporter/integration/sinatra_spec.rb +99 -0
  87. data/spec/influx_reporter/line_cache_spec.rb +38 -0
  88. data/spec/influx_reporter/logging_spec.rb +49 -0
  89. data/spec/influx_reporter/middleware_spec.rb +32 -0
  90. data/spec/influx_reporter/normalizers/action_controller_spec.rb +37 -0
  91. data/spec/influx_reporter/normalizers/action_view_spec.rb +78 -0
  92. data/spec/influx_reporter/normalizers/active_record_spec.rb +70 -0
  93. data/spec/influx_reporter/normalizers_spec.rb +16 -0
  94. data/spec/influx_reporter/sql_summarizer_spec.rb +35 -0
  95. data/spec/influx_reporter/subscriber_spec.rb +83 -0
  96. data/spec/influx_reporter/trace_spec.rb +43 -0
  97. data/spec/influx_reporter/transaction_spec.rb +98 -0
  98. data/spec/influx_reporter/util/inspector_spec.rb +41 -0
  99. data/spec/influx_reporter/util_spec.rb +20 -0
  100. data/spec/influx_reporter/worker_spec.rb +54 -0
  101. data/spec/influx_reporter_spec.rb +50 -0
  102. data/spec/spec_helper.rb +86 -0
  103. metadata +188 -0
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InfluxReporter
4
+ # @api private
5
+ class LineCache
6
+ CACHE = {}
7
+
8
+ def self.all(path)
9
+ CACHE[path] ||= begin
10
+ File.readlines(path)
11
+ rescue
12
+ []
13
+ end
14
+ end
15
+
16
+ def self.find(path, line)
17
+ return nil if line < 1
18
+ all(path)[line - 1]
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InfluxReporter
4
+ # @api private
5
+ module Logging
6
+ PREFIX = '** [InfluxReporter] '
7
+
8
+ def debug(*args, &block)
9
+ config.logger.debug(log_message(*args, &block)) if has_logger?
10
+ end
11
+
12
+ def info(*args, &block)
13
+ config.logger.info(log_message(*args, &block)) if has_logger?
14
+ end
15
+
16
+ def warn(*args, &block)
17
+ config.logger.warn(log_message(*args, &block)) if has_logger?
18
+ end
19
+
20
+ def error(*args, &block)
21
+ config.logger.error(log_message(*args, &block)) if has_logger?
22
+ end
23
+
24
+ def fatal(*args, &block)
25
+ config.logger.fatal(log_message(*args, &block)) if has_logger?
26
+ end
27
+
28
+ private
29
+
30
+ def has_logger?
31
+ respond_to?(:config) && config && config.logger
32
+ end
33
+
34
+ def log_message(*args)
35
+ msg = block_given? && yield || args.first
36
+ "#{PREFIX}#{msg}"
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InfluxReporter
4
+ class Middleware
5
+ def initialize(app)
6
+ @app = app
7
+ end
8
+
9
+ def call(env)
10
+ begin
11
+ transaction = InfluxReporter.transaction 'Rack', 'app.rack.request'
12
+ resp = @app.call env
13
+ resp[2] = BodyProxy.new(resp[2]) { transaction.submit(resp[0]) } if transaction
14
+ rescue Error
15
+ raise # Don't report InfluxReporter errors
16
+ rescue Exception => e
17
+ InfluxReporter.report e, rack_env: env
18
+ transaction&.submit(500)
19
+ raise
20
+ ensure
21
+ transaction&.release
22
+ end
23
+
24
+ if error = env['rack.exception'] || env['sinatra.error']
25
+ InfluxReporter.report error, rack_env: env
26
+ end
27
+
28
+ resp
29
+ end
30
+ end
31
+
32
+ class BodyProxy
33
+ def initialize(body, &block)
34
+ @body = body
35
+ @block = block
36
+ @closed = false
37
+ end
38
+
39
+ def respond_to?(*args)
40
+ super || @body.respond_to?(*args)
41
+ end
42
+
43
+ def close
44
+ return if closed?
45
+
46
+ @closed = true
47
+
48
+ begin
49
+ @body.close if @body.respond_to?(:close)
50
+ ensure
51
+ @block.call
52
+ end
53
+ end
54
+
55
+ def closed?
56
+ @closed
57
+ end
58
+
59
+ def method_missing(*args, &block)
60
+ @body.__send__(*args, &block)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InfluxReporter
4
+ # @api private
5
+ module Normalizers
6
+ class Normalizer
7
+ def self.register(name)
8
+ Normalizers.register name, self
9
+ end
10
+
11
+ def initialize(config)
12
+ @config = config
13
+ end
14
+
15
+ attr_reader :config
16
+ end
17
+
18
+ class Default < Normalizer
19
+ def normalize(_transaction, _name, _payload)
20
+ :skip
21
+ end
22
+ end
23
+
24
+ DEFAULT = Default.new nil
25
+
26
+ def self.register(name, cls)
27
+ (@registered ||= {})[name] = cls
28
+ end
29
+
30
+ def self.build(config)
31
+ normalizers = @registered.each_with_object({}) do |kv, coll|
32
+ name, cls = kv
33
+ coll[name] = cls.new config
34
+ end
35
+
36
+ Container.new(normalizers)
37
+ end
38
+
39
+ class Container
40
+ def initialize(normalizers)
41
+ @normalizers = normalizers
42
+ end
43
+
44
+ def keys
45
+ @normalizers.keys
46
+ end
47
+
48
+ def normalizer_for(name)
49
+ @normalizers[name] || DEFAULT
50
+ end
51
+
52
+ def normalize(transaction, name, payload)
53
+ normalizer_for(name).normalize transaction, name, payload
54
+ end
55
+ end
56
+
57
+ %w[
58
+ action_controller
59
+ active_record
60
+ action_view
61
+ ].each do |f|
62
+ require "influx_reporter/normalizers/#{f}"
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InfluxReporter
4
+ module Normalizers
5
+ module ActionController
6
+ class ProcessAction < Normalizer
7
+ register 'process_action.action_controller'
8
+ KIND = 'app.controller.action'
9
+
10
+ def normalize(transaction, _name, payload)
11
+ transaction.endpoint = endpoint(payload)
12
+ extra(transaction, payload)
13
+ [transaction.endpoint, KIND, nil]
14
+ end
15
+
16
+ private
17
+
18
+ # @param transaction [InfluxReporter::Transaction]
19
+ def extra(transaction, payload)
20
+ transaction.extra_tags do |tags|
21
+ config.payload_tags.each { |key| tags[key] = payload[key] }
22
+ end
23
+ transaction.extra_values do |values|
24
+ config.payload_values.each { |key| values[key] = payload[key] }
25
+ end
26
+ end
27
+
28
+ def endpoint(payload)
29
+ "#{payload[:controller]}##{payload[:action]}"
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InfluxReporter
4
+ module Normalizers
5
+ module ActionView
6
+ class RenderNormalizer < Normalizer
7
+ def normalize_render(payload, kind)
8
+ signature = path_for(payload[:identifier])
9
+
10
+ [signature, kind, nil]
11
+ end
12
+
13
+ private
14
+
15
+ def path_for(identifier)
16
+ return 'Unknown template' unless path = identifier
17
+ return path unless path.start_with?('/')
18
+
19
+ path && relative_path(path)
20
+ end
21
+
22
+ def relative_path(path)
23
+ root = config.view_paths.find { |vp| path.start_with? vp }
24
+ type = :app
25
+
26
+ unless root
27
+ root = Gem.path.find { |gp| path.start_with? gp }
28
+ type = :gem
29
+ end
30
+
31
+ return 'Absolute path' unless root
32
+
33
+ start = root.length
34
+ start += 1 if path[root.length] == '/'
35
+
36
+ if type == :gem
37
+ "$GEM_PATH/#{path[start, path.length]}"
38
+ else
39
+ path[start, path.length]
40
+ end
41
+ end
42
+ end
43
+
44
+ class RenderTemplate < RenderNormalizer
45
+ register 'render_template.action_view'
46
+ KIND = 'template.view'
47
+
48
+ def normalize(_transaction, _name, payload)
49
+ normalize_render(payload, KIND)
50
+ end
51
+ end
52
+
53
+ class RenderPartial < RenderNormalizer
54
+ register 'render_partial.action_view'
55
+ KIND = 'template.view.partial'
56
+
57
+ def normalize(_transaction, _name, payload)
58
+ normalize_render(payload, KIND)
59
+ end
60
+ end
61
+
62
+ class RenderCollection < RenderNormalizer
63
+ register 'render_collection.action_view'
64
+ KIND = 'template.view.collection'
65
+
66
+ def normalize(_transaction, _name, payload)
67
+ normalize_render(payload, KIND)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'influx_reporter/sql_summarizer'
4
+
5
+ module InfluxReporter
6
+ module Normalizers
7
+ module ActiveRecord
8
+ class SQL < Normalizer
9
+ register 'sql.active_record'
10
+
11
+ def initialize(*args)
12
+ super(*args)
13
+ adapter = begin
14
+ ::ActiveRecord::Base.connection.adapter_name.downcase
15
+ rescue
16
+ nil
17
+ end
18
+ @kind = "db.#{adapter || 'unknown'}.sql"
19
+ @sql_parser = SqlSummarizer.new config
20
+ end
21
+
22
+ def normalize(_transaction, _name, payload)
23
+ return :skip if %w[SCHEMA CACHE].include? payload[:name]
24
+
25
+ signature =
26
+ signature_for(payload[:sql]) || # SELECT FROM "users"
27
+ payload[:name] || # Users load
28
+ 'SQL'
29
+
30
+ return :skip if signature == 'SELECT FROM "schema_migrations"'
31
+
32
+ extra = payload[:sql].length < 256 ? { values: { sql: payload[:sql] } } : nil
33
+
34
+ [signature, @kind, extra]
35
+ end
36
+
37
+ private
38
+
39
+ def signature_for(sql)
40
+ @sql_parser.signature_for(sql)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InfluxReporter
4
+ # @api private
5
+ class SqlSummarizer
6
+ CACHE = {}.freeze
7
+ TBL = '[^ ]+'
8
+ REGEXES = {
9
+ /^SELECT .* FROM (#{TBL})/i => 'SELECT FROM ',
10
+ /^INSERT INTO (#{TBL})/i => 'INSERT INTO ',
11
+ /^UPDATE (#{TBL})/i => 'UPDATE ',
12
+ /^DELETE FROM (#{TBL})/i => 'DELETE FROM '
13
+ }.freeze
14
+
15
+ def initialize(config)
16
+ @config = config
17
+ end
18
+
19
+ def signature_for(sql)
20
+ return CACHE[sql] if CACHE[sql]
21
+
22
+ result = REGEXES.find do |regex, sig|
23
+ if match = sql.match(regex)
24
+ break sig + match[1]
25
+ end
26
+ end
27
+
28
+ result || 'SQL'
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/notifications'
4
+ require 'influx_reporter/normalizers'
5
+
6
+ module InfluxReporter
7
+ # @api private
8
+ class Subscriber
9
+ include Logging
10
+
11
+ def initialize(config, client)
12
+ @config = config
13
+ @client = client
14
+ @normalizers = Normalizers.build config
15
+ end
16
+
17
+ attr_reader :config
18
+
19
+ def register!
20
+ unregister! if @subscription
21
+ @subscription = ActiveSupport::Notifications.subscribe actions_regex, self
22
+ end
23
+
24
+ def unregister!
25
+ ActiveSupport::Notifications.unsubscribe @subscription
26
+ @subscription = nil
27
+ end
28
+
29
+ # AS::Notifications API
30
+
31
+ class Notification
32
+ def initialize(id, trace)
33
+ @id = id
34
+ @trace = trace
35
+ end
36
+ attr_reader :id, :trace
37
+ end
38
+
39
+ def start(name, id, payload)
40
+ return unless transaction = @client.current_transaction
41
+
42
+ normalized = @normalizers.normalize(transaction, name, payload)
43
+
44
+ trace = nil
45
+
46
+ unless normalized == :skip
47
+ sig, kind, extra = normalized
48
+
49
+ trace = Trace.new(transaction, sig, kind, transaction.running_traces, extra)
50
+ offset = transaction.current_offset
51
+
52
+ transaction.traces << trace
53
+
54
+ trace.start offset
55
+ end
56
+
57
+ transaction.notifications << Notification.new(id, trace)
58
+ end
59
+
60
+ def finish(_name, id, _payload)
61
+ return unless transaction = @client.current_transaction
62
+
63
+ while notification = transaction.notifications.pop
64
+ next unless notification.id == id
65
+ if trace = notification.trace
66
+ trace.done
67
+ end
68
+ return
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def actions_regex
75
+ @actions_regex ||= Regexp.new(
76
+ '(' + @normalizers.keys.join('|') + ')'
77
+ )
78
+ end
79
+ end
80
+ end