asklytics-influxdb-rails 1.0.0.beta3
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 +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +78 -0
- data/.travis.yml +37 -0
- data/CHANGELOG.md +127 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +291 -0
- data/Rakefile +34 -0
- data/config.ru +7 -0
- data/gemfiles/Gemfile.rails-4.2.x +7 -0
- data/gemfiles/Gemfile.rails-5.0.x +7 -0
- data/gemfiles/Gemfile.rails-5.1.x +7 -0
- data/gemfiles/Gemfile.rails-5.2.x +7 -0
- data/influxdb-rails.gemspec +52 -0
- data/lib/httplog.rb +8 -0
- data/lib/influxdb-rails.rb +134 -0
- data/lib/influxdb/rails/air_traffic_controller.rb +41 -0
- data/lib/influxdb/rails/backtrace.rb +44 -0
- data/lib/influxdb/rails/configuration.rb +263 -0
- data/lib/influxdb/rails/context.rb +42 -0
- data/lib/influxdb/rails/exception_presenter.rb +94 -0
- data/lib/influxdb/rails/httplog/adapters/ethon.rb +62 -0
- data/lib/influxdb/rails/httplog/adapters/excon.rb +67 -0
- data/lib/influxdb/rails/httplog/adapters/http.rb +64 -0
- data/lib/influxdb/rails/httplog/adapters/httpclient.rb +76 -0
- data/lib/influxdb/rails/httplog/adapters/net_http.rb +53 -0
- data/lib/influxdb/rails/httplog/adapters/patron.rb +44 -0
- data/lib/influxdb/rails/httplog/helpers/al_helper.rb +12 -0
- data/lib/influxdb/rails/httplog/http_configuration.rb +55 -0
- data/lib/influxdb/rails/httplog/http_log.rb +332 -0
- data/lib/influxdb/rails/instrumentation.rb +34 -0
- data/lib/influxdb/rails/logger.rb +16 -0
- data/lib/influxdb/rails/middleware/hijack_render_exception.rb +16 -0
- data/lib/influxdb/rails/middleware/hijack_rescue_action_everywhere.rb +31 -0
- data/lib/influxdb/rails/middleware/render_subscriber.rb +28 -0
- data/lib/influxdb/rails/middleware/request_subscriber.rb +69 -0
- data/lib/influxdb/rails/middleware/simple_subscriber.rb +71 -0
- data/lib/influxdb/rails/middleware/sql_subscriber.rb +35 -0
- data/lib/influxdb/rails/middleware/subscriber.rb +44 -0
- data/lib/influxdb/rails/rack.rb +39 -0
- data/lib/influxdb/rails/railtie.rb +52 -0
- data/lib/influxdb/rails/sql/normalizer.rb +27 -0
- data/lib/influxdb/rails/sql/query.rb +32 -0
- data/lib/influxdb/rails/version.rb +5 -0
- data/lib/rails/generators/influxdb/influxdb_generator.rb +15 -0
- data/lib/rails/generators/influxdb/templates/initializer.rb +11 -0
- data/spec/controllers/widgets_controller_spec.rb +15 -0
- data/spec/integration/exceptions_spec.rb +37 -0
- data/spec/integration/integration_helper.rb +1 -0
- data/spec/integration/metrics_spec.rb +28 -0
- data/spec/shared_examples/tags.rb +42 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/rails4/app.rb +44 -0
- data/spec/support/rails5/app.rb +44 -0
- data/spec/support/views/widgets/_item.html.erb +1 -0
- data/spec/support/views/widgets/index.html.erb +5 -0
- data/spec/unit/backtrace_spec.rb +85 -0
- data/spec/unit/configuration_spec.rb +125 -0
- data/spec/unit/context_spec.rb +40 -0
- data/spec/unit/exception_presenter_spec.rb +23 -0
- data/spec/unit/influxdb_rails_spec.rb +78 -0
- data/spec/unit/middleware/render_subscriber_spec.rb +92 -0
- data/spec/unit/middleware/request_subscriber_spec.rb +91 -0
- data/spec/unit/middleware/sql_subscriber_spec.rb +81 -0
- data/spec/unit/sql/normalizer_spec.rb +15 -0
- data/spec/unit/sql/query_spec.rb +29 -0
- metadata +487 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
if defined?(Patron)
|
|
4
|
+
module Patron
|
|
5
|
+
class Session
|
|
6
|
+
alias orig_request request
|
|
7
|
+
def request(action_name, url, headers, options = {})
|
|
8
|
+
bm = Benchmark.realtime do
|
|
9
|
+
@response = orig_request(action_name, url, headers, options)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
if HttpLog.url_approved?(url)
|
|
13
|
+
# HttpLog.call(
|
|
14
|
+
# method: action_name,
|
|
15
|
+
# url: url,
|
|
16
|
+
# request_body: options[:data],
|
|
17
|
+
# request_headers: headers,
|
|
18
|
+
# response_code: @response.status,
|
|
19
|
+
# response_body: @response.body,
|
|
20
|
+
# response_headers: @response.headers,
|
|
21
|
+
# benchmark: bm,
|
|
22
|
+
# encoding: @response.headers['Content-Encoding'],
|
|
23
|
+
# content_type: @response.headers['Content-Type']
|
|
24
|
+
# )
|
|
25
|
+
|
|
26
|
+
HttpLog.save_in_db(
|
|
27
|
+
method: action_name,
|
|
28
|
+
url: url,
|
|
29
|
+
request_body: options[:data],
|
|
30
|
+
request_headers: headers,
|
|
31
|
+
response_code: @response.status,
|
|
32
|
+
response_body: @response.body,
|
|
33
|
+
response_headers: @response.headers,
|
|
34
|
+
benchmark: bm,
|
|
35
|
+
encoding: @response.headers['Content-Encoding'],
|
|
36
|
+
content_type: @response.headers['Content-Type']
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
@response
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HttpLog
|
|
4
|
+
class HttpConfiguration
|
|
5
|
+
attr_accessor :enabled,
|
|
6
|
+
:compact_log,
|
|
7
|
+
:json_log,
|
|
8
|
+
:logger,
|
|
9
|
+
:logger_method,
|
|
10
|
+
:severity,
|
|
11
|
+
:prefix,
|
|
12
|
+
:log_connect,
|
|
13
|
+
:log_request,
|
|
14
|
+
:log_headers,
|
|
15
|
+
:log_data,
|
|
16
|
+
:log_status,
|
|
17
|
+
:log_response,
|
|
18
|
+
:log_benchmark,
|
|
19
|
+
:url_whitelist_pattern,
|
|
20
|
+
:url_blacklist_pattern,
|
|
21
|
+
:color,
|
|
22
|
+
:prefix_data_lines,
|
|
23
|
+
:prefix_response_lines,
|
|
24
|
+
:prefix_line_numbers,
|
|
25
|
+
:filter_parameters,
|
|
26
|
+
:http_request_tracking_id,
|
|
27
|
+
:http_request_tracking_measurement
|
|
28
|
+
|
|
29
|
+
def initialize
|
|
30
|
+
@enabled = true
|
|
31
|
+
@compact_log = false
|
|
32
|
+
@json_log = false
|
|
33
|
+
@logger = Logger.new($stdout)
|
|
34
|
+
@logger_method = :log
|
|
35
|
+
@severity = Logger::Severity::DEBUG
|
|
36
|
+
@prefix = LOG_PREFIX
|
|
37
|
+
@log_connect = true
|
|
38
|
+
@http_request_tracking_id = "al-txn-id"
|
|
39
|
+
@http_request_tracking_measurement = "rails.http_client"
|
|
40
|
+
@log_request = true
|
|
41
|
+
@log_headers = false
|
|
42
|
+
@log_data = true
|
|
43
|
+
@log_status = true
|
|
44
|
+
@log_response = true
|
|
45
|
+
@log_benchmark = true
|
|
46
|
+
@url_whitelist_pattern = nil
|
|
47
|
+
@url_blacklist_pattern = nil
|
|
48
|
+
@color = false
|
|
49
|
+
@prefix_data_lines = false
|
|
50
|
+
@prefix_response_lines = false
|
|
51
|
+
@prefix_line_numbers = false
|
|
52
|
+
@filter_parameters = []
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'logger'
|
|
5
|
+
require 'benchmark'
|
|
6
|
+
require 'rainbow'
|
|
7
|
+
require 'rack'
|
|
8
|
+
|
|
9
|
+
module HttpLog
|
|
10
|
+
LOG_PREFIX = '[httplog] '.freeze
|
|
11
|
+
PARAM_MASK = '[FILTERED]'
|
|
12
|
+
|
|
13
|
+
class BodyParsingError < StandardError; end
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
attr_writer :http_configuration
|
|
17
|
+
|
|
18
|
+
def configuration
|
|
19
|
+
@http_configuration ||= HttpConfiguration.new
|
|
20
|
+
end
|
|
21
|
+
alias config configuration
|
|
22
|
+
|
|
23
|
+
def reset!
|
|
24
|
+
@http_configuration = nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def configure
|
|
28
|
+
yield(configuration)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def call(options = {})
|
|
32
|
+
if config.json_log
|
|
33
|
+
log_json(options)
|
|
34
|
+
elsif config.compact_log
|
|
35
|
+
log_compact(options[:method], options[:url], options[:response_code], options[:benchmark])
|
|
36
|
+
else
|
|
37
|
+
HttpLog.log_request(options[:method], options[:url] )
|
|
38
|
+
HttpLog.log_headers(options[:request_headers])
|
|
39
|
+
HttpLog.log_data(options[:request_body])
|
|
40
|
+
HttpLog.log_status(options[:response_code])
|
|
41
|
+
HttpLog.log_benchmark(options[:benchmark])
|
|
42
|
+
HttpLog.log_headers(options[:response_headers])
|
|
43
|
+
HttpLog.log_body(options[:response_body], options[:encoding], options[:content_type])
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def save_in_db(options = {})
|
|
48
|
+
#log("Connecting rails.http_client #{config.http_request_tracking_measurement} : #{config.http_request_tracking_id}")
|
|
49
|
+
x_request_id = "AL_NONE"
|
|
50
|
+
#al_txn_uid = nil
|
|
51
|
+
al_request_id = "AL_NONE"
|
|
52
|
+
al_request_guid = "AL_NONE"
|
|
53
|
+
al_request_client_id = "AL_NONE"
|
|
54
|
+
|
|
55
|
+
options[:request_headers].each do |header,value|
|
|
56
|
+
al_request_client_id = value if(header === "al-request-client-id")
|
|
57
|
+
#al_source = value if(header === "al-source")
|
|
58
|
+
al_request_id = Thread.current["al_request_id"] if Thread.current["al_request_id"]
|
|
59
|
+
al_request_guid = Thread.current["al_request_guid"] if Thread.current["al_request_guid"]
|
|
60
|
+
|
|
61
|
+
#al_txn_uid = value if(header === config.http_request_tracking_id)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
data = nil
|
|
65
|
+
|
|
66
|
+
if options[:timestamp]
|
|
67
|
+
data = {
|
|
68
|
+
values: {value: options[:benchmark] , al_request_id: al_request_id, al_request_guid: al_request_guid, al_request_client_id: al_request_client_id},
|
|
69
|
+
tags: {app_name: Rails.application.class.parent, url: options[:url], method: options[:method], response_code: options[:response_code], location: InfluxDB::Rails.current.location},
|
|
70
|
+
timestamp: options[:timestamp]
|
|
71
|
+
}
|
|
72
|
+
else
|
|
73
|
+
data = {
|
|
74
|
+
values: {value: options[:benchmark] , al_request_id: al_request_id, al_request_client_id: al_request_client_id},
|
|
75
|
+
tags: {app_name: Rails.application.class.parent, url: options[:url], method: options[:method], response_code: options[:response_code], location: InfluxDB::Rails.current.location},
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
InfluxDB::Rails.client.write_point config.http_request_tracking_measurement, data
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def series(options = {})
|
|
83
|
+
{
|
|
84
|
+
value: options[:benchmark]
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def tags(options = {})
|
|
90
|
+
tags = {
|
|
91
|
+
method: options[:method],
|
|
92
|
+
url: options[:url],
|
|
93
|
+
app_name: configuration.application_name,
|
|
94
|
+
host: Socket.gethostname
|
|
95
|
+
}
|
|
96
|
+
super(tags)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def url_approved?(url)
|
|
100
|
+
return false if config.url_blacklist_pattern && url.to_s.match(config.url_blacklist_pattern)
|
|
101
|
+
|
|
102
|
+
!config.url_whitelist_pattern || url.to_s.match(config.url_whitelist_pattern)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def log(msg)
|
|
106
|
+
return unless config.enabled
|
|
107
|
+
|
|
108
|
+
config.logger.public_send(config.logger_method, config.severity, colorize(prefix + msg))
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def log_connection(host, port = nil)
|
|
112
|
+
return if config.json_log || config.compact_log || !config.log_connect
|
|
113
|
+
|
|
114
|
+
log("Connecting: #{[host, port].compact.join(':')}")
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def log_request(method, uri)
|
|
118
|
+
return unless config.log_request
|
|
119
|
+
|
|
120
|
+
log("Sending: #{method.to_s.upcase} #{masked(uri)}")
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def log_headers(headers = {})
|
|
124
|
+
return unless config.log_headers
|
|
125
|
+
|
|
126
|
+
masked(headers).each do |key, value|
|
|
127
|
+
log("Header: #{key}: #{value}")
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def log_status(status)
|
|
132
|
+
return unless config.log_status
|
|
133
|
+
|
|
134
|
+
status = Rack::Utils.status_code(status) unless status == /\d{3}/
|
|
135
|
+
log("Status: #{status}")
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def log_benchmark(seconds)
|
|
139
|
+
return unless config.log_benchmark
|
|
140
|
+
|
|
141
|
+
log("Benchmark: #{seconds.to_f.round(6)} seconds")
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def log_body(body, encoding = nil, content_type = nil)
|
|
145
|
+
return unless config.log_response
|
|
146
|
+
|
|
147
|
+
data = parse_body(body, encoding, content_type)
|
|
148
|
+
|
|
149
|
+
if config.prefix_response_lines
|
|
150
|
+
log('Response:')
|
|
151
|
+
log_data_lines(data)
|
|
152
|
+
else
|
|
153
|
+
log("Response:\n#{data}")
|
|
154
|
+
end
|
|
155
|
+
rescue BodyParsingError => e
|
|
156
|
+
log("Response: #{e.message}")
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def parse_body(body, encoding, content_type)
|
|
160
|
+
unless text_based?(content_type)
|
|
161
|
+
raise BodyParsingError, "(not showing binary data)"
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
if body.is_a?(Net::ReadAdapter)
|
|
165
|
+
# open-uri wraps the response in a Net::ReadAdapter that defers reading
|
|
166
|
+
# the content, so the reponse body is not available here.
|
|
167
|
+
raise BodyParsingError, '(not available yet)'
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
if encoding =~ /gzip/ && body && !body.empty?
|
|
171
|
+
begin
|
|
172
|
+
sio = StringIO.new(body.to_s)
|
|
173
|
+
gz = Zlib::GzipReader.new(sio)
|
|
174
|
+
body = gz.read
|
|
175
|
+
rescue Zlib::GzipFile::Error
|
|
176
|
+
log("(gzip decompression failed)")
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
utf_encoded(body.to_s, content_type)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def log_data(data)
|
|
184
|
+
return unless config.log_data
|
|
185
|
+
|
|
186
|
+
data = utf_encoded(masked(data.dup).to_s) unless data.nil?
|
|
187
|
+
|
|
188
|
+
if config.prefix_data_lines
|
|
189
|
+
log('Data:')
|
|
190
|
+
log_data_lines(data)
|
|
191
|
+
else
|
|
192
|
+
log("Data: #{data}")
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def log_compact(method, uri, status, seconds)
|
|
197
|
+
return unless config.compact_log
|
|
198
|
+
status = Rack::Utils.status_code(status) unless status == /\d{3}/
|
|
199
|
+
log("#{method.to_s.upcase} #{masked(uri)} completed with status code #{status} in #{seconds.to_f.round(6)} seconds")
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def log_json(data = {})
|
|
203
|
+
return unless config.json_log
|
|
204
|
+
|
|
205
|
+
data[:response_code] = transform_response_code(data[:response_code]) if data[:response_code].is_a?(Symbol)
|
|
206
|
+
|
|
207
|
+
parsed_body = begin
|
|
208
|
+
parse_body(data[:response_body], data[:encoding], data[:content_type])
|
|
209
|
+
rescue BodyParsingError => e
|
|
210
|
+
e.message
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
if config.compact_log
|
|
214
|
+
log({
|
|
215
|
+
method: data[:method].to_s.upcase,
|
|
216
|
+
url: masked(data[:url]),
|
|
217
|
+
response_code: data[:response_code].to_i,
|
|
218
|
+
benchmark: data[:benchmark]
|
|
219
|
+
}.to_json)
|
|
220
|
+
else
|
|
221
|
+
log({
|
|
222
|
+
method: data[:method].to_s.upcase,
|
|
223
|
+
url: masked(data[:url]),
|
|
224
|
+
request_body: masked(data[:request_body]),
|
|
225
|
+
request_headers: masked(data[:request_headers].to_h),
|
|
226
|
+
response_code: data[:response_code].to_i,
|
|
227
|
+
response_body: parsed_body,
|
|
228
|
+
response_headers: data[:response_headers].to_h,
|
|
229
|
+
benchmark: data[:benchmark]
|
|
230
|
+
}.to_json)
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def transform_response_code(response_code_name)
|
|
235
|
+
Rack::Utils::HTTP_STATUS_CODES.detect { |_k, v| v.to_s.casecmp(response_code_name.to_s).zero? }.first
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def colorize(msg)
|
|
239
|
+
return msg unless config.color
|
|
240
|
+
if config.color.is_a?(Hash)
|
|
241
|
+
msg = Rainbow(msg).color(config.color[:color]) if config.color[:color]
|
|
242
|
+
msg = Rainbow(msg).bg(config.color[:background]) if config.color[:background]
|
|
243
|
+
else
|
|
244
|
+
msg = Rainbow(msg).color(config.color)
|
|
245
|
+
end
|
|
246
|
+
msg
|
|
247
|
+
rescue StandardError
|
|
248
|
+
warn "HTTPLOG CONFIGURATION ERROR: #{config.color} is not a valid color"
|
|
249
|
+
msg
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
private
|
|
253
|
+
|
|
254
|
+
def masked(msg, key=nil)
|
|
255
|
+
return msg if config.filter_parameters.empty?
|
|
256
|
+
return msg if msg.nil?
|
|
257
|
+
|
|
258
|
+
# If a key is given, msg is just the value and can be replaced
|
|
259
|
+
# in its entirety.
|
|
260
|
+
return (config.filter_parameters.include?(key.downcase) ? PARAM_MASK : msg) if key
|
|
261
|
+
|
|
262
|
+
# Otherwise, we'll parse Strings for key=valye pairs...
|
|
263
|
+
case msg
|
|
264
|
+
when *string_classes
|
|
265
|
+
config.filter_parameters.reduce(msg) do |m,key|
|
|
266
|
+
m.to_s.gsub(/(#{key})=[^&]+/i, "#{key}=#{PARAM_MASK}")
|
|
267
|
+
end
|
|
268
|
+
# ...and recurse over hashes
|
|
269
|
+
when *hash_classes
|
|
270
|
+
Hash[msg.map {|k,v| [k, masked(v, k)]}]
|
|
271
|
+
else
|
|
272
|
+
log "*** FILTERING NOT APPLIED BECAUSE #{msg.class} IS UNEXPECTED ***"
|
|
273
|
+
msg
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
def string_classes
|
|
278
|
+
@string_classes ||= begin
|
|
279
|
+
string_classes = [String]
|
|
280
|
+
string_classes << HTTP::Response::Body if defined?(HTTP::Response::Body)
|
|
281
|
+
string_classes << HTTP::URI if defined?(HTTP::URI)
|
|
282
|
+
string_classes << URI::HTTP if defined?(URI::HTTP)
|
|
283
|
+
string_classes << HTTP::FormData::Urlencoded if defined?(HTTP::FormData::Urlencoded)
|
|
284
|
+
string_classes
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def hash_classes
|
|
289
|
+
@hash_classes ||= begin
|
|
290
|
+
hash_classes = [Hash, Enumerator]
|
|
291
|
+
hash_classes << HTTP::Headers if defined?(HTTP::Headers)
|
|
292
|
+
hash_classes
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def utf_encoded(data, content_type = nil)
|
|
297
|
+
charset = content_type.to_s.scan(/; charset=(\S+)/).flatten.first || 'UTF-8'
|
|
298
|
+
begin
|
|
299
|
+
data.force_encoding(charset)
|
|
300
|
+
rescue StandardError
|
|
301
|
+
data.force_encoding('UTF-8')
|
|
302
|
+
end
|
|
303
|
+
data.encode('UTF-8', invalid: :replace, undef: :replace)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def text_based?(content_type)
|
|
307
|
+
# This is a very naive way of determining if the content type is text-based; but
|
|
308
|
+
# it will allow application/json and the like without having to resort to more
|
|
309
|
+
# heavy-handed checks.
|
|
310
|
+
content_type =~ /^text/ ||
|
|
311
|
+
content_type =~ /^application/ && !['application/octet-stream', 'application/pdf'].include?(content_type)
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def log_data_lines(data)
|
|
315
|
+
data.each_line.with_index do |line, row|
|
|
316
|
+
if config.prefix_line_numbers
|
|
317
|
+
log("#{row + 1}: #{line.chomp}")
|
|
318
|
+
else
|
|
319
|
+
log(line.strip)
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def prefix
|
|
325
|
+
if config.prefix.respond_to?(:call)
|
|
326
|
+
config.prefix.call
|
|
327
|
+
else
|
|
328
|
+
config.prefix.to_s
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module InfluxDB
|
|
2
|
+
module Rails
|
|
3
|
+
module Instrumentation # rubocop:disable Style/Documentation
|
|
4
|
+
def benchmark_for_instrumentation # rubocop:disable Metrics/MethodLength
|
|
5
|
+
start = Time.now
|
|
6
|
+
yield
|
|
7
|
+
|
|
8
|
+
c = InfluxDB::Rails.configuration
|
|
9
|
+
return if c.ignore_current_environment?
|
|
10
|
+
|
|
11
|
+
InfluxDB::Rails.client.write_point \
|
|
12
|
+
c.series_name_for_instrumentation,
|
|
13
|
+
values: {
|
|
14
|
+
value: ((Time.now - start) * 1000).ceil,
|
|
15
|
+
},
|
|
16
|
+
tags: configuration.tags_middleware.call(
|
|
17
|
+
method: "#{controller_name}##{action_name}",
|
|
18
|
+
server: Socket.gethostname
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.included(base)
|
|
23
|
+
base.extend(ClassMethods)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
module ClassMethods # rubocop:disable Style/Documentation
|
|
27
|
+
def instrument(methods = [])
|
|
28
|
+
methods = [methods] unless methods.is_a?(Array)
|
|
29
|
+
around_filter :benchmark_for_instrumentation, only: methods
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|