legion-logging 1.4.2 → 1.5.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.
@@ -5,6 +5,13 @@ require 'securerandom'
5
5
  module Legion
6
6
  module Logging
7
7
  module Methods
8
+ COMPONENT_REGEX = %r{
9
+ /(runners|actors|actor|helpers|hooks|absorbers|matchers|transport|
10
+ exchanges|queues|messages|data|builders|tools|adapters|engines|
11
+ formatters|parsers|middleware)/
12
+ }x
13
+ EXCEPTION_PRIORITY = { warn: 0, error: 5, fatal: 9 }.freeze
14
+
8
15
  def trace(raw_message = nil, size: @trace_size, log_caller: true)
9
16
  return unless @trace_enabled
10
17
 
@@ -23,62 +30,36 @@ module Legion
23
30
  return unless log.level < 1
24
31
 
25
32
  message = yield if message.nil? && block_given?
26
- message = maybe_redact(message)
27
- message = Rainbow(message).blue if @color
28
- writer = @async_writer
29
- if writer&.alive?
30
- writer.push(AsyncWriter::LogEntry.new(level: :debug, message: message, writer_context: nil))
31
- else
32
- log.debug(message)
33
- end
33
+ raw = maybe_redact(message)
34
+ formatted = format_message_for_level(:debug, raw)
35
+ write_async_or_sync(:debug, formatted, raw)
34
36
  end
35
37
 
36
38
  def info(message = nil)
37
39
  return unless log.level < 2
38
40
 
39
41
  message = yield if message.nil? && block_given?
40
- message = maybe_redact(message)
41
- message = Rainbow(message).green if @color
42
- writer = @async_writer
43
- if writer&.alive?
44
- writer.push(AsyncWriter::LogEntry.new(level: :info, message: message, writer_context: nil))
45
- else
46
- log.info(message)
47
- end
42
+ raw = maybe_redact(message)
43
+ formatted = format_message_for_level(:info, raw)
44
+ write_async_or_sync(:info, formatted, raw)
48
45
  end
49
46
 
50
47
  def warn(message = nil)
51
48
  return unless log.level < 3
52
49
 
53
50
  message = yield if message.nil? && block_given?
54
- message = maybe_redact(message)
55
- raw = message
56
- message = Rainbow(message).yellow if @color
57
- writer = @async_writer
58
- if writer&.alive?
59
- ctx = build_writer_context(:warn, raw)
60
- writer.push(AsyncWriter::LogEntry.new(level: :warn, message: message, writer_context: ctx))
61
- else
62
- log.warn(message)
63
- fire_log_writer(:warn, raw)
64
- end
51
+ raw = maybe_redact(message)
52
+ formatted = format_message_for_level(:warn, raw)
53
+ write_async_or_sync(:warn, formatted, raw, writer_context: build_writer_context(:warn, raw))
65
54
  end
66
55
 
67
56
  def error(message = nil)
68
57
  return unless log.level < 4
69
58
 
70
59
  message = yield if message.nil? && block_given?
71
- message = maybe_redact(message)
72
- raw = message
73
- message = Rainbow(message).red if @color
74
- writer = @async_writer
75
- if writer&.alive?
76
- ctx = build_writer_context(:error, raw)
77
- writer.push(AsyncWriter::LogEntry.new(level: :error, message: message, writer_context: ctx))
78
- else
79
- log.error(message)
80
- fire_log_writer(:error, raw)
81
- end
60
+ raw = maybe_redact(message)
61
+ formatted = format_message_for_level(:error, raw)
62
+ write_async_or_sync(:error, formatted, raw, writer_context: build_writer_context(:error, raw))
82
63
  end
83
64
 
84
65
  def fatal(message = nil)
@@ -94,13 +75,22 @@ module Legion
94
75
 
95
76
  def unknown(message = nil)
96
77
  message = yield if message.nil? && block_given?
97
- message = maybe_redact(message)
98
- message = Rainbow(message).purple if @color
99
- writer = @async_writer
100
- if writer&.alive?
101
- writer.push(AsyncWriter::LogEntry.new(level: :unknown, message: message, writer_context: nil))
102
- else
103
- log.unknown(message)
78
+ raw = maybe_redact(message)
79
+ formatted = format_message_for_level(:unknown, raw)
80
+ write_async_or_sync(:unknown, formatted, raw)
81
+ end
82
+
83
+ def emit_tagged(level, message = nil, segments: nil, method_ctx: nil)
84
+ level = level.to_sym
85
+ message = yield if message.nil? && block_given?
86
+ return if message.nil?
87
+
88
+ raw = maybe_redact(message)
89
+ formatted = format_message_for_level(level, raw)
90
+
91
+ with_tagged_context(segments, method_ctx) do
92
+ write_forced(level, formatted)
93
+ fire_log_writer(level, raw) if %i[warn error fatal].include?(level)
104
94
  end
