unified_logger 0.1.3 → 0.1.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8852d1fcbbbc53507a9d23e79b9e08405955b89c60458cd91e7d3305e71a7be4
4
- data.tar.gz: 46a8534b601d0206ebffdb7237066835d436e960bf0cf17c7f5831d710b00652
3
+ metadata.gz: dac24933499290c1a3703ae8da09aaeaf46e8096e68043ce1ba4618f3b61435a
4
+ data.tar.gz: 3876aef726012b9a428a37a951ddd515126538686f91cc793bef629e9f13f5d2
5
5
  SHA512:
6
- metadata.gz: 7669986b85d090ae4098b62dd28a9a0b17bf08568b0ae8d75c1a857c3d2ca6060937128a3f6dd31f17216f7e5120f617eecb5c242e14548350a61be46d77bcb4
7
- data.tar.gz: 81dfd77bfe8205b311ad2dac30fd3f4f7dfcf4aaf6e0a6ab3f5bff84e754cbfbe1ea07b8d925c33a6dfee4147fee6a204eac9d6250dba5c2fe63cda7b7ccb817
6
+ metadata.gz: 4bc1ef83dd6f6be2cce82a64226de3fcef60bff86728a136497eacd396b4991b0260c8740126b21e32336c2e4c42d1c821d9739141b4bf8d7783f7d9a287a9fa
7
+ data.tar.gz: a0e0a7d4b2b6b3907d1c73e6ee569703fa66ec56eda323efd3823563e4396151407bdda27997188b7bfcb912e54f451295d30b6c5874f594c9925669b8527771
@@ -1,45 +1,61 @@
1
- require "English"
2
-
3
1
  module UnifiedLogger
4
2
  class JobLogger
5
- DEFAULT_MAX_RETRIES = 5
6
-
7
3
  class << self
8
- def log(job)
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
9
7
  yield
10
8
  ensure
11
- log_execution(job) if UnifiedLogger.current_logger.is_a?(UnifiedLogger::Logger)
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
12
14
  end
13
15
 
14
16
  private
15
17
 
16
- def log_execution(job)
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
+
17
22
  log = {
18
- log_type: :job,
19
- timestamp: UnifiedLogger.formatted_time,
20
- class_name: job.class.name,
21
- id: job.job_id,
22
- queue: job.queue_name,
23
- params: job.arguments,
24
- executions_count: job.executions,
25
- exception_executions: job.exception_executions,
26
- enqueued_at: job.enqueued_at,
27
- locale: job.locale,
28
- duration: job.enqueued_at.present? ? UnifiedLogger.current_time - job.enqueued_at.in_time_zone : "unknown"
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
29
35
  }
30
- log[:custom] = UnifiedLogger::Logger.fetch_and_reset_custom_logs if UnifiedLogger::Logger.custom_logs.any?
36
+ log.merge!(extra) if extra.any?
37
+ log.compact!
31
38
 
32
- if $ERROR_INFO
33
- log[:exception] = UnifiedLogger::Logger.format_exception($ERROR_INFO)
34
- log[:status] = job.executions >= DEFAULT_MAX_RETRIES ? :error : :warn
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?
41
+
42
+ if $!
43
+ log[:exception] = Logger.format_exception($!)
44
+ log[:status] = max_retries && retry_count < max_retries ? :warn : :error
35
45
  else
36
46
  log[:status] = :ok
37
47
  end
38
48
 
39
- custom = {}
40
- UnifiedLogger.transform_job_log_callable&.call(custom)
41
- log.merge!(custom)
42
- UnifiedLogger.current_logger.write(UnifiedLogger::Logger.format(log))
49
+ UnifiedLogger.transform_job_log_callable&.call(log)
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
43
59
  end
44
60
  end
45
61
  end
@@ -1,9 +1,12 @@
1
1
  module UnifiedLogger
2
2
  class Logger < ::Logger
