legion-logging 1.2.5 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c5c68f56d267c3653df2d9a713072d2ab81445f437f64761e23ebd4bc8323e73
4
- data.tar.gz: f78f23e438594a7eab25499a154b27d9fb0d043c54773c72c2f63affb9aa76b6
3
+ metadata.gz: e397ca81de1cb74f71dc9f220e59406a8f8e6cbf2d9aae6092918f428588bc60
4
+ data.tar.gz: e41480464a27679090cc3bbff835c9a9c5c5f2c52fb60edd1096d1c8e7a43031
5
5
  SHA512:
6
- metadata.gz: 05a90fe04d73b187c72bb1d5bdc732ae2b314670306174bf0430f0e996bab0ed7e76fa51209a7dd295c94f5b4bac173a67032de51f5d7d2e89fee5ddcf26c361
7
- data.tar.gz: 2a3b358b39146cb6e7bb6971f5cd7e881a528df16bd8823bea4cfa7cd8877f09bb9d8c57770176274e1a44e09b2731a86ae39354cc1ba4966aa76685b7d36eeb
6
+ metadata.gz: 355eaf1cfe6d31c27360aadc06bdd6a81b6fa07c380ca68c5fd645089fe8ba81521ffd7971c078ed5f124c9e28e73babcbb390fcffcca46f5648ad3ee2839d24
7
+ data.tar.gz: c062f3e91ccc3a322cbbf8a539321207505c7e663623027e7d59354c63ac3eb692b93389ced67be17c7614b1578021e07dc7956ad0f1469e6cd4adb57d262629
data/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
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
+
14
+ ## v1.2.6
15
+
16
+ ### Added
17
+ - `Legion::Logging::Redactor`: PII/PHI redaction module with built-in patterns for SSN, email, phone, MRN, DOB, and credit card numbers
18
+ - Sensitive field-name redaction: fields named `password`, `secret`, `token`, `api_key`, `authorization` are always fully redacted
19
+ - Recursive redaction of nested hashes and arrays
20
+ - Custom pattern support via `Legion::Settings[:logging, :redactor, :custom_patterns]`
21
+ - `Legion::Logging::Shipper`: structured log event forwarding to external collectors with batch buffering and level filtering
22
+ - `Legion::Logging::Shipper::FileTransport`: writes JSON-lines to rotated log files for pickup by Filebeat/Fluentd
23
+ - `Legion::Logging::Shipper::HttpTransport`: POSTs JSON batches to HTTP endpoints (Splunk HEC, ELK Logstash)
24
+ - All SIEM shipping features disabled by default; opt-in via `logging.shipper.enabled: true`
25
+
3
26
  ## v1.2.5
4
27
 
5
28
  ### Fixed
data/CLAUDE.md CHANGED
@@ -8,16 +8,23 @@
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 # Log level methods: debug, info, warn, error, fatal, unknown
18
- ├── Builder # Output destination (stdout/file), log level, formatter
19
- ├── Logger # Core logger configuration and setup
20
- └── Version # VERSION constant
18
+ ├── Methods # Log level methods: debug, info, warn, error, fatal, unknown
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)
22
+ ├── Logger # Core logger configuration and setup
23
+ ├── MultiIO # Write to multiple destinations simultaneously
24
+ ├── SIEMExporter # PHI-redacting SIEM export (Splunk HEC, ELK/OpenSearch)
25
+ ├── Shipper # Buffered log event forwarding (file/http transports)
26
+ ├── Redactor # PII/PHI pattern redaction
27
+ └── Version # VERSION constant
21
28
  ```
22
29
 
23
30
  ### Key Design Patterns
@@ -27,6 +34,10 @@ Legion::Logging (singleton module)
27
34
  - **Setup Method**: `Legion::Logging.setup(log_file:, level:)` configures output destination and level
28
35
  - **Structured JSON**: `format: :json` in settings outputs machine-parseable JSON log lines
29
36
  - **Shared Interface**: Same method signature (`info`, `warn`, `error`, etc.) across all Legion components
37
+ - **MultiIO**: Splits writes to stdout and a log file simultaneously (used by Builder when `log_file` is set)
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.
30
41
 
31
42
  ## Dependencies
32
43
 
@@ -43,6 +54,9 @@ Legion::Logging (singleton module)
43
54
  | `lib/legion/logging/builder.rb` | Output config and formatter |
44
55
  | `lib/legion/logging/logger.rb` | Core logger setup |
45
56
  | `lib/legion/logging/multi_io.rb` | Multi-output IO (write to multiple destinations simultaneously) |
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 |
46
60
  | `lib/legion/logging/version.rb` | VERSION constant |
47
61
 
48
62
  ## Role in LegionIO
data/Gemfile CHANGED
@@ -3,6 +3,9 @@
3
3
  source 'https://rubygems.org'
4
4
 
5
5
  gemspec
6
+
7
+ gem 'logger'
8
+
6
9
  group :test do
7
10
  gem 'rake'
8
11
  gem 'rspec'
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,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
@@ -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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module Logging
5
- VERSION = '1.2.5'
5
+ VERSION = '1.2.7'
6
6
  end
7
7
  end
@@ -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.5
4
+ version: 1.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -58,9 +58,15 @@ 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
66
+ - lib/legion/logging/redactor.rb
67
+ - lib/legion/logging/shipper.rb
68
+ - lib/legion/logging/shipper/file_transport.rb
69
+ - lib/legion/logging/shipper/http_transport.rb
64
70
  - lib/legion/logging/siem_exporter.rb
65
71
  - lib/legion/logging/version.rb
66
72
  - sonar-project.properties