rails_semantic_logger 4.1.3 → 4.12.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.
Files changed (32) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +17 -10
  3. data/Rakefile +9 -9
  4. data/lib/rails_semantic_logger/action_controller/log_subscriber.rb +125 -0
  5. data/lib/rails_semantic_logger/action_mailer/log_subscriber.rb +135 -0
  6. data/lib/rails_semantic_logger/action_view/log_subscriber.rb +111 -0
  7. data/lib/rails_semantic_logger/active_job/log_subscriber.rb +126 -0
  8. data/lib/rails_semantic_logger/active_record/log_subscriber.rb +218 -0
  9. data/lib/rails_semantic_logger/delayed_job/plugin.rb +11 -0
  10. data/lib/rails_semantic_logger/engine.rb +189 -194
  11. data/lib/rails_semantic_logger/extensions/action_cable/tagged_logger_proxy.rb +1 -1
  12. data/lib/rails_semantic_logger/extensions/action_controller/live.rb +8 -4
  13. data/lib/rails_semantic_logger/extensions/action_dispatch/debug_exceptions.rb +11 -7
  14. data/lib/rails_semantic_logger/extensions/action_view/streaming_template_renderer.rb +10 -6
  15. data/lib/rails_semantic_logger/extensions/active_job/logging.rb +10 -6
  16. data/lib/rails_semantic_logger/extensions/active_model_serializers/logging.rb +12 -9
  17. data/lib/rails_semantic_logger/extensions/active_support/logger.rb +24 -0
  18. data/lib/rails_semantic_logger/extensions/active_support/tagged_logging.rb +8 -0
  19. data/lib/rails_semantic_logger/extensions/mongoid/config.rb +11 -0
  20. data/lib/rails_semantic_logger/extensions/rack/server.rb +12 -0
  21. data/lib/rails_semantic_logger/extensions/rails/server.rb +9 -5
  22. data/lib/rails_semantic_logger/options.rb +122 -0
  23. data/lib/rails_semantic_logger/rack/logger.rb +100 -0
  24. data/lib/rails_semantic_logger/version.rb +2 -2
  25. data/lib/rails_semantic_logger.rb +58 -3
  26. metadata +46 -24
  27. data/lib/rails_semantic_logger/extensions/action_controller/log_subscriber.rb +0 -107
  28. data/lib/rails_semantic_logger/extensions/action_controller/log_subscriber_processing.rb +0 -28
  29. data/lib/rails_semantic_logger/extensions/action_view/log_subscriber.rb +0 -12
  30. data/lib/rails_semantic_logger/extensions/active_record/log_subscriber.rb +0 -44
  31. data/lib/rails_semantic_logger/extensions/rails/rack/logger.rb +0 -63
  32. data/lib/rails_semantic_logger/extensions/rails/rack/logger_info_as_debug.rb +0 -30
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 607046851ee5bfb0335e577fbb87f9c3ae6ab93f
4
- data.tar.gz: 3375c64b102e5461ba50412a3495c898524f63df
2
+ SHA256:
3
+ metadata.gz: 73a37ac27f2cf94d083cc75aa557a778f32e2de852b3be6447b072dc1da89047
4
+ data.tar.gz: 681f2145e71def6b336792fe3d581dfd89da6fd3ca471befcbd8ffa4f11abb50
5
5
  SHA512:
