legion-logging 1.5.0 → 1.5.2
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/.gitignore +2 -1
- data/CHANGELOG.md +9 -1
- data/CLAUDE.md +39 -21
- data/README.md +35 -10
- data/lib/legion/logging/helper.rb +10 -6
- data/lib/legion/logging/method_tracer.rb +74 -0
- data/lib/legion/logging/methods.rb +7 -1
- 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: 57e4eef04828ffa39cae5277822be4f2f5264d4010b05e724dbb179f0af5b771
|
|
4
|
+
data.tar.gz: 740663a6979cf6b5c5d90a12bc8c43e0fcd989b8d8253269d8a1f1bf9aff9d03
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c590642e775a8f6ed7f87f9b4525049033c3e3f42635ea2e460e5b4ddd89e646a97d8744b80755a82db340ba6e813d8e95e3a8b27ffc17a63f8f3aee99e5b5a6
|
|
7
|
+
data.tar.gz: b812f47d57057a466655f22ae1f3922b6326a22fd40297ce602850069092efaf20ff40a6689ecbfe22a7498429bb3266df70ec40efb69617d11da045695155bd
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
# Legion::Logging Changelog
|
|
2
2
|
|
|
3
|
-
## [1.5.
|
|
3
|
+
## [1.5.2] - 2026-04-27
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
- Exception stdout/file log lines now include the full backtrace instead of truncating after 10 frames with a `... N more` suffix.
|
|
7
|
+
|
|
8
|
+
## [1.5.1] - 2026-04-08
|
|
4
9
|
|
|
5
10
|
### Added
|
|
11
|
+
- `Legion::Logging::MethodTracer` module: opt-in TracePoint-based method call tracing with indent-aware call/return output and parameter formatting
|
|
12
|
+
- `Helper.included` / `Helper.extended` hooks auto-attach `MethodTracer` when `MethodTracer::ENABLED` is true
|
|
13
|
+
- `log_exception` now formats backtrace (up to 10 frames + overflow count) inline in the stdout/file log line
|
|
6
14
|
- `Legion::Logging.current_settings` and `.configuration_generation` so helper mixins can refresh memoized tagged loggers after runtime reconfiguration
|
|
7
15
|
- Component logger overrides from local `settings`, top-level `Legion::Settings[component]`, and `Legion::Settings.dig(:extensions, component)` for `log_level`, `trace`, `trace_size`, and `extended`
|
|
8
16
|
- `Methods#emit_tagged` / `TaggedLogger#dispatch` path so component-level loggers can emit with their own level while preserving tagged context
|
data/CLAUDE.md
CHANGED
|
@@ -8,40 +8,50 @@
|
|
|
8
8
|
Ruby logging class for the LegionIO framework. Provides colorized console output via Rainbow, structured JSON logging (`format: :json`), and a consistent logging interface across all Legion gems and extensions.
|
|
9
9
|
|
|
10
10
|
**GitHub**: https://github.com/LegionIO/legion-logging
|
|
11
|
-
**Version**: 1.
|
|
11
|
+
**Version**: 1.5.0
|
|
12
12
|
**License**: Apache-2.0
|
|
13
13
|
|
|
14
14
|
## Architecture
|
|
15
15
|
|
|
16
16
|
```
|
|
17
17
|
Legion::Logging (singleton module)
|
|
18
|
-
├── Methods
|
|
19
|
-
├── Builder
|
|
20
|
-
├── AsyncWriter
|
|
21
|
-
├── Hooks
|
|
22
|
-
├── EventBuilder
|
|
23
|
-
├── Helper
|
|
24
|
-
├── Logger
|
|
25
|
-
├── MultiIO
|
|
26
|
-
├──
|
|
27
|
-
├──
|
|
28
|
-
├──
|
|
29
|
-
|
|
18
|
+
├── Methods # Log level methods: debug, info, warn, error, fatal, unknown; log_exception helper
|
|
19
|
+
├── Builder # Output destination (stdout/file), log level, formatter, async: keyword
|
|
20
|
+
├── AsyncWriter # Non-blocking SizedQueue-backed writer thread; fatal calls bypass queue
|
|
21
|
+
├── Hooks # Callback registry for fatal/error/warn events (on_fatal, on_error, on_warn)
|
|
22
|
+
├── EventBuilder # Structured event payload builder (caller, exception, lex, gem metadata); fingerprint for dedup
|
|
23
|
+
├── Helper # Injectable log mixin for LEX extensions (derives logger tags from segments/class)
|
|
24
|
+
├── Logger # Core logger configuration and setup
|
|
25
|
+
├── MultiIO # Write to multiple destinations simultaneously
|
|
26
|
+
├── TaggedLogger # Logger wrapper that prepends structured tags to each message
|
|
27
|
+
├── CategoryRegistry # Registry of named log categories with description and expected_fields
|
|
28
|
+
├── MethodTracer # Tracing module for instrumenting method calls (call/return, formatted args)
|
|
29
|
+
├── SIEMExporter # PHI-redacting SIEM export (Splunk HEC, ELK/OpenSearch)
|
|
30
|
+
├── Shipper # Buffered log event forwarding; sub-transports: FileTransport, HttpTransport
|
|
31
|
+
├── Redactor # PII/PHI + secret pattern redaction; opt-in via logging.redaction.enabled
|
|
32
|
+
└── Version # VERSION constant
|
|
33
|
+
|
|
34
|
+
# Module-level writers (pluggable lambda slots replacing old Hooks for AMQP forwarding)
|
|
35
|
+
Legion::Logging.log_writer # -> lambda(->(event, routing_key:) {})
|
|
36
|
+
Legion::Logging.exception_writer # -> lambda(->(event, routing_key:, headers:, properties:) {})
|
|
30
37
|
```
|
|
31
38
|
|
|
32
39
|
### Key Design Patterns
|
|
33
40
|
|
|
34
|
-
- **Singleton Module**: `Legion::Logging` uses `class << self`
|
|
35
|
-
- **Rainbow Colorization**: Console output uses Rainbow gem for colored terminal output
|
|
36
|
-
- **Setup Method**: `Legion::Logging.setup(
|
|
37
|
-
- **Async by Default**: `setup` enables async logging — calls return immediately. Fatal calls always bypass the queue. `stop_async_writer` flushes and stops on shutdown. Buffer size configurable via `Legion::Settings
|
|
38
|
-
- **Structured JSON**: `format: :json` in settings outputs machine-parseable JSON log lines
|
|
41
|
+
- **Singleton Module**: `Legion::Logging` uses `class << self` — called directly: `Legion::Logging.info("msg")`
|
|
42
|
+
- **Rainbow Colorization**: Console output uses Rainbow gem for colored terminal output. Color auto-disabled in JSON format and when writing to a log file.
|
|
43
|
+
- **Setup Method**: `Legion::Logging.setup(level:, format:, async:, **options)` configures output, level, format, and async mode. Increments `configuration_generation` on each call.
|
|
44
|
+
- **Async by Default**: `setup` enables async logging — calls return immediately. Fatal calls always bypass the queue. `stop_async_writer` flushes and stops on shutdown. Buffer size configurable via `Legion::Settings[:logging][:async][:buffer_size]` (default 10,000). Back-pressure: callers block when buffer is full.
|
|
45
|
+
- **Structured JSON**: `format: :json` in settings outputs machine-parseable JSON log lines (disables color)
|
|
39
46
|
- **Shared Interface**: Same method signature (`info`, `warn`, `error`, etc.) across all Legion components
|
|
40
47
|
- **MultiIO**: Splits writes to stdout and a log file simultaneously (used by Builder when `log_file` is set)
|
|
41
48
|
- **SIEMExporter**: PHI redaction (SSN, phone, MRN, DOB patterns), `export_to_splunk` (HEC), `format_for_elk`
|
|
42
|
-
- **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
|
|
43
|
-
- **EventBuilder**: Builds structured event hashes from log context (caller location, exception info, lex identity, gem metadata). All from in-memory data, zero IO.
|
|
49
|
+
- **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. Hooks fire on the async writer thread; event context captured on caller thread.
|
|
50
|
+
- **EventBuilder**: Builds structured event hashes from log context (caller location, exception info, lex identity, gem metadata). All from in-memory data, zero IO. `fingerprint` produces MD5 for dedup in log aggregation.
|
|
44
51
|
- **Helper mixin**: `Legion::Logging::Helper` is injectable into LEX extensions. Derives logger tags from `segments`, `lex_filename`, or class name. Passes through `settings[:logger]` config when available.
|
|
52
|
+
- **Writer Lambdas**: `log_writer` and `exception_writer` are module-level lambda slots for forwarding to external systems (AMQP, etc.). Default implementations are no-ops. Set via `Legion::Logging.log_writer = lambda`.
|
|
53
|
+
- **CategoryRegistry**: Named log categories with description and expected_fields. Register via `Legion::Logging.register_category`. Used for structured log validation.
|
|
54
|
+
- **Redactor**: Opt-in PII/PHI redaction (`logging.redaction.enabled: true`). Guards against Settings recursive init via `@loader` ivar check. Patterns: SSN, phone, MRN, DOB, Vault tokens, JWTs, bearer tokens, lease IDs.
|
|
45
55
|
|
|
46
56
|
## Dependencies
|
|
47
57
|
|
|
@@ -62,7 +72,15 @@ Legion::Logging (singleton module)
|
|
|
62
72
|
| `lib/legion/logging/multi_io.rb` | Multi-output IO (write to multiple destinations simultaneously) |
|
|
63
73
|
| `lib/legion/logging/siem_exporter.rb` | PHI-redacting SIEM export helpers (Splunk HEC, ELK format) |
|
|
64
74
|
| `lib/legion/logging/hooks.rb` | Callback registry (fatal/error/warn hook arrays, enable/disable/clear) |
|
|
65
|
-
| `lib/legion/logging/event_builder.rb` | Structured event payload builder |
|
|
75
|
+
| `lib/legion/logging/event_builder.rb` | Structured event payload builder; `fingerprint` for MD5 dedup |
|
|
76
|
+
| `lib/legion/logging/tagged_logger.rb` | Logger wrapper that prepends structured tags to each message |
|
|
77
|
+
| `lib/legion/logging/category_registry.rb` | Named log category registration and lookup |
|
|
78
|
+
| `lib/legion/logging/method_tracer.rb` | Method call tracing instrumentation (call/return, formatted args) |
|
|
79
|
+
| `lib/legion/logging/shipper.rb` | Buffered log event forwarding to external systems |
|
|
80
|
+
| `lib/legion/logging/shipper/file_transport.rb` | File-based log shipper transport |
|
|
81
|
+
| `lib/legion/logging/shipper/http_transport.rb` | HTTP-based log shipper transport |
|
|
82
|
+
| `lib/legion/logging/siem_exporter.rb` | PHI-redacting SIEM export (Splunk HEC, ELK format) |
|
|
83
|
+
| `lib/legion/logging/redactor.rb` | PII/PHI + secret pattern redaction (opt-in) |
|
|
66
84
|
| `lib/legion/logging/version.rb` | VERSION constant |
|
|
67
85
|
|
|
68
86
|
## Role in LegionIO
|
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Logging module for the [LegionIO](https://github.com/LegionIO/LegionIO) framework. Provides colorized console output via Rainbow, structured JSON logging, multi-output IO, and a consistent logging interface across all Legion gems and extensions.
|
|
4
4
|
|
|
5
|
-
**Version**: 1.
|
|
5
|
+
**Version**: 1.5.2
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -96,23 +96,46 @@ end
|
|
|
96
96
|
|
|
97
97
|
### Exception Logging
|
|
98
98
|
|
|
99
|
-
`log_exception` provides a single call for complete
|
|
99
|
+
`log_exception` provides a single call for complete exception logging with component context. It writes a human-readable exception line through the configured logger and publishes a structured exception event when an `exception_writer` is configured.
|
|
100
100
|
|
|
101
101
|
```ruby
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
102
|
+
begin
|
|
103
|
+
runner.call
|
|
104
|
+
rescue StandardError => e
|
|
105
|
+
Legion::Logging.log_exception(
|
|
106
|
+
e,
|
|
107
|
+
handled: true,
|
|
108
|
+
component_type: :runner,
|
|
109
|
+
lex: 'my_extension',
|
|
110
|
+
task_id: 'abc-123'
|
|
111
|
+
)
|
|
112
|
+
end
|
|
107
113
|
```
|
|
108
114
|
|
|
115
|
+
The synchronous log line includes the full Ruby backtrace. Legion does not truncate it to a fixed frame count or replace the tail with `... N more`, because the missing frames are often the useful part of production failures.
|
|
116
|
+
|
|
117
|
+
Structured exception events include:
|
|
118
|
+
|
|
119
|
+
- exception class and message
|
|
120
|
+
- full backtrace array
|
|
121
|
+
- caller file, line, and function where available
|
|
122
|
+
- log level and handled/unhandled status
|
|
123
|
+
- component type, lex name, gem name, version, and source path metadata
|
|
124
|
+
- task and thread context
|
|
125
|
+
- stable error fingerprint for deduplication
|
|
126
|
+
|
|
109
127
|
### Writer Lambdas
|
|
110
128
|
|
|
111
129
|
`log_writer` and `exception_writer` are pluggable lambda slots that replace the old Hooks system. Assign them to forward events to external systems:
|
|
112
130
|
|
|
113
131
|
```ruby
|
|
114
|
-
Legion::Logging.exception_writer =
|
|
115
|
-
|
|
132
|
+
Legion::Logging.exception_writer = lambda do |payload, routing_key:, headers:, properties:|
|
|
133
|
+
publish_to_amqp(payload, routing_key:, headers:, properties:)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
Legion::Logging.log_writer = lambda do |context, routing_key:|
|
|
137
|
+
publish_log(context, routing_key:)
|
|
138
|
+
end
|
|
116
139
|
```
|
|
117
140
|
|
|
118
141
|
### EventBuilder
|
|
@@ -121,7 +144,9 @@ Legion::Logging.log_writer = ->(context, routing_key:) { publish_log(context) }
|
|
|
121
144
|
|
|
122
145
|
### Redactor
|
|
123
146
|
|
|
124
|
-
`Legion::Logging::Redactor` redacts PII/PHI patterns (SSN, phone, MRN, DOB) plus Vault tokens, JWTs, bearer tokens, and lease IDs from log messages. Redaction is opt-in: load the module (for example via `require 'legion/logging/redactor'`) and enable it with `logging.redaction.enabled: true`. When loaded and enabled, it is wired into all log methods in the write path.
|
|
147
|
+
`Legion::Logging::Redactor` redacts PII/PHI patterns (SSN, email, phone, MRN, DOB, credit card) plus Vault tokens, JWTs, bearer tokens, `vault://` URIs, `lease://` URIs, and lease IDs from log messages. Redaction is opt-in for text log lines: load the module (for example via `require 'legion/logging/redactor'`) and enable it with `logging.redaction.enabled: true`. When loaded and enabled, it is wired into all log methods in the write path.
|
|
148
|
+
|
|
149
|
+
Structured exception events are redacted before publishing when the redactor is loaded. This includes event identity fields such as `user`, so email-shaped local usernames are not forwarded raw.
|
|
125
150
|
|
|
126
151
|
## Requirements
|
|
127
152
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'securerandom'
|
|
4
4
|
require_relative 'tagged_logger'
|
|
5
|
+
require_relative 'method_tracer'
|
|
5
6
|
|
|
6
7
|
module Legion
|
|
7
8
|
module Logging
|
|
@@ -31,7 +32,6 @@ module Legion
|
|
|
31
32
|
'middleware' => :middleware
|
|
32
33
|
}.freeze
|
|
33
34
|
|
|
34
|
-
EXCEPTION_BACKTRACE_LIMIT = 10
|
|
35
35
|
EXCEPTION_PRIORITY = { warn: 0, error: 5, fatal: 9 }.freeze
|
|
36
36
|
EXCEPTION_COLORS = {
|
|
37
37
|
fatal: :darkred,
|
|
@@ -102,6 +102,14 @@ module Legion
|
|
|
102
102
|
publish_exception(event, level) if structured_exception_support?
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
+
def self.included(base)
|
|
106
|
+
MethodTracer.attach(base) if defined?(MethodTracer) && MethodTracer::ENABLED
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def self.extended(base)
|
|
110
|
+
MethodTracer.attach(base, match_singleton: true) if defined?(MethodTracer) && MethodTracer::ENABLED
|
|
111
|
+
end
|
|
112
|
+
|
|
105
113
|
private
|
|
106
114
|
|
|
107
115
|
def build_exception_event(exception:, level:, spec:, handled:, task_id:, payload_summary:)
|
|
@@ -480,11 +488,7 @@ module Legion
|
|
|
480
488
|
lines << " #{context_line}" unless context_line.empty?
|
|
481
489
|
|
|
482
490
|
bt = exception.backtrace
|
|
483
|
-
if bt&.any?
|
|
484
|
-
bt.first(EXCEPTION_BACKTRACE_LIMIT).each { |frame| lines << " #{frame}" }
|
|
485
|
-
remaining = bt.length - EXCEPTION_BACKTRACE_LIMIT
|
|
486
|
-
lines << " ... #{remaining} more" if remaining.positive?
|
|
487
|
-
end
|
|
491
|
+
bt.each { |frame| lines << " #{frame}" } if bt&.any?
|
|
488
492
|
|
|
489
493
|
lines.join("\n")
|
|
490
494
|
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Logging
|
|
5
|
+
module MethodTracer
|
|
6
|
+
ENABLED = false
|
|
7
|
+
ATTACHED = {} # rubocop:disable Style/MutableConstant
|
|
8
|
+
ATTACHED_MUTEX = Mutex.new
|
|
9
|
+
private_constant :ATTACHED_MUTEX
|
|
10
|
+
|
|
11
|
+
def self.attach(base, match_singleton: false)
|
|
12
|
+
return unless ENABLED
|
|
13
|
+
|
|
14
|
+
ATTACHED_MUTEX.synchronize do
|
|
15
|
+
return if ATTACHED.key?(base)
|
|
16
|
+
|
|
17
|
+
base_name = base.to_s
|
|
18
|
+
tp = TracePoint.new(:call, :return) do |trace|
|
|
19
|
+
next unless trace.defined_class == base || (match_singleton && trace.defined_class == base.singleton_class)
|
|
20
|
+
|
|
21
|
+
stack = (Thread.current[:_legion_trace_stack] ||= [])
|
|
22
|
+
|
|
23
|
+
case trace.event
|
|
24
|
+
when :call
|
|
25
|
+
params = format_params(trace)
|
|
26
|
+
params_segment = params.empty? ? '' : ", #{params.join(', ')}"
|
|
27
|
+
indent = ' ' * stack.size
|
|
28
|
+
puts "#{indent}-> #{trace.method_id}, #{base_name}#{params_segment}"
|
|
29
|
+
stack.push(trace.method_id)
|
|
30
|
+
when :return
|
|
31
|
+
stack.pop
|
|
32
|
+
indent = ' ' * stack.size
|
|
33
|
+
puts "#{indent}<- #{trace.method_id}, #{base_name}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
tp.enable
|
|
37
|
+
ATTACHED[base] = tp
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.detach(base)
|
|
42
|
+
ATTACHED_MUTEX.synchronize do
|
|
43
|
+
tp = ATTACHED.delete(base)
|
|
44
|
+
tp&.disable
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.detach_all
|
|
49
|
+
ATTACHED_MUTEX.synchronize do
|
|
50
|
+
ATTACHED.each_value(&:disable)
|
|
51
|
+
ATTACHED.clear
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.format_params(trace_point)
|
|
56
|
+
trace_point.parameters.filter_map do |type, name|
|
|
57
|
+
next unless name
|
|
58
|
+
|
|
59
|
+
val = begin
|
|
60
|
+
trace_point.binding.local_variable_get(name)
|
|
61
|
+
rescue StandardError
|
|
62
|
+
'?'
|
|
63
|
+
end
|
|
64
|
+
case type
|
|
65
|
+
when :req, :opt then "#{name}=#{val.inspect}"
|
|
66
|
+
when :keyreq, :key then "#{name}: #{val.inspect}"
|
|
67
|
+
when :rest then "*#{name}"
|
|
68
|
+
when :keyrest then "**#{name}"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -104,9 +104,15 @@ module Legion
|
|
|
104
104
|
source_code_uri: nil, handled: false, payload_summary: nil,
|
|
105
105
|
task_id: nil, **extra)
|
|
106
106
|
level = level.to_sym if level.respond_to?(:to_sym)
|
|
107
|
-
# 1. Log human-readable line to stdout/file (bypass writer callbacks)
|
|
107
|
+
# 1. Log human-readable line + backtrace to stdout/file (bypass writer callbacks)
|
|
108
108
|
msg = exception.respond_to?(:message) ? exception.message : exception.to_s
|
|
109
109
|
msg = maybe_redact(msg)
|
|
110
|
+
bt = Array(exception.backtrace)
|
|
111
|
+
if bt.any?
|
|
112
|
+
lines = ["#{exception.class}: #{msg}"]
|
|
113
|
+
bt.each { |frame| lines << " #{frame}" }
|
|
114
|
+
msg = lines.join("\n")
|
|
115
|
+
end
|
|
110
116
|
log.public_send(level, msg) if respond_to?(:log) && log.respond_to?(level)
|
|
111
117
|
|
|
112
118
|
# 2. Build rich exception event
|
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.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -66,6 +66,7 @@ files:
|
|
|
66
66
|
- lib/legion/logging/helper.rb
|
|
67
67
|
- lib/legion/logging/hooks.rb
|
|
68
68
|
- lib/legion/logging/logger.rb
|
|
69
|
+
- lib/legion/logging/method_tracer.rb
|
|
69
70
|
- lib/legion/logging/methods.rb
|
|
70
71
|
- lib/legion/logging/multi_io.rb
|
|
71
72
|
- lib/legion/logging/redactor.rb
|