unified_logger 0.1.5 → 0.1.7
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/lib/unified_logger/job_logger.rb +40 -20
- data/lib/unified_logger/logger.rb +67 -11
- data/lib/unified_logger/request_logger.rb +4 -3
- data/lib/unified_logger/severity.rb +41 -0
- data/lib/unified_logger/sidekiq.rb +40 -0
- data/lib/unified_logger/version.rb +1 -1
- data/lib/unified_logger.rb +4 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ff3a0a9f560baae4bcae1a17b3b4d8ef7aebd8777649f544c5fc818f4d24afa7
|
|
4
|
+
data.tar.gz: b2a9e96b83a7cf67502acc2af0d9008ba39fdef01109af2e5462e5a56a79477d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b798053594157ef35351db35ea2937867bdb69d41a1280ec08a6e577c9d729634f45352781f3baeff81274dcb46399bcdb883ad0b181bbf2ce7cc85b83707721
|
|
7
|
+
data.tar.gz: 97ad2f940f4889b7665cac57fc2318b91387bebc3d5400c2d4afb4b20f5d8efcd7b008b29e8286eb14992e7bbd0e896351ec2775e3d13cecc47ebd688956160c
|
|
@@ -1,41 +1,61 @@
|
|
|
1
1
|
module UnifiedLogger
|
|
2
2
|
class JobLogger
|
|
3
|
-
DEFAULT_MAX_RETRIES = 5
|
|
4
|
-
|
|
5
3
|
class << self
|
|
6
|
-
def log(
|
|
4
|
+
def log(class_name:, id: nil, queue: nil, params: nil,
|
|
5
|
+
enqueued_at: nil, retry_count: 0, max_retries: nil, **extra)
|
|
6
|
+
started = UnifiedLogger.current_time
|
|
7
7
|
yield
|
|
8
8
|
ensure
|
|
9
|
-
|
|
9
|
+
if UnifiedLogger.current_logger.is_a?(UnifiedLogger::Logger)
|
|
10
|
+
write_log(class_name: class_name, id: id, queue: queue, params: params,
|
|
11
|
+
enqueued_at: enqueued_at, retry_count: retry_count,
|
|
12
|
+
max_retries: max_retries, started: started, **extra)
|
|
13
|
+
end
|
|
10
14
|
end
|
|
11
15
|
|
|
12
16
|
private
|
|
13
17
|
|
|
14
|
-
def
|
|
18
|
+
def write_log(class_name:, id:, queue:, params:, enqueued_at:,
|
|
19
|
+
retry_count:, max_retries:, started:, **extra)
|
|
20
|
+
enqueued_time = parse_time(enqueued_at)
|
|
21
|
+
|
|
15
22
|
log = {
|
|
16
|
-
log_type:
|
|
17
|
-
timestamp:
|
|
18
|
-
class_name:
|
|
19
|
-
id:
|
|
20
|
-
queue:
|
|
21
|
-
params:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
log_type: :job,
|
|
24
|
+
timestamp: UnifiedLogger.formatted_time,
|
|
25
|
+
class_name: class_name,
|
|
26
|
+
id: id,
|
|
27
|
+
queue: queue,
|
|
28
|
+
params: params,
|
|
29
|
+
retry_count: retry_count,
|
|
30
|
+
enqueued_at: enqueued_at,
|
|
31
|
+
duration: started ? UnifiedLogger.current_time - started : 0,
|
|
32
|
+
queue_duration: enqueued_time && started ? started - enqueued_time : nil,
|
|
33
|
+
thread_id: Thread.current.object_id,
|
|
34
|
+
process_id: Process.pid
|
|
27
35
|
}
|
|
28
|
-
log
|
|
36
|
+
log.merge!(extra) if extra.any?
|
|
37
|
+
log.compact!
|
|
38
|
+
|
|
39
|
+
log[:logs] = Logger.fetch_and_reset_logs if Logger.logs.any?
|
|
40
|
+
log.merge!(Logger.fetch_and_reset_extra_log_fields) if Logger.extra_log_fields.any?
|
|
29
41
|
|
|
30
42
|
if $!
|
|
31
|
-
log[:exception] =
|
|
32
|
-
log[:status] =
|
|
43
|
+
log[:exception] = Logger.format_exception($!)
|
|
44
|
+
log[:status] = max_retries && retry_count < max_retries ? :warn : :error
|
|
33
45
|
else
|
|
34
46
|
log[:status] = :ok
|
|
35
47
|
end
|
|
36
48
|
|
|
37
49
|
UnifiedLogger.transform_job_log_callable&.call(log)
|
|
38
|
-
|
|
50
|
+
Logger.write_log(log)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def parse_time(value)
|
|
54
|
+
case value
|
|
55
|
+
when Numeric then Time.at(value).utc
|
|
56
|
+
when String then Time.parse(value)
|
|
57
|
+
when Time, ActiveSupport::TimeWithZone then value
|
|
58
|
+
end
|
|
39
59
|
end
|
|
40
60
|
end
|
|
41
61
|
end
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
module UnifiedLogger
|
|
2
2
|
class Logger < ::Logger
|
|
3
|
-
|
|
3
|
+
LOGS = Concurrent::ThreadLocalVar.new([])
|
|
4
|
+
EXTRA_LOG_FIELDS = Concurrent::ThreadLocalVar.new({})
|
|
4
5
|
SEVERITY_LEVELS = {
|
|
5
6
|
debug: ::Logger::DEBUG,
|
|
6
7
|
info: ::Logger::INFO,
|
|
8
|
+
note: ::Logger::Severity::NOTE,
|
|
7
9
|
warn: ::Logger::WARN,
|
|
8
10
|
error: ::Logger::ERROR,
|
|
9
11
|
fatal: ::Logger::FATAL,
|
|
@@ -27,6 +29,11 @@ module UnifiedLogger
|
|
|
27
29
|
add(::Logger::INFO, message)
|
|
28
30
|
end
|
|
29
31
|
|
|
32
|
+
def note(message = nil, &block)
|
|
33
|
+
message = block.call if message.nil? && block
|
|
34
|
+
add(::Logger::Severity::NOTE, message)
|
|
35
|
+
end
|
|
36
|
+
|
|
30
37
|
def warn(message = nil, &block)
|
|
31
38
|
message = block.call if message.nil? && block
|
|
32
39
|
add(::Logger::WARN, message)
|
|
@@ -57,18 +64,33 @@ module UnifiedLogger
|
|
|
57
64
|
end
|
|
58
65
|
|
|
59
66
|
class << self
|
|
60
|
-
def
|
|
61
|
-
|
|
67
|
+
def logs
|
|
68
|
+
LOGS.value
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def add(hash)
|
|
72
|
+
EXTRA_LOG_FIELDS.value = EXTRA_LOG_FIELDS.value.merge(hash)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def extra_log_fields
|
|
76
|
+
EXTRA_LOG_FIELDS.value
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def fetch_and_reset_extra_log_fields
|
|
80
|
+
fields = extra_log_fields
|
|
81
|
+
EXTRA_LOG_FIELDS.value = {}
|
|
82
|
+
fields
|
|
62
83
|
end
|
|
63
84
|
|
|
64
85
|
def reset_thread_logs
|
|
65
|
-
|
|
86
|
+
LOGS.value = []
|
|
87
|
+
EXTRA_LOG_FIELDS.value = {}
|
|
66
88
|
end
|
|
67
89
|
|
|
68
|
-
def
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
90
|
+
def fetch_and_reset_logs
|
|
91
|
+
current = logs
|
|
92
|
+
LOGS.value = []
|
|
93
|
+
current
|
|
72
94
|
end
|
|
73
95
|
|
|
74
96
|
def trim(data)
|
|
@@ -107,8 +129,42 @@ module UnifiedLogger
|
|
|
107
129
|
formatter.present? ? formatter.call(filtered_log) : filtered_log.to_json
|
|
108
130
|
end
|
|
109
131
|
|
|
132
|
+
def write_log(log)
|
|
133
|
+
logger = UnifiedLogger.current_logger
|
|
134
|
+
max = UnifiedLogger.config[:max_log_size]
|
|
135
|
+
if log.inspect.length <= max || !log.key?(:logs)
|
|
136
|
+
logger.write(format(log))
|
|
137
|
+
else
|
|
138
|
+
entries = log.delete(:logs)
|
|
139
|
+
logger.write(format(log))
|
|
140
|
+
write_overflow_logs(log[:id], log[:log_type], entries, max, logger)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
110
144
|
private
|
|
111
145
|
|
|
146
|
+
def write_overflow_logs(id, log_type, entries, max, logger)
|
|
147
|
+
index = 1
|
|
148
|
+
group = []
|
|
149
|
+
|
|
150
|
+
entries.each do |entry|
|
|
151
|
+
candidate = group + [entry]
|
|
152
|
+
overflow = { id: id, log_type: log_type, index: index, logs: candidate }
|
|
153
|
+
|
|
154
|
+
if overflow.inspect.length > max && group.any?
|
|
155
|
+
logger.write(format({ id: id, log_type: log_type, index: index, logs: group }))
|
|
156
|
+
index += 1
|
|
157
|
+
group = [entry]
|
|
158
|
+
else
|
|
159
|
+
group = candidate
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
return if group.empty?
|
|
164
|
+
|
|
165
|
+
logger.write(format({ id: id, log_type: log_type, index: index, logs: group }))
|
|
166
|
+
end
|
|
167
|
+
|
|
112
168
|
def filter(content)
|
|
113
169
|
return content unless content.respond_to?(:each)
|
|
114
170
|
|
|
@@ -132,16 +188,16 @@ module UnifiedLogger
|
|
|
132
188
|
return true unless severity >= level
|
|
133
189
|
|
|
134
190
|
severity_symbol = SEVERITY_MAP[severity] || :unknown
|
|
135
|
-
|
|
191
|
+
append_log(severity_symbol, message)
|
|
136
192
|
end
|
|
137
193
|
|
|
138
194
|
private
|
|
139
195
|
|
|
140
|
-
def
|
|
196
|
+
def append_log(severity, message)
|
|
141
197
|
message = sanitize_log_message(message) if message.is_a?(String)
|
|
142
198
|
log_hash = { timestamp: UnifiedLogger.formatted_time, severity: severity, message: message }
|
|
143
199
|
|
|
144
|
-
|
|
200
|
+
LOGS.value = LOGS.value + [log_hash]
|
|
145
201
|
end
|
|
146
202
|
|
|
147
203
|
def sanitize_log_message(text)
|
|
@@ -13,7 +13,7 @@ module UnifiedLogger
|
|
|
13
13
|
if UnifiedLogger.current_logger.is_a?(UnifiedLogger::Logger) && !silenced?(env["REQUEST_PATH"])
|
|
14
14
|
log = build_log(started, env, status, headers, response)
|
|
15
15
|
UnifiedLogger.transform_request_log_callable&.call(log, env)
|
|
16
|
-
UnifiedLogger
|
|
16
|
+
UnifiedLogger::Logger.write_log(log)
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
|
|
@@ -36,7 +36,7 @@ module UnifiedLogger
|
|
|
36
36
|
timestamp: UnifiedLogger.formatted_time,
|
|
37
37
|
id: env["action_dispatch.request_id"],
|
|
38
38
|
ip: env["action_dispatch.remote_ip"].to_s,
|
|
39
|
-
controller: path_parameters[:controller],
|
|
39
|
+
controller: path_parameters[:controller]&.camelize&.concat("Controller"),
|
|
40
40
|
action: path_parameters[:action],
|
|
41
41
|
request: {
|
|
42
42
|
path: env["REQUEST_PATH"],
|
|
@@ -56,7 +56,8 @@ module UnifiedLogger
|
|
|
56
56
|
duration: started ? UnifiedLogger.current_time - started : 0
|
|
57
57
|
}
|
|
58
58
|
log[:exception] = UnifiedLogger::Logger.format_exception($!) if $!.present?
|
|
59
|
-
log[:
|
|
59
|
+
log[:logs] = UnifiedLogger::Logger.fetch_and_reset_logs if UnifiedLogger::Logger.logs.any?
|
|
60
|
+
log.merge!(UnifiedLogger::Logger.fetch_and_reset_extra_log_fields) if UnifiedLogger::Logger.extra_log_fields.any?
|
|
60
61
|
|
|
61
62
|
log
|
|
62
63
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Extends Ruby's Logger::Severity to recognize the custom :note level (1.5),
|
|
4
|
+
# sitting between INFO (1) and WARN (2). This patch ensures ALL logger instances
|
|
5
|
+
# (not just UnifiedLogger::Logger) accept :note as a valid level — which is
|
|
6
|
+
# required because Rails applies config.log_level to every logger it creates.
|
|
7
|
+
|
|
8
|
+
Logger::Severity.const_set(:NOTE, 1.5) unless Logger::Severity.const_defined?(:NOTE)
|
|
9
|
+
|
|
10
|
+
if Logger::Severity.respond_to?(:coerce)
|
|
11
|
+
# Ruby 3.3+ / logger gem >= 1.6: patch coerce so level= works for :note
|
|
12
|
+
module UnifiedLoggerSeverityCoerce
|
|
13
|
+
CUSTOM_LEVELS = { "note" => 1.5 }.freeze
|
|
14
|
+
|
|
15
|
+
def coerce(severity)
|
|
16
|
+
if severity.is_a?(Numeric)
|
|
17
|
+
severity
|
|
18
|
+
else
|
|
19
|
+
key = severity.to_s.downcase
|
|
20
|
+
CUSTOM_LEVELS[key] || super
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Logger::Severity.singleton_class.prepend(UnifiedLoggerSeverityCoerce)
|
|
26
|
+
else
|
|
27
|
+
# Older Ruby: no coerce method, patch level= directly on Logger
|
|
28
|
+
module UnifiedLoggerSeverityLevel
|
|
29
|
+
def level=(severity)
|
|
30
|
+
if severity.is_a?(Numeric)
|
|
31
|
+
@level = severity
|
|
32
|
+
elsif severity.to_s.downcase == "note"
|
|
33
|
+
@level = Logger::Severity::NOTE
|
|
34
|
+
else
|
|
35
|
+
super
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
Logger.prepend(UnifiedLoggerSeverityLevel)
|
|
41
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require "unified_logger"
|
|
2
|
+
|
|
3
|
+
module UnifiedLogger
|
|
4
|
+
class SidekiqServerMiddleware
|
|
5
|
+
def call(job_instance, job_hash, queue)
|
|
6
|
+
UnifiedLogger::JobLogger.log(**attrs_from(job_instance, job_hash, queue)) { yield } # rubocop:disable Style/ExplicitBlockArgument
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def attrs_from(job_instance, job_hash, queue)
|
|
12
|
+
aj = job_hash.key?("wrapped") ? job_hash.dig("args", 0) : nil
|
|
13
|
+
retries = job_hash["retry"]
|
|
14
|
+
|
|
15
|
+
{
|
|
16
|
+
class_name: aj&.[]("job_class") || job_hash["wrapped"] || job_instance.class.name,
|
|
17
|
+
id: aj&.[]("job_id") || job_hash["jid"],
|
|
18
|
+
queue: queue,
|
|
19
|
+
params: aj&.[]("arguments") || job_hash["args"],
|
|
20
|
+
retry_count: job_hash["retry_count"].to_i,
|
|
21
|
+
max_retries: resolve_max_retries(retries),
|
|
22
|
+
enqueued_at: aj&.[]("enqueued_at") || job_hash["enqueued_at"]
|
|
23
|
+
}.compact
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def resolve_max_retries(retries)
|
|
27
|
+
return retries if retries.is_a?(Integer)
|
|
28
|
+
|
|
29
|
+
retries == false ? 0 : 25
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
if defined?(Sidekiq)
|
|
35
|
+
Sidekiq.configure_server do |config|
|
|
36
|
+
config.server_middleware do |chain|
|
|
37
|
+
chain.add UnifiedLogger::SidekiqServerMiddleware
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/lib/unified_logger.rb
CHANGED
|
@@ -11,6 +11,7 @@ require "json"
|
|
|
11
11
|
require "logger"
|
|
12
12
|
|
|
13
13
|
require_relative "unified_logger/version"
|
|
14
|
+
require_relative "unified_logger/severity"
|
|
14
15
|
require_relative "unified_logger/logger"
|
|
15
16
|
require_relative "unified_logger/request_logger"
|
|
16
17
|
require_relative "unified_logger/job_logger"
|
|
@@ -20,6 +21,7 @@ module UnifiedLogger
|
|
|
20
21
|
|
|
21
22
|
DEFAULTS = {
|
|
22
23
|
max_log_field_size: 2048,
|
|
24
|
+
max_log_size: 10_000,
|
|
23
25
|
filter_params: %i[passw secret token crypt salt certificate otp ssn set-cookie http_authorization http_cookie pin],
|
|
24
26
|
auto_insert_middleware: true,
|
|
25
27
|
silence_paths: []
|
|
@@ -55,7 +57,8 @@ module UnifiedLogger
|
|
|
55
57
|
end
|
|
56
58
|
|
|
57
59
|
delegate :trim, :format, :format_exception,
|
|
58
|
-
:
|
|
60
|
+
:logs, :fetch_and_reset_logs, :reset_thread_logs,
|
|
61
|
+
:add, :extra_log_fields, :fetch_and_reset_extra_log_fields,
|
|
59
62
|
to: :"UnifiedLogger::Logger"
|
|
60
63
|
end
|
|
61
64
|
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: unified_logger
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Marcovecchio
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-03-
|
|
10
|
+
date: 2026-03-31 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: activesupport
|
|
@@ -75,6 +75,8 @@ files:
|
|
|
75
75
|
- lib/unified_logger/logger.rb
|
|
76
76
|
- lib/unified_logger/railtie.rb
|
|
77
77
|
- lib/unified_logger/request_logger.rb
|
|
78
|
+
- lib/unified_logger/severity.rb
|
|
79
|
+
- lib/unified_logger/sidekiq.rb
|
|
78
80
|
- lib/unified_logger/version.rb
|
|
79
81
|
homepage: https://github.com/marcovecchio/unified_logger
|
|
80
82
|
licenses:
|