105
95
  end
106
96
 
@@ -116,6 +106,7 @@ module Legion
116
106
  level = level.to_sym if level.respond_to?(:to_sym)
117
107
  # 1. Log human-readable line to stdout/file (bypass writer callbacks)
118
108
  msg = exception.respond_to?(:message) ? exception.message : exception.to_s
109
+ msg = maybe_redact(msg)
119
110
  log.public_send(level, msg) if respond_to?(:log) && log.respond_to?(level)
120
111
 
121
112
  # 2. Build rich exception event
@@ -168,6 +159,81 @@ module Legion
168
159
  message
169
160
  end
170
161
 
162
+ def format_message_for_level(level, message)
163
+ return Rainbow(message).blue if level == :debug && @color
164
+ return Rainbow(message).green if level == :info && @color
165
+ return Rainbow(message).yellow if level == :warn && @color
166
+ return Rainbow(message).red if level == :error && @color
167
+ return Rainbow(message).darkred if level == :fatal && @color
168
+ return Rainbow(message).purple if level == :unknown && @color
169
+
170
+ message
171
+ end
172
+
173
+ def with_tagged_context(segments, method_ctx)
174
+ prev_segments = Thread.current[:legion_log_segments]
175
+ prev_method_ctx = Thread.current[:legion_log_method]
176
+
177
+ Thread.current[:legion_log_segments] = segments unless segments.nil?
178
+ Thread.current[:legion_log_method] = method_ctx unless method_ctx.nil?
179
+ yield
180
+ ensure
181
+ Thread.current[:legion_log_segments] = prev_segments
182
+ Thread.current[:legion_log_method] = prev_method_ctx
183
+ end
184
+
185
+ def write_forced(level, message)
186
+ logger = log
187
+ formatter = logger.formatter || ::Logger::Formatter.new
188
+ rendered = formatter.call(severity_label_for(level), Time.now, nil, message)
189
+
190
+ log_device = logger.instance_variable_get(:@logdev)
191
+ if log_device.respond_to?(:write)
192
+ log_device.write(rendered)
193
+ else
194
+ $stdout.write(rendered)
195
+ end
196
+ end
197
+
198
+ def severity_label_for(level)
199
+ return 'ANY' if level == :unknown
200
+
201
+ level.to_s.upcase
202
+ end
203
+
204
+ def write_async_or_sync(level, formatted_message, raw_message, writer_context: nil)
205
+ writer = @async_writer
206
+ caller_trace = capture_runner_trace_for_async
207
+ if writer&.alive?
208
+ queued = writer.push(AsyncWriter::LogEntry.new(
209
+ level: level,
210
+ message: formatted_message,
211
+ writer_context: writer_context,
212
+ segments: Thread.current[:legion_log_segments],
213
+ method_ctx: Thread.current[:legion_log_method],
214
+ caller_trace: caller_trace
215
+ ))
216
+ return if queued
217
+ end
218
+
219
+ with_caller_trace(caller_trace) do
220
+ log.public_send(level, formatted_message)
221
+ fire_log_writer(level, raw_message) if writer_context
222
+ end
223
+ end
224
+
225
+ def capture_runner_trace_for_async
226
+ build_runner_trace(caller_locations(4, 1)&.first)
227
+ end
228
+
229
+ def with_caller_trace(caller_trace)
230
+ prev_caller_trace = Thread.current[:legion_log_caller]
231
+ Thread.current[:legion_log_caller] = caller_trace
232
+ yield
233
+ ensure
234
+ Thread.current[:legion_log_caller] = prev_caller_trace
235
+ end
236
+
171
237
  def redaction_enabled?
172
238
  return false unless defined?(Legion::Settings)
173
239
 
@@ -219,16 +285,18 @@ module Legion
219
285
  timestamp: Time.now.to_i,
220
286
  app_id: 'legionio',
221
287
  type: 'exception_event',
222
- priority: { warn: 0, error: 5, fatal: 9 }[level] || 5,
288
+ priority: EXCEPTION_PRIORITY[level] || 5,
223
289
  delivery_mode: 2
