legion-logging 1.2.6 → 1.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/CLAUDE.md +9 -1
- data/lib/legion/logging/event_builder.rb +91 -0
- data/lib/legion/logging/hooks.rb +45 -0
- data/lib/legion/logging/logger.rb +1 -0
- data/lib/legion/logging/methods.rb +25 -0
- data/lib/legion/logging/version.rb +1 -1
- data/lib/legion/logging.rb +9 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e397ca81de1cb74f71dc9f220e59406a8f8e6cbf2d9aae6092918f428588bc60
|
|
4
|
+
data.tar.gz: e41480464a27679090cc3bbff835c9a9c5c5f2c52fb60edd1096d1c8e7a43031
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 355eaf1cfe6d31c27360aadc06bdd6a81b6fa07c380ca68c5fd645089fe8ba81521ffd7971c078ed5f124c9e28e73babcbb390fcffcca46f5648ad3ee2839d24
|
|
7
|
+
data.tar.gz: c062f3e91ccc3a322cbbf8a539321207505c7e663623027e7d59354c63ac3eb692b93389ced67be17c7614b1578021e07dc7956ad0f1469e6cd4adb57d262629
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Legion::Logging Changelog
|
|
2
2
|
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
## v1.2.7
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- `Legion::Logging::Hooks`: callback registry for fatal/error/warn log events
|
|
9
|
+
- `Legion::Logging::EventBuilder`: structured event payload builder with caller, exception, lex, and gem metadata
|
|
10
|
+
- `on_fatal`, `on_error`, `on_warn` registration methods on `Legion::Logging`
|
|
11
|
+
- `enable_hooks!`, `disable_hooks!`, `clear_hooks!` control methods
|
|
12
|
+
- Hook dispatch wired into `fatal`, `error`, `warn` methods in `Methods` module
|
|
13
|
+
|
|
3
14
|
## v1.2.6
|
|
4
15
|
|
|
5
16
|
### Added
|
data/CLAUDE.md
CHANGED
|
@@ -17,10 +17,14 @@ Ruby logging class for the LegionIO framework. Provides colorized console output
|
|
|
17
17
|
Legion::Logging (singleton module)
|
|
18
18
|
├── Methods # Log level methods: debug, info, warn, error, fatal, unknown
|
|
19
19
|
├── Builder # Output destination (stdout/file), log level, formatter
|
|
20
|
+
├── Hooks # Callback registry for fatal/error/warn events (on_fatal, on_error, on_warn)
|
|
21
|
+
├── EventBuilder # Structured event payload builder (caller, exception, lex, gem metadata)
|
|
20
22
|
├── Logger # Core logger configuration and setup
|
|
21
23
|
├── MultiIO # Write to multiple destinations simultaneously
|
|
22
24
|
├── SIEMExporter # PHI-redacting SIEM export (Splunk HEC, ELK/OpenSearch)
|
|
23
|
-
|
|
25
|
+
├── Shipper # Buffered log event forwarding (file/http transports)
|
|
26
|
+
├── Redactor # PII/PHI pattern redaction
|
|
27
|
+
└── Version # VERSION constant
|
|
24
28
|
```
|
|
25
29
|
|
|
26
30
|
### Key Design Patterns
|
|
@@ -32,6 +36,8 @@ Legion::Logging (singleton module)
|
|
|
32
36
|
- **Shared Interface**: Same method signature (`info`, `warn`, `error`, etc.) across all Legion components
|
|
33
37
|
- **MultiIO**: Splits writes to stdout and a log file simultaneously (used by Builder when `log_file` is set)
|
|
34
38
|
- **SIEMExporter**: PHI redaction (SSN, phone, MRN, DOB patterns), `export_to_splunk` (HEC), `format_for_elk`
|
|
39
|
+
- **Hook Callbacks**: `on_fatal`, `on_error`, `on_warn` register procs called after each log at those levels. Hooks are gated by `enable_hooks!`/`disable_hooks!`. Hook failures are silently rescued — never impact the logger.
|
|
40
|
+
- **EventBuilder**: Builds structured event hashes from log context (caller location, exception info, lex identity, gem metadata). All from in-memory data, zero IO.
|
|
35
41
|
|
|
36
42
|
## Dependencies
|
|
37
43
|
|
|
@@ -49,6 +55,8 @@ Legion::Logging (singleton module)
|
|
|
49
55
|
| `lib/legion/logging/logger.rb` | Core logger setup |
|
|
50
56
|
| `lib/legion/logging/multi_io.rb` | Multi-output IO (write to multiple destinations simultaneously) |
|
|
51
57
|
| `lib/legion/logging/siem_exporter.rb` | PHI-redacting SIEM export helpers (Splunk HEC, ELK format) |
|
|
58
|
+
| `lib/legion/logging/hooks.rb` | Callback registry (fatal/error/warn hook arrays, enable/disable/clear) |
|
|
59
|
+
| `lib/legion/logging/event_builder.rb` | Structured event payload builder |
|
|
52
60
|
| `lib/legion/logging/version.rb` | VERSION constant |
|
|
53
61
|
|
|
54
62
|
## Role in LegionIO
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Logging
|
|
5
|
+
module EventBuilder
|
|
6
|
+
class << self
|
|
7
|
+
def build(level:, message:, lex: nil, lex_segments: nil, context: nil, caller_offset: 2) # rubocop:disable Metrics/ParameterLists
|
|
8
|
+
event = base_fields(level, message)
|
|
9
|
+
event[:lex] = derive_lex_source(lex, lex_segments)
|
|
10
|
+
add_node(event)
|
|
11
|
+
add_caller_info(event, caller_offset)
|
|
12
|
+
add_exception_info(event, message)
|
|
13
|
+
add_gem_info(event, event[:lex])
|
|
14
|
+
event[:context] = context if context
|
|
15
|
+
event.compact
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def base_fields(level, message)
|
|
21
|
+
{
|
|
22
|
+
timestamp: Time.now.utc.iso8601(3),
|
|
23
|
+
level: level,
|
|
24
|
+
message: message.is_a?(Exception) ? message.message : strip_ansi(message.to_s),
|
|
25
|
+
pid: ::Process.pid,
|
|
26
|
+
thread: Thread.current.object_id
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def derive_lex_source(lex, lex_segments)
|
|
31
|
+
if lex_segments.is_a?(Array) && !lex_segments.empty?
|
|
32
|
+
"lex-#{lex_segments.join('-')}"
|
|
33
|
+
elsif lex && !lex.to_s.empty?
|
|
34
|
+
"lex-#{lex}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def add_node(event)
|
|
39
|
+
return unless defined?(Legion::Settings)
|
|
40
|
+
|
|
41
|
+
name = begin
|
|
42
|
+
Legion::Settings[:client][:name]
|
|
43
|
+
rescue StandardError
|
|
44
|
+
nil
|
|
45
|
+
end
|
|
46
|
+
event[:node] = name if name
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def add_caller_info(event, offset)
|
|
50
|
+
loc = caller_locations(offset + 1, 1)&.first
|
|
51
|
+
return unless loc
|
|
52
|
+
|
|
53
|
+
event[:caller] = {
|
|
54
|
+
file: loc.absolute_path || loc.path,
|
|
55
|
+
function: loc.base_label,
|
|
56
|
+
line: loc.lineno
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def add_exception_info(event, message)
|
|
61
|
+
return unless message.is_a?(Exception)
|
|
62
|
+
|
|
63
|
+
event[:exception] = {
|
|
64
|
+
class: message.class.name,
|
|
65
|
+
message: message.message
|
|
66
|
+
}
|
|
67
|
+
event[:backtrace] = message.backtrace if message.backtrace
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def add_gem_info(event, lex_source)
|
|
71
|
+
return unless lex_source
|
|
72
|
+
|
|
73
|
+
spec = Gem::Specification.find_by_name(lex_source)
|
|
74
|
+
event[:gem] = {
|
|
75
|
+
name: spec.name,
|
|
76
|
+
version: spec.version.to_s,
|
|
77
|
+
source_code_uri: spec.metadata['source_code_uri'],
|
|
78
|
+
homepage: spec.metadata['homepage_uri'] || spec.homepage,
|
|
79
|
+
path: spec.full_gem_path
|
|
80
|
+
}.compact
|
|
81
|
+
rescue Gem::MissingSpecError, ArgumentError
|
|
82
|
+
nil
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def strip_ansi(str)
|
|
86
|
+
str.gsub(/\e\[[0-9;]*m/, '')
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Logging
|
|
5
|
+
module Hooks
|
|
6
|
+
@hooks = { fatal: [], error: [], warn: [] }
|
|
7
|
+
@enabled = false
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
attr_reader :hooks
|
|
11
|
+
|
|
12
|
+
def enabled?
|
|
13
|
+
@enabled
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def enable!
|
|
17
|
+
@enabled = true
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def disable!
|
|
21
|
+
@enabled = false
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def clear!
|
|
25
|
+
@hooks.each_value(&:clear)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def register(level, &block)
|
|
29
|
+
@hooks[level] << block
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def fire(level, event)
|
|
33
|
+
return unless @enabled
|
|
34
|
+
return if @hooks[level].empty?
|
|
35
|
+
|
|
36
|
+
@hooks[level].each do |hook|
|
|
37
|
+
hook.call(event)
|
|
38
|
+
rescue StandardError
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -12,6 +12,7 @@ module Legion
|
|
|
12
12
|
include Legion::Logging::Builder
|
|
13
13
|
|
|
14
14
|
def initialize(level: 'info', log_file: nil, log_stdout: nil, lex: nil, trace: false, extended: false, trace_size: 4, format: :text, **opts) # rubocop:disable Metrics/ParameterLists
|
|
15
|
+
@lex = lex
|
|
15
16
|
set_log(logfile: log_file, log_stdout: log_stdout)
|
|
16
17
|
log_level(level)
|
|
17
18
|
log_format(format: format, lex: lex, extended: extended, **opts)
|
|
@@ -37,24 +37,30 @@ module Legion
|
|
|
37
37
|
return unless log.level < 3
|
|
38
38
|
|
|
39
39
|
message = yield if message.nil? && block_given?
|
|
40
|
+
raw = message
|
|
40
41
|
message = Rainbow(message).yellow if @color
|
|
41
42
|
log.warn(message)
|
|
43
|
+
fire_hooks(:warn, raw)
|
|
42
44
|
end
|
|
43
45
|
|
|
44
46
|
def error(message = nil)
|
|
45
47
|
return unless log.level < 4
|
|
46
48
|
|
|
47
49
|
message = yield if message.nil? && block_given?
|
|
50
|
+
raw = message
|
|
48
51
|
message = Rainbow(message).red if @color
|
|
49
52
|
log.error(message)
|
|
53
|
+
fire_hooks(:error, raw)
|
|
50
54
|
end
|
|
51
55
|
|
|
52
56
|
def fatal(message = nil)
|
|
53
57
|
return unless log.level < 5
|
|
54
58
|
|
|
55
59
|
message = yield if message.nil? && block_given?
|
|
60
|
+
raw = message
|
|
56
61
|
message = Rainbow(message).darkred if @color
|
|
57
62
|
log.fatal(message)
|
|
63
|
+
fire_hooks(:fatal, raw)
|
|
58
64
|
end
|
|
59
65
|
|
|
60
66
|
def unknown(message = nil)
|
|
@@ -77,6 +83,25 @@ module Legion
|
|
|
77
83
|
Thread.current.object_id.to_s
|
|
78
84
|
end
|
|
79
85
|
end
|
|
86
|
+
|
|
87
|
+
private
|
|
88
|
+
|
|
89
|
+
def fire_hooks(level, message)
|
|
90
|
+
return unless Legion::Logging::Hooks.enabled?
|
|
91
|
+
return if Legion::Logging::Hooks.hooks[level].empty?
|
|
92
|
+
|
|
93
|
+
lex_val = instance_variable_defined?(:@lex) ? @lex : nil
|
|
94
|
+
lex_segs = instance_variable_defined?(:@lex_segments) ? @lex_segments : nil
|
|
95
|
+
|
|
96
|
+
event = Legion::Logging::EventBuilder.build(
|
|
97
|
+
level: level,
|
|
98
|
+
message: message,
|
|
99
|
+
lex: lex_val,
|
|
100
|
+
lex_segments: lex_segs,
|
|
101
|
+
caller_offset: 4
|
|
102
|
+
)
|
|
103
|
+
Legion::Logging::Hooks.fire(level, event)
|
|
104
|
+
end
|
|
80
105
|
end
|
|
81
106
|
end
|
|
82
107
|
end
|
data/lib/legion/logging.rb
CHANGED
|
@@ -4,6 +4,8 @@ require 'legion/logging/version'
|
|
|
4
4
|
require 'legion/logging/logger'
|
|
5
5
|
require 'legion/logging/methods'
|
|
6
6
|
require 'legion/logging/builder'
|
|
7
|
+
require 'legion/logging/hooks'
|
|
8
|
+
require 'legion/logging/event_builder'
|
|
7
9
|
|
|
8
10
|
require 'json'
|
|
9
11
|
require 'logger'
|
|
@@ -15,6 +17,13 @@ module Legion
|
|
|
15
17
|
include Legion::Logging::Methods
|
|
16
18
|
include Legion::Logging::Builder
|
|
17
19
|
|
|
20
|
+
def on_fatal(&) = Hooks.register(:fatal, &)
|
|
21
|
+
def on_error(&) = Hooks.register(:error, &)
|
|
22
|
+
def on_warn(&) = Hooks.register(:warn, &)
|
|
23
|
+
def enable_hooks! = Hooks.enable!
|
|
24
|
+
def disable_hooks! = Hooks.disable!
|
|
25
|
+
def clear_hooks! = Hooks.clear!
|
|
26
|
+
|
|
18
27
|
attr_reader :color
|
|
19
28
|
|
|
20
29
|
def setup(level: 'info', format: :text, **options)
|
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.2.
|
|
4
|
+
version: 1.2.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -58,6 +58,8 @@ files:
|
|
|
58
58
|
- legion-logging.gemspec
|
|
59
59
|
- lib/legion/logging.rb
|
|
60
60
|
- lib/legion/logging/builder.rb
|
|
61
|
+
- lib/legion/logging/event_builder.rb
|
|
62
|
+
- lib/legion/logging/hooks.rb
|
|
61
63
|
- lib/legion/logging/logger.rb
|
|
62
64
|
- lib/legion/logging/methods.rb
|
|
63
65
|
- lib/legion/logging/multi_io.rb
|