rails_semantic_logger 4.15.0 → 4.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +112 -1
- data/lib/rails_semantic_logger/extensions/sidekiq/sidekiq.rb +87 -56
- data/lib/rails_semantic_logger/version.rb +1 -1
- data/lib/rails_semantic_logger.rb +1 -1
- metadata +3 -3
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
|
|
@@ -33,18 +33,26 @@ module Sidekiq
|
|
33
33
|
if defined?(::Sidekiq::JobLogger)
|
34
34
|
# Let Semantic Logger handle duration logging
|
35
35
|
class JobLogger
|
36
|
-
def call(item, queue)
|
36
|
+
def call(item, queue, &block)
|
37
37
|
klass = item["wrapped"] || item["class"]
|
38
|
-
metric = "Sidekiq/#{klass}/perform" if klass
|
39
38
|
logger = klass ? SemanticLogger[klass] : Sidekiq.logger
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
+
)
|
48
56
|
end
|
49
57
|
end
|
50
58
|
|
@@ -60,14 +68,18 @@ module Sidekiq
|
|
60
68
|
end
|
61
69
|
|
62
70
|
def job_hash_context(job_hash)
|
63
|
-
h
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
h[:bid] = job_hash["bid"] if job_hash["bid"]
|
68
|
-
h[:tags] = job_hash["tags"] if job_hash["tags"]
|
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"]
|
69
75
|
h
|
70
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
|
71
83
|
end
|
72
84
|
end
|
73
85
|
|
@@ -80,48 +92,47 @@ module Sidekiq
|
|
80
92
|
end
|
81
93
|
|
82
94
|
def self.job_hash_context(job_hash)
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
87
99
|
end
|
88
100
|
end
|
89
101
|
end
|
90
102
|
|
91
103
|
# Exception is already logged by Semantic Logger during the perform call
|
92
|
-
# Sidekiq <= v6.5
|
93
104
|
if defined?(::Sidekiq::ExceptionHandler)
|
105
|
+
# Sidekiq <= v6.5
|
94
106
|
module ExceptionHandler
|
95
107
|
class Logger
|
96
|
-
def call(
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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)
|
103
115
|
end
|
104
116
|
end
|
105
117
|
end
|
106
|
-
# Sidekiq >= v7
|
107
118
|
elsif defined?(::Sidekiq::Config)
|
119
|
+
# Sidekiq >= v7
|
108
120
|
class Config
|
109
121
|
remove_const :ERROR_HANDLER
|
110
122
|
|
111
|
-
ERROR_HANDLER = ->(ex, ctx, cfg = Sidekiq.default_configuration)
|
123
|
+
ERROR_HANDLER = ->(ex, ctx, cfg = Sidekiq.default_configuration) do
|
112
124
|
unless ctx.empty?
|
113
125
|
job_hash = ctx[:job] || {}
|
114
|
-
klass
|
115
|
-
logger
|
126
|
+
klass = job_hash["display_class"] || job_hash["wrapped"] || job_hash["class"]
|
127
|
+
logger = klass ? SemanticLogger[klass] : Sidekiq.logger
|
116
128
|
ctx[:context] ? logger.warn(ctx[:context], ctx) : logger.warn(ctx)
|
117
129
|
end
|
118
|
-
|
130
|
+
end
|
119
131
|
end
|
120
132
|
else
|
121
133
|
# Sidekiq >= 6.5
|
122
|
-
|
123
|
-
|
124
|
-
binding.irb
|
134
|
+
Sidekiq.error_handlers.delete(Sidekiq::DEFAULT_ERROR_HANDLER)
|
135
|
+
Sidekiq.error_handlers << ->(ex, ctx) do
|
125
136
|
unless ctx.empty?
|
126
137
|
job_hash = ctx[:job] || {}
|
127
138
|
klass = job_hash["display_class"] || job_hash["wrapped"] || job_hash["class"]
|
@@ -132,10 +143,13 @@ module Sidekiq
|
|
132
143
|
end
|
133
144
|
|
134
145
|
# Logging within each worker should use its own logger
|
135
|
-
|
146
|
+
case Sidekiq::VERSION.to_i
|
147
|
+
when 4
|
136
148
|
module Worker
|
137
149
|
def self.included(base)
|
138
|
-
|
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
|
139
153
|
|
140
154
|
base.extend(ClassMethods)
|
141
155
|
base.include(SemanticLogger::Loggable)
|
@@ -144,10 +158,12 @@ module Sidekiq
|
|
144
158
|
base.class_attribute :sidekiq_retries_exhausted_block
|
145
159
|
end
|
146
160
|
end
|
147
|
-
|
161
|
+
when 5
|
148
162
|
module Worker
|
149
163
|
def self.included(base)
|
150
|
-
|
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
|
151
167
|
|
152
168
|
base.extend(ClassMethods)
|
153
169
|
base.include(SemanticLogger::Loggable)
|
@@ -156,10 +172,12 @@ module Sidekiq
|
|
156
172
|
base.sidekiq_class_attribute :sidekiq_retries_exhausted_block
|
157
173
|
end
|
158
174
|
end
|
159
|
-
|
175
|
+
when 6
|
160
176
|
module Worker
|
161
177
|
def self.included(base)
|
162
|
-
|
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
|
163
181
|
|
164
182
|
base.include(Options)
|
165
183
|
base.extend(ClassMethods)
|
@@ -169,7 +187,9 @@ module Sidekiq
|
|
169
187
|
else
|
170
188
|
module Job
|
171
189
|
def self.included(base)
|
172
|
-
|
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
|
173
193
|
|
174
194
|
base.include(Options)
|
175
195
|
base.extend(ClassMethods)
|
@@ -178,14 +198,15 @@ module Sidekiq
|
|
178
198
|
end
|
179
199
|
end
|
180
200
|
|
181
|
-
if Sidekiq::
|
201
|
+
if defined?(::Sidekiq::Middleware::Server::Logging)
|
202
|
+
# Sidekiq v4
|
182
203
|
# Convert string to machine readable format
|
183
204
|
class Processor
|
184
205
|
def log_context(job_hash)
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
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
|
189
210
|
end
|
190
211
|
end
|
191
212
|
|
@@ -194,16 +215,26 @@ module Sidekiq
|
|
194
215
|
module Server
|
195
216
|
class Logging
|
196
217
|
def call(worker, item, queue)
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
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 }
|
205
230
|
end
|
206
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
|
207
238
|
end
|
208
239
|
end
|
209
240
|
end
|
@@ -69,7 +69,7 @@ require("rails_semantic_logger/extensions/active_support/logger") if defined?(Ac
|
|
69
69
|
require("rails_semantic_logger/extensions/active_support/log_subscriber") if defined?(ActiveSupport::LogSubscriber)
|
70
70
|
|
71
71
|
begin
|
72
|
-
require
|
72
|
+
require "rackup"
|
73
73
|
rescue LoadError
|
74
74
|
# No need to do anything, will fall back to Rack
|
75
75
|
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: 2024-
|
11
|
+
date: 2024-07-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -92,7 +92,7 @@ licenses:
|
|
92
92
|
metadata:
|
93
93
|
bug_tracker_uri: https://github.com/reidmorrison/rails_semantic_logger/issues
|
94
94
|
documentation_uri: https://logger.rocketjob.io
|
95
|
-
source_code_uri: https://github.com/reidmorrison/rails_semantic_logger/tree/v4.
|
95
|
+
source_code_uri: https://github.com/reidmorrison/rails_semantic_logger/tree/v4.16.0
|
96
96
|
rubygems_mfa_required: 'true'
|
97
97
|
post_install_message:
|
98
98
|
rdoc_options: []
|