sapience 0.1.1 → 0.1.2

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.simplecov +6 -2
  3. data/.travis.yml +8 -25
  4. data/CODE_OF_CONDUCT.md +1 -1
  5. data/Gemfile +5 -5
  6. data/README.md +146 -15
  7. data/Rakefile +2 -1
  8. data/config/default.yml +29 -0
  9. data/dev-entrypoint.sh +10 -0
  10. data/docker-compose.yml +42 -0
  11. data/lib/sapience/appender/datadog.rb +100 -0
  12. data/lib/sapience/appender/file.rb +11 -22
  13. data/lib/sapience/appender/sentry.rb +61 -55
  14. data/lib/sapience/appender/wrapper.rb +5 -4
  15. data/lib/sapience/base.rb +21 -13
  16. data/lib/sapience/config_loader.rb +66 -0
  17. data/lib/sapience/configuration/grape.rb +9 -0
  18. data/lib/sapience/configuration.rb +32 -22
  19. data/lib/sapience/core_ext/hash.rb +25 -0
  20. data/lib/sapience/core_ext/thread.rb +2 -2
  21. data/lib/sapience/extensions/action_cable/tagged_logger_proxy.rb +6 -0
  22. data/lib/sapience/extensions/action_controller/live.rb +6 -0
  23. data/lib/sapience/extensions/action_controller/log_subscriber.rb +127 -0
  24. data/lib/sapience/extensions/action_controller/log_subscriber_processing.rb +24 -0
  25. data/lib/sapience/extensions/action_dispatch/debug_exceptions.rb +11 -0
  26. data/lib/sapience/extensions/action_view/log_subscriber.rb +9 -0
  27. data/lib/sapience/extensions/action_view/streaming_template_renderer.rb +11 -0
  28. data/lib/sapience/extensions/active_job/logging.rb +14 -0
  29. data/lib/sapience/extensions/active_model_serializers/logging.rb +14 -0
  30. data/lib/sapience/extensions/active_record/log_subscriber.rb +35 -0
  31. data/lib/sapience/extensions/grape/middleware/logging.rb +91 -0
  32. data/lib/sapience/extensions/grape/timings.rb +25 -0
  33. data/lib/sapience/extensions/rails/rack/logger.rb +11 -0
  34. data/lib/sapience/extensions/rails/rack/logger_info_as_debug.rb +24 -0
  35. data/lib/sapience/formatters/color.rb +3 -3
  36. data/lib/sapience/formatters/default.rb +1 -1
  37. data/lib/sapience/grape.rb +25 -0
  38. data/lib/sapience/log.rb +8 -6
  39. data/lib/sapience/loggable.rb +19 -17
  40. data/lib/sapience/logger.rb +46 -126
  41. data/lib/sapience/rails.rb +65 -8
  42. data/lib/sapience/sapience.rb +74 -73
  43. data/lib/sapience/subscriber.rb +5 -1
  44. data/lib/sapience/version.rb +1 -1
  45. data/lib/sapience.rb +4 -1
  46. data/sapience.gemspec +7 -4
  47. data/test_app/Gemfile +5 -1
  48. data/test_app/Rakefile +5 -1
  49. data/test_app/app/controllers/posts_controller.rb +12 -11
  50. data/test_app/config/application.rb +0 -1
  51. data/test_app/spec/controllers/posts_controller_spec.rb +1 -1
  52. data/test_app/spec/fixtures/sapience.yml +14 -0
  53. data/test_app/spec/helpers/posts_helper_spec.rb +1 -1
  54. data/test_app/spec/integration/sapience_spec.rb +14 -0
  55. data/test_app/spec/models/post_spec.rb +1 -1
  56. data/test_app/spec/models/user_spec.rb +1 -1
  57. data/test_app/spec/rails_helper.rb +15 -0
  58. data/test_app/spec/requests/posts_spec.rb +1 -1
  59. data/test_app/spec/routing/posts_routing_spec.rb +8 -10
  60. data/test_app/spec/spec_helper.rb +0 -44
  61. metadata +76 -11
  62. 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(ArgumentError, "Supplied logger does not implement:#{does_not_implement}. It must implement all of #{LEVELS[1..-1].inspect}")
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
- raise NotImplementedError.new('Logging Appender must implement #log(log)')
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
- def initialize(klass, level = nil, filter = nil) # rubocop:disable AbcSize, PerceivedComplexity, CyclomaticComplexity
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
- raise ':filter must be a Regexp or Proc' unless filter.nil? || filter.is_a?(Regexp) || filter.is_a?(Proc)
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) != nil
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
- def log_internal(level, index, message = nil, payload = nil, exception = nil) # rubocop:disable AbcSize, PerceivedComplexity, CyclomaticComplexity
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('lib', 'sapience')
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
- def measure_internal(level, index, message, params) # rubocop:disable AbcSize, PerceivedComplexity, CyclomaticComplexity
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('Mandatory block missing when :duration option is not supplied')
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
- raise exception
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
@@ -0,0 +1,9 @@
1
+ module Sapience
2
+ class Configuration
3
+ class Grape
4
+ def self.configure
5
+ Grape::API.send(:include, Sapience::Loggable)
6
+ end
7
+ end
8
+ end
9
+ 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
- def initialize
10
- # Initial default Level for all new instances of Sapience::Logger
11
- self.default_level = :info
12
- self.backtrace_level = :info
13
- self.application = "Sapience"
14
- self.host = nil
15
- self.ap_options = { multiline: false }
16
- self.appenders = [ { file: {io: STDOUT, formatter: :color } } ]
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
- if level.is_a?(Symbol)
38
- LEVELS.index(level)
39
- elsif level.is_a?(String)
40
- level = level.downcase.to_sym
41
- LEVELS.index(level)
42
- elsif level.is_a?(Integer) && defined?(::Logger::Severity)
43
- # Mapping of Rails and Ruby Logger levels to Sapience levels
44
- @@map_levels ||= begin
45
- levels = []
46
- ::Logger::Severity.constants.each do |constant|
47
- levels[::Logger::Severity.const_get(constant)] = LEVELS.find_index(constant.downcase.to_sym) || LEVELS.find_index(:error)
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
- @@map_levels[level]
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 unless defined?(:name)
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 unless defined?(:name=)
13
+ end
14
14
  end
@@ -0,0 +1,6 @@
1
+ class ActionCable::Connection::TaggedLoggerProxy # rubocop:disable ClassAndModuleChildren
2
+ def tag(logger, &block)
3
+ current_tags = tags - logger.tags
4
+ logger.tagged(*current_tags, &block)
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # Log actual exceptions, not a string representation
2
+ module ActionController::Live # rubocop:disable ClassAndModuleChildren
3
+ def log_error(exception)
4
+ logger.fatal(exception)
5
+ end
6
+ 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,9 @@
1
+ class ActionView::LogSubscriber # rubocop:disable ClassAndModuleChildren
2
+ def info(message = nil, &block)
3
+ debug(message, &block)
4
+ end
5
+
6
+ def info?
7
+ debug?
8
+ end
9
+ 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