legion-logging 1.5.5 → 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 +10 -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/methods.rb +85 -119
- 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,15 @@
|
|
|
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
|
+
|
|
3
13
|
## [1.5.5] - 2026-05-27
|
|
4
14
|
|
|
5
15
|
### Fixed
|
|
@@ -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}")
|
|
@@ -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,59 +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
83
|
raw = maybe_redact(message)
|
|
70
84
|
formatted = format_message_for_level(:fatal, raw)
|
|
71
|
-
|
|
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
|
|
72
89
|
end
|
|
73
90
|
|
|
74
|
-
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)
|
|
75
92
|
message = yield if message.nil? && block_given?
|
|
76
93
|
raw = maybe_redact(message)
|
|
77
94
|
formatted = format_message_for_level(:unknown, raw)
|
|
78
|
-
|
|
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
|
|
79
99
|
end
|
|
80
100
|
|
|
81
|
-
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)
|
|
82
103
|
level = level.to_sym
|
|
83
104
|
message = yield if message.nil? && block_given?
|
|
84
105
|
return if message.nil?
|
|
@@ -87,19 +108,26 @@ module Legion
|
|
|
87
108
|
formatted = format_message_for_level(level, raw)
|
|
88
109
|
|
|
89
110
|
with_tagged_context(segments, method_ctx) do
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
|
103
131
|
end
|
|
104
132
|
end
|
|
105
133
|
end
|
|
@@ -237,6 +265,29 @@ module Legion
|
|
|
237
265
|
level.to_s.upcase
|
|
238
266
|
end
|
|
239
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
|
+
|
|
240
291
|
def write_async_or_sync(level, formatted_message, raw_message, writer_context: nil)
|
|
241
292
|
writer = @async_writer
|
|
242
293
|
caller_trace = capture_runner_trace_for_async
|
|
@@ -247,7 +298,11 @@ module Legion
|
|
|
247
298
|
writer_context: writer_context,
|
|
248
299
|
segments: Thread.current[:legion_log_segments],
|
|
249
300
|
method_ctx: Thread.current[:legion_log_method],
|
|
250
|
-
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]
|
|
251
306
|
))
|
|
252
307
|
return if queued
|
|
253
308
|
end
|
|
@@ -259,7 +314,7 @@ module Legion
|
|
|
259
314
|
end
|
|
260
315
|
|
|
261
316
|
def capture_runner_trace_for_async
|
|
262
|
-
build_runner_trace(caller_locations(
|
|
317
|
+
build_runner_trace(caller_locations(5, 1)&.first)
|
|
263
318
|
end
|
|
264
319
|
|
|
265
320
|
def with_caller_trace(caller_trace)
|
|
@@ -270,17 +325,6 @@ module Legion
|
|
|
270
325
|
Thread.current[:legion_log_caller] = prev_caller_trace
|
|
271
326
|
end
|
|
272
327
|
|
|
273
|
-
def redaction_enabled?
|
|
274
|
-
return false unless defined?(Legion::Settings)
|
|
275
|
-
|
|
276
|
-
loader = Legion::Settings.instance_variable_get(:@loader)
|
|
277
|
-
return false unless loader
|
|
278
|
-
|
|
279
|
-
loader.dig(:logging, :redaction, :enabled) == true
|
|
280
|
-
rescue StandardError
|
|
281
|
-
false
|
|
282
|
-
end
|
|
283
|
-
|
|
284
328
|
def build_log_headers(event, component, level)
|
|
285
329
|
headers = {
|
|
286
330
|
'legion_protocol_version' => '2.0',
|
|
@@ -301,28 +345,11 @@ module Legion
|
|
|
301
345
|
timestamp: Time.now.to_i,
|
|
302
346
|
app_id: 'legionio',
|
|
303
347
|
type: 'log_event',
|
|
304
|
-
priority: EXCEPTION_PRIORITY[level] || 0,
|
|
348
|
+
priority: HeaderBuilder::EXCEPTION_PRIORITY[level] || 0,
|
|
305
349
|
delivery_mode: 2
|
|
306
350
|
}
|
|
307
351
|
end
|
|
308
352
|
|
|
309
|
-
def append_identity_headers(headers)
|
|
310
|
-
return unless defined?(Legion::Identity::Process)
|
|
311
|
-
return if Legion::Identity::Process.respond_to?(:resolved?) && !Legion::Identity::Process.resolved?
|
|
312
|
-
|
|
313
|
-
id = identity_hash
|
|
314
|
-
append_optional_header(headers, 'x-legion-identity-canonical-name', id[:canonical_name])
|
|
315
|
-
append_optional_header(headers, 'x-legion-identity-trust', id[:trust])
|
|
316
|
-
append_optional_header(headers, 'x-legion-identity-id', id[:id])
|
|
317
|
-
append_optional_header(headers, 'x-legion-identity-kind', id[:kind])
|
|
318
|
-
append_optional_header(headers, 'x-legion-identity-mode', id[:mode])
|
|
319
|
-
append_optional_header(headers, 'x-legion-identity-source', id[:source])
|
|
320
|
-
headers['x-legion-identity-db-principal-id'] = id[:db_principal_id] if id[:db_principal_id]
|
|
321
|
-
headers['x-legion-identity-db-identity-id'] = id[:db_identity_id] if id[:db_identity_id]
|
|
322
|
-
rescue StandardError
|
|
323
|
-
nil
|
|
324
|
-
end
|
|
325
|
-
|
|
326
353
|
def publish_exception_event(event, level)
|
|
327
354
|
lex_name = event[:lex] || 'core'
|
|
328
355
|
comp = event[:component_type] || :unknown
|
|
@@ -332,67 +359,6 @@ module Legion
|
|
|
332
359
|
Legion::Logging.exception_writer.call(event, routing_key: routing_key, headers: headers, properties: properties)
|
|
333
360
|
end
|
|
334
361
|
|
|
335
|
-
def build_exception_headers(event, comp, level)
|
|
336
|
-
headers = {
|
|
337
|
-
'legion_protocol_version' => '2.0',
|
|
338
|
-
'x-error-fingerprint' => event[:error_fingerprint],
|
|
339
|
-
'x-exception-class' => event[:exception_class],
|
|
340
|
-
'x-handled' => event[:handled].to_s,
|
|
341
|
-
'x-gem-name' => event[:gem_name].to_s,
|
|
342
|
-
'x-lex-version' => event[:lex_version].to_s,
|
|
343
|
-
'x-component-type' => comp.to_s,
|
|
344
|
-
'x-level' => level.to_s
|
|
345
|
-
}
|
|
346
|
-
append_legion_version_header(headers)
|
|
347
|
-
append_optional_header(headers, 'x-task-id', event[:task_id])
|
|
348
|
-
append_optional_header(headers, 'x-conversation-id', event[:conversation_id])
|
|
349
|
-
append_optional_header(headers, 'x-user', event[:user])
|
|
350
|
-
append_identity_headers(headers)
|
|
351
|
-
headers
|
|
352
|
-
end
|
|
353
|
-
|
|
354
|
-
def append_optional_header(headers, key, value)
|
|
355
|
-
return if value.nil?
|
|
356
|
-
return if value.respond_to?(:empty?) && value.empty?
|
|
357
|
-
|
|
358
|
-
headers[key] = value.to_s
|
|
359
|
-
end
|
|
360
|
-
|
|
361
|
-
def append_legion_version_header(headers)
|
|
362
|
-
append_optional_header(headers, 'x-legion-version', Legion::VERSION) if defined?(Legion::VERSION)
|
|
363
|
-
end
|
|
364
|
-
|
|
365
|
-
def identity_hash
|
|
366
|
-
process = Legion::Identity::Process
|
|
367
|
-
return process.identity_hash if process.respond_to?(:identity_hash)
|
|
368
|
-
|
|
369
|
-
{
|
|
370
|
-
canonical_name: identity_value(process, :canonical_name),
|
|
371
|
-
id: identity_value(process, :id),
|
|
372
|
-
kind: identity_value(process, :kind),
|
|
373
|
-
mode: identity_value(process, :mode),
|
|
374
|
-
source: identity_value(process, :source),
|
|
375
|
-
trust: identity_value(process, :trust)
|
|
376
|
-
}
|
|
377
|
-
end
|
|
378
|
-
|
|
379
|
-
def identity_value(process, method_name)
|
|
380
|
-
process.public_send(method_name) if process.respond_to?(method_name)
|
|
381
|
-
end
|
|
382
|
-
|
|
383
|
-
def build_exception_properties(event, level)
|
|
384
|
-
{
|
|
385
|
-
content_type: 'application/json',
|
|
386
|
-
message_id: SecureRandom.uuid,
|
|
387
|
-
correlation_id: event[:error_fingerprint],
|
|
388
|
-
timestamp: Time.now.to_i,
|
|
389
|
-
app_id: 'legionio',
|
|
390
|
-
type: 'exception_event',
|
|
391
|
-
priority: EXCEPTION_PRIORITY[level] || 5,
|
|
392
|
-
delivery_mode: 2
|
|
393
|
-
}
|
|
394
|
-
end
|
|
395
|
-
|
|
396
362
|
def build_writer_context(level, message)
|
|
397
363
|
has_writer = !Legion::Logging.instance_variable_get(:@log_writer).nil?
|
|
398
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
|