3
- CUSTOM_LOGS = Concurrent::ThreadLocalVar.new([])
3
+ LOGS = Concurrent::ThreadLocalVar.new([])
4
+ EXTRA_LOG_FIELDS = Concurrent::ThreadLocalVar.new({})
5
+ NOTE = 1.5
4
6
  SEVERITY_LEVELS = {
5
7
  debug: ::Logger::DEBUG,
6
8
  info: ::Logger::INFO,
9
+ note: NOTE,
7
10
  warn: ::Logger::WARN,
8
11
  error: ::Logger::ERROR,
9
12
  fatal: ::Logger::FATAL,
@@ -17,6 +20,14 @@ module UnifiedLogger
17
20
  self.formatter = proc {}
18
21
  end
19
22
 
23
+ def level=(severity)
24
+ if severity == :note
25
+ @level = NOTE
26
+ else
27
+ super
28
+ end
29
+ end
30
+
20
31
  def debug(message = nil, &block)
21
32
  message = block.call if message.nil? && block
22
33
  add(::Logger::DEBUG, message)
@@ -27,6 +38,11 @@ module UnifiedLogger
27
38
  add(::Logger::INFO, message)
28
39
  end
29
40
 
41
+ def note(message = nil, &block)
42
+ message = block.call if message.nil? && block
43
+ add(NOTE, message)
44
+ end
45
+
30
46
  def warn(message = nil, &block)
31
47
  message = block.call if message.nil? && block
32
48
  add(::Logger::WARN, message)
@@ -57,18 +73,33 @@ module UnifiedLogger
57
73
  end
58
74
 
59
75
  class << self
60
- def custom_logs
61
- CUSTOM_LOGS.value
76
+ def logs
77
+ LOGS.value
78
+ end
79
+
80
+ def add(hash)
81
+ EXTRA_LOG_FIELDS.value = EXTRA_LOG_FIELDS.value.merge(hash)
82
+ end
83
+
84
+ def extra_log_fields
85
+ EXTRA_LOG_FIELDS.value
86
+ end
87
+
88
+ def fetch_and_reset_extra_log_fields
89
+ fields = extra_log_fields
90
+ EXTRA_LOG_FIELDS.value = {}
91
+ fields
62
92
  end
63
93
 
64
94
  def reset_thread_logs
65
- CUSTOM_LOGS.value = []
95
+ LOGS.value = []
96
+ EXTRA_LOG_FIELDS.value = {}
66
97
  end
67
98
 
68
- def fetch_and_reset_custom_logs
69
- logs = custom_logs
70
- reset_thread_logs
71
- logs
99
+ def fetch_and_reset_logs
100
+ current = logs
101
+ LOGS.value = []
102
+ current
72
103
  end
73
104
 
74
105
  def trim(data)
@@ -103,12 +134,46 @@ module UnifiedLogger
103
134
 
104
135
  def format(log)
105
136
  filtered_log = filter(log)
106
- formatter = UnifiedLogger.log_transformer
137
+ formatter = UnifiedLogger.format_log_callable
107
138
  formatter.present? ? formatter.call(filtered_log) : filtered_log.to_json
108
139
  end
109
140
 
141
+ def write_log(log)
142
+ logger = UnifiedLogger.current_logger
143
+ max = UnifiedLogger.config[:max_log_size]
144
+ if log.inspect.length <= max || !log.key?(:logs)
145
+ logger.write(format(log))
146
+ else
147
+ entries = log.delete(:logs)
148
+ logger.write(format(log))
149
+ write_overflow_logs(log[:id], log[:log_type], entries, max, logger)
150
+ end
151
+ end
152
+
110
153
  private
111
154
 
155
+ def write_overflow_logs(id, log_type, entries, max, logger)
156
+ index = 1
157
+ group = []
158
+
159
+ entries.each do |entry|
160
+ candidate = group + [entry]
161
+ overflow = { id: id, log_type: log_type, index: index, logs: candidate }
162
+
163
+ if overflow.inspect.length > max && group.any?
164
+ logger.write(format({ id: id, log_type: log_type, index: index, logs: group }))
165
+ index += 1
166
+ group = [entry]
167
+ else
168
+ group = candidate
169
+ end
170
+ end
171
+
172
+ return if group.empty?
173
+
174
+ logger.write(format({ id: id, log_type: log_type, index: index, logs: group }))
175
+ end
176
+
112
177
  def filter(content)
113
178
  return content unless content.respond_to?(:each)
114
179
 
@@ -132,16 +197,16 @@ module UnifiedLogger
132
197
  return true unless severity >= level
133
198
 
134
199
  severity_symbol = SEVERITY_MAP[severity] || :unknown
135
- append_custom_log(severity_symbol, message)
200
+ append_log(severity_symbol, message)
136
201
  end
137
202
 
138
203
  private
139
204
 
140
- def append_custom_log(severity, message)
205
+ def append_log(severity, message)
141
206
  message = sanitize_log_message(message) if message.is_a?(String)
142
207
  log_hash = { timestamp: UnifiedLogger.formatted_time, severity: severity, message: message }
143
208
 
144
- CUSTOM_LOGS.value = CUSTOM_LOGS.value + [log_hash]
209
+ LOGS.value = LOGS.value + [log_hash]
145
210
  end
146
211
 
147
212
  def sanitize_log_message(text)
@@ -1,5 +1,3 @@
1
- require "English"
2
-
3
1
  module UnifiedLogger
4
2
  class RequestLogger
5
3
  def initialize(app)
@@ -14,10 +12,8 @@ module UnifiedLogger
14
12
  ensure
15
13
  if UnifiedLogger.current_logger.is_a?(UnifiedLogger::Logger) && !silenced?(env["REQUEST_PATH"])
16
14
  log = build_log(started, env, status, headers, response)
17
- custom = {}
18
- UnifiedLogger.transform_request_log_callable&.call(custom, env)
19
- log.merge!(custom)
20
- UnifiedLogger.current_logger.write(UnifiedLogger::Logger.format(log))
15
+ UnifiedLogger.transform_request_log_callable&.call(log, env)
16
+ UnifiedLogger::Logger.write_log(log)
21
17
  end
22
18
  end
23
19
 
@@ -40,7 +36,7 @@ module UnifiedLogger
40
36
  timestamp: UnifiedLogger.formatted_time,
41
37
  id: env["action_dispatch.request_id"],
42
38
  ip: env["action_dispatch.remote_ip"].to_s,
43
- controller: path_parameters[:controller],
39
+ controller: path_parameters[:controller]&.camelize&.concat("Controller"),
44
40
  action: path_parameters[:action],
45
41
  request: {
46
42
  path: env["REQUEST_PATH"],
@@ -59,8 +55,9 @@ module UnifiedLogger
59
55
  process_id: Process.pid,
60
56
  duration: started ? UnifiedLogger.current_time - started : 0
61
57
  }
62
- log[:exception] = UnifiedLogger::Logger.format_exception($ERROR_INFO) if $ERROR_INFO.present?
63
- log[:custom] = UnifiedLogger::Logger.fetch_and_reset_custom_logs if UnifiedLogger::Logger.custom_logs.any?
58
+ log[:exception] = UnifiedLogger::Logger.format_exception($!) if $!.present?
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?
64
61
 
65
62
  log
66
63
  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
@@ -1,3 +1,3 @@
1
1
  module UnifiedLogger
2
- VERSION = "0.1.3".freeze
2
+ VERSION = "0.1.6".freeze
3
3
  end
@@ -20,6 +20,7 @@ module UnifiedLogger
20
20
 
21
21
  DEFAULTS = {
22
22
  max_log_field_size: 2048,
23
+ max_log_size: 10_000,
23
24
  filter_params: %i[passw secret token crypt salt certificate otp ssn set-cookie http_authorization http_cookie pin],
24
25
  auto_insert_middleware: true,
25
26
  silence_paths: []
@@ -34,7 +35,7 @@ module UnifiedLogger
34
35
  end
35
36
 
36
37
  class << self
37
- attr_reader :transform_request_log_callable, :transform_job_log_callable, :log_transformer
38
+ attr_reader :transform_request_log_callable, :transform_job_log_callable, :format_log_callable
38
39
 
39
40
  def transform_request_log=(callable)
40
41
  raise DoubleDefineError, "transform_request_log already defined" if @transform_request_log_callable
@@ -48,14 +49,15 @@ module UnifiedLogger
48
49
  @transform_job_log_callable = callable
49
50
  end
50
51
 
51
- def log_transformer=(callable)
52
- raise DoubleDefineError, "log_transformer already defined" if @log_transformer
52
+ def format_log=(callable)
53
+ raise DoubleDefineError, "format_log already defined" if @format_log_callable
53
54
 
54
- @log_transformer = callable
55
+ @format_log_callable = callable
55
56
  end
56
57
 
57
58
  delegate :trim, :format, :format_exception,
58
- :custom_logs, :fetch_and_reset_custom_logs, :reset_thread_logs,
59
+ :logs, :fetch_and_reset_logs, :reset_thread_logs,
60
+ :add, :extra_log_fields, :fetch_and_reset_extra_log_fields,
59
61
  to: :"UnifiedLogger::Logger"
60
62
  end
61
63
 
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.3
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marcovecchio
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-03-23 00:00:00.000000000 Z
10
+ date: 2026-03-30 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: activesupport
@@ -75,6 +75,7 @@ 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/sidekiq.rb
78
79
  - lib/unified_logger/version.rb
79
80
  homepage: https://github.com/marcovecchio/unified_logger
80
81
  licenses: