legion-logging 1.5.2 → 1.5.4
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 +13 -0
- data/CLAUDE.md +29 -75
- data/lib/legion/logging/async_writer.rb +3 -1
- data/lib/legion/logging/helper.rb +68 -9
- data/lib/legion/logging/methods.rb +103 -15
- data/lib/legion/logging/settings.rb +5 -4
- data/lib/legion/logging/version.rb +1 -1
- data/lib/legion/logging.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3a7e958bdf135cd4798a6619992fd2661a9864b13dab2133c378d67ccea54380
|
|
4
|
+
data.tar.gz: 280391287ca3a8e15f91979e63bdbb3f4f7bab3c0e71bcc1f47b9a4eac378cb0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 55b8db2c9e50ad4d9d91f10379821c33c0cacdeed46a1da16a23d49835108f65ab9d59305bd3c421219510932d58f684c2816c7b39e39d51dc36ad65396a8afe
|
|
7
|
+
data.tar.gz: c7914b118692f0e2738ab5a68af4290e117b275b320a43a548ae5e85366f6f8cfec5f16f3c611f2b47a9f7e90a5446063f77c97ef3bb98a5e169a6dee177e9ad
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Legion::Logging Changelog
|
|
2
2
|
|
|
3
|
+
## [1.5.4] - 2026-05-22
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Structured log and exception AMQP writer headers now include `legion_protocol_version`, best-effort `x-legion-version`, and transport-standard `x-legion-identity-*` headers when process identity is resolved.
|
|
7
|
+
- `log_writer` now receives the same `headers:` and `properties:` envelope metadata shape as `exception_writer`.
|
|
8
|
+
|
|
9
|
+
## [1.5.3] - 2026-05-13
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- `backtrace_limit:` kwarg on `log_exception` and `handle_exception` (nil=full, 0=suppress, N=cap at N frames)
|
|
13
|
+
- `backtrace_limit` key in `Legion::Logging::Settings.default` (defaults to nil — full backtraces)
|
|
14
|
+
- Settings-driven default reads from `Legion::Settings[:logging][:backtrace_limit]` when no explicit kwarg is passed
|
|
15
|
+
|
|
3
16
|
## [1.5.2] - 2026-04-27
|
|
4
17
|
|
|
5
18
|
### Changed
|
data/CLAUDE.md
CHANGED
|
@@ -1,92 +1,46 @@
|
|
|
1
|
-
# legion-logging
|
|
1
|
+
# legion-logging
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
- **Parent**: `/Users/miverso2/rubymine/legion/CLAUDE.md`
|
|
5
|
-
|
|
6
|
-
## Purpose
|
|
7
|
-
|
|
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.
|
|
3
|
+
Structured logging framework for LegionIO. Provides colorized console output (Rainbow), structured JSON logging, and a consistent interface across all Legion gems and extensions.
|
|
9
4
|
|
|
10
5
|
**GitHub**: https://github.com/LegionIO/legion-logging
|
|
11
|
-
**Version**: 1.5.
|
|
12
|
-
**License**: Apache-2.0
|
|
6
|
+
**Version**: 1.5.3
|
|
13
7
|
|
|
14
8
|
## Architecture
|
|
15
9
|
|
|
16
10
|
```
|
|
17
11
|
Legion::Logging (singleton module)
|
|
18
|
-
├── Methods #
|
|
19
|
-
├── Builder # Output destination
|
|
20
|
-
├── AsyncWriter # Non-blocking SizedQueue-backed writer thread
|
|
21
|
-
├── Hooks # Callback registry for fatal/error/warn events
|
|
22
|
-
├── EventBuilder # Structured event payload
|
|
23
|
-
├── Helper # Injectable log mixin for LEX extensions
|
|
24
|
-
├──
|
|
25
|
-
├──
|
|
26
|
-
├──
|
|
27
|
-
├──
|
|
28
|
-
├──
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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:) {})
|
|
12
|
+
├── Methods # debug, info, warn, error, fatal, unknown; log_exception
|
|
13
|
+
├── Builder # Output destination, log level, formatter, async: keyword
|
|
14
|
+
├── AsyncWriter # Non-blocking SizedQueue-backed writer thread
|
|
15
|
+
├── Hooks # Callback registry for fatal/error/warn events
|
|
16
|
+
├── EventBuilder # Structured event payload + fingerprint for dedup
|
|
17
|
+
├── Helper # Injectable log mixin for LEX extensions
|
|
18
|
+
├── TaggedLogger # Prepends structured tags to each message
|
|
19
|
+
├── CategoryRegistry # Named log categories with expected_fields
|
|
20
|
+
├── SIEMExporter # PHI-redacting SIEM export (Splunk HEC, ELK)
|
|
21
|
+
├── Shipper # Buffered forwarding (FileTransport, HttpTransport)
|
|
22
|
+
├── Redactor # PII/PHI + secret pattern redaction (opt-in)
|
|
23
|
+
└── MultiIO # Write to multiple destinations simultaneously
|
|
24
|
+
|
|
25
|
+
# Module-level writer lambdas (pluggable forwarding slots)
|
|
26
|
+
Legion::Logging.log_writer # ->(event, routing_key:) {}
|
|
27
|
+
Legion::Logging.exception_writer # ->(event, routing_key:, headers:, properties:) {}
|
|
37
28
|
```
|
|
38
29
|
|
|
39
|
-
|
|
40
|
-
|
|
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)
|
|
46
|
-
- **Shared Interface**: Same method signature (`info`, `warn`, `error`, etc.) across all Legion components
|
|
47
|
-
- **MultiIO**: Splits writes to stdout and a log file simultaneously (used by Builder when `log_file` is set)
|
|
48
|
-
- **SIEMExporter**: PHI redaction (SSN, phone, MRN, DOB patterns), `export_to_splunk` (HEC), `format_for_elk`
|
|
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.
|
|
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.
|
|
30
|
+
## Key Patterns
|
|
55
31
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
| Path | Purpose |
|
|
65
|
-
|------|---------|
|
|
66
|
-
| `lib/legion/logging.rb` | Module entry point |
|
|
67
|
-
| `lib/legion/logging/methods.rb` | Log level methods |
|
|
68
|
-
| `lib/legion/logging/builder.rb` | Output config and formatter (async: keyword) |
|
|
69
|
-
| `lib/legion/logging/async_writer.rb` | Non-blocking SizedQueue-backed writer thread with back-pressure |
|
|
70
|
-
| `lib/legion/logging/helper.rb` | Injectable log mixin for LEX extensions |
|
|
71
|
-
| `lib/legion/logging/logger.rb` | Core logger setup |
|
|
72
|
-
| `lib/legion/logging/multi_io.rb` | Multi-output IO (write to multiple destinations simultaneously) |
|
|
73
|
-
| `lib/legion/logging/siem_exporter.rb` | PHI-redacting SIEM export helpers (Splunk HEC, ELK format) |
|
|
74
|
-
| `lib/legion/logging/hooks.rb` | Callback registry (fatal/error/warn hook arrays, enable/disable/clear) |
|
|
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) |
|
|
84
|
-
| `lib/legion/logging/version.rb` | VERSION constant |
|
|
32
|
+
- **Singleton module** — `class << self`; called directly: `Legion::Logging.info("msg")`
|
|
33
|
+
- **Async by default** — `setup` enables async logging; fatal bypasses queue. Buffer size via `Settings[:logging][:async][:buffer_size]` (default 10,000). Back-pressure blocks callers when full.
|
|
34
|
+
- **Structured JSON** — `format: :json` outputs machine-parseable JSON lines (disables color)
|
|
35
|
+
- **Helper mixin** — `Legion::Logging::Helper` injects into LEX extensions; derives tags from `segments`, `lex_filename`, or class name
|
|
36
|
+
- **Writer lambdas** — `log_writer` and `exception_writer` are module-level lambda slots for forwarding to external systems (AMQP, etc.). Default no-ops.
|
|
37
|
+
- **Redactor** — Opt-in (`logging.redaction.enabled: true`). Patterns: SSN, phone, MRN, DOB, Vault tokens, JWTs, bearer tokens, lease IDs. Guards against Settings recursive init.
|
|
38
|
+
- **Hook callbacks** — `on_fatal`, `on_error`, `on_warn` register procs; gated by `enable_hooks!`/`disable_hooks!`; fire on async writer thread
|
|
39
|
+
- **EventBuilder** — Structured event hashes from log context (caller, exception, lex identity). `fingerprint` produces MD5 for dedup.
|
|
85
40
|
|
|
86
41
|
## Role in LegionIO
|
|
87
42
|
|
|
88
|
-
|
|
43
|
+
Foundational gem — dependency of `legion-cache`, `legion-data`, and `LegionIO`. First module initialized during `Legion::Service` startup.
|
|
89
44
|
|
|
90
45
|
---
|
|
91
|
-
|
|
92
46
|
**Maintained By**: Matthew Iverson (@Esity)
|
|
@@ -110,7 +110,9 @@ module Legion
|
|
|
110
110
|
lex_name = event[:lex] || 'core'
|
|
111
111
|
component = event.dig(:caller, :file).to_s[Legion::Logging::Methods::COMPONENT_REGEX, 1] || 'unknown'
|
|
112
112
|
routing_key = "legion.logging.log.#{level}.#{lex_name}.#{component}"
|
|
113
|
-
Legion::Logging.
|
|
113
|
+
headers = Legion::Logging.send(:build_log_headers, event, component, level)
|
|
114
|
+
properties = Legion::Logging.send(:build_log_properties, level)
|
|
115
|
+
Legion::Logging.log_writer.call(event, routing_key: routing_key, headers: headers, properties: properties)
|
|
114
116
|
Legion::Logging::Hooks.fire(level, entry.message, event) if defined?(Legion::Logging::Hooks)
|
|
115
117
|
rescue StandardError => e
|
|
116
118
|
warn("legion-log-writer writer error: #{e.message}")
|
|
@@ -487,8 +487,18 @@ module Legion
|
|
|
487
487
|
context_line = build_context_line(event)
|
|
488
488
|
lines << " #{context_line}" unless context_line.empty?
|
|
489
489
|
|
|
490
|
-
|
|
491
|
-
|
|
490
|
+
max_frames = if event[:backtrace_limit].nil?
|
|
491
|
+
defined?(Legion::Settings) ? Legion::Settings[:logging][:backtrace_limit] : nil
|
|
492
|
+
else
|
|
493
|
+
event[:backtrace_limit]
|
|
494
|
+
end
|
|
495
|
+
unless max_frames&.zero?
|
|
496
|
+
bt = exception.backtrace
|
|
497
|
+
if bt&.any?
|
|
498
|
+
frames = max_frames ? bt.first(max_frames) : bt
|
|
499
|
+
frames.each { |frame| lines << " #{frame}" }
|
|
500
|
+
end
|
|
501
|
+
end
|
|
492
502
|
|
|
493
503
|
lines.join("\n")
|
|
494
504
|
end
|
|
@@ -549,21 +559,70 @@ module Legion
|
|
|
549
559
|
|
|
550
560
|
def build_exception_headers(event, comp, level)
|
|
551
561
|
headers = {
|
|
552
|
-
'
|
|
553
|
-
'x-
|
|
554
|
-
'x-
|
|
555
|
-
'x-
|
|
556
|
-
'x-
|
|
557
|
-
'x-
|
|
558
|
-
'x-
|
|
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
|
|
559
570
|
}
|
|
571
|
+
append_legion_version_header(headers)
|
|
560
572
|
headers['x-task-id'] = event[:task_id].to_s if event[:task_id]
|
|
561
573
|
headers['x-conversation-id'] = event[:conversation_id].to_s if event[:conversation_id]
|
|
562
574
|
headers['x-chain-id'] = event[:chain_id].to_s if event[:chain_id]
|
|
563
575
|
headers['x-user'] = event[:user].to_s if event[:user]
|
|
576
|
+
append_identity_headers(headers)
|
|
564
577
|
headers
|
|
565
578
|
end
|
|
566
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
|
+
|
|
567
626
|
def build_exception_properties(event, level)
|
|
568
627
|
{
|
|
569
628
|
content_type: 'application/json',
|
|
@@ -102,17 +102,12 @@ module Legion
|
|
|
102
102
|
def log_exception(exception, level: :error, lex: nil, component_type: nil,
|
|
103
103
|
gem_name: nil, lex_version: nil, gem_path: nil,
|
|
104
104
|
source_code_uri: nil, handled: false, payload_summary: nil,
|
|
105
|
-
task_id: nil, **extra)
|
|
105
|
+
task_id: nil, backtrace_limit: nil, **extra)
|
|
106
106
|
level = level.to_sym if level.respond_to?(:to_sym)
|
|
107
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
|
-
|
|
111
|
-
if bt.any?
|
|
112
|
-
lines = ["#{exception.class}: #{msg}"]
|
|
113
|
-
bt.each { |frame| lines << " #{frame}" }
|
|
114
|
-
msg = lines.join("\n")
|
|
115
|
-
end
|
|
110
|
+
msg = build_exception_log_message(exception, msg, backtrace_limit)
|
|
116
111
|
log.public_send(level, msg) if respond_to?(:log) && log.respond_to?(level)
|
|
117
112
|
|
|
118
113
|
# 2. Build rich exception event
|
|
@@ -155,6 +150,30 @@ module Legion
|
|
|
155
150
|
|
|
156
151
|
private
|
|
157
152
|
|
|
153
|
+
def resolve_backtrace_limit(explicit_limit)
|
|
154
|
+
return explicit_limit unless explicit_limit.nil?
|
|
155
|
+
return nil unless defined?(Legion::Settings)
|
|
156
|
+
|
|
157
|
+
Legion::Settings[:logging][:backtrace_limit]
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def build_exception_log_message(exception, msg, backtrace_limit)
|
|
161
|
+
max_frames = resolve_backtrace_limit(backtrace_limit)
|
|
162
|
+
bt = collect_backtrace_frames(exception, max_frames)
|
|
163
|
+
return msg unless bt.any?
|
|
164
|
+
|
|
165
|
+
lines = ["#{exception.class}: #{msg}"]
|
|
166
|
+
bt.each { |frame| lines << " #{frame}" }
|
|
167
|
+
lines.join("\n")
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def collect_backtrace_frames(exception, max_frames)
|
|
171
|
+
return [] if max_frames&.zero?
|
|
172
|
+
|
|
173
|
+
frames = Array(exception.backtrace)
|
|
174
|
+
max_frames ? frames.first(max_frames) : frames
|
|
175
|
+
end
|
|
176
|
+
|
|
158
177
|
def maybe_redact(message)
|
|
159
178
|
return message unless message.is_a?(String)
|
|
160
179
|
return message unless redaction_enabled?
|
|
@@ -251,6 +270,48 @@ module Legion
|
|
|
251
270
|
false
|
|
252
271
|
end
|
|
253
272
|
|
|
273
|
+
def build_log_headers(event, component, level)
|
|
274
|
+
headers = {
|
|
275
|
+
'legion_protocol_version' => '2.0',
|
|
276
|
+
'x-component-type' => component.to_s,
|
|
277
|
+
'x-level' => level.to_s
|
|
278
|
+
}
|
|
279
|
+
append_legion_version_header(headers)
|
|
280
|
+
append_optional_header(headers, 'x-lex', event[:lex])
|
|
281
|
+
append_optional_header(headers, 'x-node', event[:node])
|
|
282
|
+
append_identity_headers(headers)
|
|
283
|
+
headers
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def build_log_properties(level)
|
|
287
|
+
{
|
|
288
|
+
content_type: 'application/json',
|
|
289
|
+
message_id: SecureRandom.uuid,
|
|
290
|
+
timestamp: Time.now.to_i,
|
|
291
|
+
app_id: 'legionio',
|
|
292
|
+
type: 'log_event',
|
|
293
|
+
priority: EXCEPTION_PRIORITY[level] || 0,
|
|
294
|
+
delivery_mode: 2
|
|
295
|
+
}
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def append_identity_headers(headers)
|
|
299
|
+
return unless defined?(Legion::Identity::Process)
|
|
300
|
+
return if Legion::Identity::Process.respond_to?(:resolved?) && !Legion::Identity::Process.resolved?
|
|
301
|
+
|
|
302
|
+
id = identity_hash
|
|
303
|
+
append_optional_header(headers, 'x-legion-identity-canonical-name', id[:canonical_name])
|
|
304
|
+
append_optional_header(headers, 'x-legion-identity-trust', id[:trust])
|
|
305
|
+
append_optional_header(headers, 'x-legion-identity-id', id[:id])
|
|
306
|
+
append_optional_header(headers, 'x-legion-identity-kind', id[:kind])
|
|
307
|
+
append_optional_header(headers, 'x-legion-identity-mode', id[:mode])
|
|
308
|
+
append_optional_header(headers, 'x-legion-identity-source', id[:source])
|
|
309
|
+
headers['x-legion-identity-db-principal-id'] = id[:db_principal_id] if id[:db_principal_id]
|
|
310
|
+
headers['x-legion-identity-db-identity-id'] = id[:db_identity_id] if id[:db_identity_id]
|
|
311
|
+
rescue StandardError
|
|
312
|
+
nil
|
|
313
|
+
end
|
|
314
|
+
|
|
254
315
|
def publish_exception_event(event, level)
|
|
255
316
|
lex_name = event[:lex] || 'core'
|
|
256
317
|
comp = event[:component_type] || :unknown
|
|
@@ -262,17 +323,20 @@ module Legion
|
|
|
262
323
|
|
|
263
324
|
def build_exception_headers(event, comp, level)
|
|
264
325
|
headers = {
|
|
265
|
-
'
|
|
266
|
-
'x-
|
|
267
|
-
'x-
|
|
268
|
-
'x-
|
|
269
|
-
'x-
|
|
270
|
-
'x-
|
|
271
|
-
'x-
|
|
326
|
+
'legion_protocol_version' => '2.0',
|
|
327
|
+
'x-error-fingerprint' => event[:error_fingerprint],
|
|
328
|
+
'x-exception-class' => event[:exception_class],
|
|
329
|
+
'x-handled' => event[:handled].to_s,
|
|
330
|
+
'x-gem-name' => event[:gem_name].to_s,
|
|
331
|
+
'x-lex-version' => event[:lex_version].to_s,
|
|
332
|
+
'x-component-type' => comp.to_s,
|
|
333
|
+
'x-level' => level.to_s
|
|
272
334
|
}
|
|
335
|
+
append_legion_version_header(headers)
|
|
273
336
|
append_optional_header(headers, 'x-task-id', event[:task_id])
|
|
274
337
|
append_optional_header(headers, 'x-conversation-id', event[:conversation_id])
|
|
275
338
|
append_optional_header(headers, 'x-user', event[:user])
|
|
339
|
+
append_identity_headers(headers)
|
|
276
340
|
headers
|
|
277
341
|
end
|
|
278
342
|
|
|
@@ -283,6 +347,28 @@ module Legion
|
|
|
283
347
|
headers[key] = value.to_s
|
|
284
348
|
end
|
|
285
349
|
|
|
350
|
+
def append_legion_version_header(headers)
|
|
351
|
+
append_optional_header(headers, 'x-legion-version', Legion::VERSION) if defined?(Legion::VERSION)
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def identity_hash
|
|
355
|
+
process = Legion::Identity::Process
|
|
356
|
+
return process.identity_hash if process.respond_to?(:identity_hash)
|
|
357
|
+
|
|
358
|
+
{
|
|
359
|
+
canonical_name: identity_value(process, :canonical_name),
|
|
360
|
+
id: identity_value(process, :id),
|
|
361
|
+
kind: identity_value(process, :kind),
|
|
362
|
+
mode: identity_value(process, :mode),
|
|
363
|
+
source: identity_value(process, :source),
|
|
364
|
+
trust: identity_value(process, :trust)
|
|
365
|
+
}
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def identity_value(process, method_name)
|
|
369
|
+
process.public_send(method_name) if process.respond_to?(method_name)
|
|
370
|
+
end
|
|
371
|
+
|
|
286
372
|
def build_exception_properties(event, level)
|
|
287
373
|
{
|
|
288
374
|
content_type: 'application/json',
|
|
@@ -328,7 +414,9 @@ module Legion
|
|
|
328
414
|
lex_name = event[:lex] || 'core'
|
|
329
415
|
component = event.dig(:caller, :file).to_s[COMPONENT_REGEX, 1] || 'unknown'
|
|
330
416
|
routing_key = "legion.logging.log.#{level}.#{lex_name}.#{component}"
|
|
331
|
-
|
|
417
|
+
headers = build_log_headers(event, component, level)
|
|
418
|
+
properties = build_log_properties(level)
|
|
419
|
+
Legion::Logging.log_writer.call(event, routing_key: routing_key, headers: headers, properties: properties)
|
|
332
420
|
Legion::Logging::Hooks.fire(level, message, event) if defined?(Legion::Logging::Hooks)
|
|
333
421
|
rescue StandardError => e
|
|
334
422
|
rk = defined?(routing_key) ? routing_key : 'unknown'
|
data/lib/legion/logging.rb
CHANGED
|
@@ -25,7 +25,7 @@ module Legion
|
|
|
25
25
|
attr_reader :color
|
|
26
26
|
attr_writer :log_writer, :exception_writer
|
|
27
27
|
|
|
28
|
-
DEFAULT_LOG_WRITER = ->(_event, routing_key:) {}
|
|
28
|
+
DEFAULT_LOG_WRITER = ->(_event, routing_key:, headers: nil, properties: nil) {}
|
|
29
29
|
DEFAULT_EXCEPTION_WRITER = ->(_event, routing_key:, headers:, properties:) {}
|
|
30
30
|
|
|
31
31
|
def log_writer
|