224
290
  }
225
291
  end
226
292
 
227
293
  def build_writer_context(level, message)
228
- return nil if Legion::Logging.instance_variable_get(:@log_writer).nil?
294
+ has_writer = !Legion::Logging.instance_variable_get(:@log_writer).nil?
295
+ has_hooks = defined?(Legion::Logging::Hooks) && Legion::Logging::Hooks.enabled?
296
+ return nil unless has_writer || has_hooks
229
297
 
230
298
  lex_val = instance_variable_defined?(:@lex) ? @lex : nil
231
- lex_segs = instance_variable_defined?(:@lex_segments) ? @lex_segments : nil
299
+ lex_segs = Thread.current[:legion_log_segments] || (instance_variable_defined?(:@lex_segments) ? @lex_segments : nil)
232
300
 
233
301
  event = Legion::Logging::EventBuilder.build(
234
302
  level: level,
@@ -242,7 +310,7 @@ module Legion
242
310
 
243
311
  def fire_log_writer(level, message)
244
312
  lex_val = instance_variable_defined?(:@lex) ? @lex : nil
245
- lex_segs = instance_variable_defined?(:@lex_segments) ? @lex_segments : nil
313
+ lex_segs = Thread.current[:legion_log_segments] || (instance_variable_defined?(:@lex_segments) ? @lex_segments : nil)
246
314
 
247
315
  event = Legion::Logging::EventBuilder.build(
248
316
  level: level,
@@ -252,13 +320,13 @@ module Legion
252
320
  caller_offset: 4
253
321
  )
254
322
  lex_name = event[:lex] || 'core'
255
- component = event.dig(:caller, :file).to_s[%r{/(runners|actors|transport|helpers|builders)/}, 1] || 'unknown'
323
+ component = event.dig(:caller, :file).to_s[COMPONENT_REGEX, 1] || 'unknown'
256
324
  routing_key = "legion.logging.log.#{level}.#{lex_name}.#{component}"
257
325
  Legion::Logging.log_writer.call(event, routing_key: routing_key)
326
+ Legion::Logging::Hooks.fire(level, message, event) if defined?(Legion::Logging::Hooks)
258
327
  rescue StandardError => e
259
- if respond_to?(:log) && log.respond_to?(:warn)
260
- log.warn("fire_log_writer failed for level=#{level}, routing_key=#{routing_key}: #{e.class}: #{e.message}")
261
- end
328
+ rk = defined?(routing_key) ? routing_key : 'unknown'
329
+ log.warn("fire_log_writer failed for level=#{level}, routing_key=#{rk}: #{e.class}: #{e.message}") if respond_to?(:log) && log.respond_to?(:warn)
262
330
  end
263
331
  end
264
332
  end
@@ -10,7 +10,6 @@ module Legion
10
10
  def write(message)
11
11
  @targets.each do |t|
12
12
  t.write(message)
13
- t.flush if t.respond_to?(:flush)
14
13
  end
15
14
  end
16
15
 
@@ -18,7 +18,18 @@ module Legion
18
18
  bearer_token: %r{Bearer\s+[A-Za-z0-9._~+/=-]{20,}}i
19
19
  }.freeze
20
20
 
21
- SENSITIVE_FIELDS = %w[password secret token api_key authorization].freeze
21
+ SENSITIVE_FIELDS = %w[
22
+ password
23
+ secret
24
+ token
25
+ api_key
26
+ access_key
27
+ private_key
28
+ public_key
29
+ authorization
30
+ ].freeze
31
+ SENSITIVE_SUFFIXES = %w[token secret password passphrase credential credentials].freeze
32
+ SAFE_KEY_FIELDS = %w[primary_key foreign_key sort_key partition_key routing_key].freeze
22
33
 
23
34
  REDACTED = '[REDACTED]'
24
35
 
@@ -49,7 +60,17 @@ module Legion
49
60
  private
50
61
 
51
62
  def sensitive_field?(key)
52
- SENSITIVE_FIELDS.include?(key.to_s.downcase)
63
+ normalized = normalize_key(key)
64
+ return false if SAFE_KEY_FIELDS.include?(normalized)
65
+ return true if SENSITIVE_FIELDS.include?(normalized)
66
+ return true if normalized.include?('authorization')
67
+ return true if normalized.start_with?('auth_') || normalized.end_with?('_auth')
68
+ return true if normalized.start_with?('bearer_') || normalized.end_with?('_bearer')
69
+ return true if SENSITIVE_SUFFIXES.any? { |suffix| normalized.end_with?("_#{suffix}") }
70
+
71
+ %w[api access client private public auth secret signing session].any? do |prefix|
72
+ normalized == "#{prefix}_key"
73
+ end
53
74
  end
