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,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InfluxReporter
|
4
|
+
class ErrorMessage
|
5
|
+
class Stacktrace
|
6
|
+
def initialize(config, frames)
|
7
|
+
@config = config
|
8
|
+
@frames = frames
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :frames
|
12
|
+
|
13
|
+
def self.from(config, exception)
|
14
|
+
return unless exception.backtrace
|
15
|
+
|
16
|
+
new(config, exception.backtrace.reverse.map do |line|
|
17
|
+
Frame.from_line config, line
|
18
|
+
end)
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_h
|
22
|
+
{ frames: frames.map(&:to_h) }
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
class Frame < Struct.new(:filename, :lineno, :abs_path, :function, :vars,
|
28
|
+
:pre_context, :context_line, :post_context)
|
29
|
+
|
30
|
+
BACKTRACE_REGEX = /^(.+?):(\d+)(?::in `(.+?)')?$/
|
31
|
+
|
32
|
+
class << self
|
33
|
+
def from_line(config, line)
|
34
|
+
_, abs_path, lineno, function = line.match(BACKTRACE_REGEX).to_a
|
35
|
+
lineno = lineno.to_i
|
36
|
+
filename = strip_load_path(abs_path)
|
37
|
+
|
38
|
+
if lines = config.context_lines
|
39
|
+
pre_context, context_line, post_context =
|
40
|
+
get_contextlines(abs_path, lineno, lines)
|
41
|
+
end
|
42
|
+
|
43
|
+
new filename, lineno, abs_path, function, nil,
|
44
|
+
pre_context, context_line, post_context
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def strip_load_path(path)
|
50
|
+
prefix = $LOAD_PATH
|
51
|
+
.map(&:to_s)
|
52
|
+
.select { |s| path.start_with?(s) }
|
53
|
+
.sort_by(&:length)
|
54
|
+
.last
|
55
|
+
|
56
|
+
return path unless prefix
|
57
|
+
|
58
|
+
path[prefix.chomp(File::SEPARATOR).length + 1..-1]
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_contextlines(path, line, context)
|
62
|
+
lines = (2 * context + 1).times.map do |i|
|
63
|
+
LineCache.find(path, line - context + i)
|
64
|
+
end
|
65
|
+
|
66
|
+
pre = lines[0..(context - 1)]
|
67
|
+
line = lines[context]
|
68
|
+
post = lines[(context + 1)..-1]
|
69
|
+
|
70
|
+
[pre, line, post]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InfluxReporter
|
4
|
+
class ErrorMessage
|
5
|
+
class User < Struct.new(:is_authenticated, :id, :username, :email)
|
6
|
+
CONTROLLER_KEY = 'action_controller.instance'
|
7
|
+
|
8
|
+
def self.from_rack_env(config, env)
|
9
|
+
controller = env[CONTROLLER_KEY]
|
10
|
+
method = config.current_user_method.to_sym
|
11
|
+
|
12
|
+
return unless controller && controller.respond_to?(method)
|
13
|
+
|
14
|
+
user = controller.send method
|
15
|
+
|
16
|
+
new(
|
17
|
+
true,
|
18
|
+
user.respond_to?(:id) ? user.id : nil,
|
19
|
+
user.respond_to?(:username) ? user.username : nil,
|
20
|
+
user.respond_to?(:email) ? user.email : nil
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InfluxReporter
|
4
|
+
# @api private
|
5
|
+
class Filter
|
6
|
+
MASK = '[FILTERED]'
|
7
|
+
|
8
|
+
def initialize(config)
|
9
|
+
@config = config
|
10
|
+
@params = rails_filters || config.filter_parameters
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :config
|
14
|
+
|
15
|
+
def apply(data, _opts = {})
|
16
|
+
case data
|
17
|
+
when String
|
18
|
+
apply_to_string data, opts = {}
|
19
|
+
when Hash
|
20
|
+
apply_to_hash data
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def apply_to_string(str, opts = {})
|
25
|
+
sep = opts[:separator] || '&'
|
26
|
+
kv_sep = opts[:kv_separator] || '='
|
27
|
+
|
28
|
+
str.split(sep).map do |kv|
|
29
|
+
key, value = kv.split(kv_sep)
|
30
|
+
[key, kv_sep, sanitize(key, value)].join
|
31
|
+
end.join(sep)
|
32
|
+
end
|
33
|
+
|
34
|
+
def apply_to_hash(hsh)
|
35
|
+
hsh.each_with_object({}) do |kv, filtered|
|
36
|
+
key, value = kv
|
37
|
+
filtered[key] = sanitize(key, value)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def sanitize(key, value)
|
42
|
+
should_filter?(key) ? MASK : value
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def should_filter?(key)
|
48
|
+
@params.any? do |param|
|
49
|
+
case param
|
50
|
+
when String, Symbol
|
51
|
+
key.to_s == param.to_s
|
52
|
+
when Regexp
|
53
|
+
param.match(key)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def rails_filters
|
59
|
+
if defined?(::Rails) && Rails.respond_to?(:application) && Rails.application
|
60
|
+
if filters = ::Rails.application.config.filter_parameters
|
61
|
+
filters.any? ? filters : nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require 'net/http'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
module InfluxReporter
|
8
|
+
# @api private
|
9
|
+
class HttpClient
|
10
|
+
include Logging
|
11
|
+
|
12
|
+
USER_AGENT = "influx_reporter-ruby/#{InfluxReporter::VERSION}"
|
13
|
+
|
14
|
+
attr_reader :state
|
15
|
+
attr_reader :adapter
|
16
|
+
|
17
|
+
def initialize(config)
|
18
|
+
@config = config
|
19
|
+
@adapter = HTTPAdapter.new(config)
|
20
|
+
@state = ClientState.new config
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :config
|
24
|
+
|
25
|
+
def post(resource, body)
|
26
|
+
path = abs_path(resource)
|
27
|
+
debug "POST #{resource}"
|
28
|
+
|
29
|
+
unless state.should_try?
|
30
|
+
info 'Temporarily skipping sending to InfluxReporter due to previous failure.'
|
31
|
+
return
|
32
|
+
end
|
33
|
+
|
34
|
+
body = JSON.dump(body) if body.is_a?(Hash) || body.is_a?(Array)
|
35
|
+
|
36
|
+
request = adapter.post path do |req|
|
37
|
+
req['Authorization'] = auth_header
|
38
|
+
req['Content-Type'] = 'application/json'
|
39
|
+
req['Content-Length'] = body.bytesize.to_s
|
40
|
+
req['User-Agent'] = USER_AGENT
|
41
|
+
req.body = body
|
42
|
+
end
|
43
|
+
|
44
|
+
begin
|
45
|
+
response = adapter.perform_request request
|
46
|
+
unless response.code.to_i.between?(200, 299)
|
47
|
+
raise Error, "Error from InfluxReporter server (#{response.code}): #{response.body}"
|
48
|
+
end
|
49
|
+
rescue
|
50
|
+
debug { JSON.parse(body).inspect }
|
51
|
+
@state.fail!
|
52
|
+
raise
|
53
|
+
end
|
54
|
+
|
55
|
+
@state.success!
|
56
|
+
|
57
|
+
response
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def auth_header
|
63
|
+
"Bearer #{@config.secret_token}"
|
64
|
+
end
|
65
|
+
|
66
|
+
def abs_path(path)
|
67
|
+
"/api/v1/organizations/#{@config.organization_id}" \
|
68
|
+
"/apps/#{@config.app_id}#{path}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def encode(event)
|
72
|
+
event_hash = @filter.process_event_hash(event.to_hash)
|
73
|
+
event_hash.to_json
|
74
|
+
end
|
75
|
+
|
76
|
+
class HTTPAdapter
|
77
|
+
def initialize(conf)
|
78
|
+
@config = conf
|
79
|
+
end
|
80
|
+
|
81
|
+
def post(path)
|
82
|
+
req = Net::HTTP::Post.new path
|
83
|
+
yield req if block_given?
|
84
|
+
req
|
85
|
+
end
|
86
|
+
|
87
|
+
def perform_request(req)
|
88
|
+
http.start do |http|
|
89
|
+
http.request req
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def http
|
96
|
+
return @http if @http
|
97
|
+
|
98
|
+
http = Net::HTTP.new server_uri.host, server_uri.port
|
99
|
+
http.use_ssl = @config.use_ssl
|
100
|
+
http.read_timeout = @config.timeout
|
101
|
+
http.open_timeout = @config.open_timeout
|
102
|
+
|
103
|
+
@http = http
|
104
|
+
end
|
105
|
+
|
106
|
+
def server_uri
|
107
|
+
@uri ||= URI(@config.server)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class ClientState
|
112
|
+
def initialize(config)
|
113
|
+
@config = config
|
114
|
+
@retry_number = 0
|
115
|
+
@last_check = Time.now.utc
|
116
|
+
end
|
117
|
+
|
118
|
+
def should_try?
|
119
|
+
return true if @status == :online
|
120
|
+
|
121
|
+
interval = ([@retry_number, 6].min**2) * @config.backoff_multiplier
|
122
|
+
return true if Time.now.utc - @last_check > interval
|
123
|
+
|
124
|
+
false
|
125
|
+
end
|
126
|
+
|
127
|
+
def fail!
|
128
|
+
@status = :error
|
129
|
+
@retry_number += 1
|
130
|
+
@last_check = Time.now.utc
|
131
|
+
end
|
132
|
+
|
133
|
+
def success!
|
134
|
+
@status = :online
|
135
|
+
@retry_number = 0
|
136
|
+
@last_check = nil
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'influxdb'
|
5
|
+
|
6
|
+
module InfluxReporter
|
7
|
+
# @api private
|
8
|
+
class InfluxDBClient
|
9
|
+
include Logging
|
10
|
+
|
11
|
+
attr_reader :state
|
12
|
+
attr_reader :client
|
13
|
+
|
14
|
+
# @param config [InfluxReporter::Configuration]
|
15
|
+
def initialize(config)
|
16
|
+
@config = config
|
17
|
+
@client = InfluxDB::Client.new config.database, config.influx_db.merge(time_precision: 'ns')
|
18
|
+
@state = ClientState.new config
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :config
|
22
|
+
|
23
|
+
def post(resource, data)
|
24
|
+
debug "POST #{resource}"
|
25
|
+
|
26
|
+
unless state.should_try?
|
27
|
+
info 'Temporarily skipping sending to InfluxReporter due to previous failure.'
|
28
|
+
return
|
29
|
+
end
|
30
|
+
|
31
|
+
begin
|
32
|
+
data = [data] unless data.is_a?(Array)
|
33
|
+
client.write_points data
|
34
|
+
rescue StandardError => e
|
35
|
+
debug { e.message }
|
36
|
+
@state.fail!
|
37
|
+
raise
|
38
|
+
end
|
39
|
+
|
40
|
+
@state.success!
|
41
|
+
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
class ClientState
|
46
|
+
def initialize(config)
|
47
|
+
@config = config
|
48
|
+
@retry_number = 0
|
49
|
+
@last_check = Time.now.utc
|
50
|
+
end
|
51
|
+
|
52
|
+
def should_try?
|
53
|
+
return true if @status == :online
|
54
|
+
|
55
|
+
interval = ([@retry_number, 6].min**2) * @config.backoff_multiplier
|
56
|
+
return true if Time.now.utc - @last_check > interval
|
57
|
+
|
58
|
+
false
|
59
|
+
end
|
60
|
+
|
61
|
+
def fail!
|
62
|
+
@status = :error
|
63
|
+
@retry_number += 1
|
64
|
+
@last_check = Time.now.utc
|
65
|
+
end
|
66
|
+
|
67
|
+
def success!
|
68
|
+
@status = :online
|
69
|
+
@retry_number = 0
|
70
|
+
@last_check = nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'influx_reporter/util/constantize'
|
4
|
+
|
5
|
+
module InfluxReporter
|
6
|
+
# @api private
|
7
|
+
module Injections
|
8
|
+
class Registration
|
9
|
+
def initialize(const_name, require_paths, injector)
|
10
|
+
@const_name = const_name
|
11
|
+
@require_paths = Array(require_paths)
|
12
|
+
@injector = injector
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :const_name, :require_paths, :injector
|
16
|
+
|
17
|
+
def install
|
18
|
+
injector.install
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.require_hooks
|
23
|
+
@require_hooks ||= {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.installed
|
27
|
+
@installed ||= {}
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.register(*args)
|
31
|
+
registration = Registration.new(*args)
|
32
|
+
|
33
|
+
if const_defined?(registration.const_name)
|
34
|
+
installed[registration.const_name] = registration
|
35
|
+
registration.install
|
36
|
+
else
|
37
|
+
register_require_hook registration
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.register_require_hook(registration)
|
42
|
+
registration.require_paths.each do |path|
|
43
|
+
require_hooks[path] = registration
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.hook_into(name)
|
48
|
+
return unless registration = lookup(name)
|
49
|
+
|
50
|
+
if const_defined?(registration.const_name)
|
51
|
+
installed[registration.const_name] = registration
|
52
|
+
registration.install
|
53
|
+
|
54
|
+
registration.require_paths.each do |path|
|
55
|
+
require_hooks.delete path
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.lookup(require_path)
|
61
|
+
require_hooks[require_path]
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.const_defined?(const_name)
|
65
|
+
const = begin
|
66
|
+
Util.constantize(const_name)
|
67
|
+
rescue
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
!!const
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# @api private
|
76
|
+
module ::Kernel
|
77
|
+
alias_method :require_without_op, :require
|
78
|
+
|
79
|
+
def require(name)
|
80
|
+
res = require_without_op name
|
81
|
+
|
82
|
+
begin
|
83
|
+
InfluxReporter::Injections.hook_into name
|
84
|
+
rescue Exception
|
85
|
+
end
|
86
|
+
|
87
|
+
res
|
88
|
+
end
|
89
|
+
end
|