sapience 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.simplecov +6 -2
- data/.travis.yml +8 -25
- data/CODE_OF_CONDUCT.md +1 -1
- data/Gemfile +5 -5
- data/README.md +146 -15
- data/Rakefile +2 -1
- data/config/default.yml +29 -0
- data/dev-entrypoint.sh +10 -0
- data/docker-compose.yml +42 -0
- data/lib/sapience/appender/datadog.rb +100 -0
- data/lib/sapience/appender/file.rb +11 -22
- data/lib/sapience/appender/sentry.rb +61 -55
- data/lib/sapience/appender/wrapper.rb +5 -4
- data/lib/sapience/base.rb +21 -13
- data/lib/sapience/config_loader.rb +66 -0
- data/lib/sapience/configuration/grape.rb +9 -0
- data/lib/sapience/configuration.rb +32 -22
- data/lib/sapience/core_ext/hash.rb +25 -0
- data/lib/sapience/core_ext/thread.rb +2 -2
- data/lib/sapience/extensions/action_cable/tagged_logger_proxy.rb +6 -0
- data/lib/sapience/extensions/action_controller/live.rb +6 -0
- data/lib/sapience/extensions/action_controller/log_subscriber.rb +127 -0
- data/lib/sapience/extensions/action_controller/log_subscriber_processing.rb +24 -0
- data/lib/sapience/extensions/action_dispatch/debug_exceptions.rb +11 -0
- data/lib/sapience/extensions/action_view/log_subscriber.rb +9 -0
- data/lib/sapience/extensions/action_view/streaming_template_renderer.rb +11 -0
- data/lib/sapience/extensions/active_job/logging.rb +14 -0
- data/lib/sapience/extensions/active_model_serializers/logging.rb +14 -0
- data/lib/sapience/extensions/active_record/log_subscriber.rb +35 -0
- data/lib/sapience/extensions/grape/middleware/logging.rb +91 -0
- data/lib/sapience/extensions/grape/timings.rb +25 -0
- data/lib/sapience/extensions/rails/rack/logger.rb +11 -0
- data/lib/sapience/extensions/rails/rack/logger_info_as_debug.rb +24 -0
- data/lib/sapience/formatters/color.rb +3 -3
- data/lib/sapience/formatters/default.rb +1 -1
- data/lib/sapience/grape.rb +25 -0
- data/lib/sapience/log.rb +8 -6
- data/lib/sapience/loggable.rb +19 -17
- data/lib/sapience/logger.rb +46 -126
- data/lib/sapience/rails.rb +65 -8
- data/lib/sapience/sapience.rb +74 -73
- data/lib/sapience/subscriber.rb +5 -1
- data/lib/sapience/version.rb +1 -1
- data/lib/sapience.rb +4 -1
- data/sapience.gemspec +7 -4
- data/test_app/Gemfile +5 -1
- data/test_app/Rakefile +5 -1
- data/test_app/app/controllers/posts_controller.rb +12 -11
- data/test_app/config/application.rb +0 -1
- data/test_app/spec/controllers/posts_controller_spec.rb +1 -1
- data/test_app/spec/fixtures/sapience.yml +14 -0
- data/test_app/spec/helpers/posts_helper_spec.rb +1 -1
- data/test_app/spec/integration/sapience_spec.rb +14 -0
- data/test_app/spec/models/post_spec.rb +1 -1
- data/test_app/spec/models/user_spec.rb +1 -1
- data/test_app/spec/rails_helper.rb +15 -0
- data/test_app/spec/requests/posts_spec.rb +1 -1
- data/test_app/spec/routing/posts_routing_spec.rb +8 -10
- data/test_app/spec/spec_helper.rb +0 -44
- metadata +76 -11
- data/lib/sapience/appender/statsd.rb +0 -68
@@ -38,6 +38,7 @@ module Sapience
|
|
38
38
|
# logger = Sapience['test']
|
39
39
|
# logger.info('Hello World', some: :payload)
|
40
40
|
#
|
41
|
+
# rubocop:disable LineLength
|
41
42
|
def initialize(options, &block)
|
42
43
|
# Backward compatibility
|
43
44
|
options = { logger: options } unless options.is_a?(Hash)
|
@@ -45,20 +46,20 @@ module Sapience
|
|
45
46
|
@logger = options.delete(:logger)
|
46
47
|
|
47
48
|
# Check if the custom appender responds to all the log levels. For example Ruby ::Logger
|
48
|
-
if does_not_implement = LEVELS[1..-1].find { |i| !@logger.respond_to?(i) }
|
49
|
-
fail
|
49
|
+
if (does_not_implement = LEVELS[1..-1].find { |i| !@logger.respond_to?(i) })
|
50
|
+
fail ArgumentError, "Supplied logger does not implement:#{does_not_implement}. It must implement all of #{LEVELS[1..-1].inspect}"
|
50
51
|
end
|
51
52
|
|
52
|
-
fail "Sapience::Appender::Wrapper missing mandatory parameter :logger" unless @logger
|
53
|
+
fail ArgumentError, "Sapience::Appender::Wrapper missing mandatory parameter :logger" unless @logger
|
53
54
|
super(options, &block)
|
54
55
|
end
|
56
|
+
# rubocop:enable LineLength
|
55
57
|
|
56
58
|
# Pass log calls to the underlying Rails, log4j or Ruby logger
|
57
59
|
# trace entries are mapped to debug since :trace is not supported by the
|
58
60
|
# Ruby or Rails Loggers
|
59
61
|
def log(log)
|
60
62
|
return false unless should_log?(log)
|
61
|
-
|
62
63
|
@logger.send(log.level == :trace ? :debug : log.level, formatter.call(log, self))
|
63
64
|
true
|
64
65
|
end
|
data/lib/sapience/base.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
module Sapience
|
2
|
+
# rubocop:disable ClassLength
|
2
3
|
class Base
|
3
4
|
# Class name to be logged
|
4
5
|
attr_accessor :name, :filter
|
@@ -176,7 +177,7 @@ module Sapience
|
|
176
177
|
|
177
178
|
# Write log data to underlying data storage
|
178
179
|
def log(_log_)
|
179
|
-
|
180
|
+
fail NotImplementedError, "Logging Appender must implement #log(log)"
|
180
181
|
end
|
181
182
|
|
182
183
|
private
|
@@ -198,10 +199,11 @@ module Sapience
|
|
198
199
|
# regular expression. All other messages will be ignored
|
199
200
|
# Proc: Only include log messages where the supplied Proc returns true
|
200
201
|
# The Proc must return true or false
|
201
|
-
|
202
|
+
# rubocop:disable AbcSize, PerceivedComplexity, CyclomaticComplexity, LineLength
|
203
|
+
def initialize(klass, level = nil, filter = nil)
|
202
204
|
# Support filtering all messages to this logger using a Regular Expression
|
203
205
|
# or Proc
|
204
|
-
|
206
|
+
fail ArgumentError, ":filter must be a Regexp or Proc" unless filter.nil? || filter.is_a?(Regexp) || filter.is_a?(Proc)
|
205
207
|
|
206
208
|
@filter = filter.is_a?(Regexp) ? filter.freeze : filter
|
207
209
|
@name = klass.is_a?(String) ? klass : klass.name
|
@@ -213,6 +215,7 @@ module Sapience
|
|
213
215
|
self.level = level
|
214
216
|
end
|
215
217
|
end
|
218
|
+
# rubocop:enable AbcSize, PerceivedComplexity, CyclomaticComplexity, LineLength
|
216
219
|
|
217
220
|
# Return the level index for fast comparisons
|
218
221
|
# Returns the global default level index if the level has not been explicitly
|
@@ -226,7 +229,7 @@ module Sapience
|
|
226
229
|
return true if @filter.nil?
|
227
230
|
|
228
231
|
if @filter.is_a?(Regexp)
|
229
|
-
(@filter =~ log.name)
|
232
|
+
!(@filter =~ log.name).nil?
|
230
233
|
elsif @filter.is_a?(Proc)
|
231
234
|
@filter.call(log) == true
|
232
235
|
end
|
@@ -239,7 +242,8 @@ module Sapience
|
|
239
242
|
end
|
240
243
|
|
241
244
|
# Log message at the specified level
|
242
|
-
|
245
|
+
# rubocop:disable AbcSize, PerceivedComplexity, CyclomaticComplexity, LineLength
|
246
|
+
def log_internal(level, index, message = nil, payload = nil, exception = nil)
|
243
247
|
# Exception being logged?
|
244
248
|
if exception.nil? && payload.nil? && message.respond_to?(:backtrace) && message.respond_to?(:message)
|
245
249
|
exception = message
|
@@ -281,7 +285,7 @@ module Sapience
|
|
281
285
|
log.message = payload.delete(:message)
|
282
286
|
log.metric = payload.delete(:metric)
|
283
287
|
log.metric_amount = payload.delete(:metric_amount) || 1
|
284
|
-
if duration = payload.delete(:duration)
|
288
|
+
if (duration = payload.delete(:duration))
|
285
289
|
return false if duration <= min_duration
|
286
290
|
log.duration = duration
|
287
291
|
end
|
@@ -290,8 +294,9 @@ module Sapience
|
|
290
294
|
|
291
295
|
self.log(log) if include_message?(log)
|
292
296
|
end
|
297
|
+
# rubocop:enable AbcSize, PerceivedComplexity, CyclomaticComplexity, LineLength
|
293
298
|
|
294
|
-
SELF_PATTERN = File.join(
|
299
|
+
SELF_PATTERN = File.join("lib", "sapience")
|
295
300
|
|
296
301
|
# Extract the callers backtrace leaving out Sapience
|
297
302
|
def extract_backtrace
|
@@ -303,13 +308,14 @@ module Sapience
|
|
303
308
|
end
|
304
309
|
|
305
310
|
# Measure the supplied block and log the message
|
306
|
-
|
311
|
+
# rubocop:disable AbcSize, PerceivedComplexity, CyclomaticComplexity, LineLength
|
312
|
+
def measure_internal(level, index, message, params)
|
307
313
|
start = Time.now
|
308
314
|
exception = nil
|
309
315
|
begin
|
310
316
|
if block_given?
|
311
317
|
result =
|
312
|
-
if silence_level = params[:silence]
|
318
|
+
if (silence_level = params[:silence])
|
313
319
|
# In case someone accidentally sets `silence: true` instead of `silence: :error`
|
314
320
|
silence_level = :error if silence_level == true
|
315
321
|
silence(silence_level) { yield(params) }
|
@@ -333,7 +339,7 @@ module Sapience
|
|
333
339
|
if block_given?
|
334
340
|
1000.0 * (end_time - start)
|
335
341
|
else
|
336
|
-
params[:duration] || fail(
|
342
|
+
params[:duration] || fail("Mandatory block missing when :duration option is not supplied")
|
337
343
|
end
|
338
344
|
|
339
345
|
# Add scoped payload
|
@@ -364,18 +370,20 @@ module Sapience
|
|
364
370
|
logged_exception = nil
|
365
371
|
backtrace = exception.backtrace
|
366
372
|
end
|
367
|
-
log = Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, logged_exception, metric, backtrace)
|
373
|
+
log = Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, logged_exception, metric, backtrace) # rubocop:disable LineLength
|
368
374
|
self.log(log) if include_message?(log)
|
369
|
-
|
375
|
+
fail exception
|
370
376
|
elsif duration >= min_duration
|
371
377
|
# Only log if the block took longer than 'min_duration' to complete
|
372
378
|
# Add caller stack trace
|
373
379
|
backtrace = extract_backtrace if index >= Sapience.config.backtrace_level_index
|
374
380
|
|
375
|
-
log = Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, nil, metric, backtrace)
|
381
|
+
log = Log.new(level, Thread.current.name, name, message, payload, end_time, duration, tags, index, nil, metric, backtrace) # rubocop:disable LineLength
|
376
382
|
self.log(log) if include_message?(log)
|
377
383
|
end
|
378
384
|
end
|
379
385
|
end
|
386
|
+
# rubocop:enable AbcSize, PerceivedComplexity, CyclomaticComplexity, LineLength
|
380
387
|
end
|
388
|
+
# rubocop:enable ClassLength
|
381
389
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "yaml"
|
5
|
+
require "pathname"
|
6
|
+
require "erb"
|
7
|
+
|
8
|
+
module Sapience
|
9
|
+
# This class represents the configuration of the RuboCop application
|
10
|
+
# and all its cops. A Config is associated with a YAML configuration
|
11
|
+
# file from which it was read. Several different Configs can be used
|
12
|
+
# during a run of the sapience program, if files in several
|
13
|
+
# directories are inspected.
|
14
|
+
module ConfigLoader
|
15
|
+
SAPIENCE_FILE = "sapience.yml".freeze
|
16
|
+
SAPIENCE_HOME = File.realpath(File.join(File.dirname(__FILE__), "..", ".."))
|
17
|
+
DEFAULT_FILE = File.join(SAPIENCE_HOME, "config", "default.yml")
|
18
|
+
|
19
|
+
def self.load_from_file
|
20
|
+
file_path = config_file_path
|
21
|
+
path = File.absolute_path(file_path)
|
22
|
+
load_yaml_configuration(path)
|
23
|
+
end
|
24
|
+
|
25
|
+
class << self
|
26
|
+
private
|
27
|
+
|
28
|
+
def config_file_path
|
29
|
+
return application_config_file if File.exist?(application_config_file)
|
30
|
+
|
31
|
+
DEFAULT_FILE
|
32
|
+
end
|
33
|
+
|
34
|
+
def application_config_file
|
35
|
+
File.join(Rack::Directory.new("").root, "config", SAPIENCE_FILE)
|
36
|
+
end
|
37
|
+
|
38
|
+
def load_yaml_configuration(absolute_path)
|
39
|
+
text = IO.read(absolute_path, encoding: "UTF-8")
|
40
|
+
erb = ERB.new(text)
|
41
|
+
yaml_code = erb.result
|
42
|
+
|
43
|
+
hash = yaml_safe_load(yaml_code, absolute_path) || {}
|
44
|
+
|
45
|
+
unless hash.is_a?(Hash)
|
46
|
+
fail(TypeError, "Malformed configuration in #{absolute_path}")
|
47
|
+
end
|
48
|
+
|
49
|
+
hash
|
50
|
+
end
|
51
|
+
|
52
|
+
def yaml_safe_load(yaml_code, filename)
|
53
|
+
if YAML.respond_to?(:safe_load) # Ruby 2.1+
|
54
|
+
if defined?(SafeYAML) && SafeYAML.respond_to?(:load)
|
55
|
+
SafeYAML.load(yaml_code, filename,
|
56
|
+
whitelisted_tags: %w(!ruby/regexp))
|
57
|
+
else
|
58
|
+
YAML.safe_load(yaml_code, [Regexp], [], false, filename)
|
59
|
+
end
|
60
|
+
else
|
61
|
+
YAML.load(yaml_code, filename)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -1,19 +1,29 @@
|
|
1
1
|
require "ostruct"
|
2
2
|
|
3
3
|
module Sapience
|
4
|
+
# rubocop:disable ClassVars
|
4
5
|
class Configuration
|
5
6
|
attr_reader :default_level, :backtrace_level, :backtrace_level_index
|
6
7
|
attr_writer :host
|
7
8
|
attr_accessor :application, :ap_options, :appenders
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
DEFAULT = {
|
11
|
+
log_level: :info,
|
12
|
+
application: "Sapience",
|
13
|
+
host: nil,
|
14
|
+
ap_options: { multiline: false },
|
15
|
+
appenders: [{ file: { io: STDOUT, formatter: :color } }],
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
# Initial default Level for all new instances of Sapience::Logger
|
19
|
+
def initialize(options = {}) # rubocop:disable AbcSize
|
20
|
+
@options = DEFAULT.merge(options.deep_symbolize_keys!)
|
21
|
+
self.default_level = @options[:log_level].to_sym
|
22
|
+
self.backtrace_level = @options[:log_level].to_sym
|
23
|
+
self.application = @options[:application]
|
24
|
+
self.host = @options[:host]
|
25
|
+
self.ap_options = @options[:ap_options]
|
26
|
+
self.appenders = @options[:appenders]
|
17
27
|
end
|
18
28
|
|
19
29
|
# Sets the global default log level
|
@@ -34,22 +44,22 @@ module Sapience
|
|
34
44
|
return if level.nil?
|
35
45
|
|
36
46
|
index =
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
end
|
49
|
-
levels
|
47
|
+
if level.is_a?(Symbol)
|
48
|
+
LEVELS.index(level)
|
49
|
+
elsif level.is_a?(String)
|
50
|
+
level = level.downcase.to_sym
|
51
|
+
LEVELS.index(level)
|
52
|
+
elsif level.is_a?(Integer) && defined?(::Logger::Severity)
|
53
|
+
# Mapping of Rails and Ruby Logger levels to Sapience levels
|
54
|
+
@@map_levels ||= begin
|
55
|
+
levels = []
|
56
|
+
::Logger::Severity.constants.each do |constant|
|
57
|
+
levels[::Logger::Severity.const_get(constant)] = LEVELS.find_index(constant.downcase.to_sym) || LEVELS.find_index(:error) # rubocop:disable LineLength
|
50
58
|
end
|
51
|
-
|
59
|
+
levels
|
52
60
|
end
|
61
|
+
@@map_levels[level]
|
62
|
+
end
|
53
63
|
fail "Invalid level:#{level.inspect} being requested. Must be one of #{LEVELS.inspect}" unless index
|
54
64
|
index
|
55
65
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class Hash
|
2
|
+
# Returns a Hash with all keys symbolized
|
3
|
+
def deep_symbolize_keys!
|
4
|
+
deep_transform_keys! { |key| key.to_sym rescue key } # rubocop:disable RescueModifier
|
5
|
+
end
|
6
|
+
|
7
|
+
def deep_transform_keys!(&block)
|
8
|
+
_deep_transform_keys_in_object!(self, &block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def _deep_transform_keys_in_object!(object, &block)
|
12
|
+
case object
|
13
|
+
when Hash
|
14
|
+
object.keys.each do |key|
|
15
|
+
value = object.delete(key)
|
16
|
+
object[yield(key)] = _deep_transform_keys_in_object!(value, &block)
|
17
|
+
end
|
18
|
+
object
|
19
|
+
when Array
|
20
|
+
object.map! { |e| _deep_transform_keys_in_object!(e, &block) }
|
21
|
+
else
|
22
|
+
object
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -5,10 +5,10 @@ class Thread
|
|
5
5
|
# String representation of this thread's object_id
|
6
6
|
def name
|
7
7
|
@name ||= object_id.to_s
|
8
|
-
end
|
8
|
+
end
|
9
9
|
|
10
10
|
# Set the name of this thread
|
11
11
|
def name=(name)
|
12
12
|
@name = name.to_s
|
13
|
-
end
|
13
|
+
end
|
14
14
|
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require "action_controller/log_subscriber"
|
2
|
+
|
3
|
+
class ActionController::LogSubscriber # rubocop:disable ClassAndModuleChildren
|
4
|
+
# Log as debug to hide Processing messages in production
|
5
|
+
def start_processing(event)
|
6
|
+
controller_logger(event).debug { "Processing ##{event.payload[:action]}" }
|
7
|
+
end
|
8
|
+
|
9
|
+
def process_action(event) # rubocop:disable AbcSize, CyclomaticComplexity, PerceivedComplexity
|
10
|
+
controller_logger(event).info do
|
11
|
+
payload = event.payload.dup
|
12
|
+
payload[:params].except!(*INTERNAL_PARAMS)
|
13
|
+
payload.delete(:params) if payload[:params].empty?
|
14
|
+
|
15
|
+
format = payload[:format]
|
16
|
+
payload[:format] = format.to_s.upcase if format.is_a?(Symbol)
|
17
|
+
|
18
|
+
payload[:path] = extract_path(payload[:path]) if payload.key?(:path)
|
19
|
+
|
20
|
+
exception = payload.delete(:exception)
|
21
|
+
if payload[:status].nil? && exception.present?
|
22
|
+
exception_class_name = exception.first
|
23
|
+
payload[:status] = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Rounds off the runtimes. For example, :view_runtime, :mongo_runtime, etc.
|
27
|
+
payload.keys.each do |key|
|
28
|
+
payload[key] = payload[key].to_f.round(2) if key.to_s.match(/(.*)_runtime/)
|
29
|
+
end
|
30
|
+
|
31
|
+
payload[:message] = "Completed ##{payload[:action]}"
|
32
|
+
payload[:status_message] = Rack::Utils::HTTP_STATUS_CODES[payload[:status]] if payload[:status].present?
|
33
|
+
payload[:duration] = event.duration
|
34
|
+
# Causes excessive log output with Rails 5 RC1
|
35
|
+
payload.delete(:headers)
|
36
|
+
payload
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def halted_callback(event)
|
41
|
+
controller_logger(event).info do
|
42
|
+
"Filter chain halted as #{event.payload[:filter].inspect} rendered or redirected"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def send_file(event)
|
47
|
+
controller_logger(event).info("Sent file") { { path: event.payload[:path], duration: event.duration } }
|
48
|
+
end
|
49
|
+
|
50
|
+
def redirect_to(event)
|
51
|
+
controller_logger(event).info("Redirected to") { { location: event.payload[:location] } }
|
52
|
+
end
|
53
|
+
|
54
|
+
def send_data(event)
|
55
|
+
controller_logger(event).info("Sent data") { { file_name: event.payload[:filename], duration: event.duration } }
|
56
|
+
end
|
57
|
+
|
58
|
+
def unpermitted_parameters(event)
|
59
|
+
controller_logger(event).debug do
|
60
|
+
unpermitted_keys = event.payload[:keys]
|
61
|
+
"Unpermitted parameter#{"s" if unpermitted_keys.size > 1}: #{unpermitted_keys.join(", ")}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
# Returns the logger for the supplied event.
|
68
|
+
# Returns ActionController::Base.logger if no controller is present
|
69
|
+
def controller_logger(event)
|
70
|
+
if (controller = event.payload[:controller])
|
71
|
+
begin
|
72
|
+
controller.constantize.logger
|
73
|
+
rescue NameError
|
74
|
+
ActionController::Base.logger
|
75
|
+
end
|
76
|
+
else
|
77
|
+
ActionController::Base.logger
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def extract_path(path)
|
82
|
+
index = path.index("?")
|
83
|
+
index ? path[0, index] : path
|
84
|
+
end
|
85
|
+
|
86
|
+
def write_fragment(event)
|
87
|
+
controller_logger(event).info do
|
88
|
+
key_or_path = event.payload[:key] || event.payload[:path]
|
89
|
+
{ message: "Write fragment #{key_or_path}", duration: event.duration }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def read_fragment(event)
|
94
|
+
controller_logger(event).info do
|
95
|
+
key_or_path = event.payload[:key] || event.payload[:path]
|
96
|
+
{ message: "Read fragment #{key_or_path}", duration: event.duration }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def exist_fragment(event)
|
101
|
+
controller_logger(event).info do
|
102
|
+
key_or_path = event.payload[:key] || event.payload[:path]
|
103
|
+
{ message: "Exist fragment #{key_or_path}", duration: event.duration }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def expire_fragment(event)
|
108
|
+
controller_logger(event).info do
|
109
|
+
key_or_path = event.payload[:key] || event.payload[:path]
|
110
|
+
{ message: "Expire fragment #{key_or_path}", duration: event.duration }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def expire_page(event)
|
115
|
+
controller_logger(event).info do
|
116
|
+
key_or_path = event.payload[:key] || event.payload[:path]
|
117
|
+
{ message: "Expire page #{key_or_path}", duration: event.duration }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def write_page(event)
|
122
|
+
controller_logger(event).info do
|
123
|
+
key_or_path = event.payload[:key] || event.payload[:path]
|
124
|
+
{ message: "Write page #{key_or_path}", duration: event.duration }
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "action_controller/log_subscriber"
|
2
|
+
|
3
|
+
class ActionController::LogSubscriber # rubocop:disable ClassAndModuleChildren
|
4
|
+
# Log as info to show Processing messages in production
|
5
|
+
def start_processing(event)
|
6
|
+
controller_logger(event).info { "Processing ##{event.payload[:action]}" }
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
# Returns the logger for the supplied event.
|
12
|
+
# Returns ActionController::Base.logger if no controller is present
|
13
|
+
def controller_logger(event)
|
14
|
+
if (controller = event.payload[:controller])
|
15
|
+
begin
|
16
|
+
controller.constantize.logger
|
17
|
+
rescue NameError
|
18
|
+
ActionController::Base.logger
|
19
|
+
end
|
20
|
+
else
|
21
|
+
ActionController::Base.logger
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# Log actual exceptions, not a string representation
|
2
|
+
|
3
|
+
class ActionDispatch::DebugExceptions # rubocop:disable ClassAndModuleChildren
|
4
|
+
private
|
5
|
+
|
6
|
+
def log_error(_request, wrapper)
|
7
|
+
ActiveSupport::Deprecation.silence do
|
8
|
+
ActionController::Base.logger.fatal(wrapper.exception)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# Log actual exceptions, not a string representation
|
2
|
+
|
3
|
+
class ActionView::StreamingTemplateRenderer # rubocop:disable ClassAndModuleChildren
|
4
|
+
class Body
|
5
|
+
private
|
6
|
+
|
7
|
+
def log_error(exception) #:nodoc:
|
8
|
+
ActionView::Base.logger.fatal(exception)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Patch ActiveJob logger
|
2
|
+
require "active_job/logging"
|
3
|
+
|
4
|
+
module ActiveJob::Logging # rubocop:disable ClassAndModuleChildren
|
5
|
+
include Sapience::Loggable
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
alias_method :tag_logger_old, :tag_logger
|
10
|
+
|
11
|
+
def tag_logger(*tags, &block)
|
12
|
+
logger.tagged(*tags, &block)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Patch ActiveModelSerializers logger
|
2
|
+
require "active_model_serializers/logging"
|
3
|
+
|
4
|
+
module ActiveModelSerializers::Logging # rubocop:disable ClassAndModuleChildren
|
5
|
+
include Sapience::Loggable
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
alias_method :tag_logger_old, :tag_logger
|
10
|
+
|
11
|
+
def tag_logger(*tags, &block)
|
12
|
+
logger.tagged(*tags, &block)
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
ActiveRecord::LogSubscriber # rubocop:disable Lint/Void
|
2
|
+
|
3
|
+
class ActiveRecord::LogSubscriber # rubocop:disable ClassAndModuleChildren
|
4
|
+
def sql(event) # rubocop:disable AbcSize
|
5
|
+
self.class.runtime += event.duration
|
6
|
+
|
7
|
+
return unless logger.debug?
|
8
|
+
|
9
|
+
payload = event.payload
|
10
|
+
name = payload[:name]
|
11
|
+
return if IGNORE_PAYLOAD_NAMES.include?(name)
|
12
|
+
|
13
|
+
log = {
|
14
|
+
message: name,
|
15
|
+
sql: payload[:sql],
|
16
|
+
duration: event.duration,
|
17
|
+
}
|
18
|
+
unless (payload[:binds] || []).empty?
|
19
|
+
log[:binds] = binds = {}
|
20
|
+
# Changed with Rails 5
|
21
|
+
if Rails.version.to_i >= 5
|
22
|
+
payload[:binds].each do |attr|
|
23
|
+
attr_name, value = render_bind(attr)
|
24
|
+
binds[attr_name] = value
|
25
|
+
end
|
26
|
+
else
|
27
|
+
payload[:binds].each do |col, v|
|
28
|
+
attr_name, value = render_bind(col, v)
|
29
|
+
binds[attr_name] = value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
debug(log)
|
34
|
+
end
|
35
|
+
end
|