rails_semantic_logger 4.12.0 → 4.16.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 +112 -1
- data/lib/rails_semantic_logger/action_controller/log_subscriber.rb +5 -3
- data/lib/rails_semantic_logger/action_mailer/log_subscriber.rb +11 -13
- data/lib/rails_semantic_logger/action_view/log_subscriber.rb +51 -6
- data/lib/rails_semantic_logger/active_job/log_subscriber.rb +35 -5
- data/lib/rails_semantic_logger/active_record/log_subscriber.rb +1 -3
- data/lib/rails_semantic_logger/engine.rb +11 -7
- data/lib/rails_semantic_logger/extensions/action_dispatch/debug_exceptions.rb +11 -3
- data/lib/rails_semantic_logger/extensions/active_job/logging.rb +5 -1
- data/lib/rails_semantic_logger/extensions/active_support/log_subscriber.rb +13 -0
- data/lib/rails_semantic_logger/extensions/active_support/logger.rb +11 -8
- data/lib/rails_semantic_logger/extensions/rackup/server.rb +12 -0
- data/lib/rails_semantic_logger/extensions/rails/server.rb +1 -1
- data/lib/rails_semantic_logger/extensions/sidekiq/sidekiq.rb +242 -0
- data/lib/rails_semantic_logger/rack/logger.rb +1 -1
- data/lib/rails_semantic_logger/version.rb +1 -1
- data/lib/rails_semantic_logger.rb +22 -3
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7da18e2122c13f2b4325a6f3000dd74d22517608b3a32355ca3d2bf4e5f08789
|
4
|
+
data.tar.gz: b0503c3227cb0bfa5faa1c6f00b408968783c8d09092a960497ab9f004798875
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 30446e6a7553868e37cb56550d0a3e5c15b137991609e2cbb7aee84c7c7f5a75abe0bdd7f5e6263418546645f7a0892472e042ee09e0764be9f0ff9d90311917
|
7
|
+
data.tar.gz: 051ff94e24b1d9a4231f01d4148a966333b8d7b57539f7fa15ab953ad0a2fc9c998b61413ae2350716763f323eafa6d7302893b0c7b691e165c2200f7a8f3ef1
|
data/README.md
CHANGED
@@ -3,12 +3,112 @@
|
|
3
3
|
|
4
4
|
Rails Semantic Logger replaces the Rails default logger with [Semantic Logger](https://logger.rocketjob.io/)
|
5
5
|
|
6
|
+
When any large Rails application is deployed to production one of the first steps is to move to centralized logging, so that logs can be viewed and searched from a central location.
|
7
|
+
|
8
|
+
Centralized logging quickly falls apart when trying to consume the current human readable log files:
|
9
|
+
- Log entries often span multiple lines, resulting in unrelated log lines in the centralized logging system. For example, stack traces.
|
10
|
+
- Complex Regular Expressions are needed to parse the text lines and make them machine readable. For example to build queries, or alerts that are looking for specific elements in the message.
|
11
|
+
- Writing searches, alerts, or dashboards based on text logs is incredibly brittle, since a small change to the text logged can often break the parsing of those logs.
|
12
|
+
- Every log entry often has a completely different format, making it difficult to make consistent searches against the data.
|
13
|
+
|
14
|
+
For these and many other reasons switching to structured logging, or logs in JSON format, in testing and production makes centralized logging incredibly powerful.
|
15
|
+
|
16
|
+
For example, adding these lines to `config/application.rb` and removing any other log overrides from other environments, will switch automatically to structured logging when running inside Kubernetes:
|
17
|
+
~~~ruby
|
18
|
+
# Setup structured logging
|
19
|
+
config.semantic_logger.application = "my_application"
|
20
|
+
config.semantic_logger.environment = ENV["STACK_NAME"] || Rails.env
|
21
|
+
config.log_level = ENV["LOG_LEVEL"] || :info
|
22
|
+
|
23
|
+
# Switch to JSON Logging output to stdout when running on Kubernetes
|
24
|
+
if ENV["LOG_TO_CONSOLE"] || ENV["KUBERNETES_SERVICE_HOST"]
|
25
|
+
config.rails_semantic_logger.add_file_appender = false
|
26
|
+
config.semantic_logger.add_appender(io: $stdout, formatter: :json)
|
27
|
+
end
|
28
|
+
~~~
|
29
|
+
|
30
|
+
Then configure the centralized logging system to tell it that the data is in JSON format, so that it will parse it for you into a hierarchy.
|
31
|
+
|
32
|
+
For example, the following will instruct [Observe](https://www.observeinc.com/) to parse the JSON data and create machine readable data from it:
|
33
|
+
~~~ruby
|
34
|
+
interface "log", "log":log
|
35
|
+
|
36
|
+
make_col event:parse_json(log)
|
37
|
+
|
38
|
+
make_col
|
39
|
+
time:parse_isotime(event.timestamp),
|
40
|
+
application:string(event.application),
|
41
|
+
environment:string(event.environment),
|
42
|
+
duration:duration_ms(event.duration_ms),
|
43
|
+
level:string(event.level),
|
44
|
+
name:string(event.name),
|
45
|
+
message:string(event.message),
|
46
|
+
named_tags:event.named_tags,
|
47
|
+
payload:event.payload,
|
48
|
+
metric:string(event.metric),
|
49
|
+
metric_amount:float64(event.metric_amount),
|
50
|
+
tags:array(event.tags),
|
51
|
+
exception:event.exception,
|
52
|
+
host:string(event.host),
|
53
|
+
pid:int64(event.pid),
|
54
|
+
thread:string(event.thread),
|
55
|
+
file:string(event.file),
|
56
|
+
line:int64(event.line),
|
57
|
+
dimensions:event.dimensions,
|
58
|
+
backtrace:array(event.backtrace),
|
59
|
+
level_index:int64(event.level_index)
|
60
|
+
|
61
|
+
set_valid_from(time)
|
62
|
+
drop_col timestamp, log, event, stream
|
63
|
+
rename_col timestamp:time
|
64
|
+
~~~
|
65
|
+
|
66
|
+
Now queries can be built to drill down into each of these fields, including `payload` which is a nested object.
|
67
|
+
|
68
|
+
For example to find all failed Sidekiq job calls where the causing exception class name is `NoMethodError`:
|
69
|
+
~~~ruby
|
70
|
+
filter environment = "uat2"
|
71
|
+
filter level = "error"
|
72
|
+
filter metric = "sidekiq.job.perform"
|
73
|
+
filter (string(exception.cause.name) = "NoMethodError")
|
74
|
+
~~~
|
75
|
+
|
76
|
+
Example: create a dashboard showing the duration of all successful Sidekiq jobs:
|
77
|
+
~~~ruby
|
78
|
+
filter environment = "production"
|
79
|
+
filter level = "info"
|
80
|
+
filter metric = "sidekiq.job.perform"
|
81
|
+
timechart duration:avg(duration), group_by(name)
|
82
|
+
~~~
|
83
|
+
|
84
|
+
Example: create a dashboard showing the queue latency of all Sidekiq jobs.
|
85
|
+
The queue latency is the time between when the job was enqueued and when it was started:
|
86
|
+
~~~ruby
|
87
|
+
filter environment = "production"
|
88
|
+
filter level = "info"
|
89
|
+
filter metric = "sidekiq.queue.latency"
|
90
|
+
timechart duration:avg(duration), group_by(name)
|
91
|
+
~~~
|
92
|
+
|
6
93
|
* http://github.com/reidmorrison/rails_semantic_logger
|
7
94
|
|
8
95
|
## Documentation
|
9
96
|
|
10
97
|
For complete documentation see: https://logger.rocketjob.io/rails
|
11
98
|
|
99
|
+
## Upgrading to Semantic Logger V4.16 - Sidekiq Metrics Support
|
100
|
+
|
101
|
+
Rails Semantic Logger now supports Sidekiq metrics.
|
102
|
+
Below are the metrics that are now available when the JSON logging format is used:
|
103
|
+
- `sidekiq.job.perform` - The duration of each Sidekiq job.
|
104
|
+
- `sidekiq.queue.latency` - The time between when a Sidekiq job was enqueued and when it was started.
|
105
|
+
|
106
|
+
## Upgrading to Semantic Logger v4.15 & V4.16 - Sidekiq Support
|
107
|
+
|
108
|
+
Rails Semantic Logger introduces direct support for Sidekiq v4, v5, v6, and v7.
|
109
|
+
Please remove any previous custom patches or configurations to make Sidekiq work with Semantic Logger.
|
110
|
+
To see the complete list of patches being made, and to contribute your own changes, see: [Sidekiq Patches](https://github.com/reidmorrison/rails_semantic_logger/blob/master/lib/rails_semantic_logger/extensions/sidekiq/sidekiq.rb)
|
111
|
+
|
12
112
|
## Upgrading to Semantic Logger v4.4
|
13
113
|
|
14
114
|
With some forking frameworks it is necessary to call `reopen` after the fork. With v4.4 the
|
@@ -19,7 +119,18 @@ I.e. Please remove the following line if being called anywhere:
|
|
19
119
|
SemanticLogger::Processor.instance.instance_variable_set(:@queue, Queue.new)
|
20
120
|
~~~
|
21
121
|
|
22
|
-
##
|
122
|
+
## New Versions of Rails, etc.
|
123
|
+
|
124
|
+
The primary purpose of the Rails Semantic Logger gem is to patch other gems, primarily Rails, to make them support structured logging though Semantic Logger.
|
125
|
+
|
126
|
+
When new versions of Rails and other gems are published they often make changes to the internals, so the existing patches stop working.
|
127
|
+
|
128
|
+
Rails Semantic Logger survives only when someone in the community upgrades to a newer Rails or other supported libraries, runs into problems,
|
129
|
+
and then contributes the fix back to the community by means of a pull request.
|
130
|
+
|
131
|
+
Additionally, when new popular gems come out, we rely only the community to supply the necessary patches in Rails Semantic Logger to make those gems support structured logging.
|
132
|
+
|
133
|
+
## Supported Platforms
|
23
134
|
|
24
135
|
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).
|
25
136
|
|
@@ -14,10 +14,10 @@ module RailsSemanticLogger
|
|
14
14
|
|
15
15
|
# Unused, but needed for Devise 401 status code monkey patch to still work.
|
16
16
|
::ActionController::Base.log_process_action(payload)
|
17
|
-
|
17
|
+
|
18
18
|
params = payload[:params]
|
19
19
|
|
20
|
-
if params.
|
20
|
+
if params.is_a?(Hash) || params.is_a?(::ActionController::Parameters)
|
21
21
|
# According to PR https://github.com/reidmorrison/rails_semantic_logger/pull/37/files
|
22
22
|
# params is not always a Hash.
|
23
23
|
payload[:params] = params.to_unsafe_h unless params.is_a?(Hash)
|
@@ -79,7 +79,9 @@ module RailsSemanticLogger
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def send_data(event)
|
82
|
-
controller_logger(event).info(message:
|
82
|
+
controller_logger(event).info(message: "Sent data",
|
83
|
+
payload: {file_name: event.payload[:filename]},
|
84
|
+
duration: event.duration)
|
83
85
|
end
|
84
86
|
|
85
87
|
def unpermitted_parameters(event)
|
@@ -9,22 +9,22 @@ module RailsSemanticLogger
|
|
9
9
|
message_id = event.payload[:message_id]
|
10
10
|
duration = event.duration.round(1)
|
11
11
|
if ex
|
12
|
-
log_with_formatter event: event, log_duration: true, level: :error do |
|
12
|
+
log_with_formatter event: event, log_duration: true, level: :error do |_fmt|
|
13
13
|
{
|
14
|
-
message:
|
14
|
+
message: "Error delivering mail #{message_id} (#{duration}ms)",
|
15
15
|
exception: ex
|
16
16
|
}
|
17
17
|
end
|
18
18
|
else
|
19
|
-
message =
|
19
|
+
message =
|
20
20
|
if event.payload[:perform_deliveries]
|
21
21
|
"Delivered mail #{message_id} (#{duration}ms)"
|
22
22
|
else
|
23
23
|
"Skipped delivery of mail #{message_id} as `perform_deliveries` is false"
|
24
24
|
end
|
25
|
-
|
26
|
-
log_with_formatter event: event, log_duration: true do |
|
27
|
-
{
|
25
|
+
|
26
|
+
log_with_formatter event: event, log_duration: true do |_fmt|
|
27
|
+
{message: message}
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
@@ -34,8 +34,8 @@ module RailsSemanticLogger
|
|
34
34
|
mailer = event.payload[:mailer]
|
35
35
|
action = event.payload[:action]
|
36
36
|
duration = event.duration.round(1)
|
37
|
-
log_with_formatter event: event do |
|
38
|
-
{
|
37
|
+
log_with_formatter event: event do |_fmt|
|
38
|
+
{message: "#{mailer}##{action}: processed outbound mail in #{duration}ms"}
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
@@ -74,8 +74,6 @@ module RailsSemanticLogger
|
|
74
74
|
event.payload[:date].to_time.utc
|
75
75
|
elsif event.payload[:date].is_a?(String)
|
76
76
|
Time.parse(date).utc
|
77
|
-
else
|
78
|
-
nil
|
79
77
|
end
|
80
78
|
end
|
81
79
|
|
@@ -92,10 +90,10 @@ module RailsSemanticLogger
|
|
92
90
|
end
|
93
91
|
|
94
92
|
def formatted_args
|
95
|
-
if defined?(mailer.
|
93
|
+
if defined?(mailer.constantize.log_arguments?) && !mailer.constantize.log_arguments?
|
96
94
|
""
|
97
|
-
|
98
|
-
JSON.pretty_generate(event.payload[:args].map { |arg| format(arg) })
|
95
|
+
elsif event.payload[:args].present?
|
96
|
+
JSON.pretty_generate(event.payload[:args].map { |arg| format(arg) })
|
99
97
|
end
|
100
98
|
end
|
101
99
|
|
@@ -22,7 +22,7 @@ module RailsSemanticLogger
|
|
22
22
|
payload = {
|
23
23
|
template: from_rails_root(event.payload[:identifier])
|
24
24
|
}
|
25
|
-
payload[:within]
|
25
|
+
payload[:within] = from_rails_root(event.payload[:layout]) if event.payload[:layout]
|
26
26
|
payload[:allocations] = event.allocations if event.respond_to?(:allocations)
|
27
27
|
|
28
28
|
logger.measure(
|
@@ -39,8 +39,8 @@ module RailsSemanticLogger
|
|
39
39
|
payload = {
|
40
40
|
partial: from_rails_root(event.payload[:identifier])
|
41
41
|
}
|
42
|
-
payload[:within]
|
43
|
-
payload[:cache]
|
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
44
|
payload[:allocations] = event.allocations if event.respond_to?(:allocations)
|
45
45
|
|
46
46
|
logger.measure(
|
@@ -60,7 +60,7 @@ module RailsSemanticLogger
|
|
60
60
|
template: from_rails_root(identifier),
|
61
61
|
count: event.payload[:count]
|
62
62
|
}
|
63
|
-
payload[:cache_hits]
|
63
|
+
payload[:cache_hits] = event.payload[:cache_hits] if event.payload[:cache_hits]
|
64
64
|
payload[:allocations] = event.allocations if event.respond_to?(:allocations)
|
65
65
|
|
66
66
|
logger.measure(
|
@@ -72,16 +72,61 @@ module RailsSemanticLogger
|
|
72
72
|
end
|
73
73
|
|
74
74
|
def start(name, id, payload)
|
75
|
-
if
|
75
|
+
if ["render_template.action_view", "render_layout.action_view"].include?(name) && should_log?
|
76
|
+
qualifier = " layout" if name == "render_layout.action_view"
|
76
77
|
payload = {template: from_rails_root(payload[:identifier])}
|
77
78
|
payload[:within] = from_rails_root(payload[:layout]) if payload[:layout]
|
78
79
|
|
79
|
-
logger.send(self.class.rendered_log_level, message: "Rendering", payload: payload)
|
80
|
+
logger.send(self.class.rendered_log_level, message: "Rendering#{qualifier}", payload: payload)
|
80
81
|
end
|
81
82
|
|
82
83
|
super
|
83
84
|
end
|
84
85
|
|
86
|
+
if (Rails::VERSION::MAJOR == 7 && Rails::VERSION::MINOR >= 1) || Rails::VERSION::MAJOR > 7
|
87
|
+
class Start
|
88
|
+
def start(name, _id, payload)
|
89
|
+
return unless %w[render_template.action_view render_layout.action_view].include?(name)
|
90
|
+
|
91
|
+
qualifier = " layout" if name == "render_layout.action_view"
|
92
|
+
payload = {template: from_rails_root(payload[:identifier])}
|
93
|
+
payload[:within] = from_rails_root(payload[:layout]) if payload[:layout]
|
94
|
+
|
95
|
+
logger.debug(message: "Rendering#{qualifier}", payload: payload)
|
96
|
+
end
|
97
|
+
|
98
|
+
def finish(name, id, payload)
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def from_rails_root(string)
|
104
|
+
string = string.sub(rails_root, "")
|
105
|
+
string.sub!(VIEWS_PATTERN, "")
|
106
|
+
string
|
107
|
+
end
|
108
|
+
|
109
|
+
def rails_root
|
110
|
+
@root ||= "#{Rails.root}/"
|
111
|
+
end
|
112
|
+
|
113
|
+
def logger
|
114
|
+
@logger ||= SemanticLogger["ActionView"]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.attach_to(*)
|
119
|
+
ActiveSupport::Notifications.unsubscribe("render_template.action_view")
|
120
|
+
ActiveSupport::Notifications.unsubscribe("render_layout.action_view")
|
121
|
+
ActiveSupport::Notifications.subscribe("render_template.action_view",
|
122
|
+
RailsSemanticLogger::ActionView::LogSubscriber::Start.new)
|
123
|
+
ActiveSupport::Notifications.subscribe("render_layout.action_view",
|
124
|
+
RailsSemanticLogger::ActionView::LogSubscriber::Start.new)
|
125
|
+
|
126
|
+
super
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
85
130
|
private
|
86
131
|
|
87
132
|
@logger = SemanticLogger["ActionView"]
|
@@ -4,14 +4,44 @@ module RailsSemanticLogger
|
|
4
4
|
module ActiveJob
|
5
5
|
class LogSubscriber < ::ActiveSupport::LogSubscriber
|
6
6
|
def enqueue(event)
|
7
|
-
|
8
|
-
|
7
|
+
ex = event.payload[:exception_object]
|
8
|
+
|
9
|
+
if ex
|
10
|
+
log_with_formatter level: :error, event: event do |fmt|
|
11
|
+
{
|
12
|
+
message: "Failed enqueuing #{fmt.job_info} (#{ex.class} (#{ex.message})",
|
13
|
+
exception: ex
|
14
|
+
}
|
15
|
+
end
|
16
|
+
elsif event.payload[:aborted]
|
17
|
+
log_with_formatter level: :info, event: event do |fmt|
|
18
|
+
{message: "Failed enqueuing #{fmt.job_info}, a before_enqueue callback halted the enqueuing execution."}
|
19
|
+
end
|
20
|
+
else
|
21
|
+
log_with_formatter event: event do |fmt|
|
22
|
+
{message: "Enqueued #{fmt.job_info}"}
|
23
|
+
end
|
9
24
|
end
|
10
25
|
end
|
11
26
|
|
12
27
|
def enqueue_at(event)
|
13
|
-
|
14
|
-
|
28
|
+
ex = event.payload[:exception_object]
|
29
|
+
|
30
|
+
if ex
|
31
|
+
log_with_formatter level: :error, event: event do |fmt|
|
32
|
+
{
|
33
|
+
message: "Failed enqueuing #{fmt.job_info} (#{ex.class} (#{ex.message})",
|
34
|
+
exception: ex
|
35
|
+
}
|
36
|
+
end
|
37
|
+
elsif event.payload[:aborted]
|
38
|
+
log_with_formatter level: :info, event: event do |fmt|
|
39
|
+
{message: "Failed enqueuing #{fmt.job_info}, a before_enqueue callback halted the enqueuing execution."}
|
40
|
+
end
|
41
|
+
else
|
42
|
+
log_with_formatter event: event do |fmt|
|
43
|
+
{message: "Enqueued #{fmt.job_info} at #{fmt.scheduled_at}"}
|
44
|
+
end
|
15
45
|
end
|
16
46
|
end
|
17
47
|
|
@@ -26,7 +56,7 @@ module RailsSemanticLogger
|
|
26
56
|
if ex
|
27
57
|
log_with_formatter event: event, log_duration: true, level: :error do |fmt|
|
28
58
|
{
|
29
|
-
message:
|
59
|
+
message: "Error performing #{fmt.job_info} in #{event.duration.round(2)}ms",
|
30
60
|
exception: ex
|
31
61
|
}
|
32
62
|
end
|
@@ -169,9 +169,7 @@ module RailsSemanticLogger
|
|
169
169
|
def render_bind_v6_1(attr, value)
|
170
170
|
case attr
|
171
171
|
when ActiveModel::Attribute
|
172
|
-
if attr.type.binary? && attr.value
|
173
|
-
value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
|
174
|
-
end
|
172
|
+
value = "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>" if attr.type.binary? && attr.value
|
175
173
|
when Array
|
176
174
|
attr = attr.first
|
177
175
|
else
|
@@ -77,7 +77,7 @@ module RailsSemanticLogger
|
|
77
77
|
logger = SemanticLogger[Rails]
|
78
78
|
logger.warn(
|
79
79
|
"Rails Error: Unable to access log file. Please ensure that #{path} exists and is chmod 0666. " \
|
80
|
-
|
80
|
+
"The log level has been raised to WARN and the output directed to STDERR until the problem is fixed.",
|
81
81
|
e
|
82
82
|
)
|
83
83
|
logger
|
@@ -111,14 +111,14 @@ module RailsSemanticLogger
|
|
111
111
|
if defined?(Sidekiq)
|
112
112
|
if Sidekiq.respond_to?(:logger=)
|
113
113
|
Sidekiq.logger = SemanticLogger[Sidekiq]
|
114
|
-
elsif Sidekiq::VERSION[0..1] ==
|
114
|
+
elsif Sidekiq::VERSION[0..1] == "7."
|
115
115
|
method = Sidekiq.server? ? :configure_server : :configure_client
|
116
116
|
Sidekiq.public_send(method) { |cfg| cfg.logger = SemanticLogger[Sidekiq] }
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
120
120
|
# Replace the Sidetiq logger
|
121
|
-
Sidetiq.logger
|
121
|
+
Sidetiq.logger = SemanticLogger[Sidetiq] if defined?(Sidetiq) && Sidetiq.respond_to?(:logger=)
|
122
122
|
|
123
123
|
# Replace the DelayedJob logger
|
124
124
|
if defined?(Delayed::Worker)
|
@@ -127,7 +127,7 @@ module RailsSemanticLogger
|
|
127
127
|
end
|
128
128
|
|
129
129
|
# Replace the Bugsnag logger
|
130
|
-
Bugsnag.configure { |config| config.logger = SemanticLogger[Bugsnag] } if defined?(Bugsnag)
|
130
|
+
Bugsnag.configure(false) { |config| config.logger = SemanticLogger[Bugsnag] } if defined?(Bugsnag)
|
131
131
|
|
132
132
|
# Set the IOStreams PGP logger
|
133
133
|
IOStreams::Pgp.logger = SemanticLogger["IOStreams::Pgp"] if defined?(IOStreams)
|
@@ -138,12 +138,14 @@ module RailsSemanticLogger
|
|
138
138
|
config = Rails.application.config
|
139
139
|
|
140
140
|
# Replace the Bugsnag logger
|
141
|
-
Bugsnag.configure { |bugsnag_config| bugsnag_config.logger = SemanticLogger[Bugsnag] } if defined?(Bugsnag)
|
141
|
+
Bugsnag.configure(false) { |bugsnag_config| bugsnag_config.logger = SemanticLogger[Bugsnag] } if defined?(Bugsnag)
|
142
142
|
|
143
143
|
# Rails Patches
|
144
144
|
require("rails_semantic_logger/extensions/action_cable/tagged_logger_proxy") if defined?(::ActionCable)
|
145
145
|
require("rails_semantic_logger/extensions/action_controller/live") if defined?(::ActionController::Live)
|
146
|
-
|
146
|
+
if defined?(::ActionDispatch::DebugExceptions)
|
147
|
+
require("rails_semantic_logger/extensions/action_dispatch/debug_exceptions")
|
148
|
+
end
|
147
149
|
if defined?(::ActionView::StreamingTemplateRenderer::Body)
|
148
150
|
require("rails_semantic_logger/extensions/action_view/streaming_template_renderer")
|
149
151
|
end
|
@@ -222,6 +224,8 @@ module RailsSemanticLogger
|
|
222
224
|
:action_mailer
|
223
225
|
)
|
224
226
|
end
|
227
|
+
|
228
|
+
require("rails_semantic_logger/extensions/sidekiq/sidekiq") if defined?(::Sidekiq)
|
225
229
|
end
|
226
230
|
|
227
231
|
#
|
@@ -237,7 +241,7 @@ module RailsSemanticLogger
|
|
237
241
|
end
|
238
242
|
|
239
243
|
# Re-open appenders after Resque has forked a worker
|
240
|
-
Resque.after_fork { |_job| ::SemanticLogger.reopen } if defined?(Resque)
|
244
|
+
Resque.after_fork { |_job| ::SemanticLogger.reopen } if defined?(Resque.after_fork)
|
241
245
|
|
242
246
|
# Re-open appenders after Spring has forked a process
|
243
247
|
Spring.after_fork { |_job| ::SemanticLogger.reopen } if defined?(Spring.after_fork)
|
@@ -6,9 +6,17 @@ module ActionDispatch
|
|
6
6
|
private
|
7
7
|
|
8
8
|
undef_method :log_error
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
if (Rails::VERSION::MAJOR == 7 && Rails::VERSION::MINOR >= 1) || Rails::VERSION::MAJOR > 7
|
10
|
+
def log_error(_request, wrapper)
|
11
|
+
Rails.application.deprecators.silence do
|
12
|
+
ActionController::Base.logger.fatal(wrapper.exception)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
else
|
16
|
+
def log_error(_request, wrapper)
|
17
|
+
ActiveSupport::Deprecation.silence do
|
18
|
+
ActionController::Base.logger.fatal(wrapper.exception)
|
19
|
+
end
|
12
20
|
end
|
13
21
|
end
|
14
22
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
if ActiveSupport::VERSION::STRING == "7.1.1"
|
2
|
+
require "active_support/log_subscriber"
|
3
|
+
|
4
|
+
module ActiveSupport
|
5
|
+
class LogSubscriber
|
6
|
+
# @override Rails 7.1
|
7
|
+
def silenced?(event)
|
8
|
+
native_log_level = @event_levels.fetch(event, ::Logger::Severity::FATAL)
|
9
|
+
logger.nil? || SemanticLogger::Levels.index(logger.level) > SemanticLogger::Levels.index(native_log_level)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -4,20 +4,23 @@ module ActiveSupport
|
|
4
4
|
# More hacks to try and stop Rails from being it's own worst enemy.
|
5
5
|
class Logger
|
6
6
|
class << self
|
7
|
-
undef :logger_outputs_to
|
7
|
+
undef :logger_outputs_to?
|
8
|
+
|
9
|
+
# Prevent broadcasting since SemanticLogger already supports multiple loggers
|
10
|
+
if method_defined?(:broadcast)
|
11
|
+
undef :broadcast
|
12
|
+
def broadcast(_logger)
|
13
|
+
Module.new
|
14
|
+
end
|
15
|
+
end
|
8
16
|
end
|
9
17
|
|
10
18
|
# Prevent Console from trying to merge loggers
|
11
|
-
def self.logger_outputs_to?(*
|
19
|
+
def self.logger_outputs_to?(*_args)
|
12
20
|
true
|
13
21
|
end
|
14
22
|
|
15
|
-
|
16
|
-
def self.broadcast(logger)
|
17
|
-
Module.new
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.new(*args, **kwargs)
|
23
|
+
def self.new(*_args, **_kwargs)
|
21
24
|
SemanticLogger[self]
|
22
25
|
end
|
23
26
|
end
|
@@ -0,0 +1,242 @@
|
|
1
|
+
# Sidekiq patches
|
2
|
+
#
|
3
|
+
# To re-enable stdout logging for sidekiq server processes, add the following snippet to config/initializers/sidekiq.rb:
|
4
|
+
# Sidekiq.configure_server do |config|
|
5
|
+
# SemanticLogger.add_appender(io: $stdout, level: :debug, formatter: :color)
|
6
|
+
# end
|
7
|
+
if Sidekiq::VERSION.to_i == 4
|
8
|
+
require "sidekiq/exception_handler"
|
9
|
+
require "sidekiq/logging"
|
10
|
+
require "sidekiq/middleware/server/logging"
|
11
|
+
require "sidekiq/processor"
|
12
|
+
require "sidekiq/worker"
|
13
|
+
elsif Sidekiq::VERSION.to_i == 5
|
14
|
+
require "sidekiq/exception_handler"
|
15
|
+
require "sidekiq/job_logger"
|
16
|
+
require "sidekiq/logging"
|
17
|
+
require "sidekiq/worker"
|
18
|
+
elsif Sidekiq::VERSION.to_i == 6 && Sidekiq::VERSION.to_f < 6.5
|
19
|
+
require "sidekiq/exception_handler"
|
20
|
+
require "sidekiq/job_logger"
|
21
|
+
require "sidekiq/worker"
|
22
|
+
elsif Sidekiq::VERSION.to_i == 6
|
23
|
+
require "sidekiq/job_logger"
|
24
|
+
require "sidekiq/worker"
|
25
|
+
else
|
26
|
+
require "sidekiq/config"
|
27
|
+
require "sidekiq/job_logger"
|
28
|
+
require "sidekiq/job"
|
29
|
+
end
|
30
|
+
|
31
|
+
module Sidekiq
|
32
|
+
# Sidekiq > v4
|
33
|
+
if defined?(::Sidekiq::JobLogger)
|
34
|
+
# Let Semantic Logger handle duration logging
|
35
|
+
class JobLogger
|
36
|
+
def call(item, queue, &block)
|
37
|
+
klass = item["wrapped"] || item["class"]
|
38
|
+
logger = klass ? SemanticLogger[klass] : Sidekiq.logger
|
39
|
+
|
40
|
+
SemanticLogger.tagged(queue: queue) do
|
41
|
+
# Latency is the time between when the job was enqueued and when it started executing.
|
42
|
+
logger.info(
|
43
|
+
"Start #perform",
|
44
|
+
metric: "sidekiq.queue.latency",
|
45
|
+
metric_amount: job_latency_ms(item)
|
46
|
+
)
|
47
|
+
|
48
|
+
# Measure the duration of running the job
|
49
|
+
logger.measure_info(
|
50
|
+
"Completed #perform",
|
51
|
+
on_exception_level: :error,
|
52
|
+
log_exception: :full,
|
53
|
+
metric: "sidekiq.job.perform",
|
54
|
+
&block
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def prepare(job_hash, &block)
|
60
|
+
level = job_hash["log_level"]
|
61
|
+
if level
|
62
|
+
SemanticLogger.silence(level) do
|
63
|
+
SemanticLogger.tagged(job_hash_context(job_hash), &block)
|
64
|
+
end
|
65
|
+
else
|
66
|
+
SemanticLogger.tagged(job_hash_context(job_hash), &block)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def job_hash_context(job_hash)
|
71
|
+
h = {jid: job_hash["jid"]}
|
72
|
+
h[:bid] = job_hash["bid"] if job_hash["bid"]
|
73
|
+
h[:tags] = job_hash["tags"] if job_hash["tags"]
|
74
|
+
h[:queue] = job_hash["queue"] if job_hash["queue"]
|
75
|
+
h
|
76
|
+
end
|
77
|
+
|
78
|
+
def job_latency_ms(job)
|
79
|
+
return unless job && job["enqueued_at"]
|
80
|
+
|
81
|
+
(Time.now.to_f - job["enqueued_at"].to_f) * 1000
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Sidekiq <= v6
|
87
|
+
if defined?(::Sidekiq::Logging)
|
88
|
+
# Replace Sidekiq logging context
|
89
|
+
module Logging
|
90
|
+
def self.with_context(msg, &block)
|
91
|
+
SemanticLogger.tagged(msg, &block)
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.job_hash_context(job_hash)
|
95
|
+
h = {jid: job_hash["jid"]}
|
96
|
+
h[:bid] = job_hash["bid"] if job_hash["bid"]
|
97
|
+
h[:queue] = job_hash["queue"] if job_hash["queue"]
|
98
|
+
h
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Exception is already logged by Semantic Logger during the perform call
|
104
|
+
if defined?(::Sidekiq::ExceptionHandler)
|
105
|
+
# Sidekiq <= v6.5
|
106
|
+
module ExceptionHandler
|
107
|
+
class Logger
|
108
|
+
def call(_exception, ctx)
|
109
|
+
return if ctx.empty?
|
110
|
+
|
111
|
+
job_hash = ctx[:job] || {}
|
112
|
+
klass = job_hash["display_class"] || job_hash["wrapped"] || job_hash["class"]
|
113
|
+
logger = klass ? SemanticLogger[klass] : Sidekiq.logger
|
114
|
+
ctx[:context] ? logger.warn(ctx[:context], ctx) : logger.warn(ctx)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
elsif defined?(::Sidekiq::Config)
|
119
|
+
# Sidekiq >= v7
|
120
|
+
class Config
|
121
|
+
remove_const :ERROR_HANDLER
|
122
|
+
|
123
|
+
ERROR_HANDLER = ->(ex, ctx, cfg = Sidekiq.default_configuration) do
|
124
|
+
unless ctx.empty?
|
125
|
+
job_hash = ctx[:job] || {}
|
126
|
+
klass = job_hash["display_class"] || job_hash["wrapped"] || job_hash["class"]
|
127
|
+
logger = klass ? SemanticLogger[klass] : Sidekiq.logger
|
128
|
+
ctx[:context] ? logger.warn(ctx[:context], ctx) : logger.warn(ctx)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
else
|
133
|
+
# Sidekiq >= 6.5
|
134
|
+
Sidekiq.error_handlers.delete(Sidekiq::DEFAULT_ERROR_HANDLER)
|
135
|
+
Sidekiq.error_handlers << ->(ex, ctx) do
|
136
|
+
unless ctx.empty?
|
137
|
+
job_hash = ctx[:job] || {}
|
138
|
+
klass = job_hash["display_class"] || job_hash["wrapped"] || job_hash["class"]
|
139
|
+
logger = klass ? SemanticLogger[klass] : Sidekiq.logger
|
140
|
+
ctx[:context] ? logger.warn(ctx[:context], ctx) : logger.warn(ctx)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Logging within each worker should use its own logger
|
146
|
+
case Sidekiq::VERSION.to_i
|
147
|
+
when 4
|
148
|
+
module Worker
|
149
|
+
def self.included(base)
|
150
|
+
if base.ancestors.any? { |c| c.name == "ActiveJob::Base" }
|
151
|
+
raise ArgumentError, "You cannot include Sidekiq::Worker in an ActiveJob: #{base.name}"
|
152
|
+
end
|
153
|
+
|
154
|
+
base.extend(ClassMethods)
|
155
|
+
base.include(SemanticLogger::Loggable)
|
156
|
+
base.class_attribute :sidekiq_options_hash
|
157
|
+
base.class_attribute :sidekiq_retry_in_block
|
158
|
+
base.class_attribute :sidekiq_retries_exhausted_block
|
159
|
+
end
|
160
|
+
end
|
161
|
+
when 5
|
162
|
+
module Worker
|
163
|
+
def self.included(base)
|
164
|
+
if base.ancestors.any? { |c| c.name == "ActiveJob::Base" }
|
165
|
+
raise ArgumentError, "You cannot include Sidekiq::Worker in an ActiveJob: #{base.name}"
|
166
|
+
end
|
167
|
+
|
168
|
+
base.extend(ClassMethods)
|
169
|
+
base.include(SemanticLogger::Loggable)
|
170
|
+
base.sidekiq_class_attribute :sidekiq_options_hash
|
171
|
+
base.sidekiq_class_attribute :sidekiq_retry_in_block
|
172
|
+
base.sidekiq_class_attribute :sidekiq_retries_exhausted_block
|
173
|
+
end
|
174
|
+
end
|
175
|
+
when 6
|
176
|
+
module Worker
|
177
|
+
def self.included(base)
|
178
|
+
if base.ancestors.any? { |c| c.name == "ActiveJob::Base" }
|
179
|
+
raise ArgumentError, "Sidekiq::Worker cannot be included in an ActiveJob: #{base.name}"
|
180
|
+
end
|
181
|
+
|
182
|
+
base.include(Options)
|
183
|
+
base.extend(ClassMethods)
|
184
|
+
base.include(SemanticLogger::Loggable)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
else
|
188
|
+
module Job
|
189
|
+
def self.included(base)
|
190
|
+
if base.ancestors.any? { |c| c.name == "ActiveJob::Base" }
|
191
|
+
raise ArgumentError, "Sidekiq::Job cannot be included in an ActiveJob: #{base.name}"
|
192
|
+
end
|
193
|
+
|
194
|
+
base.include(Options)
|
195
|
+
base.extend(ClassMethods)
|
196
|
+
base.include(SemanticLogger::Loggable)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
if defined?(::Sidekiq::Middleware::Server::Logging)
|
202
|
+
# Sidekiq v4
|
203
|
+
# Convert string to machine readable format
|
204
|
+
class Processor
|
205
|
+
def log_context(job_hash)
|
206
|
+
h = {jid: job_hash["jid"]}
|
207
|
+
h[:bid] = job_hash["bid"] if job_hash["bid"]
|
208
|
+
h[:queue] = job_hash["queue"] if job_hash["queue"]
|
209
|
+
h
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Let Semantic Logger handle duration logging
|
214
|
+
module Middleware
|
215
|
+
module Server
|
216
|
+
class Logging
|
217
|
+
def call(worker, item, queue)
|
218
|
+
SemanticLogger.tagged(queue: queue) do
|
219
|
+
worker.logger.info(
|
220
|
+
"Start #perform",
|
221
|
+
metric: "sidekiq.queue.latency",
|
222
|
+
metric_amount: job_latency_ms(item)
|
223
|
+
)
|
224
|
+
worker.logger.measure_info(
|
225
|
+
"Completed #perform",
|
226
|
+
on_exception_level: :error,
|
227
|
+
log_exception: :full,
|
228
|
+
metric: "sidekiq.job.perform"
|
229
|
+
) { yield }
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def job_latency_ms(job)
|
234
|
+
return unless job && job["enqueued_at"]
|
235
|
+
|
236
|
+
(Time.now.to_f - job["enqueued_at"].to_f) * 1000
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
@@ -37,7 +37,7 @@ module RailsSemanticLogger
|
|
37
37
|
def call_app(request, env)
|
38
38
|
instrumenter = ActiveSupport::Notifications.instrumenter
|
39
39
|
instrumenter_state = instrumenter.start "request.action_dispatch", request: request
|
40
|
-
instrumenter_finish =
|
40
|
+
instrumenter_finish = lambda {
|
41
41
|
instrumenter.finish_with_state(instrumenter_state, "request.action_dispatch", request: request)
|
42
42
|
}
|
43
43
|
|
@@ -6,21 +6,27 @@ module RailsSemanticLogger
|
|
6
6
|
module ActionController
|
7
7
|
autoload :LogSubscriber, "rails_semantic_logger/action_controller/log_subscriber"
|
8
8
|
end
|
9
|
+
|
9
10
|
module ActionMailer
|
10
11
|
autoload :LogSubscriber, "rails_semantic_logger/action_mailer/log_subscriber"
|
11
12
|
end
|
13
|
+
|
12
14
|
module ActionView
|
13
15
|
autoload :LogSubscriber, "rails_semantic_logger/action_view/log_subscriber"
|
14
16
|
end
|
17
|
+
|
15
18
|
module ActiveJob
|
16
19
|
autoload :LogSubscriber, "rails_semantic_logger/active_job/log_subscriber"
|
17
20
|
end
|
21
|
+
|
18
22
|
module ActiveRecord
|
19
23
|
autoload :LogSubscriber, "rails_semantic_logger/active_record/log_subscriber"
|
20
24
|
end
|
25
|
+
|
21
26
|
module Rack
|
22
27
|
autoload :Logger, "rails_semantic_logger/rack/logger"
|
23
28
|
end
|
29
|
+
|
24
30
|
module DelayedJob
|
25
31
|
autoload :Plugin, "rails_semantic_logger/delayed_job/plugin"
|
26
32
|
end
|
@@ -48,9 +54,11 @@ module RailsSemanticLogger
|
|
48
54
|
end
|
49
55
|
|
50
56
|
def self.subscriber_patterns(subscriber)
|
51
|
-
subscriber.patterns.respond_to?(:keys)
|
52
|
-
subscriber.patterns.keys
|
57
|
+
if subscriber.patterns.respond_to?(:keys)
|
58
|
+
subscriber.patterns.keys
|
59
|
+
else
|
53
60
|
subscriber.patterns
|
61
|
+
end
|
54
62
|
end
|
55
63
|
|
56
64
|
private_class_method :subscriber_patterns, :unattach
|
@@ -58,4 +66,15 @@ end
|
|
58
66
|
|
59
67
|
require("rails_semantic_logger/extensions/mongoid/config") if defined?(Mongoid)
|
60
68
|
require("rails_semantic_logger/extensions/active_support/logger") if defined?(ActiveSupport::Logger)
|
61
|
-
require("rails_semantic_logger/extensions/
|
69
|
+
require("rails_semantic_logger/extensions/active_support/log_subscriber") if defined?(ActiveSupport::LogSubscriber)
|
70
|
+
|
71
|
+
begin
|
72
|
+
require "rackup"
|
73
|
+
rescue LoadError
|
74
|
+
# No need to do anything, will fall back to Rack
|
75
|
+
end
|
76
|
+
if defined?(Rackup::Server)
|
77
|
+
require("rails_semantic_logger/extensions/rackup/server")
|
78
|
+
elsif defined?(Rack::Server)
|
79
|
+
require("rails_semantic_logger/extensions/rack/server")
|
80
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_semantic_logger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.16.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Reid Morrison
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-07-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -75,11 +75,14 @@ files:
|
|
75
75
|
- lib/rails_semantic_logger/extensions/action_view/streaming_template_renderer.rb
|
76
76
|
- lib/rails_semantic_logger/extensions/active_job/logging.rb
|
77
77
|
- lib/rails_semantic_logger/extensions/active_model_serializers/logging.rb
|
78
|
+
- lib/rails_semantic_logger/extensions/active_support/log_subscriber.rb
|
78
79
|
- lib/rails_semantic_logger/extensions/active_support/logger.rb
|
79
80
|
- lib/rails_semantic_logger/extensions/active_support/tagged_logging.rb
|
80
81
|
- lib/rails_semantic_logger/extensions/mongoid/config.rb
|
81
82
|
- lib/rails_semantic_logger/extensions/rack/server.rb
|
83
|
+
- lib/rails_semantic_logger/extensions/rackup/server.rb
|
82
84
|
- lib/rails_semantic_logger/extensions/rails/server.rb
|
85
|
+
- lib/rails_semantic_logger/extensions/sidekiq/sidekiq.rb
|
83
86
|
- lib/rails_semantic_logger/options.rb
|
84
87
|
- lib/rails_semantic_logger/rack/logger.rb
|
85
88
|
- lib/rails_semantic_logger/version.rb
|
@@ -89,7 +92,7 @@ licenses:
|
|
89
92
|
metadata:
|
90
93
|
bug_tracker_uri: https://github.com/reidmorrison/rails_semantic_logger/issues
|
91
94
|
documentation_uri: https://logger.rocketjob.io
|
92
|
-
source_code_uri: https://github.com/reidmorrison/rails_semantic_logger/tree/
|
95
|
+
source_code_uri: https://github.com/reidmorrison/rails_semantic_logger/tree/v4.16.0
|
93
96
|
rubygems_mfa_required: 'true'
|
94
97
|
post_install_message:
|
95
98
|
rdoc_options: []
|
@@ -106,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
109
|
- !ruby/object:Gem::Version
|
107
110
|
version: '0'
|
108
111
|
requirements: []
|
109
|
-
rubygems_version: 3.
|
112
|
+
rubygems_version: 3.5.3
|
110
113
|
signing_key:
|
111
114
|
specification_version: 4
|
112
115
|
summary: Feature rich logging framework that replaces the Rails logger.
|