6
- metadata.gz: 44f58b19256318c9fa3acb07fb08a862000acf7e15bfe27dda9fdb29052627da9c484da91fbefde02840cacdd8d7ec7556189a5297c898db39b9fde19fd1d491
7
- data.tar.gz: 860b06c8b1083e12befe6b074e1a8d0281186fa30a3f02f5342465bd44c50905a3703f90071b376a04db8c01b897d6273ab6b4fac4d81a0d6115cbd6ca5b5322
6
+ metadata.gz: 8d299a14cb4c3eaf282e4bf502d8e60c2a36095b138c17969cf0a8f79b0431a731adbe8963ae2017bf429f12b7f42fe65d5c199a2c5f88e984a71d4605766bea
7
+ data.tar.gz: d837dc46c0dcd38b19dcf2a62c926c91ef1775542d55c8919d3a76db2e62764ab8675890238da78ab19b157f3b920e9d92257d5b2e347bb66d0c5a2bde60441f
data/README.md CHANGED
@@ -1,26 +1,33 @@
1
- # rails_semantic_logger
2
- ![](https://img.shields.io/gem/v/rails_semantic_logger.svg) ![](https://img.shields.io/gem/dt/semantic_logger.svg) ![](https://img.shields.io/badge/status-production%20ready-blue.svg)
1
+ # Rails Semantic Logger
2
+ [![Gem Version](https://img.shields.io/gem/v/rails_semantic_logger.svg)](https://rubygems.org/gems/rails_semantic_logger) [![Build Status](https://github.com/reidmorrison/rails_semantic_logger/workflows/build/badge.svg)](https://github.com/reidmorrison/rails_semantic_logger/actions?query=workflow%3Abuild) [![Downloads](https://img.shields.io/gem/dt/rails_semantic_logger.svg)](https://rubygems.org/gems/rails_semantic_logger) [![License](https://img.shields.io/badge/license-Apache%202.0-brightgreen.svg)](http://opensource.org/licenses/Apache-2.0) ![](https://img.shields.io/badge/status-Production%20Ready-blue.svg)
3
3
 
4
- Next generation logging system for Rails to support highly concurrent, high throughput, low latency systems
4
+ Rails Semantic Logger replaces the Rails default logger with [Semantic Logger](https://logger.rocketjob.io/)
5
5
 
6
- Rails Semantic Logger replaces the Rails default logger with [Semantic Logger](http://github.com/rocketjob/semantic_logger)
7
-
8
- * http://github.com/rocketjob/rails_semantic_logger
6
+ * http://github.com/reidmorrison/rails_semantic_logger
9
7
 
10
8
  ## Documentation
11
9
 
12
- For complete documentation see: http://rocketjob.github.io/semantic_logger/rails
10
+ For complete documentation see: https://logger.rocketjob.io/rails
11
+
12
+ ## Upgrading to Semantic Logger v4.4
13
+
14
+ With some forking frameworks it is necessary to call `reopen` after the fork. With v4.4 the
15
+ workaround for Ruby 2.5 crashes is no longer needed.
16
+ I.e. Please remove the following line if being called anywhere:
17
+
18
+ ~~~ruby
19
+ SemanticLogger::Processor.instance.instance_variable_set(:@queue, Queue.new)
20
+ ~~~
13
21
 
14
22
  ## Supports
15
23
 
16
- - Ruby 1.9.3, 2.0, 2.1, 2.2 (or above) Or, JRuby 1.7, 9.0 (or above)
17
- - Rails 3.2, 4, 5 (or above)
24
+ For the complete list of supported Ruby and Rails versions, see the [Testing file](https://github.com/reidmorrison/rails_semantic_logger/blob/master/.github/workflows/ci.yml).
18
25
 
19
26
  ## Author
20
27
 
21
28
  [Reid Morrison](https://github.com/reidmorrison)
22
29
 
23
- [Contributors](https://github.com/rocketjob/rails_semantic_logger/graphs/contributors)
30
+ [Contributors](https://github.com/reidmorrison/rails_semantic_logger/graphs/contributors)
24
31
 
25
32
  ## Versioning
26
33
 
data/Rakefile CHANGED
@@ -1,30 +1,30 @@
1
1
  # Setup bundler to avoid having to run bundle exec all the time.
2
- require 'rubygems'
3
- require 'bundler/setup'
2
+ require "rubygems"
3
+ require "bundler/setup"
4
4
 
5
- require 'rake/testtask'
6
- require_relative 'lib/rails_semantic_logger/version'
5
+ require "rake/testtask"
6
+ require_relative "lib/rails_semantic_logger/version"
7
7
 
8
8
  task :gem do
9
- system 'gem build rails_semantic_logger.gemspec'
9
+ system "gem build rails_semantic_logger.gemspec"
10
10
  end
11
11
 
12
- task :publish => :gem do
12
+ task publish: :gem do
13
13
  system "git tag -a v#{RailsSemanticLogger::VERSION} -m 'Tagging #{RailsSemanticLogger::VERSION}'"
14
- system 'git push --tags'
14
+ system "git push --tags"
15
15
  system "gem push rails_semantic_logger-#{RailsSemanticLogger::VERSION}.gem"
16
16
  system "rm rails_semantic_logger-#{RailsSemanticLogger::VERSION}.gem"
17
17
  end
18
18
 
19
19
  Rake::TestTask.new(:test) do |t|
20
- t.pattern = 'test/**/*_test.rb'
20
+ t.pattern = "test/**/*_test.rb"
21
21
  t.verbose = true
22
22
  t.warning = false
23
23
  end
24
24
 
25
25
  # By default run tests against all appraisals
26
26
  if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
27
- require 'appraisal'
27
+ require "appraisal"
28
28
  task default: :appraisal
29
29
  else
30
30
  task default: :test
@@ -0,0 +1,125 @@
1
+ module RailsSemanticLogger
2
+ module ActionController
3
+ class LogSubscriber < ActiveSupport::LogSubscriber
4
+ INTERNAL_PARAMS = %w[controller action format _method only_path].freeze
5
+
6
+ # Log as debug to hide Processing messages in production
7
+ def start_processing(event)
8
+ controller_logger(event).debug { "Processing ##{event.payload[:action]}" }
9
+ end
10
+
11
+ def process_action(event)
12
+ controller_logger(event).info do
13
+ payload = event.payload.dup
14
+
15
+ # Unused, but needed for Devise 401 status code monkey patch to still work.
16
+ ::ActionController::Base.log_process_action(payload)
17
+
18
+ params = payload[:params]
19
+
20
+ if params.kind_of?(Hash) || params.kind_of?(::ActionController::Parameters)
21
+ # According to PR https://github.com/reidmorrison/rails_semantic_logger/pull/37/files
22
+ # params is not always a Hash.
23
+ payload[:params] = params.to_unsafe_h unless params.is_a?(Hash)
24
+ payload[:params] = params.except(*INTERNAL_PARAMS)
25
+
26
+ if payload[:params].empty?
27
+ payload.delete(:params)
28
+ elsif params["file"]
29
+ # When logging to JSON the entire tempfile is logged, so convert it to a string.
30
+ payload[:params]["file"] = params["file"].inspect
31
+ end
32
+ end
33
+
34
+ format = payload[:format]
35
+ payload[:format] = format.to_s.upcase if format.is_a?(Symbol)
36
+
37
+ payload[:path] = extract_path(payload[:path]) if payload.key?(:path)
38
+
39
+ exception = payload.delete(:exception)
40
+ if payload[:status].nil? && exception.present?
41
+ exception_class_name = exception.first
42
+ payload[:status] = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
43
+ end
44
+
45
+ # Rounds off the runtimes. For example, :view_runtime, :mongo_runtime, etc.
46
+ payload.keys.each do |key|
47
+ payload[key] = payload[key].to_f.round(2) if key.to_s =~ /(.*)_runtime/
48
+ end
49
+
50
+ # Rails 6+ includes allocation count
51
+ payload[:allocations] = event.allocations if event.respond_to?(:allocations)
52
+
53
+ payload[:status_message] = ::Rack::Utils::HTTP_STATUS_CODES[payload[:status]] if payload[:status].present?
54
+
55
+ # Causes excessive log output with Rails 5 RC1
56
+ payload.delete(:headers)
57
+ # Causes recursion in Rails 6.1.rc1
58
+ payload.delete(:request)
59
+ payload.delete(:response)
60
+
61
+ {
62
+ message: "Completed ##{payload[:action]}",
63
+ duration: event.duration,
64
+ payload: payload
65
+ }
66
+ end
67
+ end
68
+
69
+ def halted_callback(event)
70
+ controller_logger(event).info { "Filter chain halted as #{event.payload[:filter].inspect} rendered or redirected" }
71
+ end
72
+
73
+ def send_file(event)
74
+ controller_logger(event).info(message: "Sent file", payload: {path: event.payload[:path]}, duration: event.duration)
75
+ end
76
+
77
+ def redirect_to(event)
78
+ controller_logger(event).info(message: "Redirected to", payload: {location: event.payload[:location]})
79
+ end
80
+
81
+ def send_data(event)
82
+ controller_logger(event).info(message: "Sent data", payload: {file_name: event.payload[:filename]}, duration: event.duration)
83
+ end
84
+
85
+ def unpermitted_parameters(event)
86
+ controller_logger(event).debug do
87
+ unpermitted_keys = event.payload[:keys]
88
+ "Unpermitted parameter#{'s' if unpermitted_keys.size > 1}: #{unpermitted_keys.join(', ')}"
89
+ end
90
+ end
91
+
92
+ %w[write_fragment read_fragment exist_fragment?
93
+ expire_fragment expire_page write_page].each do |method|
94
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
95
+ def #{method}(event)
96
+ # enable_fragment_cache_logging as of Rails 5
97
+ return if ::ActionController::Base.respond_to?(:enable_fragment_cache_logging) && !::ActionController::Base.enable_fragment_cache_logging
98
+ controller_logger(event).info do
99
+ key_or_path = event.payload[:key] || event.payload[:path]
100
+ {message: "#{method.to_s.humanize} \#{key_or_path}", duration: event.duration}
101
+ end
102
+ end
103
+ METHOD
104
+ end
105
+
106
+ private
107
+
108
+ # Returns the logger for the supplied event.
109
+ # Returns ActionController::Base.logger if no controller is present
110
+ def controller_logger(event)
111
+ controller = event.payload[:controller]
112
+ return ::ActionController::Base.logger unless controller
113
+
114
+ controller.constantize.logger || ::ActionController::Base.logger
115
+ rescue NameError
116
+ ::ActionController::Base.logger
117
+ end
118
+
119
+ def extract_path(path)
120
+ index = path.index("?")
121
+ index ? path[0, index] : path
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,135 @@
1
+ require "active_support/log_subscriber"
2
+ require "action_mailer"
3
+
4
+ module RailsSemanticLogger
5
+ module ActionMailer
6
+ class LogSubscriber < ::ActiveSupport::LogSubscriber
7
+ def deliver(event)
8
+ ex = event.payload[:exception_object]
9
+ message_id = event.payload[:message_id]
10
+ duration = event.duration.round(1)
11
+ if ex
12
+ log_with_formatter event: event, log_duration: true, level: :error do |fmt|
13
+ {
14
+ message: "Error delivering mail #{message_id} (#{duration}ms)",
15
+ exception: ex
16
+ }
17
+ end
18
+ else
19
+ message = begin
20
+ if event.payload[:perform_deliveries]
21
+ "Delivered mail #{message_id} (#{duration}ms)"
22
+ else
23
+ "Skipped delivery of mail #{message_id} as `perform_deliveries` is false"
24
+ end
25
+ end
26
+ log_with_formatter event: event, log_duration: true do |fmt|
27
+ { message: message }
28
+ end
29
+ end
30
+ end
31
+
32
+ # An email was generated.
33
+ def process(event)
34
+ mailer = event.payload[:mailer]
35
+ action = event.payload[:action]
36
+ duration = event.duration.round(1)
37
+ log_with_formatter event: event do |fmt|
38
+ { message: "#{mailer}##{action}: processed outbound mail in #{duration}ms" }
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ class EventFormatter
45
+ def initialize(event:, log_duration: false)
46
+ @event = event
47
+ @log_duration = log_duration
48
+ end
49
+
50
+ def mailer
51
+ event.payload[:mailer]
52
+ end
53
+
54
+ def payload
55
+ {}.tap do |h|
56
+ h[:event_name] = event.name
57
+ h[:mailer] = mailer
58
+ h[:action] = action
59
+ h[:message_id] = event.payload[:message_id]
60
+ h[:perform_deliveries] = event.payload[:perform_deliveries]
61
+ h[:subject] = event.payload[:subject]
62
+ h[:to] = event.payload[:to]
63
+ h[:from] = event.payload[:from]
64
+ h[:bcc] = event.payload[:bcc]
65
+ h[:cc] = event.payload[:cc]
66
+ h[:date] = date
67
+ h[:duration] = event.duration.round(2) if log_duration?
68
+ h[:args] = formatted_args
69
+ end
70
+ end
71
+
72
+ def date
73
+ if event.payload[:date].respond_to?(:to_time)
74
+ event.payload[:date].to_time.utc
75
+ elsif event.payload[:date].is_a?(String)
76
+ Time.parse(date).utc
77
+ else
78
+ nil
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ attr_reader :event
85
+
86
+ def mailer
87
+ event.payload[:mailer]
88
+ end
89
+
90
+ def action
91
+ event.payload[:action]
92
+ end
93
+
94
+ def formatted_args
95
+ if defined?(mailer.contantize.log_arguments?) && !mailer.contantize.log_arguments?
96
+ ""
97
+ else
98
+ JSON.pretty_generate(event.payload[:args].map { |arg| format(arg) }) if event.payload[:args].present?
99
+ end
100
+ end
101
+
102
+ def format(arg)
103
+ case arg
104
+ when Hash
105
+ arg.transform_values { |value| format(value) }
106
+ when Array
107
+ arg.map { |value| format(value) }
108
+ when GlobalID::Identification
109
+ begin
110
+ arg.to_global_id
111
+ rescue StandardError
112
+ arg
113
+ end
114
+ else
115
+ arg
116
+ end
117
+ end
118
+
119
+ def log_duration?
120
+ @log_duration
121
+ end
122
+ end
123
+
124
+ def log_with_formatter(level: :info, **kw_args)
125
+ fmt = EventFormatter.new(**kw_args)
126
+ msg = yield fmt
127
+ logger.public_send(level, **msg, payload: fmt.payload)
128
+ end
129
+
130
+ def logger
131
+ ::ActionMailer::Base.logger
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,111 @@
1
+ require "active_support/log_subscriber"
2
+
3
+ module RailsSemanticLogger
4
+ module ActionView
5
+ # Output Semantic logs from Action View.
6
+ class LogSubscriber < ActiveSupport::LogSubscriber
7
+ VIEWS_PATTERN = %r{^app/views/}.freeze
8
+
9
+ class << self
10
+ attr_reader :logger
11
+ attr_accessor :rendered_log_level
12
+ end
13
+
14
+ def initialize
15
+ @rails_root = nil
16
+ super
17
+ end
18
+
19
+ def render_template(event)
20
+ return unless should_log?
21
+
22
+ payload = {
23
+ template: from_rails_root(event.payload[:identifier])
24
+ }
25
+ payload[:within] = from_rails_root(event.payload[:layout]) if event.payload[:layout]
26
+ payload[:allocations] = event.allocations if event.respond_to?(:allocations)
27
+
28
+ logger.measure(
29
+ self.class.rendered_log_level,
30
+ "Rendered",
31
+ payload: payload,
32
+ duration: event.duration
33
+ )
34
+ end
35
+
36
+ def render_partial(event)
37
+ return unless should_log?
38
+
39
+ payload = {
40
+ partial: from_rails_root(event.payload[:identifier])
41
+ }
42
+ payload[:within] = from_rails_root(event.payload[:layout]) if event.payload[:layout]
43
+ payload[:cache] = event.payload[:cache_hit] unless event.payload[:cache_hit].nil?
44
+ payload[:allocations] = event.allocations if event.respond_to?(:allocations)
45
+
46
+ logger.measure(
47
+ self.class.rendered_log_level,
48
+ "Rendered",
49
+ payload: payload,
50
+ duration: event.duration
51
+ )
52
+ end
53
+
54
+ def render_collection(event)
55
+ return unless should_log?
56
+
57
+ identifier = event.payload[:identifier] || "templates"
58
+
59
+ payload = {
60
+ template: from_rails_root(identifier),
61
+ count: event.payload[:count]
62
+ }
63
+ payload[:cache_hits] = event.payload[:cache_hits] if event.payload[:cache_hits]
64
+ payload[:allocations] = event.allocations if event.respond_to?(:allocations)
65
+
66
+ logger.measure(
67
+ self.class.rendered_log_level,
68
+ "Rendered",
69
+ payload: payload,
70
+ duration: event.duration
71
+ )
72
+ end
73
+
74
+ def start(name, id, payload)
75
+ if (name == "render_template.action_view") && should_log?
76
+ payload = {template: from_rails_root(payload[:identifier])}
77
+ payload[:within] = from_rails_root(payload[:layout]) if payload[:layout]
78
+
79
+ logger.send(self.class.rendered_log_level, message: "Rendering", payload: payload)
80
+ end
81
+
82
+ super
83
+ end
84
+
85
+ private
86
+
87
+ @logger = SemanticLogger["ActionView"]
88
+ @rendered_log_level = :debug
89
+
90
+ EMPTY = "".freeze
91
+
92
+ def should_log?
93
+ logger.send("#{self.class.rendered_log_level}?")
94
+ end
95
+
96
+ def from_rails_root(string)
97
+ string = string.sub(rails_root, EMPTY)
98
+ string.sub!(VIEWS_PATTERN, EMPTY)
99
+ string
100
+ end
101
+
102
+ def rails_root
103
+ @rails_root ||= "#{Rails.root}/"
104
+ end
105
+
106
+ def logger
107
+ self.class.logger
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,126 @@
1
+ require "active_job"
2
+
3
+ module RailsSemanticLogger
4
+ module ActiveJob
5
+ class LogSubscriber < ::ActiveSupport::LogSubscriber
6
+ def enqueue(event)
7
+ log_with_formatter event: event do |fmt|
8
+ {message: "Enqueued #{fmt.job_info}"}
9
+ end
10
+ end
11
+
12
+ def enqueue_at(event)
13
+ log_with_formatter event: event do |fmt|
14
+ {message: "Enqueued #{fmt.job_info} at #{fmt.scheduled_at}"}
15
+ end
16
+ end
17
+
18
+ def perform_start(event)
19
+ log_with_formatter event: event do |fmt|
20
+ {message: "Performing #{fmt.job_info}"}
21
+ end
22
+ end
23
+
24
+ def perform(event)
25
+ ex = event.payload[:exception_object]
26
+ if ex
27
+ log_with_formatter event: event, log_duration: true, level: :error do |fmt|
28
+ {
29
+ message: "Error performing #{fmt.job_info} in #{event.duration.round(2)}ms",
30
+ exception: ex
31
+ }
32
+ end
33
+ else
34
+ log_with_formatter event: event, log_duration: true do |fmt|
35
+ {message: "Performed #{fmt.job_info} in #{event.duration.round(2)}ms"}
36
+ end
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ class EventFormatter
43
+ def initialize(event:, log_duration: false)
44
+ @event = event
45
+ @log_duration = log_duration
46
+ end
47
+
48
+ def job_info
49
+ "#{job.class.name} (Job ID: #{job.job_id}) to #{queue_name}"
50
+ end
51
+
52
+ def payload
53
+ {}.tap do |h|
54
+ h[:event_name] = event.name
55
+ h[:adapter] = adapter_name
56
+ h[:queue] = job.queue_name
57
+ h[:job_class] = job.class.name
58
+ h[:job_id] = job.job_id
59
+ h[:provider_job_id] = job.try(:provider_job_id) # Not available in Rails 4.2
60
+ h[:duration] = event.duration.round(2) if log_duration?
61
+ h[:arguments] = formatted_args
62
+ end
63
+ end
64
+
65
+ def queue_name
66
+ adapter_name + "(#{job.queue_name})"
67
+ end
68
+
69
+ def scheduled_at
70
+ Time.at(event.payload[:job].scheduled_at).utc
71
+ end
72
+
73
+ private
74
+
75
+ attr_reader :event
76
+
77
+ def job
78
+ event.payload[:job]
79
+ end
80
+
81
+ def adapter_name
82
+ event.payload[:adapter].class.name.demodulize.remove("Adapter")
83
+ end
84
+
85
+ def formatted_args
86
+ if defined?(job.class.log_arguments?) && !job.class.log_arguments?
87
+ ""
88
+ else
89
+ JSON.pretty_generate(job.arguments.map { |arg| format(arg) })
90
+ end
91
+ end
92
+
93
+ def format(arg)
94
+ case arg
95
+ when Hash
96
+ arg.transform_values { |value| format(value) }
97
+ when Array
98
+ arg.map { |value| format(value) }
99
+ when GlobalID::Identification
100
+ begin
101
+ arg.to_global_id
102
+ rescue StandardError
103
+ arg
104
+ end
105
+ else
106
+ arg
107
+ end
108
+ end
109
+
110
+ def log_duration?
111
+ @log_duration
112
+ end
113
+ end
114
+
115
+ def log_with_formatter(level: :info, **kw_args)
116
+ fmt = EventFormatter.new(**kw_args)
117
+ msg = yield fmt
118
+ logger.public_send(level, **msg, payload: fmt.payload)
119
+ end
120
+
121
+ def logger
122
+ ::ActiveJob::Base.logger
123
+ end
124
+ end
125
+ end
126
+ end