54
75
 
55
76
  def all_patterns
@@ -79,6 +100,16 @@ module Legion
79
100
  def reset_pattern_cache!
80
101
  @all_patterns = nil
81
102
  end
103
+
104
+ def refresh_patterns!
105
+ reset_pattern_cache!
106
+ end
107
+
108
+ public :refresh_patterns!
109
+
110
+ def normalize_key(key)
111
+ key.to_s.downcase.gsub(/[^a-z0-9]+/, '_').gsub(/\A_+|_+\z/, '')
112
+ end
82
113
  end
83
114
  end
84
115
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Logging
5
+ module Settings
6
+ def self.default
7
+ {
8
+ level: :info,
9
+ trace: true,
10
+ trace_size: 4,
11
+ extended: true
12
+ }
13
+ end
14
+ end
15
+ end
16
+ end
@@ -11,10 +11,18 @@ module Legion
11
11
 
12
12
  class << self
13
13
  def ship(event)
14
+ ship_batch([event])
15
+ end
16
+
17
+ def ship_batch(events)
18
+ batch = Array(events)
19
+ return true if batch.empty?
20
+
14
21
  path = resolve_path
15
22
  FileUtils.mkdir_p(File.dirname(path))
16
23
  File.open(path, 'a') do |f|
17
- f.puts(::JSON.generate(event))
24
+ f.write(batch.map { |event| ::JSON.generate(event) }.join("\n"))
25
+ f.write("\n")
18
26
  end
19
27
  true
20
28
  rescue StandardError => e
@@ -10,6 +10,10 @@ module Legion
10
10
  module HttpTransport
11
11
  class << self
12
12
  def ship(events)
13
+ ship_batch(events)
14
+ end
15
+
16
+ def ship_batch(events)
13
17
  endpoint = resolve_endpoint
14
18
  return false unless endpoint
15
19
 
@@ -29,7 +33,7 @@ module Legion
29
33
  def post(uri, body)
30
34
  req = Net::HTTP::Post.new(uri)
31
35
  req['Content-Type'] = 'application/json'
32
- apply_auth(req)
36
+ apply_auth(req, uri)
33
37
 
34
38
  Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https',
35
39
  open_timeout: 5, read_timeout: 10) do |http|
@@ -50,11 +54,11 @@ module Legion
50
54
  uri.path.include?('/services/collector')
51
55
  end
52
56
 
53
- def apply_auth(req)
57
+ def apply_auth(req, uri)
54
58
  token = auth_token
55
59
  return unless token
56
60
 
57
- req['Authorization'] = if splunk_hec?(URI(req.path.empty? ? '/' : req.uri&.to_s || '/'))
61
+ req['Authorization'] = if splunk_hec?(uri)
58
62
  "Splunk #{token}"
59
63
  else
60
64
  "Bearer #{token}"
@@ -30,21 +30,25 @@ module Legion
30
30
  transport = TRANSPORTS[transport_type]
31
31
  return unless transport
32
32
 
33
- batch = nil
34
- @mutex.synchronize do
35
- batch = @buffer.dup
36
- @buffer.clear
33
+ @flush_mutex ||= Mutex.new
34
+ @flush_mutex.synchronize do
35
+ batch = nil
36
+ @mutex.synchronize { batch = @buffer.dup }
37
+ return if batch.empty?
38
+
39
+ delivered = deliver(transport, batch)
40
+ @mutex.synchronize { @buffer.shift(batch.size) if delivered }
41
+ delivered
37
42
  end
38
-
39
- deliver(transport, batch)
40
43
  end
41
44
 
42
45
  def start
43
46
  return unless enabled?
44
47
  return if @flush_thread&.alive?
45
48
 
46
- @buffer = []
47
- @mutex = Mutex.new
49
+ @buffer ||= []
50
+ @mutex ||= Mutex.new
51
+ @flush_mutex ||= Mutex.new
48
52
  interval = flush_interval
49
53
  @flush_thread = Thread.new do
50
54
  loop do
@@ -83,14 +87,14 @@ module Legion
83
87
  end
84
88
 
85
89
  def deliver(transport, batch)
