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,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :influx_reporter do
|
4
|
+
desc 'Notify InfluxReporter of a release'
|
5
|
+
task release: :environment do
|
6
|
+
unless rev = ENV['REV']
|
7
|
+
puts "Please specify a revision in an env variable\n" \
|
8
|
+
'eg. REV=abc123 rake influx_reporter:release'
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
# empty env means dev
|
13
|
+
ENV['RAILS_ENV'] ||= 'development'
|
14
|
+
|
15
|
+
# log to STDOUT
|
16
|
+
InfluxReporter::Client.inst.config.logger = Logger.new STDOUT
|
17
|
+
|
18
|
+
unless InfluxReporter.release({
|
19
|
+
rev: rev,
|
20
|
+
branch: ENV['BRANCH'],
|
21
|
+
status: 'completed'
|
22
|
+
}, inline: true)
|
23
|
+
exit 1 # release returned nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
task deployment: :release
|
28
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'influx_reporter/util'
|
4
|
+
|
5
|
+
module InfluxReporter
|
6
|
+
class Trace
|
7
|
+
DEFAULT_KIND = 'code.custom'
|
8
|
+
|
9
|
+
def initialize(transaction, signature, kind = nil, parents = [], extra = nil)
|
10
|
+
@transaction = transaction
|
11
|
+
@signature = signature
|
12
|
+
@kind = kind || DEFAULT_KIND
|
13
|
+
@parents = parents || []
|
14
|
+
@extra = extra || {}
|
15
|
+
|
16
|
+
@timestamp = Util.nanos
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_accessor :signature, :kind, :parents, :extra
|
20
|
+
attr_reader :transaction, :timestamp, :duration, :relative_start, :start_time
|
21
|
+
|
22
|
+
def start(relative_to)
|
23
|
+
@start_time = Util.nanos
|
24
|
+
@relative_start = start_time - relative_to
|
25
|
+
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def done(ms = Util.nanos)
|
30
|
+
@duration = ms - start_time
|
31
|
+
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def done?
|
36
|
+
!!duration
|
37
|
+
end
|
38
|
+
|
39
|
+
def running?
|
40
|
+
!done?
|
41
|
+
end
|
42
|
+
|
43
|
+
def inspect
|
44
|
+
info = %w[signature kind parents extra timestamp duration relative_start]
|
45
|
+
"<Trace #{info.map { |m| "#{m}:#{send(m).inspect}" }.join(' ')}>"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InfluxReporter
|
4
|
+
module TraceHelpers
|
5
|
+
module ClassMethods
|
6
|
+
def trace_class_method(method, signature, kind)
|
7
|
+
__trace_method_on(singleton_class, method, signature, kind)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def __trace_method_on(klass, method, signature, kind)
|
13
|
+
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
14
|
+
alias :"__without_opb_#{method}" :"#{method}"
|
15
|
+
|
16
|
+
def #{method}(*args, &block)
|
17
|
+
InfluxReporter.trace "#{signature}", "#{kind}" do
|
18
|
+
__without_opb_#{method}(*args, &block)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
RUBY
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.included(kls)
|
26
|
+
kls.class_eval do
|
27
|
+
extend ClassMethods
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'influx_reporter/util'
|
4
|
+
|
5
|
+
module InfluxReporter
|
6
|
+
class Transaction
|
7
|
+
ROOT_TRACE_NAME = 'transaction'
|
8
|
+
|
9
|
+
# @param client [InfluxReporter::Client]
|
10
|
+
# @param endpoint [String]
|
11
|
+
# @param kind [String]
|
12
|
+
# @param result [Integer]
|
13
|
+
def initialize(client, endpoint, kind = 'code.custom', result = nil)
|
14
|
+
@client = client
|
15
|
+
@config = client.config if client.respond_to?(:config)
|
16
|
+
@endpoint = endpoint
|
17
|
+
@kind = kind
|
18
|
+
@result = result
|
19
|
+
|
20
|
+
@timestamp = Util.nanos
|
21
|
+
|
22
|
+
@root_trace = Trace.new(self, ROOT_TRACE_NAME, ROOT_TRACE_NAME)
|
23
|
+
@traces = [@root_trace]
|
24
|
+
@notifications = []
|
25
|
+
|
26
|
+
@start_time = Util.nanos
|
27
|
+
@root_trace.start @start_time
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_accessor :endpoint, :kind, :result, :duration
|
31
|
+
attr_reader :timestamp, :start_time, :traces, :notifications, :root_trace, :config
|
32
|
+
|
33
|
+
def release
|
34
|
+
@client.current_transaction = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def done(result = nil)
|
38
|
+
@result = result
|
39
|
+
|
40
|
+
@root_trace.done Util.nanos
|
41
|
+
@duration = @root_trace.duration
|
42
|
+
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
def done?
|
47
|
+
@root_trace.done?
|
48
|
+
end
|
49
|
+
|
50
|
+
def submit(result = nil)
|
51
|
+
done result
|
52
|
+
|
53
|
+
release
|
54
|
+
|
55
|
+
@client.submit_transaction self
|
56
|
+
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
def trace(signature, kind = nil, extra = nil)
|
61
|
+
trace = Trace.new(self, signature, kind, running_traces, extra)
|
62
|
+
|
63
|
+
rel_time = current_offset
|
64
|
+
|
65
|
+
traces << trace
|
66
|
+
|
67
|
+
trace.start rel_time
|
68
|
+
|
69
|
+
return trace unless block_given?
|
70
|
+
|
71
|
+
begin
|
72
|
+
result = yield trace
|
73
|
+
ensure
|
74
|
+
trace.done
|
75
|
+
end
|
76
|
+
|
77
|
+
result
|
78
|
+
end
|
79
|
+
|
80
|
+
def extra_tags
|
81
|
+
@root_trace.extra[:tags] ||= {}
|
82
|
+
yield @root_trace.extra[:tags]
|
83
|
+
end
|
84
|
+
|
85
|
+
def extra_values
|
86
|
+
@root_trace.extra[:values] ||= {}
|
87
|
+
yield @root_trace.extra[:values]
|
88
|
+
end
|
89
|
+
|
90
|
+
def running_traces
|
91
|
+
traces.select(&:running?)
|
92
|
+
end
|
93
|
+
|
94
|
+
def current_trace
|
95
|
+
traces.reverse.find(&:running?)
|
96
|
+
end
|
97
|
+
|
98
|
+
def current_offset
|
99
|
+
if curr = current_trace
|
100
|
+
return curr.start_time
|
101
|
+
end
|
102
|
+
|
103
|
+
start_time
|
104
|
+
end
|
105
|
+
|
106
|
+
def inspect
|
107
|
+
info = %w[endpoint kind result duration timestamp start_time]
|
108
|
+
<<~TEXT
|
109
|
+
<Transaction #{info.map { |m| "#{m}:#{send(m).inspect}" }.join(' ')}>
|
110
|
+
#{traces.map(&:inspect).join("\n ")}"
|
111
|
+
TEXT
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InfluxReporter
|
4
|
+
# @api private
|
5
|
+
module Util
|
6
|
+
def self.nearest_minute
|
7
|
+
now = Time.now.utc
|
8
|
+
now - now.to_i % 60
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.nanos
|
12
|
+
now = Time.now.utc
|
13
|
+
now.to_i * 1_000_000_000 + now.nsec
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'influx_reporter/util/inspector'
|
18
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InfluxReporter
|
4
|
+
module Util
|
5
|
+
# From https://github.com/rails/rails/blob/861b70e92f4a1fc0e465ffcf2ee62680519c8f6f/activesupport/lib/active_support/inflector/methods.rb#L249
|
6
|
+
#
|
7
|
+
# Tries to find a constant with the name specified in the argument string.
|
8
|
+
#
|
9
|
+
# 'Module'.constantize # => Module
|
10
|
+
# 'Test::Unit'.constantize # => Test::Unit
|
11
|
+
#
|
12
|
+
# The name is assumed to be the one of a top-level constant, no matter
|
13
|
+
# whether it starts with "::" or not. No lexical context is taken into
|
14
|
+
# account:
|
15
|
+
#
|
16
|
+
# C = 'outside'
|
17
|
+
# module M
|
18
|
+
# C = 'inside'
|
19
|
+
# C # => 'inside'
|
20
|
+
# 'C'.constantize # => 'outside', same as ::C
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# NameError is raised when the name is not in CamelCase or the constant is
|
24
|
+
# unknown.
|
25
|
+
def self.constantize(camel_cased_word)
|
26
|
+
names = camel_cased_word.split('::')
|
27
|
+
|
28
|
+
# Trigger a built-in NameError exception including the ill-formed constant in the message.
|
29
|
+
Object.const_get(camel_cased_word) if names.empty?
|
30
|
+
|
31
|
+
# Remove the first blank element in case of '::ClassName' notation.
|
32
|
+
names.shift if names.size > 1 && names.first.empty?
|
33
|
+
|
34
|
+
names.inject(Object) do |constant, name|
|
35
|
+
if constant == Object
|
36
|
+
constant.const_get(name)
|
37
|
+
else
|
38
|
+
candidate = constant.const_get(name)
|
39
|
+
next candidate if constant.const_defined?(name, false)
|
40
|
+
next candidate unless Object.const_defined?(name)
|
41
|
+
|
42
|
+
# Go down the ancestors to check if it is owned directly. The check
|
43
|
+
# stops when we reach Object or the end of ancestors tree.
|
44
|
+
constant = constant.ancestors.inject do |const, ancestor|
|
45
|
+
break const if ancestor == Object
|
46
|
+
break ancestor if ancestor.const_defined?(name, false)
|
47
|
+
const
|
48
|
+
end
|
49
|
+
|
50
|
+
# owner is in Object, so raise
|
51
|
+
constant.const_get(name, false)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InfluxReporter
|
4
|
+
module Util
|
5
|
+
class Inspector
|
6
|
+
DEFAULTS = {
|
7
|
+
width: 120
|
8
|
+
}.freeze
|
9
|
+
|
10
|
+
NEWLINE = "\n"
|
11
|
+
SPACE = ' '
|
12
|
+
|
13
|
+
def initialize(config = {})
|
14
|
+
@config = DEFAULTS.merge(config)
|
15
|
+
end
|
16
|
+
|
17
|
+
def ms(nanos)
|
18
|
+
nanos.to_f / 1_000_000
|
19
|
+
end
|
20
|
+
|
21
|
+
def transaction(transaction, opts = {})
|
22
|
+
w = @config[:width].to_f
|
23
|
+
f = w / ms(transaction.duration)
|
24
|
+
|
25
|
+
traces = transaction.traces
|
26
|
+
|
27
|
+
traces = traces.each_with_object([]) do |trace, state|
|
28
|
+
descriptions = [
|
29
|
+
"#{trace.signature} - #{trace.kind}",
|
30
|
+
"transaction:#{trace.transaction.endpoint}"
|
31
|
+
]
|
32
|
+
|
33
|
+
if opts[:include_parents]
|
34
|
+
descriptions << "parents:#{trace.parents.map(&:signature).join(',')}"
|
35
|
+
end
|
36
|
+
|
37
|
+
descriptions << "duration:#{ms trace.duration}ms - rel:#{ms trace.relative_start}ms"
|
38
|
+
|
39
|
+
start_diff = ms(trace.start_time) - ms(transaction.start_time)
|
40
|
+
indent = (start_diff.floor * f).to_i
|
41
|
+
|
42
|
+
longest_desc_length = descriptions.map(&:length).max
|
43
|
+
desc_indent = [[indent, w - longest_desc_length].min, 0].max
|
44
|
+
|
45
|
+
lines = descriptions.map do |desc|
|
46
|
+
"#{SPACE * desc_indent}#{desc}"
|
47
|
+
end
|
48
|
+
|
49
|
+
if trace.duration
|
50
|
+
span = (ms(trace.duration) * f).ceil.to_i
|
51
|
+
lines << "#{SPACE * indent}+#{'-' * [(span - 2), 0].max}+"
|
52
|
+
else
|
53
|
+
lines << "#{SPACE * indent}UNFINISHED"
|
54
|
+
end
|
55
|
+
|
56
|
+
state << lines.join("\n")
|
57
|
+
end.join("\n")
|
58
|
+
|
59
|
+
<<-STR.gsub(/^\s{8}/, '')
|
60
|
+
\n#{'=' * w.to_i}
|
61
|
+
#{transaction.endpoint} - kind:#{transaction.kind} - #{transaction.duration.to_f / 1_000_000}ms
|
62
|
+
+#{'-' * (w.to_i - 2)}+
|
63
|
+
#{traces}
|
64
|
+
STR
|
65
|
+
rescue => e
|
66
|
+
puts e
|
67
|
+
puts e.backtrace.join("\n")
|
68
|
+
transaction.inspect
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InfluxReporter
|
4
|
+
# @api private
|
5
|
+
class Worker
|
6
|
+
include Logging
|
7
|
+
|
8
|
+
class PostRequest < Struct.new(:path, :data)
|
9
|
+
# require all parameters
|
10
|
+
def initialize(path, data)
|
11
|
+
super(path, data)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class StopMessage; end
|
16
|
+
|
17
|
+
def initialize(config, queue, influx_client)
|
18
|
+
@config = config
|
19
|
+
@queue = queue
|
20
|
+
@influx_client = influx_client
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :config
|
24
|
+
|
25
|
+
def run
|
26
|
+
loop do
|
27
|
+
while action = @queue.pop
|
28
|
+
case action
|
29
|
+
when PostRequest
|
30
|
+
process_request action
|
31
|
+
when StopMessage
|
32
|
+
Thread.exit
|
33
|
+
else
|
34
|
+
raise Error, "Unknown entity in worker queue: #{action.inspect}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def process_request(req)
|
43
|
+
unless config.validate!
|
44
|
+
info 'Invalid config - Skipping posting to influxdb'
|
45
|
+
return
|
46
|
+
end
|
47
|
+
|
48
|
+
begin
|
49
|
+
@influx_client.post(req.path, req.data)
|
50
|
+
rescue => e
|
51
|
+
fatal "Failed POST: #{e.inspect}"
|
52
|
+
debug e.backtrace.join("\n")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|