legion-logging 1.2.5 → 1.2.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 +12 -0
- data/CLAUDE.md +10 -4
- data/Gemfile +3 -0
- data/README.md +24 -1
- data/lib/legion/logging/redactor.rb +79 -0
- data/lib/legion/logging/shipper/file_transport.rb +42 -0
- data/lib/legion/logging/shipper/http_transport.rb +79 -0
- data/lib/legion/logging/shipper.rb +133 -0
- data/lib/legion/logging/version.rb +1 -1
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d4a463a1d341069c5c5ed0f15e98dfc25a3572e440bbba3f09d63ffc825c7f0f
|
|
4
|
+
data.tar.gz: 5e6a1dbbec201fb5fcd4e4c6d000ecba84f53bf78a5f25f74e5b75145ff11683
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dae39ad0842e8fdf132993d465a9ff6bedab0e6174bf42c9f9ab4ffad98377d3352b6ecc3aca218480fb2c6be9eb36c9efb80c0230e0c95a2dbe2011da6461ad
|
|
7
|
+
data.tar.gz: 07c5e18d680c68e83bd03a3d20f64a2cf9ab62c513a00773d5a55a58e9f7c3fcf0f98268b8b3a63de8e18509fe0a237922e4eec6de64132a815e970da96d2a33
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Legion::Logging Changelog
|
|
2
2
|
|
|
3
|
+
## v1.2.6
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `Legion::Logging::Redactor`: PII/PHI redaction module with built-in patterns for SSN, email, phone, MRN, DOB, and credit card numbers
|
|
7
|
+
- Sensitive field-name redaction: fields named `password`, `secret`, `token`, `api_key`, `authorization` are always fully redacted
|
|
8
|
+
- Recursive redaction of nested hashes and arrays
|
|
9
|
+
- Custom pattern support via `Legion::Settings[:logging, :redactor, :custom_patterns]`
|
|
10
|
+
- `Legion::Logging::Shipper`: structured log event forwarding to external collectors with batch buffering and level filtering
|
|
11
|
+
- `Legion::Logging::Shipper::FileTransport`: writes JSON-lines to rotated log files for pickup by Filebeat/Fluentd
|
|
12
|
+
- `Legion::Logging::Shipper::HttpTransport`: POSTs JSON batches to HTTP endpoints (Splunk HEC, ELK Logstash)
|
|
13
|
+
- All SIEM shipping features disabled by default; opt-in via `logging.shipper.enabled: true`
|
|
14
|
+
|
|
3
15
|
## v1.2.5
|
|
4
16
|
|
|
5
17
|
### Fixed
|
data/CLAUDE.md
CHANGED
|
@@ -8,16 +8,19 @@
|
|
|
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.2.5
|
|
11
12
|
**License**: Apache-2.0
|
|
12
13
|
|
|
13
14
|
## Architecture
|
|
14
15
|
|
|
15
16
|
```
|
|
16
17
|
Legion::Logging (singleton module)
|
|
17
|
-
├── Methods
|
|
18
|
-
├── Builder
|
|
19
|
-
├── Logger
|
|
20
|
-
|
|
18
|
+
├── Methods # Log level methods: debug, info, warn, error, fatal, unknown
|
|
19
|
+
├── Builder # Output destination (stdout/file), log level, formatter
|
|
20
|
+
├── Logger # Core logger configuration and setup
|
|
21
|
+
├── MultiIO # Write to multiple destinations simultaneously
|
|
22
|
+
├── SIEMExporter # PHI-redacting SIEM export (Splunk HEC, ELK/OpenSearch)
|
|
23
|
+
└── Version # VERSION constant (1.2.5)
|
|
21
24
|
```
|
|
22
25
|
|
|
23
26
|
### Key Design Patterns
|
|
@@ -27,6 +30,8 @@ Legion::Logging (singleton module)
|
|
|
27
30
|
- **Setup Method**: `Legion::Logging.setup(log_file:, level:)` configures output destination and level
|
|
28
31
|
- **Structured JSON**: `format: :json` in settings outputs machine-parseable JSON log lines
|
|
29
32
|
- **Shared Interface**: Same method signature (`info`, `warn`, `error`, etc.) across all Legion components
|
|
33
|
+
- **MultiIO**: Splits writes to stdout and a log file simultaneously (used by Builder when `log_file` is set)
|
|
34
|
+
- **SIEMExporter**: PHI redaction (SSN, phone, MRN, DOB patterns), `export_to_splunk` (HEC), `format_for_elk`
|
|
30
35
|
|
|
31
36
|
## Dependencies
|
|
32
37
|
|
|
@@ -43,6 +48,7 @@ Legion::Logging (singleton module)
|
|
|
43
48
|
| `lib/legion/logging/builder.rb` | Output config and formatter |
|
|
44
49
|
| `lib/legion/logging/logger.rb` | Core logger setup |
|
|
45
50
|
| `lib/legion/logging/multi_io.rb` | Multi-output IO (write to multiple destinations simultaneously) |
|
|
51
|
+
| `lib/legion/logging/siem_exporter.rb` | PHI-redacting SIEM export helpers (Splunk HEC, ELK format) |
|
|
46
52
|
| `lib/legion/logging/version.rb` | VERSION constant |
|
|
47
53
|
|
|
48
54
|
## Role in LegionIO
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# legion-logging
|
|
2
2
|
|
|
3
|
-
Logging module for the [LegionIO](https://github.com/LegionIO/LegionIO) framework. Provides colorized console output via Rainbow and a consistent logging interface across all Legion gems and extensions.
|
|
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
|
+
|
|
5
|
+
**Version**: 1.2.5
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
6
8
|
|
|
@@ -39,6 +41,27 @@ Legion::Logging.setup(level: 'info', format: :json)
|
|
|
39
41
|
|
|
40
42
|
This is useful for log aggregation pipelines (Elasticsearch, Splunk, etc.).
|
|
41
43
|
|
|
44
|
+
### Multi-Output IO
|
|
45
|
+
|
|
46
|
+
`Legion::Logging::MultiIO` writes to multiple destinations simultaneously — for example, stdout and a file at the same time. Used internally by the Builder when `log_file` is set alongside console output.
|
|
47
|
+
|
|
48
|
+
### SIEM Export
|
|
49
|
+
|
|
50
|
+
`Legion::Logging::SIEMExporter` provides PHI-redacting export helpers for security event pipelines:
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
# Redact PHI patterns (SSN, phone, MRN, DOB) from a string
|
|
54
|
+
clean = Legion::Logging::SIEMExporter.redact_phi(raw_message)
|
|
55
|
+
|
|
56
|
+
# Export to Splunk HEC
|
|
57
|
+
Legion::Logging::SIEMExporter.export_to_splunk(event, hec_url: url, token: token)
|
|
58
|
+
|
|
59
|
+
# Format for ELK/OpenSearch
|
|
60
|
+
Legion::Logging::SIEMExporter.format_for_elk(event, index: 'legion')
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
PHI patterns redacted: SSN (`###-##-####`), phone (`###-###-####`), MRN (`XX#######`), DOB (`##/##/####`).
|
|
64
|
+
|
|
42
65
|
## Requirements
|
|
43
66
|
|
|
44
67
|
- Ruby >= 3.4
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Logging
|
|
5
|
+
module Redactor
|
|
6
|
+
PATTERNS = {
|
|
7
|
+
ssn: /\b\d{3}-\d{2}-\d{4}\b/,
|
|
8
|
+
email: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/,
|
|
9
|
+
phone: /\b(?:\+1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/,
|
|
10
|
+
mrn: /\bMRN[:\s]*\d{6,10}\b/i,
|
|
11
|
+
dob: %r{\bDOB[:\s]*\d{1,2}[/-]\d{1,2}[/-]\d{2,4}\b}i,
|
|
12
|
+
credit_card: /\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
15
|
+
SENSITIVE_FIELDS = %w[password secret token api_key authorization].freeze
|
|
16
|
+
|
|
17
|
+
REDACTED = '[REDACTED]'
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
def redact(event)
|
|
21
|
+
return event unless event.is_a?(Hash)
|
|
22
|
+
|
|
23
|
+
event.each_with_object({}) do |(key, value), result|
|
|
24
|
+
result[key] = sensitive_field?(key) ? REDACTED : redact_value(value)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def redact_value(value)
|
|
29
|
+
case value
|
|
30
|
+
when String then redact_string(value)
|
|
31
|
+
when Hash then redact(value)
|
|
32
|
+
when Array then value.map { |v| redact_value(v) }
|
|
33
|
+
else value
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def redact_string(str)
|
|
38
|
+
result = str.dup
|
|
39
|
+
all_patterns.each_value { |pattern| result.gsub!(pattern, REDACTED) }
|
|
40
|
+
result
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def sensitive_field?(key)
|
|
46
|
+
SENSITIVE_FIELDS.include?(key.to_s.downcase)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def all_patterns
|
|
50
|
+
@all_patterns ||= build_patterns
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def build_patterns
|
|
54
|
+
patterns = PATTERNS.dup
|
|
55
|
+
custom = custom_patterns
|
|
56
|
+
custom.each { |name, regex| patterns[name.to_sym] = regex }
|
|
57
|
+
patterns
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def custom_patterns
|
|
61
|
+
return {} unless defined?(Legion::Settings)
|
|
62
|
+
|
|
63
|
+
raw = Legion::Settings[:logging, :redactor, :custom_patterns]
|
|
64
|
+
return {} unless raw.is_a?(Hash)
|
|
65
|
+
|
|
66
|
+
raw.each_with_object({}) do |(name, pattern_str), acc|
|
|
67
|
+
acc[name] = Regexp.new(pattern_str)
|
|
68
|
+
rescue RegexpError
|
|
69
|
+
# skip invalid patterns
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def reset_pattern_cache!
|
|
74
|
+
@all_patterns = nil
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
module Legion
|
|
7
|
+
module Logging
|
|
8
|
+
module Shipper
|
|
9
|
+
module FileTransport
|
|
10
|
+
DEFAULT_PATH = '/var/log/legion/siem.log'
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def ship(event)
|
|
14
|
+
path = resolve_path
|
|
15
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
16
|
+
File.open(path, 'a') do |f|
|
|
17
|
+
f.puts(::JSON.generate(event))
|
|
18
|
+
end
|
|
19
|
+
true
|
|
20
|
+
rescue StandardError => e
|
|
21
|
+
Legion::Logging.error("FileTransport ship failed: #{e.message}") if defined?(Legion::Logging)
|
|
22
|
+
false
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def resolve_path
|
|
28
|
+
return settings_path if settings_path
|
|
29
|
+
|
|
30
|
+
DEFAULT_PATH
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def settings_path
|
|
34
|
+
return nil unless defined?(Legion::Settings)
|
|
35
|
+
|
|
36
|
+
Legion::Settings[:logging, :shipper, :file, :path]
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'net/http'
|
|
5
|
+
require 'uri'
|
|
6
|
+
|
|
7
|
+
module Legion
|
|
8
|
+
module Logging
|
|
9
|
+
module Shipper
|
|
10
|
+
module HttpTransport
|
|
11
|
+
class << self
|
|
12
|
+
def ship(events)
|
|
13
|
+
endpoint = resolve_endpoint
|
|
14
|
+
return false unless endpoint
|
|
15
|
+
|
|
16
|
+
uri = URI(endpoint)
|
|
17
|
+
batch = Array(events)
|
|
18
|
+
body = build_body(batch, uri)
|
|
19
|
+
|
|
20
|
+
response = post(uri, body)
|
|
21
|
+
response.is_a?(Net::HTTPSuccess)
|
|
22
|
+
rescue StandardError => e
|
|
23
|
+
Legion::Logging.error("HttpTransport ship failed: #{e.message}") if defined?(Legion::Logging)
|
|
24
|
+
false
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def post(uri, body)
|
|
30
|
+
req = Net::HTTP::Post.new(uri)
|
|
31
|
+
req['Content-Type'] = 'application/json'
|
|
32
|
+
apply_auth(req)
|
|
33
|
+
|
|
34
|
+
Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https',
|
|
35
|
+
open_timeout: 5, read_timeout: 10) do |http|
|
|
36
|
+
http.request(req, body)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def build_body(events, uri)
|
|
41
|
+
# Splunk HEC expects { event: ... } per event; others expect an array
|
|
42
|
+
if splunk_hec?(uri)
|
|
43
|
+
events.map { |e| ::JSON.generate({ event: e, time: Time.now.to_f }) }.join("\n")
|
|
44
|
+
else
|
|
45
|
+
::JSON.generate(events)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def splunk_hec?(uri)
|
|
50
|
+
uri.path.include?('/services/collector')
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def apply_auth(req)
|
|
54
|
+
token = auth_token
|
|
55
|
+
return unless token
|
|
56
|
+
|
|
57
|
+
req['Authorization'] = if splunk_hec?(URI(req.path.empty? ? '/' : req.uri&.to_s || '/'))
|
|
58
|
+
"Splunk #{token}"
|
|
59
|
+
else
|
|
60
|
+
"Bearer #{token}"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def auth_token
|
|
65
|
+
return nil unless defined?(Legion::Settings)
|
|
66
|
+
|
|
67
|
+
Legion::Settings[:logging, :shipper, :auth_token]
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def resolve_endpoint
|
|
71
|
+
return nil unless defined?(Legion::Settings)
|
|
72
|
+
|
|
73
|
+
Legion::Settings[:logging, :shipper, :endpoint]
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'redactor'
|
|
4
|
+
require_relative 'shipper/file_transport'
|
|
5
|
+
require_relative 'shipper/http_transport'
|
|
6
|
+
|
|
7
|
+
module Legion
|
|
8
|
+
module Logging
|
|
9
|
+
module Shipper
|
|
10
|
+
LEVEL_ORDER = %w[debug info warn error fatal].freeze
|
|
11
|
+
|
|
12
|
+
TRANSPORTS = {
|
|
13
|
+
file: FileTransport,
|
|
14
|
+
http: HttpTransport
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
def ship(event)
|
|
19
|
+
return unless enabled?
|
|
20
|
+
return unless shippable_level?(event[:level] || event['level'])
|
|
21
|
+
|
|
22
|
+
redacted = Redactor.redact(event)
|
|
23
|
+
transport = TRANSPORTS[transport_type]
|
|
24
|
+
buffer_event(redacted) if transport
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def flush
|
|
28
|
+
return if @buffer.nil? || @buffer.empty?
|
|
29
|
+
|
|
30
|
+
transport = TRANSPORTS[transport_type]
|
|
31
|
+
return unless transport
|
|
32
|
+
|
|
33
|
+
batch = nil
|
|
34
|
+
@mutex.synchronize do
|
|
35
|
+
batch = @buffer.dup
|
|
36
|
+
@buffer.clear
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
deliver(transport, batch)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def start
|
|
43
|
+
return unless enabled?
|
|
44
|
+
return if @flush_thread&.alive?
|
|
45
|
+
|
|
46
|
+
@buffer = []
|
|
47
|
+
@mutex = Mutex.new
|
|
48
|
+
interval = flush_interval
|
|
49
|
+
@flush_thread = Thread.new do
|
|
50
|
+
loop do
|
|
51
|
+
sleep interval
|
|
52
|
+
flush
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
@flush_thread.abort_on_exception = false
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def stop
|
|
59
|
+
@flush_thread&.kill
|
|
60
|
+
@flush_thread = nil
|
|
61
|
+
flush
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def enabled?
|
|
65
|
+
return false unless defined?(Legion::Settings)
|
|
66
|
+
|
|
67
|
+
Legion::Settings[:logging, :shipper, :enabled] == true
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def buffer_event(event)
|
|
73
|
+
@buffer ||= []
|
|
74
|
+
@mutex ||= Mutex.new
|
|
75
|
+
|
|
76
|
+
full = false
|
|
77
|
+
@mutex.synchronize do
|
|
78
|
+
@buffer << event
|
|
79
|
+
full = @buffer.size >= batch_size
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
flush if full
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def deliver(transport, batch)
|
|
86
|
+
if transport.method(:ship).arity == 1
|
|
87
|
+
# HttpTransport accepts a batch array
|
|
88
|
+
transport.ship(batch)
|
|
89
|
+
else
|
|
90
|
+
batch.each { |e| transport.ship(e) }
|
|
91
|
+
end
|
|
92
|
+
rescue StandardError => e
|
|
93
|
+
Legion::Logging.error("Shipper deliver failed: #{e.message}") if defined?(Legion::Logging)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def shippable_level?(level)
|
|
97
|
+
return true if level.nil?
|
|
98
|
+
|
|
99
|
+
min = minimum_level
|
|
100
|
+
LEVEL_ORDER.index(level.to_s.downcase).to_i >= LEVEL_ORDER.index(min).to_i
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def transport_type
|
|
104
|
+
return :file unless defined?(Legion::Settings)
|
|
105
|
+
|
|
106
|
+
key = Legion::Settings[:logging, :shipper, :transport]
|
|
107
|
+
key ? key.to_sym : :file
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def batch_size
|
|
111
|
+
return 100 unless defined?(Legion::Settings)
|
|
112
|
+
|
|
113
|
+
Legion::Settings[:logging, :shipper, :batch_size] || 100
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def flush_interval
|
|
117
|
+
return 5 unless defined?(Legion::Settings)
|
|
118
|
+
|
|
119
|
+
Legion::Settings[:logging, :shipper, :flush_interval] || 5
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def minimum_level
|
|
123
|
+
return 'warn' unless defined?(Legion::Settings)
|
|
124
|
+
|
|
125
|
+
levels = Legion::Settings[:logging, :shipper, :levels]
|
|
126
|
+
return 'warn' unless levels.is_a?(Array) && !levels.empty?
|
|
127
|
+
|
|
128
|
+
levels.min_by { |l| LEVEL_ORDER.index(l.to_s) || 99 }.to_s
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
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.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -61,6 +61,10 @@ files:
|
|
|
61
61
|
- lib/legion/logging/logger.rb
|
|
62
62
|
- lib/legion/logging/methods.rb
|
|
63
63
|
- lib/legion/logging/multi_io.rb
|
|
64
|
+
- lib/legion/logging/redactor.rb
|
|
65
|
+
- lib/legion/logging/shipper.rb
|
|
66
|
+
- lib/legion/logging/shipper/file_transport.rb
|
|
67
|
+
- lib/legion/logging/shipper/http_transport.rb
|
|
64
68
|
- lib/legion/logging/siem_exporter.rb
|
|
65
69
|
- lib/legion/logging/version.rb
|
|
66
70
|
- sonar-project.properties
|