86
- if transport.method(:ship).arity == 1
87
- # HttpTransport accepts a batch array
88
- transport.ship(batch)
90
+ if transport.respond_to?(:ship_batch)
91
+ transport.ship_batch(batch)
89
92
  else
90
- batch.each { |e| transport.ship(e) }
93
+ batch.all? { |event| transport.ship(event) }
91
94
  end
92
95
  rescue StandardError => e
93
96
  Legion::Logging.error("Shipper deliver failed: #{e.message}") if defined?(Legion::Logging)
97
+ false
94
98
  end
95
99
 
96
100
  def shippable_level?(level)
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Logging
5
+ class TaggedLogger
6
+ LEVELS = { debug: 0, info: 1, warn: 2, error: 3, fatal: 4, unknown: 5 }.freeze
7
+
8
+ attr_reader :segments, :trace_enabled, :extended
9
+
10
+ def initialize(
11
+ segments:,
12
+ level: Legion::Logging::Settings.default[:level],
13
+ trace: Legion::Logging::Settings.default[:trace],
14
+ trace_size: Legion::Logging::Settings.default[:trace_size],
15
+ extended: Legion::Logging::Settings.default[:extended],
16
+ **_opts
17
+ )
18
+ @segments = segments
19
+ @level_value =
20
+ if level.is_a?(Integer)
21
+ level
22
+ else
23
+ default_level = Legion::Logging::Settings.default[:level].to_s.downcase.to_sym
24
+ LEVELS.fetch(level.to_s.downcase.to_sym, LEVELS.fetch(default_level, LEVELS[:info]))
25
+ end
26
+ @trace_enabled = trace
27
+ @trace_size = trace_size
28
+ @extended = extended
29
+ end
30
+
31
+ def level
32
+ @level_value
33
+ end
34
+
35
+ def debug(message = nil)
36
+ return unless @level_value < 1
37
+
38
+ message = yield if message.nil? && block_given?
39
+ with_segments { dispatch(:debug, message) }
40
+ end
41
+
42
+ def info(message = nil)
43
+ return unless @level_value < 2
44
+
45
+ message = yield if message.nil? && block_given?
46
+ with_segments { dispatch(:info, message) }
47
+ end
48
+
49
+ def warn(message = nil)
50
+ return unless @level_value < 3
51
+
52
+ message = yield if message.nil? && block_given?
53
+ with_segments { dispatch(:warn, message) }
54
+ end
55
+
56
+ def error(message = nil)
57
+ return unless @level_value < 4
58
+
59
+ message = yield if message.nil? && block_given?
60
+ with_segments { dispatch(:error, message) }
61
+ end
62
+
63
+ def fatal(message = nil)
64
+ return unless @level_value < 5
65
+
66
+ message = yield if message.nil? && block_given?
67
+ with_segments { dispatch(:fatal, message) }
68
+ end
69
+
70
+ def unknown(message = nil)
71
+ message = yield if message.nil? && block_given?
72
+ with_segments { dispatch(:unknown, message) }
73
+ end
74
+
75
+ def trace(raw_message = nil, size: @trace_size, log_caller: true)
76
+ return unless @trace_enabled
77
+
78
+ raw_message = yield if raw_message.nil? && block_given?
79
+ message = "Tracing: #{raw_message} "
80
+ if log_caller
81
+ frames = size ? caller_locations(2, size) : caller_locations(2)
82
+ message.concat(frames&.join(', ').to_s)
83
+ end
84
+ with_segments { Legion::Logging.unknown(message) }
85
+ end
86
+
87
+ def thread(kvl: false, **_opts)
88
+ if kvl
89
+ "thread=#{Thread.current.object_id}"
90
+ else
91
+ Thread.current.object_id.to_s
92
+ end
93
+ end
94
+
95
+ private
96
+
97
+ def dispatch(level, message)
98
+ return unless defined?(Legion::Logging)
99
+
100
+ if Legion::Logging.respond_to?(:emit_tagged)
101
+ Legion::Logging.emit_tagged(level, message, segments: @segments)
102
+ return
103
+ end
104
+
105
+ if Legion::Logging.respond_to?(level)
106
+ Legion::Logging.public_send(level, message)
107
+ return
108
+ end
109
+
110
+ fallback = fallback_level(level)
111
+ Legion::Logging.public_send(fallback, message) if fallback && Legion::Logging.respond_to?(fallback)
112
+ end
113
+
114
+ def fallback_level(level)
115
+ return :debug if level == :unknown
116
+
117
+ nil
118
+ end
119
+
120
+ def with_segments
121
+ prev = Thread.current[:legion_log_segments]
122
+ Thread.current[:legion_log_segments] = @segments
123
+ yield
124
+ ensure
125
+ Thread.current[:legion_log_segments] = prev
126
+ end
127
+ end
128
+ end
129
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module Logging
5
- VERSION = '1.4.2'
5
+ VERSION = '1.5.0'
6
6
  end
