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.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.rspec +2 -0
- data/.rubocop.yml +51 -0
- data/.travis.yml +42 -0
- data/.yardopts +3 -0
- data/Dockerfile +9 -0
- data/Gemfile +7 -0
- data/HISTORY.md +1 -0
- data/LICENSE +14 -0
- data/README.md +189 -0
- data/Rakefile +27 -0
- data/gemfiles/Gemfile.base +29 -0
- data/gemfiles/Gemfile.rails-3.2.x +4 -0
- data/gemfiles/Gemfile.rails-4.0.x +4 -0
- data/gemfiles/Gemfile.rails-4.1.x +4 -0
- data/gemfiles/Gemfile.rails-4.2.x +5 -0
- data/gemfiles/Gemfile.rails-5.0.x +5 -0
- data/gemfiles/Gemfile.rails-HEAD +7 -0
- data/influx_reporter.gemspec +26 -0
- data/lib/influx_reporter.rb +142 -0
- data/lib/influx_reporter/client.rb +306 -0
- data/lib/influx_reporter/configuration.rb +88 -0
- data/lib/influx_reporter/data_builders.rb +18 -0
- data/lib/influx_reporter/data_builders/error.rb +52 -0
- data/lib/influx_reporter/data_builders/transactions.rb +120 -0
- data/lib/influx_reporter/error.rb +7 -0
- data/lib/influx_reporter/error_message.rb +85 -0
- data/lib/influx_reporter/error_message/exception.rb +14 -0
- data/lib/influx_reporter/error_message/http.rb +73 -0
- data/lib/influx_reporter/error_message/stacktrace.rb +76 -0
- data/lib/influx_reporter/error_message/user.rb +25 -0
- data/lib/influx_reporter/filter.rb +66 -0
- data/lib/influx_reporter/http_client.rb +140 -0
- data/lib/influx_reporter/influx_db_client.rb +74 -0
- data/lib/influx_reporter/injections.rb +89 -0
- data/lib/influx_reporter/injections/json.rb +21 -0
- data/lib/influx_reporter/injections/net_http.rb +50 -0
- data/lib/influx_reporter/injections/redis.rb +25 -0
- data/lib/influx_reporter/injections/sequel.rb +37 -0
- data/lib/influx_reporter/injections/sinatra.rb +59 -0
- data/lib/influx_reporter/integration/delayed_job.rb +30 -0
- data/lib/influx_reporter/integration/rails/inject_exceptions_catcher.rb +25 -0
- data/lib/influx_reporter/integration/railtie.rb +56 -0
- data/lib/influx_reporter/integration/resque.rb +18 -0
- data/lib/influx_reporter/integration/sidekiq.rb +130 -0
- data/lib/influx_reporter/line_cache.rb +21 -0
- data/lib/influx_reporter/logging.rb +39 -0
- data/lib/influx_reporter/middleware.rb +63 -0
- data/lib/influx_reporter/normalizers.rb +65 -0
- data/lib/influx_reporter/normalizers/action_controller.rb +34 -0
- data/lib/influx_reporter/normalizers/action_view.rb +72 -0
- data/lib/influx_reporter/normalizers/active_record.rb +45 -0
- data/lib/influx_reporter/sql_summarizer.rb +31 -0
- data/lib/influx_reporter/subscriber.rb +80 -0
- data/lib/influx_reporter/tasks.rb +28 -0
- data/lib/influx_reporter/trace.rb +48 -0
- data/lib/influx_reporter/trace_helpers.rb +31 -0
- data/lib/influx_reporter/transaction.rb +114 -0
- data/lib/influx_reporter/util.rb +18 -0
- data/lib/influx_reporter/util/constantize.rb +56 -0
- data/lib/influx_reporter/util/inspector.rb +72 -0
- data/lib/influx_reporter/util/timestamp.rb +11 -0
- data/lib/influx_reporter/version.rb +5 -0
- data/lib/influx_reporter/worker.rb +56 -0
- data/spec/influx_reporter/client_spec.rb +264 -0
- data/spec/influx_reporter/configuration_spec.rb +42 -0
- data/spec/influx_reporter/data_builders/error_spec.rb +40 -0
- data/spec/influx_reporter/data_builders/transactions_spec.rb +48 -0
- data/spec/influx_reporter/error_message/exception_spec.rb +22 -0
- data/spec/influx_reporter/error_message/http_spec.rb +55 -0
- data/spec/influx_reporter/error_message/stacktrace_spec.rb +56 -0
- data/spec/influx_reporter/error_message/user_spec.rb +26 -0
- data/spec/influx_reporter/error_message_spec.rb +102 -0
- data/spec/influx_reporter/filter_spec.rb +33 -0
- data/spec/influx_reporter/injections/net_http_spec.rb +35 -0
- data/spec/influx_reporter/injections/sequel_spec.rb +33 -0
- data/spec/influx_reporter/injections/sinatra_spec.rb +13 -0
- data/spec/influx_reporter/injections_spec.rb +50 -0
- data/spec/influx_reporter/integration/delayed_job_spec.rb +37 -0
- data/spec/influx_reporter/integration/json_spec.rb +41 -0
- data/spec/influx_reporter/integration/rails_spec.rb +92 -0
- data/spec/influx_reporter/integration/redis_spec.rb +20 -0
- data/spec/influx_reporter/integration/resque_spec.rb +42 -0
- data/spec/influx_reporter/integration/sidekiq_spec.rb +40 -0
- data/spec/influx_reporter/integration/sinatra_spec.rb +99 -0
- data/spec/influx_reporter/line_cache_spec.rb +38 -0
- data/spec/influx_reporter/logging_spec.rb +49 -0
- data/spec/influx_reporter/middleware_spec.rb +32 -0
- data/spec/influx_reporter/normalizers/action_controller_spec.rb +37 -0
- data/spec/influx_reporter/normalizers/action_view_spec.rb +78 -0
- data/spec/influx_reporter/normalizers/active_record_spec.rb +70 -0
- data/spec/influx_reporter/normalizers_spec.rb +16 -0
- data/spec/influx_reporter/sql_summarizer_spec.rb +35 -0
- data/spec/influx_reporter/subscriber_spec.rb +83 -0
- data/spec/influx_reporter/trace_spec.rb +43 -0
- data/spec/influx_reporter/transaction_spec.rb +98 -0
- data/spec/influx_reporter/util/inspector_spec.rb +41 -0
- data/spec/influx_reporter/util_spec.rb +20 -0
- data/spec/influx_reporter/worker_spec.rb +54 -0
- data/spec/influx_reporter_spec.rb +50 -0
- data/spec/spec_helper.rb +86 -0
- 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
|