rails_semantic_logger 4.20.0 → 5.0.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 +4 -4
- data/README.md +55 -98
- data/Rakefile +7 -4
- data/lib/rails_semantic_logger/action_controller/log_subscriber.rb +86 -16
- data/lib/rails_semantic_logger/action_mailer/log_subscriber.rb +36 -22
- data/lib/rails_semantic_logger/action_view/log_subscriber.rb +74 -40
- data/lib/rails_semantic_logger/active_job/log_subscriber.rb +216 -7
- data/lib/rails_semantic_logger/active_record/log_subscriber.rb +62 -160
- data/lib/rails_semantic_logger/appenders.rb +91 -0
- data/lib/rails_semantic_logger/engine.rb +47 -36
- data/lib/rails_semantic_logger/extensions/action_cable/tagged_logger_proxy.rb +44 -3
- data/lib/rails_semantic_logger/extensions/action_dispatch/debug_exceptions.rb +5 -14
- data/lib/rails_semantic_logger/extensions/active_job/logging.rb +2 -2
- data/lib/rails_semantic_logger/extensions/active_model_serializers/logging.rb +2 -2
- data/lib/rails_semantic_logger/extensions/active_support/logger.rb +24 -15
- data/lib/rails_semantic_logger/extensions/rails/server.rb +1 -1
- data/lib/rails_semantic_logger/extensions/sidekiq/sidekiq.rb +4 -4
- data/lib/rails_semantic_logger/options.rb +171 -20
- data/lib/rails_semantic_logger/rack/logger.rb +6 -13
- data/lib/rails_semantic_logger/sidekiq/defaults.rb +4 -2
- data/lib/rails_semantic_logger/sidekiq/job_logger.rb +13 -5
- data/lib/rails_semantic_logger/solid_queue/log_subscriber.rb +179 -0
- data/lib/rails_semantic_logger/version.rb +1 -1
- data/lib/rails_semantic_logger.rb +81 -26
- metadata +15 -21
- data/lib/rails_semantic_logger/delayed_job/plugin.rb +0 -11
- data/lib/rails_semantic_logger/extensions/active_support/log_subscriber.rb +0 -13
- data/lib/rails_semantic_logger/extensions/rack/server.rb +0 -12
- data/lib/rails_semantic_logger/extensions/rackup/server.rb +0 -12
|
@@ -1,42 +1,61 @@
|
|
|
1
|
+
# This subscriber is a reimplementation of Rails' own ActiveRecord::LogSubscriber that emits
|
|
2
|
+
# structured (message + payload) log entries instead of formatted text. When Rails changes its
|
|
3
|
+
# subscriber, those changes must be brought across here. Compare against the upstream source for
|
|
4
|
+
# each supported Rails version:
|
|
5
|
+
#
|
|
6
|
+
# Rails 8.1: https://github.com/rails/rails/blob/8-1-stable/activerecord/lib/active_record/log_subscriber.rb
|
|
7
|
+
# Rails 8.0: https://github.com/rails/rails/blob/8-0-stable/activerecord/lib/active_record/log_subscriber.rb
|
|
8
|
+
# Rails 7.2: https://github.com/rails/rails/blob/7-2-stable/activerecord/lib/active_record/log_subscriber.rb
|
|
9
|
+
#
|
|
10
|
+
# The upstream subscriber is functionally identical across Rails 7.2, 8.0, and 8.1 for everything
|
|
11
|
+
# that affects structured output. The only differences between those versions are in the internal
|
|
12
|
+
# `query_source_location` helper (used by `verbose_query_logs` to print the "↳ source" line) and a
|
|
13
|
+
# `:nodoc:` comment, neither of which changes the event payload. As a result no version-specific
|
|
14
|
+
# behavior is required here.
|
|
15
|
+
#
|
|
16
|
+
# As of Rails 8.1 there is also a parallel ActiveRecord::StructuredEventSubscriber (it emits
|
|
17
|
+
# structured events to Rails.event rather than text to Rails.logger). It is the authoritative,
|
|
18
|
+
# Rails-maintained reference for field names and payload shape; the fields below (e.g. :lock_wait
|
|
19
|
+
# and the strict_loading_violation payload) follow it. We do not use it (see CLAUDE.md), but diff
|
|
20
|
+
# against it when adding fields:
|
|
21
|
+
#
|
|
22
|
+
# Rails 8.1: https://github.com/rails/rails/blob/8-1-stable/activerecord/lib/active_record/structured_event_subscriber.rb
|
|
1
23
|
module RailsSemanticLogger
|
|
2
24
|
module ActiveRecord
|
|
3
25
|
class LogSubscriber < ActiveSupport::LogSubscriber
|
|
4
26
|
IGNORE_PAYLOAD_NAMES = %w[SCHEMA EXPLAIN].freeze
|
|
5
27
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
end
|
|
28
|
+
def strict_loading_violation(event)
|
|
29
|
+
return unless logger.debug?
|
|
9
30
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
::ActiveRecord::RuntimeRegistry.sql_runtime = value
|
|
14
|
-
end
|
|
31
|
+
payload = event.payload
|
|
32
|
+
owner = payload[:owner]
|
|
33
|
+
reflection = payload[:reflection]
|
|
15
34
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
end
|
|
35
|
+
log_payload = {owner: owner.name, association: reflection.name}
|
|
36
|
+
log_payload[:class] = reflection.klass.name unless reflection.polymorphic?
|
|
19
37
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
end
|
|
38
|
+
logger.debug(
|
|
39
|
+
message: reflection.strict_loading_violation_message(owner),
|
|
40
|
+
payload: log_payload
|
|
41
|
+
)
|
|
25
42
|
end
|
|
26
43
|
|
|
27
44
|
def sql(event)
|
|
28
|
-
self.class.runtime += event.duration if self.class.respond_to?(:runtime)
|
|
29
45
|
return unless logger.debug?
|
|
30
46
|
|
|
31
47
|
payload = event.payload
|
|
32
48
|
name = payload[:name]
|
|
33
49
|
return if IGNORE_PAYLOAD_NAMES.include?(name)
|
|
34
50
|
|
|
35
|
-
log_payload
|
|
36
|
-
log_payload[:binds]
|
|
37
|
-
log_payload[:allocations] = event.allocations
|
|
38
|
-
log_payload[:cached]
|
|
39
|
-
|
|
51
|
+
log_payload = {sql: payload[:sql]}
|
|
52
|
+
log_payload[:binds] = bind_values(payload) unless (payload[:binds] || []).empty?
|
|
53
|
+
log_payload[:allocations] = event.allocations
|
|
54
|
+
log_payload[:cached] = true if payload[:cached]
|
|
55
|
+
if payload[:async]
|
|
56
|
+
log_payload[:async] = true
|
|
57
|
+
log_payload[:lock_wait] = payload[:lock_wait] if payload[:lock_wait]
|
|
58
|
+
end
|
|
40
59
|
|
|
41
60
|
log = {
|
|
42
61
|
message: name,
|
|
@@ -54,137 +73,38 @@ module RailsSemanticLogger
|
|
|
54
73
|
|
|
55
74
|
private
|
|
56
75
|
|
|
57
|
-
@logger = SemanticLogger["ActiveRecord"]
|
|
58
|
-
|
|
59
76
|
# When multiple values are received for a single bound field, it is converted into an array
|
|
60
77
|
def add_bind_value(binds, key, value)
|
|
61
78
|
key = key.downcase.to_sym unless key.nil?
|
|
62
79
|
|
|
63
|
-
if
|
|
64
|
-
value = "[FILTERED]"
|
|
65
|
-
elsif binds.key?(key)
|
|
66
|
-
value = (Array(binds[key]) << value)
|
|
67
|
-
end
|
|
68
|
-
|
|
80
|
+
value = (Array(binds[key]) << value) if binds.key?(key)
|
|
69
81
|
binds[key] = value
|
|
70
82
|
end
|
|
71
83
|
|
|
72
|
-
def rails_filter_params_include?(key)
|
|
73
|
-
filter_parameters = Rails.configuration.filter_parameters
|
|
74
|
-
|
|
75
|
-
return filter_parameters.first.match? key if filter_parameters.first.is_a? Regexp
|
|
76
|
-
|
|
77
|
-
filter_parameters.include? key
|
|
78
|
-
end
|
|
79
|
-
|
|
80
84
|
def logger
|
|
81
|
-
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
#
|
|
85
|
-
# Rails 3,4,5 hell trying to get the bind values
|
|
86
|
-
#
|
|
87
|
-
|
|
88
|
-
def bind_values_v3(payload)
|
|
89
|
-
binds = {}
|
|
90
|
-
payload[:binds].each do |col, v|
|
|
91
|
-
if col
|
|
92
|
-
add_bind_value(binds, col.name, v)
|
|
93
|
-
else
|
|
94
|
-
binds[nil] = v
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
binds
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def bind_values_v4(payload)
|
|
101
|
-
binds = {}
|
|
102
|
-
payload[:binds].each do |col, v|
|
|
103
|
-
attr_name, value = render_bind(col, v)
|
|
104
|
-
add_bind_value(binds, attr_name, value)
|
|
105
|
-
end
|
|
106
|
-
binds
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
def bind_values_v5_0_0(payload)
|
|
110
|
-
binds = {}
|
|
111
|
-
payload[:binds].each do |attr|
|
|
112
|
-
attr_name, value = render_bind(attr)
|
|
113
|
-
add_bind_value(binds, attr_name, value)
|
|
114
|
-
end
|
|
115
|
-
binds
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def bind_values_v5_0_3(payload)
|
|
119
|
-
binds = {}
|
|
120
|
-
casted_params = type_casted_binds(payload[:binds], payload[:type_casted_binds])
|
|
121
|
-
payload[:binds].zip(casted_params).map do |attr, value|
|
|
122
|
-
attr_name, value = render_bind(attr, value)
|
|
123
|
-
add_bind_value(binds, attr_name, value)
|
|
124
|
-
end
|
|
125
|
-
binds
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
def bind_values_v5_1_5(payload)
|
|
129
|
-
binds = {}
|
|
130
|
-
casted_params = type_casted_binds(payload[:type_casted_binds])
|
|
131
|
-
payload[:binds].zip(casted_params).map do |attr, value|
|
|
132
|
-
attr_name, value = render_bind(attr, value)
|
|
133
|
-
add_bind_value(binds, attr_name, value)
|
|
134
|
-
end
|
|
135
|
-
binds
|
|
85
|
+
::ActiveRecord::Base.logger
|
|
136
86
|
end
|
|
137
87
|
|
|
138
|
-
def
|
|
88
|
+
def bind_values(payload)
|
|
139
89
|
binds = {}
|
|
140
90
|
casted_params = type_casted_binds(payload[:type_casted_binds])
|
|
141
91
|
payload[:binds].each_with_index do |attr, i|
|
|
142
|
-
|
|
92
|
+
filtered_value = filter(attribute_name(attr, i), casted_params[i])
|
|
93
|
+
attr_name, value = render_bind(attr, filtered_value)
|
|
143
94
|
add_bind_value(binds, attr_name, value)
|
|
144
95
|
end
|
|
145
96
|
binds
|
|
146
97
|
end
|
|
147
98
|
|
|
148
|
-
def
|
|
149
|
-
if
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
value = value ? "<#{value.bytesize} bytes of binary data>" : "<NULL binary data>"
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
[column.name, value]
|
|
157
|
-
else
|
|
158
|
-
[nil, value]
|
|
99
|
+
def attribute_name(attr, index)
|
|
100
|
+
if attr.respond_to?(:name)
|
|
101
|
+
attr.name
|
|
102
|
+
elsif attr.respond_to?(:[]) && attr[index].respond_to?(:name)
|
|
103
|
+
attr[index].name
|
|
159
104
|
end
|
|
160
105
|
end
|
|
161
106
|
|
|
162
|
-
def
|
|
163
|
-
value =
|
|
164
|
-
if attribute.type.binary? && attribute.value
|
|
165
|
-
if attribute.value.is_a?(Hash)
|
|
166
|
-
"<#{attribute.value_for_database.to_s.bytesize} bytes of binary data>"
|
|
167
|
-
else
|
|
168
|
-
"<#{attribute.value.bytesize} bytes of binary data>"
|
|
169
|
-
end
|
|
170
|
-
else
|
|
171
|
-
attribute.value_for_database
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
[attribute.name, value]
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
def render_bind_v5_0_3(attr, value)
|
|
178
|
-
if attr.is_a?(Array)
|
|
179
|
-
attr = attr.first
|
|
180
|
-
elsif attr.type.binary? && attr.value
|
|
181
|
-
value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
[attr&.name, value]
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
def render_bind_v6_1(attr, value)
|
|
107
|
+
def render_bind(attr, value)
|
|
188
108
|
case attr
|
|
189
109
|
when ActiveModel::Attribute
|
|
190
110
|
value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>" if attr.type.binary? && attr.value
|
|
@@ -197,37 +117,19 @@ module RailsSemanticLogger
|
|
|
197
117
|
[attr&.name || :nil, value]
|
|
198
118
|
end
|
|
199
119
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
120
|
+
# Filter sensitive bind values using the same parameter filter Rails uses for ActiveRecord,
|
|
121
|
+
# which is derived from ActiveRecord::Base.filter_attributes (config.filter_parameters).
|
|
122
|
+
def filter(name, value)
|
|
123
|
+
return value if name.nil?
|
|
203
124
|
|
|
204
|
-
|
|
205
|
-
|
|
125
|
+
filtered = ::ActiveRecord::Base.inspection_filter.filter_param(name.to_s, value)
|
|
126
|
+
# filter_param returns the same object when nothing was filtered, otherwise a mask object
|
|
127
|
+
# (a String delegate). Coerce the mask to a plain String for clean structured output.
|
|
128
|
+
filtered.equal?(value) ? value : filtered.to_s
|
|
206
129
|
end
|
|
207
130
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
alias render_bind render_bind_v5_0_0
|
|
211
|
-
elsif Rails::VERSION::MAJOR == 5 &&
|
|
212
|
-
((Rails::VERSION::MINOR.zero? && Rails::VERSION::TINY <= 6) ||
|
|
213
|
-
(Rails::VERSION::MINOR == 1 && Rails::VERSION::TINY <= 4)) # 5.0.3 - 5.0.6 && 5.1.0 - 5.1.4
|
|
214
|
-
alias bind_values bind_values_v5_0_3
|
|
215
|
-
alias render_bind render_bind_v5_0_3
|
|
216
|
-
alias type_casted_binds type_casted_binds_v5_0_3
|
|
217
|
-
elsif (Rails::VERSION::MAJOR == 6 && Rails::VERSION::MINOR > 0) ||
|
|
218
|
-
Rails::VERSION::MAJOR >= 7 # ~> 6.1.0 && >= 7.x.x
|
|
219
|
-
alias bind_values bind_values_v6_1
|
|
220
|
-
alias render_bind render_bind_v6_1
|
|
221
|
-
alias type_casted_binds type_casted_binds_v5_1_5
|
|
222
|
-
elsif Rails::VERSION::MAJOR >= 5 # ~> 5.1.5 && ~> 5.0.7 && 6.x.x
|
|
223
|
-
alias bind_values bind_values_v5_1_5
|
|
224
|
-
alias render_bind render_bind_v5_0_3
|
|
225
|
-
alias type_casted_binds type_casted_binds_v5_1_5
|
|
226
|
-
elsif Rails.version.to_i >= 4 # 4.x
|
|
227
|
-
alias bind_values bind_values_v4
|
|
228
|
-
alias render_bind render_bind_v4_2
|
|
229
|
-
else # 3.x
|
|
230
|
-
alias bind_values bind_values_v3
|
|
131
|
+
def type_casted_binds(casted_binds)
|
|
132
|
+
casted_binds.respond_to?(:call) ? casted_binds.call : casted_binds
|
|
231
133
|
end
|
|
232
134
|
end
|
|
233
135
|
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
module RailsSemanticLogger
|
|
2
|
+
# Collects the appenders declared by the application via:
|
|
3
|
+
#
|
|
4
|
+
# config.rails_semantic_logger.appenders do |appenders|
|
|
5
|
+
# appenders.add(file_name: "log/#{Rails.env}.log", formatter: :json)
|
|
6
|
+
# appenders.add_server(io: $stdout, formatter: :color)
|
|
7
|
+
# appenders.add_console(io: $stderr, formatter: :color)
|
|
8
|
+
# end
|
|
9
|
+
#
|
|
10
|
+
# When at least one appender is declared this way, Rails Semantic Logger stops
|
|
11
|
+
# building its own default file appender (`format`, `ap_options`, `filter`, and
|
|
12
|
+
# `add_file_appender` no longer apply) and instead creates exactly the appenders
|
|
13
|
+
# declared here.
|
|
14
|
+
#
|
|
15
|
+
# The methods name the *context* in which the appender is created; the
|
|
16
|
+
# destination and formatting are ordinary `SemanticLogger.add_appender` arguments:
|
|
17
|
+
#
|
|
18
|
+
# #add - always created, during Rails initialization.
|
|
19
|
+
# #add_server - created only when serving requests (`rails server`, a rack
|
|
20
|
+
# server, Sidekiq in server mode). Defaults to `$stdout`.
|
|
21
|
+
# #add_console - created only inside a `rails console` session. Defaults to
|
|
22
|
+
# `$stderr` so log output does not tangle with command results.
|
|
23
|
+
#
|
|
24
|
+
# Because each call appends to its context, any appender works in any context,
|
|
25
|
+
# and a context can have several (e.g. a server-only stdout *and* file appender).
|
|
26
|
+
class Appenders
|
|
27
|
+
include Enumerable
|
|
28
|
+
|
|
29
|
+
# Destination keys understood by SemanticLogger.add_appender. When none is
|
|
30
|
+
# supplied to #add_server / #add_console, the context's default stream is used.
|
|
31
|
+
DESTINATIONS = %i[io file_name appender logger metric].freeze
|
|
32
|
+
|
|
33
|
+
def initialize
|
|
34
|
+
@definitions = []
|
|
35
|
+
@server = []
|
|
36
|
+
@console = []
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Declare an appender. Accepts the same arguments (and optional block) as
|
|
40
|
+
# SemanticLogger.add_appender. Returns self so calls can be chained.
|
|
41
|
+
def add(**args, &block)
|
|
42
|
+
@definitions << [args, block]
|
|
43
|
+
self
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Declare an appender that is only created when the application is serving
|
|
47
|
+
# requests: `rails server`, a rack server started directly (puma, etc.), or
|
|
48
|
+
# Sidekiq in server mode. It is never created during a non-serving boot (rake
|
|
49
|
+
# tasks, runners, generators), so it only appears where it is useful.
|
|
50
|
+
#
|
|
51
|
+
# Accepts the same arguments (and optional block) as SemanticLogger.add_appender.
|
|
52
|
+
# When no destination is given it defaults to `$stdout`; the formatter defaults
|
|
53
|
+
# to `:color`.
|
|
54
|
+
def add_server(**args, &block)
|
|
55
|
+
@server << [defaults(args, io: $stdout), block]
|
|
56
|
+
self
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Declare an appender that is only created when running inside a `rails console`
|
|
60
|
+
# session. Identical to #add_server except it defaults to `$stderr`.
|
|
61
|
+
#
|
|
62
|
+
# Accepts the same arguments (and optional block) as SemanticLogger.add_appender.
|
|
63
|
+
def add_console(**args, &block)
|
|
64
|
+
@console << [defaults(args, io: $stderr), block]
|
|
65
|
+
self
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# The appenders declared via #add_server / #add_console, each as
|
|
69
|
+
# [Hash args, Proc|nil block].
|
|
70
|
+
attr_reader :server, :console
|
|
71
|
+
|
|
72
|
+
# Yields each #add declaration as [Hash args, Proc|nil block].
|
|
73
|
+
def each(&)
|
|
74
|
+
@definitions.each(&)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def any?
|
|
78
|
+
@definitions.any? || @server.any? || @console.any?
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
# Apply the context default stream and formatter, without overriding a
|
|
84
|
+
# destination (io/file_name/...) the caller already supplied.
|
|
85
|
+
def defaults(args, io:)
|
|
86
|
+
args = {formatter: :color}.merge(args)
|
|
87
|
+
args[:io] = io unless DESTINATIONS.any? { |key| args.key?(key) }
|
|
88
|
+
args
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -42,7 +42,11 @@ module RailsSemanticLogger
|
|
|
42
42
|
# own file loggers which would result in duplicate logging to the same log file
|
|
43
43
|
Rails.logger = config.logger =
|
|
44
44
|
begin
|
|
45
|
-
if config.rails_semantic_logger.
|
|
45
|
+
if config.rails_semantic_logger.appenders?
|
|
46
|
+
# The application declared its own appenders; create those and skip the default file appender.
|
|
47
|
+
RailsSemanticLogger.add_appenders(config.rails_semantic_logger.appenders)
|
|
48
|
+
elsif config.rails_semantic_logger.add_file_appender
|
|
49
|
+
# DEPRECATED: This option is deprecated and will be removed in a future version.
|
|
46
50
|
path = config.paths["log"].first
|
|
47
51
|
FileUtils.mkdir_p(File.dirname(path)) unless File.exist?(File.dirname(path))
|
|
48
52
|
|
|
@@ -83,11 +87,15 @@ module RailsSemanticLogger
|
|
|
83
87
|
logger
|
|
84
88
|
end
|
|
85
89
|
|
|
90
|
+
# Logger and init-time appenders are now built; settings read above no
|
|
91
|
+
# longer take effect, so flag late changes (e.g. from config/initializers).
|
|
92
|
+
config.rails_semantic_logger.logger_initialized!
|
|
93
|
+
|
|
86
94
|
# Replace Rails loggers
|
|
87
95
|
%i[active_record action_controller action_mailer action_view].each do |name|
|
|
88
96
|
ActiveSupport.on_load(name) { include SemanticLogger::Loggable }
|
|
89
97
|
end
|
|
90
|
-
ActiveSupport.on_load(:action_cable) { self.logger = SemanticLogger[
|
|
98
|
+
ActiveSupport.on_load(:action_cable) { self.logger = SemanticLogger[::ActionCable] }
|
|
91
99
|
end
|
|
92
100
|
|
|
93
101
|
# Before any initializers run, but after the gems have been loaded
|
|
@@ -105,7 +113,7 @@ module RailsSemanticLogger
|
|
|
105
113
|
Mongo::Logger.logger = SemanticLogger[Mongo] if defined?(Mongo::Logger)
|
|
106
114
|
|
|
107
115
|
# Replace the Resque Logger
|
|
108
|
-
Resque.logger
|
|
116
|
+
Resque.logger = SemanticLogger[Resque] if defined?(Resque) && Resque.respond_to?(:logger=)
|
|
109
117
|
|
|
110
118
|
# Replace the Sidekiq logger
|
|
111
119
|
if config.rails_semantic_logger.replace_sidekiq_logger && defined?(::Sidekiq)
|
|
@@ -115,14 +123,14 @@ module RailsSemanticLogger
|
|
|
115
123
|
|
|
116
124
|
::Sidekiq.configure_server do |config|
|
|
117
125
|
config.logger = ::SemanticLogger[::Sidekiq]
|
|
118
|
-
if
|
|
126
|
+
if ::Sidekiq::VERSION.to_i < 6 || (::Sidekiq::VERSION.to_i == 6 && ::Sidekiq::VERSION.to_f < 6.5)
|
|
119
127
|
config.options[:job_logger] = RailsSemanticLogger::Sidekiq::JobLogger
|
|
120
128
|
else
|
|
121
129
|
config[:job_logger] = RailsSemanticLogger::Sidekiq::JobLogger
|
|
122
130
|
end
|
|
123
131
|
|
|
124
|
-
# Add back the default console logger unless
|
|
125
|
-
|
|
132
|
+
# Add back the default console logger (unless the app declared its own appenders, or one exists)
|
|
133
|
+
RailsSemanticLogger.add_server_appenders
|
|
126
134
|
|
|
127
135
|
# Replace default error handler when present
|
|
128
136
|
existing = RailsSemanticLogger::Sidekiq::Defaults.delete_default_error_handler(config.error_handlers)
|
|
@@ -136,20 +144,29 @@ module RailsSemanticLogger
|
|
|
136
144
|
end
|
|
137
145
|
end
|
|
138
146
|
|
|
147
|
+
# Replace the SolidQueue logger
|
|
148
|
+
if config.rails_semantic_logger.replace_solid_queue_logger && defined?(::SolidQueue) && ::SolidQueue.respond_to?(:logger=)
|
|
149
|
+
::SolidQueue.logger = SemanticLogger[::SolidQueue]
|
|
150
|
+
end
|
|
151
|
+
|
|
139
152
|
# Replace the Sidetiq logger
|
|
140
153
|
Sidetiq.logger = SemanticLogger[Sidetiq] if defined?(Sidetiq) && Sidetiq.respond_to?(:logger=)
|
|
141
154
|
|
|
142
155
|
# Replace the DelayedJob logger
|
|
143
|
-
if defined?(Delayed::Worker)
|
|
144
|
-
Delayed::Worker.logger = SemanticLogger[Delayed::Worker]
|
|
145
|
-
Delayed::Worker.plugins << RailsSemanticLogger::DelayedJob::Plugin
|
|
146
|
-
end
|
|
156
|
+
Delayed::Worker.logger = SemanticLogger[Delayed::Worker] if defined?(Delayed::Worker)
|
|
147
157
|
|
|
148
158
|
# Replace the Bugsnag logger
|
|
149
159
|
Bugsnag.configure(false) { |config| config.logger = SemanticLogger[Bugsnag] } if defined?(Bugsnag)
|
|
150
160
|
|
|
151
161
|
# Set the IOStreams PGP logger
|
|
152
|
-
|
|
162
|
+
if defined?(IOStreams)
|
|
163
|
+
if IOStreams.respond_to?(:logger=)
|
|
164
|
+
IOStreams.logger = SemanticLogger[IOStreams]
|
|
165
|
+
else
|
|
166
|
+
# Older IOStreams versions
|
|
167
|
+
IOStreams::Pgp.logger = SemanticLogger[IOStreams::Pgp]
|
|
168
|
+
end
|
|
169
|
+
end
|
|
153
170
|
end
|
|
154
171
|
|
|
155
172
|
# After any initializers run, but after the gems have been loaded
|
|
@@ -226,7 +243,9 @@ module RailsSemanticLogger
|
|
|
226
243
|
if defined?(::ActionController)
|
|
227
244
|
require "action_controller/log_subscriber"
|
|
228
245
|
|
|
229
|
-
RailsSemanticLogger::ActionController::LogSubscriber
|
|
246
|
+
subscriber = RailsSemanticLogger::ActionController::LogSubscriber
|
|
247
|
+
subscriber.action_message_format = config.rails_semantic_logger.action_message_format
|
|
248
|
+
subscriber.processing_log_level = :info if config.rails_semantic_logger.processing
|
|
230
249
|
RailsSemanticLogger.swap_subscriber(
|
|
231
250
|
::ActionController::LogSubscriber,
|
|
232
251
|
RailsSemanticLogger::ActionController::LogSubscriber,
|
|
@@ -248,43 +267,35 @@ module RailsSemanticLogger
|
|
|
248
267
|
if config.rails_semantic_logger.replace_sidekiq_logger && defined?(::Sidekiq)
|
|
249
268
|
require("rails_semantic_logger/extensions/sidekiq/sidekiq")
|
|
250
269
|
end
|
|
251
|
-
end
|
|
252
270
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
PhusionPassenger.on_event(:starting_worker_process) do |forked|
|
|
261
|
-
SemanticLogger.reopen if forked
|
|
271
|
+
# SolidQueue
|
|
272
|
+
if config.rails_semantic_logger.replace_solid_queue_logger && defined?(::SolidQueue::LogSubscriber)
|
|
273
|
+
RailsSemanticLogger.swap_subscriber(
|
|
274
|
+
::SolidQueue::LogSubscriber,
|
|
275
|
+
RailsSemanticLogger::SolidQueue::LogSubscriber,
|
|
276
|
+
:solid_queue
|
|
277
|
+
)
|
|
262
278
|
end
|
|
263
279
|
end
|
|
264
280
|
|
|
265
|
-
#
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
#
|
|
269
|
-
Spring.after_fork { |_job| ::SemanticLogger.reopen } if defined?(Spring.after_fork)
|
|
270
|
-
|
|
271
|
-
# Re-open appenders after SolidQueue worker/dispatcher/scheduler has finished booting
|
|
272
|
-
SolidQueue.on_start { ::SemanticLogger.reopen } if defined?(SolidQueue.on_start)
|
|
273
|
-
SolidQueue.on_worker_start { ::SemanticLogger.reopen } if defined?(SolidQueue.on_worker_start)
|
|
274
|
-
SolidQueue.on_dispatcher_start { ::SemanticLogger.reopen } if defined?(SolidQueue.on_dispatcher_start)
|
|
275
|
-
SolidQueue.on_scheduler_start { ::SemanticLogger.reopen } if defined?(SolidQueue.on_scheduler_start)
|
|
281
|
+
# Forking frameworks (Passenger, Resque, Spring, SolidQueue, etc.) no longer
|
|
282
|
+
# need explicit reopen hooks: Semantic Logger v5 reopens appenders automatically
|
|
283
|
+
# in the child via a Process._fork / Process.daemon hook. Apps that opt out with
|
|
284
|
+
# `SemanticLogger.reopen_on_fork = false` are responsible for reopening themselves.
|
|
276
285
|
|
|
277
286
|
console do |_app|
|
|
278
287
|
# Don't use a background thread for logging
|
|
279
288
|
SemanticLogger.sync!
|
|
280
289
|
# Add a stderr logger when running inside a Rails console unless one has already been added.
|
|
281
|
-
|
|
282
|
-
SemanticLogger.add_appender(io: STDERR, formatter: :color)
|
|
283
|
-
end
|
|
290
|
+
RailsSemanticLogger.add_console_appenders
|
|
284
291
|
|
|
285
292
|
# Include method names on log entries in the console
|
|
286
293
|
SemanticLogger.backtrace_level = SemanticLogger.default_level
|
|
287
294
|
end
|
|
295
|
+
|
|
296
|
+
# Initialization is complete; the post-init settings have been consumed, so
|
|
297
|
+
# flag changes made after this point (e.g. at runtime) as having no effect.
|
|
298
|
+
config.rails_semantic_logger.fully_initialized!
|
|
288
299
|
end
|
|
289
300
|
end
|
|
290
301
|
end
|
|
@@ -1,11 +1,52 @@
|
|
|
1
1
|
require "action_cable/connection/tagged_logger_proxy"
|
|
2
2
|
|
|
3
|
+
# Upstream source (identical across supported versions):
|
|
4
|
+
# https://github.com/rails/rails/blob/v7.2.2/actioncable/lib/action_cable/connection/tagged_logger_proxy.rb
|
|
5
|
+
# https://github.com/rails/rails/blob/v8.0.2/actioncable/lib/action_cable/connection/tagged_logger_proxy.rb
|
|
6
|
+
# https://github.com/rails/rails/blob/v8.1.3/actioncable/lib/action_cable/connection/tagged_logger_proxy.rb
|
|
3
7
|
module ActionCable
|
|
4
8
|
module Connection
|
|
5
9
|
class TaggedLoggerProxy
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
# Mirrors upstream #tag, including the `respond_to?(:tagged)` guard so a
|
|
11
|
+
# target logger without tagging support (e.g. when handed a non-tagged
|
|
12
|
+
# ActiveRecord::Base.logger by the worker pool) simply yields. Diverges in
|
|
13
|
+
# one place: upstream reads the already-applied tags via
|
|
14
|
+
# `logger.formatter.current_tags` (ActiveSupport::TaggedLogging), but
|
|
15
|
+
# Semantic Logger exposes them via `#tags`, so resolve from whichever the
|
|
16
|
+
# target logger supports. See #220.
|
|
17
|
+
def tag(logger, &)
|
|
18
|
+
if logger.respond_to?(:tagged)
|
|
19
|
+
current_tags = tags - applied_tags_for(logger)
|
|
20
|
+
logger.tagged(*current_tags, &)
|
|
21
|
+
else
|
|
22
|
+
yield
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Upstream defines the severity methods with a single-arg signature
|
|
27
|
+
# (`message = nil`), which discards Semantic Logger's richer payload and
|
|
28
|
+
# exception arguments and raises ArgumentError when they are supplied.
|
|
29
|
+
# Redefine them to forward the full Semantic Logger signature down to the
|
|
30
|
+
# wrapped logger, while still applying the connection's tags. See #220.
|
|
31
|
+
%i[debug info warn error fatal unknown].each do |severity|
|
|
32
|
+
define_method(severity) do |message = nil, payload = nil, exception = nil, &block|
|
|
33
|
+
tag(@logger) { @logger.public_send(severity, message, payload, exception, &block) }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# Tags already applied to the target logger, so they are not duplicated.
|
|
40
|
+
# Semantic Logger exposes them via `#tags`; ActiveSupport::TaggedLogging
|
|
41
|
+
# via `formatter.current_tags`. Fall back to none when neither is present.
|
|
42
|
+
def applied_tags_for(logger)
|
|
43
|
+
if logger.respond_to?(:tags)
|
|
44
|
+
Array(logger.tags)
|
|
45
|
+
elsif logger.respond_to?(:formatter) && logger.formatter.respond_to?(:current_tags)
|
|
46
|
+
logger.formatter.current_tags
|
|
47
|
+
else
|
|
48
|
+
[]
|
|
49
|
+
end
|
|
9
50
|
end
|
|
10
51
|
end
|
|
11
52
|
end
|
|
@@ -6,21 +6,12 @@ module ActionDispatch
|
|
|
6
6
|
private
|
|
7
7
|
|
|
8
8
|
undef_method :log_error
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return if !log_rescued_responses?(request) && wrapper.rescue_response?
|
|
9
|
+
def log_error(request, wrapper)
|
|
10
|
+
Rails.application.deprecators.silence do
|
|
11
|
+
return if !log_rescued_responses?(request) && wrapper.rescue_response?
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
else
|
|
19
|
-
def log_error(_request, wrapper)
|
|
20
|
-
ActiveSupport::Deprecation.silence do
|
|
21
|
-
level = wrapper.respond_to?(:rescue_response?) && wrapper.rescue_response? ? :debug : :fatal
|
|
22
|
-
ActionController::Base.logger.log(level, wrapper.exception)
|
|
23
|
-
end
|
|
13
|
+
level = request.get_header("action_dispatch.debug_exception_log_level")
|
|
14
|
+
ActionController::Base.logger.log(level, wrapper.exception)
|
|
24
15
|
end
|
|
25
16
|
end
|
|
26
17
|
end
|