7
7
  end
@@ -1,13 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'legion/logging/version'
4
+ require 'legion/logging/settings'
4
5
  require 'legion/logging/logger'
5
6
  require 'legion/logging/methods'
6
7
  require 'legion/logging/builder'
7
8
  require 'legion/logging/event_builder'
8
9
  require 'legion/logging/async_writer'
10
+ require 'legion/logging/tagged_logger'
9
11
  require 'legion/logging/helper'
10
12
  require 'legion/logging/category_registry'
13
+ require 'legion/logging/hooks'
11
14
 
12
15
  require 'json'
13
16
  require 'logger'
@@ -22,7 +25,7 @@ module Legion
22
25
  attr_reader :color
23
26
  attr_writer :log_writer, :exception_writer
24
27
 
25
- DEFAULT_LOG_WRITER = ->(_event, routing_key:) {}
28
+ DEFAULT_LOG_WRITER = ->(_event, routing_key:) {}
26
29
  DEFAULT_EXCEPTION_WRITER = ->(_event, routing_key:, headers:, properties:) {}
27
30
 
28
31
  def log_writer
@@ -33,6 +36,14 @@ module Legion
33
36
  @exception_writer || DEFAULT_EXCEPTION_WRITER
34
37
  end
35
38
 
39
+ def current_settings
40
+ (@current_settings || {}).dup
41
+ end
42
+
43
+ def configuration_generation
44
+ @configuration_generation || 0
45
+ end
46
+
36
47
  def register_category(name, description: nil, expected_fields: [])
37
48
  CategoryRegistry.register_category(name, description: description, expected_fields: expected_fields)
38
49
  end
@@ -41,18 +52,57 @@ module Legion
41
52
  CategoryRegistry.registered_categories
42
53
  end
43
54
 
44
- def setup(level: 'info', format: :text, async: true, **options)
55
+ def on_fatal(&)
56
+ Hooks.on_fatal(&)
57
+ end
58
+
59
+ def on_error(&)
60
+ Hooks.on_error(&)
61
+ end
62
+
63
+ def on_warn(&)
64
+ Hooks.on_warn(&)
65
+ end
66
+
67
+ def enable_hooks!
68
+ Hooks.enable_hooks!
69
+ end
70
+
71
+ def disable_hooks!
72
+ Hooks.disable_hooks!
73
+ end
74
+
75
+ def clear_hooks!
76
+ Hooks.clear_hooks!
77
+ end
78
+
79
+ def setup(level: 'debug', format: :text, async: true, **options)
45
80
  output(**options)
46
81
  log_level(level)
47
82
  log_format(format: format, **options)
48
83
  @color = options[:color]
49
84
  @color = format != :json && (options[:color] || (options[:color].nil? && options[:log_file].nil?))
85
+ @current_settings = {
86
+ level: level,
87
+ format: format.to_sym,
88
+ async: async,
89
+ trace: options.fetch(:trace, true),
90
+ trace_size: options.fetch(:trace_size, 4),
91
+ extended: options.fetch(:extended, true),
92
+ log_file: options[:log_file],
93
+ log_stdout: options[:log_stdout],
94
+ include_pid: options.fetch(:include_pid, false),
95
+ color: @color
96
+ }.freeze
97
+ @configuration_generation = configuration_generation + 1
98
+ Legion::Logging::Redactor.refresh_patterns! if defined?(Legion::Logging::Redactor)
50
99
  if async
51
- buffer = if defined?(Legion::Settings)
52
- Legion::Settings.dig(:logging, :async, :buffer_size) || 10_000
53
- else
54
- 10_000
100
+ buffer = if defined?(Legion::Settings) && Legion::Settings.respond_to?(:[])
101
+ logging_settings = Legion::Settings[:logging]
102
+ async_settings = logging_settings[:async] if logging_settings.is_a?(Hash)
103
+ async_settings[:buffer_size] if async_settings.is_a?(Hash)
55
104
  end
105
+ buffer ||= 10_000
56
106
  start_async_writer(buffer_size: buffer)
57
107
  else
58
108
  stop_async_writer