legion-logging 1.5.4 → 1.5.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 +4 -4
- data/CHANGELOG.md +18 -0
- data/lib/legion/logging/async_writer.rb +23 -12
- data/lib/legion/logging/builder.rb +28 -1
- data/lib/legion/logging/header_builder.rb +103 -0
- data/lib/legion/logging/helper.rb +47 -106
- data/lib/legion/logging/hooks.rb +1 -1
- data/lib/legion/logging/logger.rb +1 -1
- data/lib/legion/logging/methods.rb +91 -114
- data/lib/legion/logging/multi_io.rb +2 -0
- data/lib/legion/logging/shipper.rb +33 -27
- data/lib/legion/logging/siem_exporter.rb +2 -1
- data/lib/legion/logging/tagged_logger.rb +18 -16
- data/lib/legion/logging/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ff7f461a7f614eb2480b2ef960992d463c7e26ee8cae0283d9f381e859e28c8d
|
|
4
|
+
data.tar.gz: a5bc8f5e7fd81d72ef8cc4ae98225e677645581340611103ac99807bb2f6c691
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 006d30ed434deec409edd42ca5b5ac9245a4657dc8fc6280fc8b982c704d9ef1b1aee299bf6f905698a382f84cbc5ca3205adfd4691f2010d6aec0dea818a986
|
|
7
|
+
data.tar.gz: f6288442156789fd8bbb9858910a3deb01fffea277341c2c0e5b8288ee3cab7150b07b4c348faad78b3645f808823f536a856140fc1c5ce4935b4b805d13caea
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Legion::Logging Changelog
|
|
2
2
|
|
|
3
|
+
## [1.5.6] - 2026-06-01
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `task_id:`, `conv_id:`, `request_id:` optional kwargs on all log level methods (`.debug`, `.info`, `.warn`, `.error`, `.fatal`, `.unknown`) in both `Methods` and `TaggedLogger`
|
|
7
|
+
- Text formatter appends context ID (`conv_id` or `task_id` fallback) as `{conv_12345}` after the method segment in the prefix
|
|
8
|
+
- Text formatter inserts `request_id=<value>` between severity and message when present
|
|
9
|
+
- JSON formatter includes `conversation_id` and `request_id` fields when present
|
|
10
|
+
- `AsyncWriter::LogEntry` carries `conv_id` and `request_id` for correct propagation to the writer thread
|
|
11
|
+
- Thread-local `legion_log_conv_id` and `legion_log_request_id` for block-scoped context without per-call kwargs
|
|
12
|
+
|
|
13
|
+
## [1.5.5] - 2026-05-27
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- `emit_tagged` (used by all Legion::Logging::Helper consumers) now routes through the async writer instead of doing synchronous IO#write — eliminates IO mutex contention across all consumer threads
|
|
17
|
+
- `fatal` level now routes through async writer consistently with all other levels
|
|
18
|
+
- `log_exception` now writes through async writer instead of direct `log.public_send`
|
|
19
|
+
- `Legion::Logging::Logger` defaults to `async: true`, ensuring all per-extension logger instances use non-blocking writes
|
|
20
|
+
|
|
3
21
|
## [1.5.4] - 2026-05-22
|
|
4
22
|
|
|
5
23
|
### Added
|
|
@@ -5,8 +5,13 @@ require_relative 'methods'
|
|
|
5
5
|
module Legion
|
|
6
6
|
module Logging
|
|
7
7
|
class AsyncWriter
|
|
8
|
-
LogEntry = ::Data.define(:level, :message, :writer_context, :segments, :method_ctx, :caller_trace
|
|
8
|
+
LogEntry = ::Data.define(:level, :message, :writer_context, :segments, :method_ctx, :caller_trace,
|
|
9
|
+
:conv_id, :request_id, :exchange_id, :chain_id)
|
|
9
10
|
SHUTDOWN = :shutdown
|
|
11
|
+
THREAD_KEYS = %i[
|
|
12
|
+
legion_log_segments legion_log_method legion_log_caller
|
|
13
|
+
legion_log_conv_id legion_log_request_id legion_log_exchange_id legion_log_chain_id
|
|
14
|
+
].freeze
|
|
10
15
|
|
|
11
16
|
attr_reader :logger
|
|
12
17
|
|
|
@@ -74,20 +79,26 @@ module Legion
|
|
|
74
79
|
end
|
|
75
80
|
|
|
76
81
|
def write_entry(entry)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
Thread.current[:legion_log_method] = entry.method_ctx
|
|
82
|
-
Thread.current[:legion_log_caller] = entry.caller_trace
|
|
83
|
-
@logger.send(entry.level, entry.message)
|
|
84
|
-
fire_writer(entry) if entry.writer_context
|
|
82
|
+
with_entry_context(entry) do
|
|
83
|
+
@logger.send(entry.level, entry.message)
|
|
84
|
+
fire_writer(entry) if entry.writer_context
|
|
85
|
+
end
|
|
85
86
|
rescue StandardError => e
|
|
86
87
|
warn("legion-log-writer error: #{e.message} (#{e.backtrace&.first})")
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def with_entry_context(entry)
|
|
91
|
+
prev = THREAD_KEYS.map { |k| [k, Thread.current[k]] }
|
|
92
|
+
Thread.current[:legion_log_segments] = entry.segments
|
|
93
|
+
Thread.current[:legion_log_method] = entry.method_ctx
|
|
94
|
+
Thread.current[:legion_log_caller] = entry.caller_trace
|
|
95
|
+
Thread.current[:legion_log_conv_id] = entry.conv_id
|
|
96
|
+
Thread.current[:legion_log_request_id] = entry.request_id
|
|
97
|
+
Thread.current[:legion_log_exchange_id] = entry.exchange_id
|
|
98
|
+
Thread.current[:legion_log_chain_id] = entry.chain_id
|
|
99
|
+
yield
|
|
87
100
|
ensure
|
|
88
|
-
Thread.current[
|
|
89
|
-
Thread.current[:legion_log_method] = prev_method_ctx
|
|
90
|
-
Thread.current[:legion_log_caller] = prev_caller
|
|
101
|
+
prev&.each { |k, v| Thread.current[k] = v }
|
|
91
102
|
end
|
|
92
103
|
|
|
93
104
|
def drain
|
|
@@ -31,6 +31,14 @@ module Legion
|
|
|
31
31
|
entry[:segments] = segments if segments
|
|
32
32
|
method_ctx = Thread.current[:legion_log_method]
|
|
33
33
|
entry[:method] = method_ctx if method_ctx
|
|
34
|
+
conv_id = Thread.current[:legion_log_conv_id]
|
|
35
|
+
entry[:conversation_id] = conv_id if conv_id.is_a?(String) && !conv_id.empty?
|
|
36
|
+
request_id = Thread.current[:legion_log_request_id]
|
|
37
|
+
entry[:request_id] = request_id if request_id.is_a?(String) && !request_id.empty?
|
|
38
|
+
exchange_id = Thread.current[:legion_log_exchange_id]
|
|
39
|
+
entry[:exchange_id] = exchange_id if exchange_id.is_a?(String) && !exchange_id.empty?
|
|
40
|
+
chain_id = Thread.current[:legion_log_chain_id]
|
|
41
|
+
entry[:chain_id] = chain_id if chain_id.is_a?(String) && !chain_id.empty?
|
|
34
42
|
"#{::JSON.generate(entry)}\n"
|
|
35
43
|
rescue StandardError => e
|
|
36
44
|
warn("Legion::Logging::Builder#json_format formatter failed: #{e.message}")
|
|
@@ -49,7 +57,12 @@ module Legion
|
|
|
49
57
|
if runner_trace.is_a?(Hash) && (options[:extended] || severity == 'debug')
|
|
50
58
|
string.concat("[#{runner_trace[:type]}:#{runner_trace[:file]}:#{runner_trace[:function]}:#{runner_trace[:line_number]}]")
|
|
51
59
|
end
|
|
52
|
-
|
|
60
|
+
ctx_pairs = build_context_kv_pairs
|
|
61
|
+
if ctx_pairs.empty?
|
|
62
|
+
string.concat(" #{severity} #{msg}\n")
|
|
63
|
+
else
|
|
64
|
+
string.concat(" #{severity} #{msg} #{ctx_pairs}\n")
|
|
65
|
+
end
|
|
53
66
|
string
|
|
54
67
|
end
|
|
55
68
|
end
|
|
@@ -66,9 +79,23 @@ module Legion
|
|
|
66
79
|
|
|
67
80
|
method_ctx = Thread.current[:legion_log_method]
|
|
68
81
|
tag = "#{tag}{#{method_ctx}}" if tag && method_ctx
|
|
82
|
+
|
|
83
|
+
context_id = Thread.current[:legion_log_conv_id]
|
|
84
|
+
tag = "#{tag}{#{context_id}}" if tag && context_id.is_a?(String) && !context_id.empty?
|
|
69
85
|
tag
|
|
70
86
|
end
|
|
71
87
|
|
|
88
|
+
def build_context_kv_pairs
|
|
89
|
+
pairs = []
|
|
90
|
+
request_id = Thread.current[:legion_log_request_id]
|
|
91
|
+
pairs << "request_id=#{request_id}" if request_id.is_a?(String) && !request_id.empty?
|
|
92
|
+
exchange_id = Thread.current[:legion_log_exchange_id]
|
|
93
|
+
pairs << "exchange_id=#{exchange_id}" if exchange_id.is_a?(String) && !exchange_id.empty?
|
|
94
|
+
chain_id = Thread.current[:legion_log_chain_id]
|
|
95
|
+
pairs << "chain_id=#{chain_id}" if chain_id.is_a?(String) && !chain_id.empty?
|
|
96
|
+
pairs.join(' ')
|
|
97
|
+
end
|
|
98
|
+
|
|
72
99
|
def build_runner_trace(loc = caller_locations(6, 1)&.first)
|
|
73
100
|
return unless loc
|
|
74
101
|
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Logging
|
|
7
|
+
module HeaderBuilder
|
|
8
|
+
EXCEPTION_PRIORITY = { warn: 0, error: 5, fatal: 9 }.freeze
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def build_exception_headers(event, comp, level)
|
|
13
|
+
headers = {
|
|
14
|
+
'legion_protocol_version' => '2.0',
|
|
15
|
+
'x-error-fingerprint' => event[:error_fingerprint],
|
|
16
|
+
'x-exception-class' => event[:exception_class],
|
|
17
|
+
'x-handled' => event[:handled].to_s,
|
|
18
|
+
'x-gem-name' => event[:gem_name].to_s,
|
|
19
|
+
'x-lex-version' => event[:lex_version].to_s,
|
|
20
|
+
'x-component-type' => comp.to_s,
|
|
21
|
+
'x-level' => level.to_s
|
|
22
|
+
}
|
|
23
|
+
append_legion_version_header(headers)
|
|
24
|
+
append_optional_header(headers, 'x-task-id', event[:task_id])
|
|
25
|
+
append_optional_header(headers, 'x-conversation-id', event[:conversation_id])
|
|
26
|
+
append_optional_header(headers, 'x-chain-id', event[:chain_id])
|
|
27
|
+
append_optional_header(headers, 'x-user', event[:user])
|
|
28
|
+
append_identity_headers(headers)
|
|
29
|
+
headers
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def build_exception_properties(event, level)
|
|
33
|
+
{
|
|
34
|
+
content_type: 'application/json',
|
|
35
|
+
message_id: SecureRandom.uuid,
|
|
36
|
+
correlation_id: event[:error_fingerprint],
|
|
37
|
+
timestamp: Time.now.to_i,
|
|
38
|
+
app_id: 'legionio',
|
|
39
|
+
type: 'exception_event',
|
|
40
|
+
priority: EXCEPTION_PRIORITY[level] || 5,
|
|
41
|
+
delivery_mode: 2
|
|
42
|
+
}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def append_identity_headers(headers)
|
|
46
|
+
return unless defined?(Legion::Identity::Process)
|
|
47
|
+
return if Legion::Identity::Process.respond_to?(:resolved?) && !Legion::Identity::Process.resolved?
|
|
48
|
+
|
|
49
|
+
id = identity_hash
|
|
50
|
+
append_optional_header(headers, 'x-legion-identity-canonical-name', id[:canonical_name])
|
|
51
|
+
append_optional_header(headers, 'x-legion-identity-trust', id[:trust])
|
|
52
|
+
append_optional_header(headers, 'x-legion-identity-id', id[:id])
|
|
53
|
+
append_optional_header(headers, 'x-legion-identity-kind', id[:kind])
|
|
54
|
+
append_optional_header(headers, 'x-legion-identity-mode', id[:mode])
|
|
55
|
+
append_optional_header(headers, 'x-legion-identity-source', id[:source])
|
|
56
|
+
headers['x-legion-identity-db-principal-id'] = id[:db_principal_id] if id[:db_principal_id]
|
|
57
|
+
headers['x-legion-identity-db-identity-id'] = id[:db_identity_id] if id[:db_identity_id]
|
|
58
|
+
rescue StandardError
|
|
59
|
+
nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def append_optional_header(headers, key, value)
|
|
63
|
+
return if value.nil?
|
|
64
|
+
return if value.respond_to?(:empty?) && value.empty?
|
|
65
|
+
|
|
66
|
+
headers[key] = value.to_s
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def append_legion_version_header(headers)
|
|
70
|
+
append_optional_header(headers, 'x-legion-version', Legion::VERSION) if defined?(Legion::VERSION)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def identity_hash
|
|
74
|
+
process = Legion::Identity::Process
|
|
75
|
+
return process.identity_hash if process.respond_to?(:identity_hash)
|
|
76
|
+
|
|
77
|
+
{
|
|
78
|
+
canonical_name: identity_value(process, :canonical_name),
|
|
79
|
+
id: identity_value(process, :id),
|
|
80
|
+
kind: identity_value(process, :kind),
|
|
81
|
+
mode: identity_value(process, :mode),
|
|
82
|
+
source: identity_value(process, :source),
|
|
83
|
+
trust: identity_value(process, :trust)
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def identity_value(process, method_name)
|
|
88
|
+
process.public_send(method_name) if process.respond_to?(method_name)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def redaction_enabled?
|
|
92
|
+
return false unless defined?(Legion::Settings)
|
|
93
|
+
|
|
94
|
+
loader = Legion::Settings.instance_variable_get(:@loader)
|
|
95
|
+
return false unless loader
|
|
96
|
+
|
|
97
|
+
loader.dig(:logging, :redaction, :enabled) == true
|
|
98
|
+
rescue StandardError
|
|
99
|
+
false
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
require 'securerandom'
|
|
4
4
|
require_relative 'tagged_logger'
|
|
5
5
|
require_relative 'method_tracer'
|
|
6
|
+
require_relative 'header_builder'
|
|
6
7
|
|
|
7
8
|
module Legion
|
|
8
9
|
module Logging
|
|
9
10
|
module Helper
|
|
11
|
+
include Legion::Logging::HeaderBuilder
|
|
12
|
+
|
|
10
13
|
SEGMENT_CACHE = {} # rubocop:disable Style/MutableConstant
|
|
11
14
|
SEGMENT_CACHE_MUTEX = Mutex.new
|
|
12
15
|
private_constant :SEGMENT_CACHE_MUTEX
|
|
@@ -32,7 +35,6 @@ module Legion
|
|
|
32
35
|
'middleware' => :middleware
|
|
33
36
|
}.freeze
|
|
34
37
|
|
|
35
|
-
EXCEPTION_PRIORITY = { warn: 0, error: 5, fatal: 9 }.freeze
|
|
36
38
|
EXCEPTION_COLORS = {
|
|
37
39
|
fatal: :darkred,
|
|
38
40
|
error: :red,
|
|
@@ -359,27 +361,64 @@ module Legion
|
|
|
359
361
|
return unless defined?(Legion::Settings)
|
|
360
362
|
return unless Legion::Settings.respond_to?(:loaded?) ? Legion::Settings.loaded? : true
|
|
361
363
|
|
|
362
|
-
|
|
363
|
-
return unless
|
|
364
|
+
keys = derive_component_settings_keys
|
|
365
|
+
return unless keys&.any?
|
|
366
|
+
|
|
367
|
+
if keys.length > 1
|
|
368
|
+
result = dig_settings(Legion::Settings[:extensions], keys)
|
|
369
|
+
return result if result.is_a?(Hash)
|
|
370
|
+
end
|
|
364
371
|
|
|
365
|
-
|
|
372
|
+
single_key = keys.length == 1 ? keys.first : keys.join('_').to_sym
|
|
373
|
+
top_level = Legion::Settings[single_key]
|
|
366
374
|
return top_level if top_level.is_a?(Hash)
|
|
367
375
|
|
|
368
|
-
extension_settings =
|
|
376
|
+
extension_settings = if keys.length > 1
|
|
377
|
+
dig_settings(Legion::Settings[:extensions], keys)
|
|
378
|
+
else
|
|
379
|
+
Legion::Settings.dig(:extensions, single_key)
|
|
380
|
+
end
|
|
369
381
|
extension_settings if extension_settings.is_a?(Hash)
|
|
370
382
|
rescue StandardError
|
|
371
383
|
nil
|
|
372
384
|
end
|
|
373
385
|
|
|
374
|
-
def
|
|
386
|
+
def derive_component_settings_keys
|
|
387
|
+
key = respond_to?(:ancestors) ? ancestors.first : self.class
|
|
388
|
+
parts = key.to_s.split('::')
|
|
389
|
+
parts.shift if parts.first == 'Legion'
|
|
390
|
+
|
|
391
|
+
if parts.first == 'Extensions'
|
|
392
|
+
parts.shift
|
|
393
|
+
ext_parts = parts.take_while { |p| !COMPONENT_MAP.key?(p.downcase) }
|
|
394
|
+
return ext_parts.map { |p| camelize_to_snake_key(p).to_sym } if ext_parts.any?
|
|
395
|
+
elsif parts.first && !parts.first.start_with?('#')
|
|
396
|
+
return [camelize_to_snake_key(parts.first).to_sym]
|
|
397
|
+
end
|
|
398
|
+
|
|
375
399
|
base = log_name
|
|
376
400
|
return unless base
|
|
377
401
|
|
|
378
|
-
base.to_s.tr('-', '_').to_sym
|
|
402
|
+
base.to_s.split('-').map { |s| s.tr('-', '_').to_sym }
|
|
379
403
|
rescue StandardError
|
|
380
404
|
nil
|
|
381
405
|
end
|
|
382
406
|
|
|
407
|
+
def camelize_to_snake_key(str)
|
|
408
|
+
str.to_s
|
|
409
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
410
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
411
|
+
.downcase
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
def dig_settings(hash, keys)
|
|
415
|
+
keys.reduce(hash) do |current, key|
|
|
416
|
+
return nil unless current.is_a?(Hash)
|
|
417
|
+
|
|
418
|
+
current[key] || current[key.to_s]
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
383
422
|
def global_log_level
|
|
384
423
|
runtime_level = if defined?(Legion::Logging) &&
|
|
385
424
|
Legion::Logging.respond_to?(:current_settings)
|
|
@@ -468,15 +507,7 @@ module Legion
|
|
|
468
507
|
Thread.current[:legion_log_segments] = segments
|
|
469
508
|
|
|
470
509
|
message = format_exception_output(exception, event)
|
|
471
|
-
|
|
472
|
-
message = colorize_exception(message, level) if Legion::Logging.respond_to?(:color) && Legion::Logging.color
|
|
473
|
-
|
|
474
|
-
logger = Legion::Logging.respond_to?(:log) ? Legion::Logging.log : nil
|
|
475
|
-
if logger.respond_to?(level)
|
|
476
|
-
logger.public_send(level, message)
|
|
477
|
-
elsif Legion::Logging.respond_to?(level)
|
|
478
|
-
Legion::Logging.public_send(level, message)
|
|
479
|
-
end
|
|
510
|
+
Legion::Logging.public_send(level, message) if Legion::Logging.respond_to?(level)
|
|
480
511
|
ensure
|
|
481
512
|
Thread.current[:legion_log_segments] = prev_segs
|
|
482
513
|
end
|
|
@@ -524,17 +555,6 @@ module Legion
|
|
|
524
555
|
parts.join(' | ')
|
|
525
556
|
end
|
|
526
557
|
|
|
527
|
-
def redaction_enabled?
|
|
528
|
-
return false unless defined?(Legion::Settings)
|
|
529
|
-
|
|
530
|
-
loader = Legion::Settings.instance_variable_get(:@loader)
|
|
531
|
-
return false unless loader
|
|
532
|
-
|
|
533
|
-
loader.dig(:logging, :redaction, :enabled) == true
|
|
534
|
-
rescue StandardError
|
|
535
|
-
false
|
|
536
|
-
end
|
|
537
|
-
|
|
538
558
|
# -- Exception structured publish --
|
|
539
559
|
|
|
540
560
|
def publish_exception(event, level)
|
|
@@ -556,85 +576,6 @@ module Legion
|
|
|
556
576
|
defined?(Legion::Logging::EventBuilder) &&
|
|
557
577
|
Legion::Logging.respond_to?(:exception_writer)
|
|
558
578
|
end
|
|
559
|
-
|
|
560
|
-
def build_exception_headers(event, comp, level)
|
|
561
|
-
headers = {
|
|
562
|
-
'legion_protocol_version' => '2.0',
|
|
563
|
-
'x-error-fingerprint' => event[:error_fingerprint],
|
|
564
|
-
'x-exception-class' => event[:exception_class],
|
|
565
|
-
'x-handled' => event[:handled].to_s,
|
|
566
|
-
'x-gem-name' => event[:gem_name].to_s,
|
|
567
|
-
'x-lex-version' => event[:lex_version].to_s,
|
|
568
|
-
'x-component-type' => comp.to_s,
|
|
569
|
-
'x-level' => level.to_s
|
|
570
|
-
}
|
|
571
|
-
append_legion_version_header(headers)
|
|
572
|
-
headers['x-task-id'] = event[:task_id].to_s if event[:task_id]
|
|
573
|
-
headers['x-conversation-id'] = event[:conversation_id].to_s if event[:conversation_id]
|
|
574
|
-
headers['x-chain-id'] = event[:chain_id].to_s if event[:chain_id]
|
|
575
|
-
headers['x-user'] = event[:user].to_s if event[:user]
|
|
576
|
-
append_identity_headers(headers)
|
|
577
|
-
headers
|
|
578
|
-
end
|
|
579
|
-
|
|
580
|
-
def append_identity_headers(headers)
|
|
581
|
-
return unless defined?(Legion::Identity::Process)
|
|
582
|
-
return if Legion::Identity::Process.respond_to?(:resolved?) && !Legion::Identity::Process.resolved?
|
|
583
|
-
|
|
584
|
-
id = identity_hash
|
|
585
|
-
append_optional_header(headers, 'x-legion-identity-canonical-name', id[:canonical_name])
|
|
586
|
-
append_optional_header(headers, 'x-legion-identity-trust', id[:trust])
|
|
587
|
-
append_optional_header(headers, 'x-legion-identity-id', id[:id])
|
|
588
|
-
append_optional_header(headers, 'x-legion-identity-kind', id[:kind])
|
|
589
|
-
append_optional_header(headers, 'x-legion-identity-mode', id[:mode])
|
|
590
|
-
append_optional_header(headers, 'x-legion-identity-source', id[:source])
|
|
591
|
-
headers['x-legion-identity-db-principal-id'] = id[:db_principal_id] if id[:db_principal_id]
|
|
592
|
-
headers['x-legion-identity-db-identity-id'] = id[:db_identity_id] if id[:db_identity_id]
|
|
593
|
-
rescue StandardError
|
|
594
|
-
nil
|
|
595
|
-
end
|
|
596
|
-
|
|
597
|
-
def append_optional_header(headers, key, value)
|
|
598
|
-
return if value.nil?
|
|
599
|
-
return if value.respond_to?(:empty?) && value.empty?
|
|
600
|
-
|
|
601
|
-
headers[key] = value.to_s
|
|
602
|
-
end
|
|
603
|
-
|
|
604
|
-
def append_legion_version_header(headers)
|
|
605
|
-
append_optional_header(headers, 'x-legion-version', Legion::VERSION) if defined?(Legion::VERSION)
|
|
606
|
-
end
|
|
607
|
-
|
|
608
|
-
def identity_hash
|
|
609
|
-
process = Legion::Identity::Process
|
|
610
|
-
return process.identity_hash if process.respond_to?(:identity_hash)
|
|
611
|
-
|
|
612
|
-
{
|
|
613
|
-
canonical_name: identity_value(process, :canonical_name),
|
|
614
|
-
id: identity_value(process, :id),
|
|
615
|
-
kind: identity_value(process, :kind),
|
|
616
|
-
mode: identity_value(process, :mode),
|
|
617
|
-
source: identity_value(process, :source),
|
|
618
|
-
trust: identity_value(process, :trust)
|
|
619
|
-
}
|
|
620
|
-
end
|
|
621
|
-
|
|
622
|
-
def identity_value(process, method_name)
|
|
623
|
-
process.public_send(method_name) if process.respond_to?(method_name)
|
|
624
|
-
end
|
|
625
|
-
|
|
626
|
-
def build_exception_properties(event, level)
|
|
627
|
-
{
|
|
628
|
-
content_type: 'application/json',
|
|
629
|
-
message_id: SecureRandom.uuid,
|
|
630
|
-
correlation_id: event[:error_fingerprint],
|
|
631
|
-
timestamp: Time.now.to_i,
|
|
632
|
-
app_id: 'legionio',
|
|
633
|
-
type: 'exception_event',
|
|
634
|
-
priority: EXCEPTION_PRIORITY[level] || 5,
|
|
635
|
-
delivery_mode: 2
|
|
636
|
-
}
|
|
637
|
-
end
|
|
638
579
|
end
|
|
639
580
|
end
|
|
640
581
|
end
|
data/lib/legion/logging/hooks.rb
CHANGED
|
@@ -19,7 +19,7 @@ module Legion
|
|
|
19
19
|
def fire(level, message, event)
|
|
20
20
|
return unless @enabled
|
|
21
21
|
|
|
22
|
-
hooks_for(level).each do |hook|
|
|
22
|
+
hooks_for(level).dup.each do |hook|
|
|
23
23
|
hook.call(message, event)
|
|
24
24
|
rescue StandardError => e
|
|
25
25
|
warn("Legion::Logging::Hooks#fire callback failed: #{e.message}")
|
|
@@ -11,7 +11,7 @@ module Legion
|
|
|
11
11
|
include Legion::Logging::Methods
|
|
12
12
|
include Legion::Logging::Builder
|
|
13
13
|
|
|
14
|
-
def initialize(level: 'info', log_file: nil, log_stdout: nil, lex: nil, trace: false, extended: false, trace_size: 4, format: :text, async:
|
|
14
|
+
def initialize(level: 'info', log_file: nil, log_stdout: nil, lex: nil, trace: false, extended: false, trace_size: 4, format: :text, async: true, **opts)
|
|
15
15
|
@lex = lex
|
|
16
16
|
set_log(logfile: log_file, log_stdout: log_stdout)
|
|
17
17
|
log_level(level)
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'securerandom'
|
|
4
|
+
require_relative 'header_builder'
|
|
4
5
|
|
|
5
6
|
module Legion
|
|
6
7
|
module Logging
|
|
7
8
|
module Methods
|
|
9
|
+
include Legion::Logging::HeaderBuilder
|
|
10
|
+
|
|
8
11
|
COMPONENT_REGEX = %r{
|
|
9
12
|
/(runners|actors|actor|helpers|hooks|absorbers|matchers|transport|
|
|
10
13
|
exchanges|queues|messages|data|builders|tools|adapters|engines|
|
|
11
14
|
formatters|parsers|middleware)/
|
|
12
15
|
}x
|
|
13
|
-
EXCEPTION_PRIORITY = { warn: 0, error: 5, fatal: 9 }.freeze
|
|
14
16
|
|
|
15
17
|
def trace(raw_message = nil, size: @trace_size, log_caller: true)
|
|
16
18
|
return unless @trace_enabled
|
|
@@ -26,61 +28,78 @@ module Legion
|
|
|
26
28
|
log.unknown(message)
|
|
27
29
|
end
|
|
28
30
|
|
|
29
|
-
def debug(message = nil)
|
|
31
|
+
def debug(message = nil, task_id: nil, conv_id: nil, request_id: nil, exchange_id: nil, chain_id: nil, **_ctx)
|
|
30
32
|
return unless log.level < 1
|
|
31
33
|
|
|
32
34
|
message = yield if message.nil? && block_given?
|
|
33
35
|
raw = maybe_redact(message)
|
|
34
36
|
formatted = format_message_for_level(:debug, raw)
|
|
35
|
-
|
|
37
|
+
with_context_ids(task_id: task_id, conv_id: conv_id, request_id: request_id,
|
|
38
|
+
exchange_id: exchange_id, chain_id: chain_id) do
|
|
39
|
+
write_async_or_sync(:debug, formatted, raw)
|
|
40
|
+
end
|
|
36
41
|
end
|
|
37
42
|
|
|
38
|
-
def info(message = nil)
|
|
43
|
+
def info(message = nil, task_id: nil, conv_id: nil, request_id: nil, exchange_id: nil, chain_id: nil, **_ctx)
|
|
39
44
|
return unless log.level < 2
|
|
40
45
|
|
|
41
46
|
message = yield if message.nil? && block_given?
|
|
42
47
|
raw = maybe_redact(message)
|
|
43
48
|
formatted = format_message_for_level(:info, raw)
|
|
44
|
-
|
|
49
|
+
with_context_ids(task_id: task_id, conv_id: conv_id, request_id: request_id,
|
|
50
|
+
exchange_id: exchange_id, chain_id: chain_id) do
|
|
51
|
+
write_async_or_sync(:info, formatted, raw)
|
|
52
|
+
end
|
|
45
53
|
end
|
|
46
54
|
|
|
47
|
-
def warn(message = nil)
|
|
55
|
+
def warn(message = nil, task_id: nil, conv_id: nil, request_id: nil, exchange_id: nil, chain_id: nil, **_ctx)
|
|
48
56
|
return unless log.level < 3
|
|
49
57
|
|
|
50
58
|
message = yield if message.nil? && block_given?
|
|
51
59
|
raw = maybe_redact(message)
|
|
52
60
|
formatted = format_message_for_level(:warn, raw)
|
|
53
|
-
|
|
61
|
+
with_context_ids(task_id: task_id, conv_id: conv_id, request_id: request_id,
|
|
62
|
+
exchange_id: exchange_id, chain_id: chain_id) do
|
|
63
|
+
write_async_or_sync(:warn, formatted, raw, writer_context: build_writer_context(:warn, raw))
|
|
64
|
+
end
|
|
54
65
|
end
|
|
55
66
|
|
|
56
|
-
def error(message = nil)
|
|
67
|
+
def error(message = nil, task_id: nil, conv_id: nil, request_id: nil, exchange_id: nil, chain_id: nil, **_ctx)
|
|
57
68
|
return unless log.level < 4
|
|
58
69
|
|
|
59
70
|
message = yield if message.nil? && block_given?
|
|
60
71
|
raw = maybe_redact(message)
|
|
61
72
|
formatted = format_message_for_level(:error, raw)
|
|
62
|
-
|
|
73
|
+
with_context_ids(task_id: task_id, conv_id: conv_id, request_id: request_id,
|
|
74
|
+
exchange_id: exchange_id, chain_id: chain_id) do
|
|
75
|
+
write_async_or_sync(:error, formatted, raw, writer_context: build_writer_context(:error, raw))
|
|
76
|
+
end
|
|
63
77
|
end
|
|
64
78
|
|
|
65
|
-
def fatal(message = nil)
|
|
79
|
+
def fatal(message = nil, task_id: nil, conv_id: nil, request_id: nil, exchange_id: nil, chain_id: nil, **_ctx)
|
|
66
80
|
return unless log.level < 5
|
|
67
81
|
|
|
68
82
|
message = yield if message.nil? && block_given?
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
83
|
+
raw = maybe_redact(message)
|
|
84
|
+
formatted = format_message_for_level(:fatal, raw)
|
|
85
|
+
with_context_ids(task_id: task_id, conv_id: conv_id, request_id: request_id,
|
|
86
|
+
exchange_id: exchange_id, chain_id: chain_id) do
|
|
87
|
+
write_async_or_sync(:fatal, formatted, raw, writer_context: build_writer_context(:fatal, raw))
|
|
88
|
+
end
|
|
74
89
|
end
|
|
75
90
|
|
|
76
|
-
def unknown(message = nil)
|
|
91
|
+
def unknown(message = nil, task_id: nil, conv_id: nil, request_id: nil, exchange_id: nil, chain_id: nil, **_ctx)
|
|
77
92
|
message = yield if message.nil? && block_given?
|
|
78
93
|
raw = maybe_redact(message)
|
|
79
94
|
formatted = format_message_for_level(:unknown, raw)
|
|
80
|
-
|
|
95
|
+
with_context_ids(task_id: task_id, conv_id: conv_id, request_id: request_id,
|
|
96
|
+
exchange_id: exchange_id, chain_id: chain_id) do
|
|
97
|
+
write_async_or_sync(:unknown, formatted, raw)
|
|
98
|
+
end
|
|
81
99
|
end
|
|
82
100
|
|
|
83
|
-
def emit_tagged(level, message = nil, segments: nil, method_ctx: nil
|
|
101
|
+
def emit_tagged(level, message = nil, segments: nil, method_ctx: nil,
|
|
102
|
+
task_id: nil, conv_id: nil, request_id: nil, exchange_id: nil, chain_id: nil, **_ctx)
|
|
84
103
|
level = level.to_sym
|
|
85
104
|
message = yield if message.nil? && block_given?
|
|
86
105
|
return if message.nil?
|
|
@@ -89,8 +108,27 @@ module Legion
|
|
|
89
108
|
formatted = format_message_for_level(level, raw)
|
|
90
109
|
|
|
91
110
|
with_tagged_context(segments, method_ctx) do
|
|
92
|
-
|
|
93
|
-
|
|
111
|
+
with_context_ids(task_id: task_id, conv_id: conv_id, request_id: request_id,
|
|
112
|
+
exchange_id: exchange_id, chain_id: chain_id) do
|
|
113
|
+
ctx = %i[warn error fatal].include?(level) ? build_writer_context(level, raw) : nil
|
|
114
|
+
writer = @async_writer
|
|
115
|
+
caller_trace = capture_runner_trace_for_async
|
|
116
|
+
if writer&.alive?
|
|
117
|
+
writer.push(AsyncWriter::LogEntry.new(
|
|
118
|
+
level: level, message: formatted, writer_context: ctx,
|
|
119
|
+
segments: Thread.current[:legion_log_segments],
|
|
120
|
+
method_ctx: Thread.current[:legion_log_method],
|
|
121
|
+
caller_trace: caller_trace,
|
|
122
|
+
conv_id: Thread.current[:legion_log_conv_id],
|
|
123
|
+
request_id: Thread.current[:legion_log_request_id],
|
|
124
|
+
exchange_id: Thread.current[:legion_log_exchange_id],
|
|
125
|
+
chain_id: Thread.current[:legion_log_chain_id]
|
|
126
|
+
))
|
|
127
|
+
else
|
|
128
|
+
with_caller_trace(caller_trace) { write_forced(level, formatted) }
|
|
129
|
+
fire_log_writer(level, raw) if ctx
|
|
130
|
+
end
|
|
131
|
+
end
|
|
94
132
|
end
|
|
95
133
|
end
|
|
96
134
|
|
|
@@ -104,11 +142,12 @@ module Legion
|
|
|
104
142
|
source_code_uri: nil, handled: false, payload_summary: nil,
|
|
105
143
|
task_id: nil, backtrace_limit: nil, **extra)
|
|
106
144
|
level = level.to_sym if level.respond_to?(:to_sym)
|
|
107
|
-
# 1. Log human-readable line + backtrace
|
|
145
|
+
# 1. Log human-readable line + backtrace via async writer
|
|
108
146
|
msg = exception.respond_to?(:message) ? exception.message : exception.to_s
|
|
109
147
|
msg = maybe_redact(msg)
|
|
110
148
|
msg = build_exception_log_message(exception, msg, backtrace_limit)
|
|
111
|
-
|
|
149
|
+
formatted = format_message_for_level(level, msg)
|
|
150
|
+
write_async_or_sync(level, formatted, msg)
|
|
112
151
|
|
|
113
152
|
# 2. Build rich exception event
|
|
114
153
|
event = Legion::Logging::EventBuilder.build_exception(
|
|
@@ -226,6 +265,29 @@ module Legion
|
|
|
226
265
|
level.to_s.upcase
|
|
227
266
|
end
|
|
228
267
|
|
|
268
|
+
def with_context_ids(task_id: nil, conv_id: nil, request_id: nil, exchange_id: nil, chain_id: nil)
|
|
269
|
+
prev_conv = Thread.current[:legion_log_conv_id]
|
|
270
|
+
prev_req = Thread.current[:legion_log_request_id]
|
|
271
|
+
prev_exchange = Thread.current[:legion_log_exchange_id]
|
|
272
|
+
prev_chain = Thread.current[:legion_log_chain_id]
|
|
273
|
+
|
|
274
|
+
context_id = conv_id || task_id || Thread.current[:legion_log_conv_id]
|
|
275
|
+
req_id = request_id || Thread.current[:legion_log_request_id]
|
|
276
|
+
exch_id = exchange_id || Thread.current[:legion_log_exchange_id]
|
|
277
|
+
ch_id = chain_id || Thread.current[:legion_log_chain_id]
|
|
278
|
+
|
|
279
|
+
Thread.current[:legion_log_conv_id] = context_id if context_id.is_a?(String) && !context_id.empty?
|
|
280
|
+
Thread.current[:legion_log_request_id] = req_id if req_id.is_a?(String) && !req_id.empty?
|
|
281
|
+
Thread.current[:legion_log_exchange_id] = exch_id if exch_id.is_a?(String) && !exch_id.empty?
|
|
282
|
+
Thread.current[:legion_log_chain_id] = ch_id if ch_id.is_a?(String) && !ch_id.empty?
|
|
283
|
+
yield
|
|
284
|
+
ensure
|
|
285
|
+
Thread.current[:legion_log_conv_id] = prev_conv
|
|
286
|
+
Thread.current[:legion_log_request_id] = prev_req
|
|
287
|
+
Thread.current[:legion_log_exchange_id] = prev_exchange
|
|
288
|
+
Thread.current[:legion_log_chain_id] = prev_chain
|
|
289
|
+
end
|
|
290
|
+
|
|
229
291
|
def write_async_or_sync(level, formatted_message, raw_message, writer_context: nil)
|
|
230
292
|
writer = @async_writer
|
|
231
293
|
caller_trace = capture_runner_trace_for_async
|
|
@@ -236,7 +298,11 @@ module Legion
|
|
|
236
298
|
writer_context: writer_context,
|
|
237
299
|
segments: Thread.current[:legion_log_segments],
|
|
238
300
|
method_ctx: Thread.current[:legion_log_method],
|
|
239
|
-
caller_trace: caller_trace
|
|
301
|
+
caller_trace: caller_trace,
|
|
302
|
+
conv_id: Thread.current[:legion_log_conv_id],
|
|
303
|
+
request_id: Thread.current[:legion_log_request_id],
|
|
304
|
+
exchange_id: Thread.current[:legion_log_exchange_id],
|
|
305
|
+
chain_id: Thread.current[:legion_log_chain_id]
|
|
240
306
|
))
|
|
241
307
|
return if queued
|
|
242
308
|
end
|
|
@@ -248,7 +314,7 @@ module Legion
|
|
|
248
314
|
end
|
|
249
315
|
|
|
250
316
|
def capture_runner_trace_for_async
|
|
251
|
-
build_runner_trace(caller_locations(
|
|
317
|
+
build_runner_trace(caller_locations(5, 1)&.first)
|
|
252
318
|
end
|
|
253
319
|
|
|
254
320
|
def with_caller_trace(caller_trace)
|
|
@@ -259,17 +325,6 @@ module Legion
|
|
|
259
325
|
Thread.current[:legion_log_caller] = prev_caller_trace
|
|
260
326
|
end
|
|
261
327
|
|
|
262
|
-
def redaction_enabled?
|
|
263
|
-
return false unless defined?(Legion::Settings)
|
|
264
|
-
|
|
265
|
-
loader = Legion::Settings.instance_variable_get(:@loader)
|
|
266
|
-
return false unless loader
|
|
267
|
-
|
|
268
|
-
loader.dig(:logging, :redaction, :enabled) == true
|
|
269
|
-
rescue StandardError
|
|
270
|
-
false
|
|
271
|
-
end
|
|
272
|
-
|
|
273
328
|
def build_log_headers(event, component, level)
|
|
274
329
|
headers = {
|
|
275
330
|
'legion_protocol_version' => '2.0',
|
|
@@ -290,28 +345,11 @@ module Legion
|
|
|
290
345
|
timestamp: Time.now.to_i,
|
|
291
346
|
app_id: 'legionio',
|
|
292
347
|
type: 'log_event',
|
|
293
|
-
priority: EXCEPTION_PRIORITY[level] || 0,
|
|
348
|
+
priority: HeaderBuilder::EXCEPTION_PRIORITY[level] || 0,
|
|
294
349
|
delivery_mode: 2
|
|
295
350
|
}
|
|
296
351
|
end
|
|
297
352
|
|
|
298
|
-
def append_identity_headers(headers)
|
|
299
|
-
return unless defined?(Legion::Identity::Process)
|
|
300
|
-
return if Legion::Identity::Process.respond_to?(:resolved?) && !Legion::Identity::Process.resolved?
|
|
301
|
-
|
|
302
|
-
id = identity_hash
|
|
303
|
-
append_optional_header(headers, 'x-legion-identity-canonical-name', id[:canonical_name])
|
|
304
|
-
append_optional_header(headers, 'x-legion-identity-trust', id[:trust])
|
|
305
|
-
append_optional_header(headers, 'x-legion-identity-id', id[:id])
|
|
306
|
-
append_optional_header(headers, 'x-legion-identity-kind', id[:kind])
|
|
307
|
-
append_optional_header(headers, 'x-legion-identity-mode', id[:mode])
|
|
308
|
-
append_optional_header(headers, 'x-legion-identity-source', id[:source])
|
|
309
|
-
headers['x-legion-identity-db-principal-id'] = id[:db_principal_id] if id[:db_principal_id]
|
|
310
|
-
headers['x-legion-identity-db-identity-id'] = id[:db_identity_id] if id[:db_identity_id]
|
|
311
|
-
rescue StandardError
|
|
312
|
-
nil
|
|
313
|
-
end
|
|
314
|
-
|
|
315
353
|
def publish_exception_event(event, level)
|
|
316
354
|
lex_name = event[:lex] || 'core'
|
|
317
355
|
comp = event[:component_type] || :unknown
|
|
@@ -321,67 +359,6 @@ module Legion
|
|
|
321
359
|
Legion::Logging.exception_writer.call(event, routing_key: routing_key, headers: headers, properties: properties)
|
|
322
360
|
end
|
|
323
361
|
|
|
324
|
-
def build_exception_headers(event, comp, level)
|
|
325
|
-
headers = {
|
|
326
|
-
'legion_protocol_version' => '2.0',
|
|
327
|
-
'x-error-fingerprint' => event[:error_fingerprint],
|
|
328
|
-
'x-exception-class' => event[:exception_class],
|
|
329
|
-
'x-handled' => event[:handled].to_s,
|
|
330
|
-
'x-gem-name' => event[:gem_name].to_s,
|
|
331
|
-
'x-lex-version' => event[:lex_version].to_s,
|
|
332
|
-
'x-component-type' => comp.to_s,
|
|
333
|
-
'x-level' => level.to_s
|
|
334
|
-
}
|
|
335
|
-
append_legion_version_header(headers)
|
|
336
|
-
append_optional_header(headers, 'x-task-id', event[:task_id])
|
|
337
|
-
append_optional_header(headers, 'x-conversation-id', event[:conversation_id])
|
|
338
|
-
append_optional_header(headers, 'x-user', event[:user])
|
|
339
|
-
append_identity_headers(headers)
|
|
340
|
-
headers
|
|
341
|
-
end
|
|
342
|
-
|
|
343
|
-
def append_optional_header(headers, key, value)
|
|
344
|
-
return if value.nil?
|
|
345
|
-
return if value.respond_to?(:empty?) && value.empty?
|
|
346
|
-
|
|
347
|
-
headers[key] = value.to_s
|
|
348
|
-
end
|
|
349
|
-
|
|
350
|
-
def append_legion_version_header(headers)
|
|
351
|
-
append_optional_header(headers, 'x-legion-version', Legion::VERSION) if defined?(Legion::VERSION)
|
|
352
|
-
end
|
|
353
|
-
|
|
354
|
-
def identity_hash
|
|
355
|
-
process = Legion::Identity::Process
|
|
356
|
-
return process.identity_hash if process.respond_to?(:identity_hash)
|
|
357
|
-
|
|
358
|
-
{
|
|
359
|
-
canonical_name: identity_value(process, :canonical_name),
|
|
360
|
-
id: identity_value(process, :id),
|
|
361
|
-
kind: identity_value(process, :kind),
|
|
362
|
-
mode: identity_value(process, :mode),
|
|
363
|
-
source: identity_value(process, :source),
|
|
364
|
-
trust: identity_value(process, :trust)
|
|
365
|
-
}
|
|
366
|
-
end
|
|
367
|
-
|
|
368
|
-
def identity_value(process, method_name)
|
|
369
|
-
process.public_send(method_name) if process.respond_to?(method_name)
|
|
370
|
-
end
|
|
371
|
-
|
|
372
|
-
def build_exception_properties(event, level)
|
|
373
|
-
{
|
|
374
|
-
content_type: 'application/json',
|
|
375
|
-
message_id: SecureRandom.uuid,
|
|
376
|
-
correlation_id: event[:error_fingerprint],
|
|
377
|
-
timestamp: Time.now.to_i,
|
|
378
|
-
app_id: 'legionio',
|
|
379
|
-
type: 'exception_event',
|
|
380
|
-
priority: EXCEPTION_PRIORITY[level] || 5,
|
|
381
|
-
delivery_mode: 2
|
|
382
|
-
}
|
|
383
|
-
end
|
|
384
|
-
|
|
385
362
|
def build_writer_context(level, message)
|
|
386
363
|
has_writer = !Legion::Logging.instance_variable_get(:@log_writer).nil?
|
|
387
364
|
has_hooks = defined?(Legion::Logging::Hooks) && Legion::Logging::Hooks.enabled?
|
|
@@ -25,43 +25,49 @@ module Legion
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def flush
|
|
28
|
-
|
|
28
|
+
@mutex ||= Mutex.new
|
|
29
|
+
batch = @mutex.synchronize do
|
|
30
|
+
return true if @buffer.nil? || @buffer.empty?
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
flushed = @buffer.dup
|
|
33
|
+
@buffer.clear
|
|
34
|
+
flushed
|
|
35
|
+
end
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
batch = nil
|
|
36
|
-
@mutex.synchronize { batch = @buffer.dup }
|
|
37
|
-
return if batch.empty?
|
|
37
|
+
transport = TRANSPORTS[transport_type]
|
|
38
|
+
return true unless transport
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
end
|
|
40
|
+
delivered = deliver(transport, batch)
|
|
41
|
+
@mutex.synchronize { @buffer.prepend(*batch) } unless delivered
|
|
42
|
+
delivered
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def start
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
@start_mutex ||= Mutex.new
|
|
47
|
+
@start_mutex.synchronize do
|
|
48
|
+
return unless enabled?
|
|
49
|
+
return if @flush_thread&.alive?
|
|
50
|
+
|
|
51
|
+
@buffer ||= []
|
|
52
|
+
@mutex ||= Mutex.new
|
|
53
|
+
@running = true
|
|
54
|
+
interval = flush_interval
|
|
55
|
+
@flush_thread = Thread.new do
|
|
56
|
+
while @running
|
|
57
|
+
sleep interval
|
|
58
|
+
flush
|
|
59
|
+
end
|
|
57
60
|
end
|
|
61
|
+
@flush_thread.name = 'legion-log-shipper'
|
|
62
|
+
@flush_thread.abort_on_exception = false
|
|
58
63
|
end
|
|
59
|
-
@flush_thread.abort_on_exception = false
|
|
60
64
|
end
|
|
61
65
|
|
|
62
66
|
def stop
|
|
63
|
-
@
|
|
67
|
+
@running = false
|
|
68
|
+
thread = @flush_thread
|
|
64
69
|
@flush_thread = nil
|
|
70
|
+
thread&.join(5)
|
|
65
71
|
flush
|
|
66
72
|
end
|
|
67
73
|
|
|
@@ -74,8 +80,8 @@ module Legion
|
|
|
74
80
|
private
|
|
75
81
|
|
|
76
82
|
def buffer_event(event)
|
|
77
|
-
@buffer
|
|
78
|
-
@mutex
|
|
83
|
+
@buffer ||= []
|
|
84
|
+
@mutex ||= Mutex.new
|
|
79
85
|
|
|
80
86
|
full = false
|
|
81
87
|
@mutex.synchronize do
|
|
@@ -28,7 +28,8 @@ module Legion
|
|
|
28
28
|
req['Content-Type'] = 'application/json'
|
|
29
29
|
req.body = ::JSON.dump({ event: redact_phi(event.to_s), time: Time.now.to_f })
|
|
30
30
|
|
|
31
|
-
Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https'
|
|
31
|
+
Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https',
|
|
32
|
+
open_timeout: 5, read_timeout: 10) do |http|
|
|
32
33
|
http.request(req)
|
|
33
34
|
end
|
|
34
35
|
rescue StandardError => e
|
|
@@ -32,44 +32,44 @@ module Legion
|
|
|
32
32
|
@level_value
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
def debug(message = nil)
|
|
35
|
+
def debug(message = nil, **ctx)
|
|
36
36
|
return unless @level_value < 1
|
|
37
37
|
|
|
38
38
|
message = yield if message.nil? && block_given?
|
|
39
|
-
with_segments { dispatch(:debug, message) }
|
|
39
|
+
with_segments { dispatch(:debug, message, **ctx) }
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
def info(message = nil)
|
|
42
|
+
def info(message = nil, **ctx)
|
|
43
43
|
return unless @level_value < 2
|
|
44
44
|
|
|
45
45
|
message = yield if message.nil? && block_given?
|
|
46
|
-
with_segments { dispatch(:info, message) }
|
|
46
|
+
with_segments { dispatch(:info, message, **ctx) }
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
def warn(message = nil)
|
|
49
|
+
def warn(message = nil, **ctx)
|
|
50
50
|
return unless @level_value < 3
|
|
51
51
|
|
|
52
52
|
message = yield if message.nil? && block_given?
|
|
53
|
-
with_segments { dispatch(:warn, message) }
|
|
53
|
+
with_segments { dispatch(:warn, message, **ctx) }
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
def error(message = nil)
|
|
56
|
+
def error(message = nil, **ctx)
|
|
57
57
|
return unless @level_value < 4
|
|
58
58
|
|
|
59
59
|
message = yield if message.nil? && block_given?
|
|
60
|
-
with_segments { dispatch(:error, message) }
|
|
60
|
+
with_segments { dispatch(:error, message, **ctx) }
|
|
61
61
|
end
|
|
62
62
|
|
|
63
|
-
def fatal(message = nil)
|
|
63
|
+
def fatal(message = nil, **ctx)
|
|
64
64
|
return unless @level_value < 5
|
|
65
65
|
|
|
66
66
|
message = yield if message.nil? && block_given?
|
|
67
|
-
with_segments { dispatch(:fatal, message) }
|
|
67
|
+
with_segments { dispatch(:fatal, message, **ctx) }
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
-
def unknown(message = nil)
|
|
70
|
+
def unknown(message = nil, **ctx)
|
|
71
71
|
message = yield if message.nil? && block_given?
|
|
72
|
-
with_segments { dispatch(:unknown, message) }
|
|
72
|
+
with_segments { dispatch(:unknown, message, **ctx) }
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def trace(raw_message = nil, size: @trace_size, log_caller: true)
|
|
@@ -94,21 +94,23 @@ module Legion
|
|
|
94
94
|
|
|
95
95
|
private
|
|
96
96
|
|
|
97
|
-
def dispatch(level, message)
|
|
97
|
+
def dispatch(level, message, **ctx)
|
|
98
98
|
return unless defined?(Legion::Logging)
|
|
99
99
|
|
|
100
100
|
if Legion::Logging.respond_to?(:emit_tagged)
|
|
101
|
-
Legion::Logging.emit_tagged(level, message, segments: @segments)
|
|
101
|
+
Legion::Logging.emit_tagged(level, message, segments: @segments, **ctx)
|
|
102
102
|
return
|
|
103
103
|
end
|
|
104
104
|
|
|
105
105
|
if Legion::Logging.respond_to?(level)
|
|
106
|
-
Legion::Logging.public_send(level, message)
|
|
106
|
+
Legion::Logging.public_send(level, message, **ctx)
|
|
107
107
|
return
|
|
108
108
|
end
|
|
109
109
|
|
|
110
110
|
fallback = fallback_level(level)
|
|
111
|
-
|
|
111
|
+
return unless fallback && Legion::Logging.respond_to?(fallback)
|
|
112
|
+
|
|
113
|
+
Legion::Logging.public_send(fallback, message, **ctx)
|
|
112
114
|
end
|
|
113
115
|
|
|
114
116
|
def fallback_level(level)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: legion-logging
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.5.
|
|
4
|
+
version: 1.5.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -63,6 +63,7 @@ files:
|
|
|
63
63
|
- lib/legion/logging/builder.rb
|
|
64
64
|
- lib/legion/logging/category_registry.rb
|
|
65
65
|
- lib/legion/logging/event_builder.rb
|
|
66
|
+
- lib/legion/logging/header_builder.rb
|
|
66
67
|
- lib/legion/logging/helper.rb
|
|
67
68
|
- lib/legion/logging/hooks.rb
|
|
68
69
|
- lib/legion/logging/logger.rb
|