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.
- checksums.yaml +5 -5
- data/README.md +17 -10
- data/Rakefile +9 -9
- data/lib/rails_semantic_logger/action_controller/log_subscriber.rb +125 -0
- data/lib/rails_semantic_logger/action_mailer/log_subscriber.rb +135 -0
- data/lib/rails_semantic_logger/action_view/log_subscriber.rb +111 -0
- data/lib/rails_semantic_logger/active_job/log_subscriber.rb +126 -0
- data/lib/rails_semantic_logger/active_record/log_subscriber.rb +218 -0
- data/lib/rails_semantic_logger/delayed_job/plugin.rb +11 -0
- data/lib/rails_semantic_logger/engine.rb +189 -194
- data/lib/rails_semantic_logger/extensions/action_cable/tagged_logger_proxy.rb +1 -1
- data/lib/rails_semantic_logger/extensions/action_controller/live.rb +8 -4
- data/lib/rails_semantic_logger/extensions/action_dispatch/debug_exceptions.rb +11 -7
- data/lib/rails_semantic_logger/extensions/action_view/streaming_template_renderer.rb +10 -6
- data/lib/rails_semantic_logger/extensions/active_job/logging.rb +10 -6
- data/lib/rails_semantic_logger/extensions/active_model_serializers/logging.rb +12 -9
- data/lib/rails_semantic_logger/extensions/active_support/logger.rb +24 -0
- data/lib/rails_semantic_logger/extensions/active_support/tagged_logging.rb +8 -0
- data/lib/rails_semantic_logger/extensions/mongoid/config.rb +11 -0
- data/lib/rails_semantic_logger/extensions/rack/server.rb +12 -0
- data/lib/rails_semantic_logger/extensions/rails/server.rb +9 -5
- data/lib/rails_semantic_logger/options.rb +122 -0
- data/lib/rails_semantic_logger/rack/logger.rb +100 -0
- data/lib/rails_semantic_logger/version.rb +2 -2
- data/lib/rails_semantic_logger.rb +58 -3
- metadata +46 -24
- data/lib/rails_semantic_logger/extensions/action_controller/log_subscriber.rb +0 -107
- data/lib/rails_semantic_logger/extensions/action_controller/log_subscriber_processing.rb +0 -28
- data/lib/rails_semantic_logger/extensions/action_view/log_subscriber.rb +0 -12
- data/lib/rails_semantic_logger/extensions/active_record/log_subscriber.rb +0 -44
- data/lib/rails_semantic_logger/extensions/rails/rack/logger.rb +0 -63
- 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
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 73a37ac27f2cf94d083cc75aa557a778f32e2de852b3be6447b072dc1da89047
|
4
|
+
data.tar.gz: 681f2145e71def6b336792fe3d581dfd89da6fd3ca471befcbd8ffa4f11abb50
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d299a14cb4c3eaf282e4bf502d8e60c2a36095b138c17969cf0a8f79b0431a731adbe8963ae2017bf429f12b7f42fe65d5c199a2c5f88e984a71d4605766bea
|
7
|
+
data.tar.gz: d837dc46c0dcd38b19dcf2a62c926c91ef1775542d55c8919d3a76db2e62764ab8675890238da78ab19b157f3b920e9d92257d5b2e347bb66d0c5a2bde60441f
|
data/README.md
CHANGED
@@ -1,26 +1,33 @@
|
|
1
|
-
#
|
2
|
-
 ](https://rubygems.org/gems/rails_semantic_logger) [](https://github.com/reidmorrison/rails_semantic_logger/actions?query=workflow%3Abuild) [](https://rubygems.org/gems/rails_semantic_logger) [](http://opensource.org/licenses/Apache-2.0) 
|
3
3
|
|
4
|
-
|
4
|
+
Rails Semantic Logger replaces the Rails default logger with [Semantic Logger](https://logger.rocketjob.io/)
|
5
5
|
|
6
|
-
|
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:
|
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
|
-
|
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/
|
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
|
3
|
-
require
|
2
|
+
require "rubygems"
|
3
|
+
require "bundler/setup"
|
4
4
|
|
5
|
-
require
|
6
|
-
require_relative
|
5
|
+
require "rake/testtask"
|
6
|
+
require_relative "lib/rails_semantic_logger/version"
|
7
7
|
|
8
8
|
task :gem do
|
9
|
-
system
|
9
|
+
system "gem build rails_semantic_logger.gemspec"
|
10
10
|
end
|
11
11
|
|
12
|
-
task :
|
12
|
+
task publish: :gem do
|
13
13
|
system "git tag -a v#{RailsSemanticLogger::VERSION} -m 'Tagging #{RailsSemanticLogger::VERSION}'"
|
14
|
-
system
|
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 =
|
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
|
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
|