e11y 0.2.0 → 1.0.0
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/.rubocop.yml +130 -10
- data/CHANGELOG.md +56 -1
- data/CLAUDE.md +168 -0
- data/CONTRIBUTING.md +640 -0
- data/README.md +134 -702
- data/RELEASE.md +18 -3
- data/Rakefile +108 -29
- data/config/README.md +1 -1
- data/config/loki-local-config.yaml +12 -0
- data/config/otel-collector-config.yaml +44 -0
- data/cucumber.yml +1 -0
- data/docker-compose.yml +18 -2
- data/docs/ADAPTERS.md +76 -0
- data/docs/ADAPTIVE_SAMPLING.md +59 -0
- data/docs/COMPARISON.md +104 -0
- data/docs/CONFIGURATION.md +52 -0
- data/docs/DISTRIBUTED_TRACING.md +44 -0
- data/docs/LIMITATIONS.md +13 -0
- data/docs/METRICS_DSL.md +84 -0
- data/docs/PERFORMANCE.md +60 -0
- data/docs/PII_FILTERING.md +40 -0
- data/docs/PRESETS.md +65 -0
- data/docs/QUICK-START.md +546 -587
- data/docs/RAILS_INTEGRATION.md +29 -0
- data/docs/SCHEMA_VALIDATION.md +63 -0
- data/docs/SLO-PROMQL-ALERTS.md +161 -0
- data/docs/TESTING.md +69 -0
- data/docs/{ADR-001-architecture.md → architecture/ADR-001-architecture.md} +35 -64
- data/docs/{ADR-002-metrics-yabeda.md → architecture/ADR-002-metrics-yabeda.md} +62 -236
- data/docs/{ADR-003-slo-observability.md → architecture/ADR-003-slo-observability.md} +27 -466
- data/docs/{ADR-004-adapter-architecture.md → architecture/ADR-004-adapter-architecture.md} +163 -146
- data/docs/{ADR-005-tracing-context.md → architecture/ADR-005-tracing-context.md} +10 -9
- data/docs/{ADR-006-security-compliance.md → architecture/ADR-006-security-compliance.md} +184 -191
- data/docs/{ADR-007-opentelemetry-integration.md → architecture/ADR-007-opentelemetry-integration.md} +3 -21
- data/docs/{ADR-008-rails-integration.md → architecture/ADR-008-rails-integration.md} +209 -339
- data/docs/{ADR-009-cost-optimization.md → architecture/ADR-009-cost-optimization.md} +45 -54
- data/docs/architecture/ADR-010-developer-experience.md +522 -0
- data/docs/{ADR-011-testing-strategy.md → architecture/ADR-011-testing-strategy.md} +41 -83
- data/docs/{ADR-013-reliability-error-handling.md → architecture/ADR-013-reliability-error-handling.md} +37 -12
- data/docs/{ADR-014-event-driven-slo.md → architecture/ADR-014-event-driven-slo.md} +12 -24
- data/docs/{ADR-015-middleware-order.md → architecture/ADR-015-middleware-order.md} +23 -41
- data/docs/{ADR-016-self-monitoring-slo.md → architecture/ADR-016-self-monitoring-slo.md} +52 -349
- data/docs/{ADR-017-multi-rails-compatibility.md → architecture/ADR-017-multi-rails-compatibility.md} +4 -11
- data/docs/architecture/ADR-018-memory-optimization.md +366 -0
- data/docs/{ADR-INDEX.md → architecture/ADR-INDEX.md} +11 -6
- data/docs/{00-ICP-AND-TIMELINE.md → prd/00-ICP-AND-TIMELINE.md} +6 -6
- data/docs/{01-SCALE-REQUIREMENTS.md → prd/01-SCALE-REQUIREMENTS.md} +6 -6
- data/docs/prd/01-overview-vision.md +19 -14
- data/docs/use_cases/README.md +22 -23
- data/docs/use_cases/UC-001-request-scoped-debug-buffering.md +50 -44
- data/docs/use_cases/UC-002-business-event-tracking.md +26 -95
- data/docs/use_cases/UC-003-event-metrics.md +66 -0
- data/docs/use_cases/UC-004-zero-config-slo-tracking.md +42 -101
- data/docs/use_cases/UC-005-sentry-integration.md +13 -15
- data/docs/use_cases/UC-006-trace-context-management.md +30 -28
- data/docs/use_cases/UC-007-pii-filtering.md +35 -87
- data/docs/use_cases/UC-008-opentelemetry-integration.md +51 -89
- data/docs/use_cases/UC-009-multi-service-tracing.md +4 -4
- data/docs/use_cases/UC-010-background-job-tracking.md +5 -5
- data/docs/use_cases/UC-011-rate-limiting.md +95 -168
- data/docs/use_cases/UC-012-audit-trail.md +21 -46
- data/docs/use_cases/UC-013-high-cardinality-protection.md +29 -167
- data/docs/use_cases/UC-014-adaptive-sampling.md +2 -2
- data/docs/use_cases/UC-015-cost-optimization.md +46 -99
- data/docs/use_cases/UC-016-rails-logger-migration.md +39 -213
- data/docs/use_cases/UC-017-local-development.md +203 -777
- data/docs/use_cases/UC-018-testing-events.md +3 -3
- data/docs/use_cases/UC-019-retention-based-routing.md +53 -106
- data/docs/use_cases/UC-020-event-versioning.md +8 -9
- data/docs/use_cases/UC-021-error-handling-retry-dlq.md +18 -22
- data/docs/use_cases/UC-022-event-registry.md +15 -21
- data/docs/use_cases/backlog.md +119 -87
- data/e11y.gemspec +2 -2
- data/gems/e11y-devtools/README.md +136 -0
- data/gems/e11y-devtools/config/routes.rb +8 -0
- data/gems/e11y-devtools/e11y-devtools.gemspec +25 -0
- data/gems/e11y-devtools/exe/e11y +34 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/server.rb +96 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tool_base.rb +25 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/clear.rb +31 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/errors.rb +35 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/event_detail.rb +33 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/events_by_trace.rb +33 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/interactions.rb +40 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/recent_events.rb +34 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/search.rb +34 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/stats.rb +30 -0
- data/gems/e11y-devtools/lib/e11y/devtools/overlay/assets/overlay.js +115 -0
- data/gems/e11y-devtools/lib/e11y/devtools/overlay/controller.rb +54 -0
- data/gems/e11y-devtools/lib/e11y/devtools/overlay/engine.rb +26 -0
- data/gems/e11y-devtools/lib/e11y/devtools/overlay/middleware.rb +80 -0
- data/gems/e11y-devtools/lib/e11y/devtools/overlay/rails_controller.rb +42 -0
- data/gems/e11y-devtools/lib/e11y/devtools/tui/app.rb +262 -0
- data/gems/e11y-devtools/lib/e11y/devtools/tui/grouping.rb +66 -0
- data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/event_detail.rb +62 -0
- data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/event_list.rb +70 -0
- data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/interaction_list.rb +47 -0
- data/gems/e11y-devtools/lib/e11y/devtools/version.rb +8 -0
- data/gems/e11y-devtools/lib/e11y/devtools.rb +13 -0
- data/gems/e11y-devtools/spec/e11y/devtools/mcp/tools_spec.rb +107 -0
- data/gems/e11y-devtools/spec/e11y/devtools/overlay/controller_spec.rb +58 -0
- data/gems/e11y-devtools/spec/e11y/devtools/overlay/middleware_spec.rb +46 -0
- data/gems/e11y-devtools/spec/e11y/devtools/tui/app_spec.rb +85 -0
- data/gems/e11y-devtools/spec/e11y/devtools/tui/grouping_spec.rb +64 -0
- data/gems/e11y-devtools/spec/spec_helper.rb +5 -0
- data/gems/e11y-devtools/spec/tui/widgets/event_list_spec.rb +44 -0
- data/gems/e11y-devtools/spec/tui/widgets/interaction_list_spec.rb +62 -0
- data/lib/e11y/adapters/audit_encrypted.rb +53 -11
- data/lib/e11y/adapters/base.rb +33 -34
- data/lib/e11y/adapters/dev_log/file_store.rb +143 -0
- data/lib/e11y/adapters/dev_log/query.rb +219 -0
- data/lib/e11y/adapters/dev_log.rb +118 -0
- data/lib/e11y/adapters/file.rb +3 -6
- data/lib/e11y/adapters/in_memory.rb +52 -5
- data/lib/e11y/adapters/in_memory_test.rb +29 -0
- data/lib/e11y/adapters/loki.rb +58 -23
- data/lib/e11y/adapters/null.rb +82 -0
- data/lib/e11y/adapters/opentelemetry_collector.rb +183 -0
- data/lib/e11y/adapters/otel_logs.rb +136 -23
- data/lib/e11y/adapters/sentry.rb +4 -7
- data/lib/e11y/adapters/stdout.rb +73 -7
- data/lib/e11y/adapters/yabeda.rb +153 -29
- data/lib/e11y/buffers/adaptive_buffer.rb +3 -17
- data/lib/e11y/buffers/{request_scoped_buffer.rb → ephemeral_buffer.rb} +72 -58
- data/lib/e11y/buffers/ring_buffer.rb +3 -16
- data/lib/e11y/configuration.rb +272 -0
- data/lib/e11y/console.rb +10 -17
- data/lib/e11y/current.rb +53 -1
- data/lib/e11y/debug/pipeline_inspector.rb +96 -0
- data/lib/e11y/documentation/generator.rb +48 -0
- data/lib/e11y/event/base.rb +176 -82
- data/lib/e11y/event/value_sampling_config.rb +1 -5
- data/lib/e11y/events/rails/database/query.rb +1 -4
- data/lib/e11y/events/rails/job/failed.rb +2 -0
- data/lib/e11y/instruments/active_job.rb +46 -12
- data/lib/e11y/instruments/rails_instrumentation.rb +49 -24
- data/lib/e11y/instruments/sidekiq.rb +137 -31
- data/lib/e11y/linters/base.rb +11 -0
- data/lib/e11y/linters/pii/pii_declaration_linter.rb +120 -0
- data/lib/e11y/linters/slo/config_consistency_linter.rb +76 -0
- data/lib/e11y/linters/slo/explicit_declaration_linter.rb +36 -0
- data/lib/e11y/linters/slo/slo_status_from_linter.rb +41 -0
- data/lib/e11y/logger/bridge.rb +26 -7
- data/lib/e11y/metrics/cardinality_protection.rb +10 -15
- data/lib/e11y/metrics/cardinality_tracker.rb +16 -6
- data/lib/e11y/metrics/registry.rb +3 -5
- data/lib/e11y/metrics/test_backend.rb +62 -0
- data/lib/e11y/metrics.rb +56 -10
- data/lib/e11y/middleware/adapter_resolver.rb +40 -0
- data/lib/e11y/middleware/audit_signing.rb +43 -6
- data/lib/e11y/middleware/baggage_protection.rb +75 -0
- data/lib/e11y/middleware/dev_log_source.rb +24 -0
- data/lib/e11y/middleware/event_slo.rb +23 -9
- data/lib/e11y/middleware/otel_span.rb +23 -0
- data/lib/e11y/middleware/pii_filter.rb +104 -75
- data/lib/e11y/middleware/rate_limiting.rb +54 -27
- data/lib/e11y/middleware/request.rb +70 -23
- data/lib/e11y/middleware/routing.rb +78 -21
- data/lib/e11y/middleware/sampling.rb +66 -17
- data/lib/e11y/middleware/self_monitoring_emit.rb +39 -0
- data/lib/e11y/middleware/trace_context.rb +45 -10
- data/lib/e11y/middleware/track_latency.rb +34 -0
- data/lib/e11y/middleware/validation.rb +7 -16
- data/lib/e11y/middleware/versioning.rb +26 -22
- data/lib/e11y/opentelemetry/semantic_conventions.rb +109 -0
- data/lib/e11y/opentelemetry/span_creator.rb +142 -0
- data/lib/e11y/pii/patterns.rb +12 -1
- data/lib/e11y/pipeline/builder.rb +1 -1
- data/lib/e11y/presets/audit_event.rb +13 -2
- data/lib/e11y/railtie.rb +52 -15
- data/lib/e11y/registry.rb +306 -0
- data/lib/e11y/reliability/circuit_breaker.rb +19 -21
- data/lib/e11y/reliability/dlq/base.rb +71 -0
- data/lib/e11y/reliability/dlq/file_adapter.rb +301 -0
- data/lib/e11y/reliability/dlq/file_storage.rb +63 -34
- data/lib/e11y/reliability/dlq/filter.rb +37 -54
- data/lib/e11y/reliability/retry_handler.rb +26 -29
- data/lib/e11y/reliability/retry_rate_limiter.rb +3 -11
- data/lib/e11y/sampling/error_spike_detector.rb +0 -2
- data/lib/e11y/sampling/load_monitor.rb +5 -9
- data/lib/e11y/sampling/stratified_tracker.rb +18 -0
- data/lib/e11y/self_monitoring/buffer_monitor.rb +2 -0
- data/lib/e11y/self_monitoring/performance_monitor.rb +19 -61
- data/lib/e11y/self_monitoring/reliability_monitor.rb +4 -74
- data/lib/e11y/slo/config_loader.rb +40 -0
- data/lib/e11y/slo/config_validator.rb +58 -0
- data/lib/e11y/slo/dashboard_generator.rb +122 -0
- data/lib/e11y/slo/event_driven.rb +8 -0
- data/lib/e11y/slo/tracker.rb +31 -4
- data/lib/e11y/testing/have_tracked_event_matcher.rb +190 -0
- data/lib/e11y/testing/rspec_matchers.rb +21 -0
- data/lib/e11y/testing/snapshot_matcher.rb +86 -0
- data/lib/e11y/trace_context/sampler.rb +35 -0
- data/lib/e11y/tracing/faraday_middleware.rb +31 -0
- data/lib/e11y/tracing/net_http_patch.rb +33 -0
- data/lib/e11y/tracing/propagator.rb +116 -0
- data/lib/e11y/tracing.rb +47 -0
- data/lib/e11y/version.rb +1 -1
- data/lib/e11y/versioning/version_extractor.rb +32 -0
- data/lib/e11y.rb +141 -265
- data/lib/generators/e11y/event/event_generator.rb +22 -0
- data/lib/generators/e11y/event/templates/event.rb.tt +16 -0
- data/lib/generators/e11y/grafana_dashboard/grafana_dashboard_generator.rb +30 -0
- data/lib/generators/e11y/grafana_dashboard/templates/e11y_dashboard.json +81 -0
- data/lib/generators/e11y/install/install_generator.rb +34 -0
- data/lib/generators/e11y/install/templates/e11y.rb +239 -0
- data/lib/generators/e11y/prometheus_alerts/prometheus_alerts_generator.rb +29 -0
- data/lib/generators/e11y/prometheus_alerts/templates/e11y_alerts.yml +28 -0
- data/lib/tasks/e11y_docs.rake +30 -0
- data/lib/tasks/e11y_events.rake +71 -0
- data/lib/tasks/e11y_lint.rake +91 -0
- data/lib/tasks/e11y_slo.rake +29 -0
- metadata +129 -39
- data/docs/ADR-010-developer-experience.md +0 -2166
- data/docs/API-REFERENCE-L28.md +0 -914
- data/docs/COMPREHENSIVE-CONFIGURATION.md +0 -2366
- data/docs/CONTRIBUTING.md +0 -312
- data/docs/IMPLEMENTATION_NOTES.md +0 -2804
- data/docs/IMPLEMENTATION_PLAN.md +0 -1971
- data/docs/IMPLEMENTATION_PLAN_ARCHITECTURE.md +0 -586
- data/docs/PLAN.md +0 -148
- data/docs/README.md +0 -296
- data/docs/design/00-memory-optimization.md +0 -593
- data/docs/guides/MIGRATION-L27-L28.md +0 -692
- data/docs/guides/PERFORMANCE-BENCHMARKS.md +0 -434
- data/docs/guides/README.md +0 -44
- data/docs/use_cases/UC-003-pattern-based-metrics.md +0 -1627
- data/lib/e11y/adapters/registry.rb +0 -141
- /data/docs/{ADR-012-event-evolution.md → architecture/ADR-012-event-evolution.md} +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module E11y
|
|
4
4
|
module Buffers
|
|
5
|
-
#
|
|
5
|
+
# Ephemeral buffer (request/job-scoped) using thread-local storage for debug event buffering.
|
|
6
6
|
#
|
|
7
7
|
# This buffer stores debug events in thread-local storage during request processing.
|
|
8
8
|
# Events are flushed only when an error occurs, keeping logs clean during successful requests.
|
|
@@ -12,21 +12,21 @@ module E11y
|
|
|
12
12
|
#
|
|
13
13
|
# @example Basic usage
|
|
14
14
|
# # In Rails middleware
|
|
15
|
-
#
|
|
15
|
+
# EphemeralBuffer.initialize!
|
|
16
16
|
#
|
|
17
17
|
# # Track debug events (buffered)
|
|
18
|
-
#
|
|
18
|
+
# EphemeralBuffer.add_event({ event_name: "debug", severity: :debug })
|
|
19
19
|
#
|
|
20
20
|
# # On error - flush all buffered events
|
|
21
|
-
#
|
|
21
|
+
# EphemeralBuffer.flush_on_error
|
|
22
22
|
#
|
|
23
23
|
# # On success - discard buffered events
|
|
24
|
-
#
|
|
24
|
+
# EphemeralBuffer.discard
|
|
25
25
|
#
|
|
26
26
|
# @see UC-001 Request-Scoped Debug Buffering
|
|
27
|
-
class
|
|
27
|
+
class EphemeralBuffer
|
|
28
28
|
# Thread-local storage keys
|
|
29
|
-
THREAD_KEY_BUFFER = :
|
|
29
|
+
THREAD_KEY_BUFFER = :e11y_ephemeral_buffer
|
|
30
30
|
THREAD_KEY_REQUEST_ID = :e11y_request_id
|
|
31
31
|
THREAD_KEY_ERROR_OCCURRED = :e11y_error_occurred
|
|
32
32
|
THREAD_KEY_BUFFER_LIMIT = :e11y_buffer_limit
|
|
@@ -42,7 +42,7 @@ module E11y
|
|
|
42
42
|
# @return [void]
|
|
43
43
|
#
|
|
44
44
|
# @example
|
|
45
|
-
#
|
|
45
|
+
# EphemeralBuffer.initialize!(request_id: "req-123", buffer_limit: 200)
|
|
46
46
|
def initialize!(request_id: nil, buffer_limit: DEFAULT_BUFFER_LIMIT)
|
|
47
47
|
Thread.current[THREAD_KEY_BUFFER] = []
|
|
48
48
|
Thread.current[THREAD_KEY_REQUEST_ID] = request_id || generate_request_id
|
|
@@ -60,42 +60,19 @@ module E11y
|
|
|
60
60
|
#
|
|
61
61
|
# @example
|
|
62
62
|
# # Debug event - buffered
|
|
63
|
-
#
|
|
63
|
+
# EphemeralBuffer.add_event({ event_name: "test", severity: :debug })
|
|
64
64
|
# # => true
|
|
65
65
|
#
|
|
66
66
|
# # Error event - not buffered, triggers flush
|
|
67
|
-
#
|
|
67
|
+
# EphemeralBuffer.add_event({ event_name: "error", severity: :error })
|
|
68
68
|
# # => false (and flushes buffer)
|
|
69
|
-
# rubocop:disable Metrics/MethodLength, Naming/PredicateMethod
|
|
70
69
|
def add_event(event_data)
|
|
71
|
-
return false unless active?
|
|
70
|
+
return false unless active?
|
|
71
|
+
return handle_error_event(event_data) if error_severity?(event_data[:severity])
|
|
72
|
+
return false unless event_data[:severity] == :debug
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
# Trigger flush on error severity
|
|
76
|
-
if error_severity?(severity)
|
|
77
|
-
Thread.current[THREAD_KEY_ERROR_OCCURRED] = true
|
|
78
|
-
flush_on_error
|
|
79
|
-
return false # Error events not buffered
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# Only buffer debug events
|
|
83
|
-
return false unless severity == :debug
|
|
84
|
-
|
|
85
|
-
current_buffer = buffer
|
|
86
|
-
return false if current_buffer.nil?
|
|
87
|
-
|
|
88
|
-
# Check buffer limit
|
|
89
|
-
if current_buffer.size >= buffer_limit
|
|
90
|
-
increment_metric("e11y.request_buffer.overflow")
|
|
91
|
-
return false # Buffer full, drop event
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
current_buffer << event_data
|
|
95
|
-
increment_metric("e11y.request_buffer.events_buffered")
|
|
96
|
-
true
|
|
74
|
+
append_to_buffer(event_data)
|
|
97
75
|
end
|
|
98
|
-
# rubocop:enable Metrics/MethodLength, Naming/PredicateMethod
|
|
99
76
|
|
|
100
77
|
# Flush buffered events on error
|
|
101
78
|
#
|
|
@@ -108,7 +85,7 @@ module E11y
|
|
|
108
85
|
# @example
|
|
109
86
|
# # In rescue block
|
|
110
87
|
# rescue StandardError => e
|
|
111
|
-
#
|
|
88
|
+
# EphemeralBuffer.flush_on_error
|
|
112
89
|
# raise
|
|
113
90
|
# end
|
|
114
91
|
def flush_on_error(target: nil)
|
|
@@ -117,15 +94,18 @@ module E11y
|
|
|
117
94
|
|
|
118
95
|
flushed_count = current_buffer.size
|
|
119
96
|
|
|
97
|
+
# Resolve flush targets once per flush (avoids N config lookups when flushing N events)
|
|
98
|
+
flush_targets = resolve_flush_targets
|
|
99
|
+
|
|
120
100
|
# Flush events to main buffer/adapters
|
|
121
101
|
current_buffer.each do |event_data|
|
|
122
102
|
# TODO: Send to E11y::Collector.collect(event_data) when available
|
|
123
103
|
# For now, placeholder
|
|
124
|
-
flush_event(event_data, target: target)
|
|
104
|
+
flush_event(event_data, target: target, flush_targets: flush_targets)
|
|
125
105
|
end
|
|
126
106
|
|
|
127
107
|
current_buffer.clear
|
|
128
|
-
|
|
108
|
+
E11y::Metrics.increment(:e11y_ephemeral_buffer_total, event: "flushed_on_error", value: flushed_count)
|
|
129
109
|
flushed_count
|
|
130
110
|
end
|
|
131
111
|
|
|
@@ -135,8 +115,8 @@ module E11y
|
|
|
135
115
|
#
|
|
136
116
|
# @example
|
|
137
117
|
# # In middleware ensure block (success path)
|
|
138
|
-
# unless
|
|
139
|
-
#
|
|
118
|
+
# unless EphemeralBuffer.error_occurred?
|
|
119
|
+
# EphemeralBuffer.discard
|
|
140
120
|
# end
|
|
141
121
|
def discard
|
|
142
122
|
current_buffer = buffer
|
|
@@ -144,7 +124,7 @@ module E11y
|
|
|
144
124
|
|
|
145
125
|
discarded_count = current_buffer.size
|
|
146
126
|
current_buffer.clear
|
|
147
|
-
|
|
127
|
+
E11y::Metrics.increment(:e11y_ephemeral_buffer_total, event: "discarded", value: discarded_count)
|
|
148
128
|
discarded_count
|
|
149
129
|
end
|
|
150
130
|
|
|
@@ -195,6 +175,28 @@ module E11y
|
|
|
195
175
|
|
|
196
176
|
private
|
|
197
177
|
|
|
178
|
+
def handle_error_event(_event_data) # rubocop:disable Naming/PredicateMethod
|
|
179
|
+
Thread.current[THREAD_KEY_ERROR_OCCURRED] = true
|
|
180
|
+
flush_on_error
|
|
181
|
+
false
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def append_to_buffer(event_data)
|
|
185
|
+
current_buffer = buffer
|
|
186
|
+
return false if current_buffer.nil?
|
|
187
|
+
return record_buffer_overflow if current_buffer.size >= buffer_limit
|
|
188
|
+
|
|
189
|
+
event_to_store = event_data.merge(request_id: request_id)
|
|
190
|
+
current_buffer << event_to_store
|
|
191
|
+
E11y::Metrics.increment(:e11y_ephemeral_buffer_total, event: "events_buffered")
|
|
192
|
+
true
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def record_buffer_overflow # rubocop:disable Naming/PredicateMethod
|
|
196
|
+
E11y::Metrics.increment(:e11y_ephemeral_buffer_total, event: "overflow")
|
|
197
|
+
false
|
|
198
|
+
end
|
|
199
|
+
|
|
198
200
|
# Get buffer limit (with fallback)
|
|
199
201
|
#
|
|
200
202
|
# @return [Integer] Buffer limit
|
|
@@ -218,27 +220,39 @@ module E11y
|
|
|
218
220
|
SecureRandom.uuid
|
|
219
221
|
end
|
|
220
222
|
|
|
221
|
-
#
|
|
223
|
+
# Resolve flush targets: adapter instances (when debug_adapters set) or nil (use pipeline).
|
|
224
|
+
# Cached per flush to avoid repeated config lookups when flushing N events.
|
|
222
225
|
#
|
|
223
|
-
# @
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
# Placeholder for E11y::Collector integration
|
|
228
|
-
# Will be implemented when Collector/Adapter classes are available
|
|
226
|
+
# @return [Array<Object>, nil] Adapter instances to write to, or nil to use pipeline
|
|
227
|
+
def resolve_flush_targets
|
|
228
|
+
da = E11y.config.ephemeral_buffer_debug_adapters
|
|
229
|
+
return nil unless da&.any?
|
|
229
230
|
|
|
230
|
-
|
|
231
|
-
increment_metric("e11y.request_buffer.event_flushed")
|
|
231
|
+
da.filter_map { |name| E11y.configuration.adapters[name] }
|
|
232
232
|
end
|
|
233
233
|
|
|
234
|
-
#
|
|
234
|
+
# Flush single event to adapters via pipeline or debug_adapters
|
|
235
235
|
#
|
|
236
|
-
#
|
|
237
|
-
#
|
|
236
|
+
# When config.ephemeral_buffer_debug_adapters is set, sends directly to those
|
|
237
|
+
# adapters. Otherwise uses the full pipeline (fallback_adapters).
|
|
238
|
+
#
|
|
239
|
+
# @param event_data [Hash] Event to flush
|
|
240
|
+
# @param target [Symbol, nil] Optional target adapter (not yet implemented)
|
|
241
|
+
# @param flush_targets [Array<Object>, nil] Pre-resolved adapter instances (from flush_on_error)
|
|
238
242
|
# @return [void]
|
|
239
|
-
def
|
|
240
|
-
|
|
241
|
-
|
|
243
|
+
def flush_event(event_data, target: nil, flush_targets: nil) # rubocop:disable Lint/UnusedMethodArgument
|
|
244
|
+
return unless event_data
|
|
245
|
+
|
|
246
|
+
event_to_send = event_data.merge(from_ephemeral_buffer_flush: true)
|
|
247
|
+
targets = flush_targets || resolve_flush_targets
|
|
248
|
+
|
|
249
|
+
if targets
|
|
250
|
+
targets.each { |adapter| adapter&.write(event_to_send) }
|
|
251
|
+
else
|
|
252
|
+
E11y.config.built_pipeline.call(event_to_send)
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
E11y::Metrics.increment(:e11y_ephemeral_buffer_total, event: "event_flushed")
|
|
242
256
|
end
|
|
243
257
|
end
|
|
244
258
|
end
|
|
@@ -165,7 +165,7 @@ module E11y
|
|
|
165
165
|
# @return [Array<Hash>] All buffered events
|
|
166
166
|
#
|
|
167
167
|
# @example
|
|
168
|
-
#
|
|
168
|
+
# events = buffer.flush_all
|
|
169
169
|
# # => [event1, event2, ...]
|
|
170
170
|
def flush_all
|
|
171
171
|
pop(@size.value)
|
|
@@ -208,7 +208,6 @@ module E11y
|
|
|
208
208
|
#
|
|
209
209
|
# @param event [Hash] Event that caused overflow
|
|
210
210
|
# @return [Boolean] true if event was eventually added, false if dropped
|
|
211
|
-
# rubocop:disable Metrics/MethodLength
|
|
212
211
|
def handle_overflow(event)
|
|
213
212
|
case @overflow_strategy
|
|
214
213
|
when :drop_oldest
|
|
@@ -217,21 +216,20 @@ module E11y
|
|
|
217
216
|
push(event) # Retry push (recursive, but will succeed)
|
|
218
217
|
when :drop_newest
|
|
219
218
|
# Drop new event, keep buffer unchanged
|
|
220
|
-
|
|
219
|
+
E11y::Metrics.increment(:e11y_buffer_overflow_total, event: "drop_newest")
|
|
221
220
|
false
|
|
222
221
|
when :block
|
|
223
222
|
# Wait for space, with timeout
|
|
224
223
|
wait_for_space
|
|
225
224
|
if full?
|
|
226
225
|
# Timeout reached, drop event
|
|
227
|
-
|
|
226
|
+
E11y::Metrics.increment(:e11y_buffer_overflow_total, event: "block_timeout")
|
|
228
227
|
false
|
|
229
228
|
else
|
|
230
229
|
push(event) # Retry after space freed
|
|
231
230
|
end
|
|
232
231
|
end
|
|
233
232
|
end
|
|
234
|
-
# rubocop:enable Metrics/MethodLength
|
|
235
233
|
|
|
236
234
|
# Wait for buffer space (with timeout)
|
|
237
235
|
#
|
|
@@ -251,17 +249,6 @@ module E11y
|
|
|
251
249
|
sleep 0.001 # 1ms
|
|
252
250
|
end
|
|
253
251
|
end
|
|
254
|
-
|
|
255
|
-
# Increment metric (placeholder for Phase 3: Metrics)
|
|
256
|
-
#
|
|
257
|
-
# TODO Phase 3: Replace with actual Yabeda metrics
|
|
258
|
-
#
|
|
259
|
-
# @param metric_name [String] Metric to increment
|
|
260
|
-
# @return [void]
|
|
261
|
-
def increment_metric(metric_name)
|
|
262
|
-
# Placeholder - will be implemented in Phase 3
|
|
263
|
-
# Yabeda.e11y.buffer_overflow.increment(strategy: @overflow_strategy)
|
|
264
|
-
end
|
|
265
252
|
end
|
|
266
253
|
end
|
|
267
254
|
end
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module E11y
|
|
4
|
+
# Configuration class for E11y.
|
|
5
|
+
#
|
|
6
|
+
# Adapters are referenced by name (e.g., :logs, :errors_tracker).
|
|
7
|
+
# The actual implementation (Loki, Sentry, etc.) is configured separately.
|
|
8
|
+
#
|
|
9
|
+
# @example Configure adapters
|
|
10
|
+
# E11y.configure do |config|
|
|
11
|
+
# config.adapters[:logs] = E11y::Adapters::Loki.new(url: "...")
|
|
12
|
+
# config.adapters[:errors_tracker] = E11y::Adapters::Sentry.new(dsn: "...")
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# @example Configure severity => adapter mapping
|
|
16
|
+
# E11y.configure do |config|
|
|
17
|
+
# config.adapter_mapping[:error] = [:logs, :errors_tracker]
|
|
18
|
+
# config.adapter_mapping[:info] = [:logs]
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# @example Configure middleware pipeline
|
|
22
|
+
# E11y.configure do |config|
|
|
23
|
+
# config.pipeline.use E11y::Middleware::Sampling, default_sample_rate: 0.1
|
|
24
|
+
# end
|
|
25
|
+
class Configuration
|
|
26
|
+
attr_accessor :adapters, :log_level, :logger, :enabled, :environment, :service_name, :default_retention_period,
|
|
27
|
+
:routing_rules, :fallback_adapters, :enable_http_tracing,
|
|
28
|
+
:rails_instrumentation_enabled, :rails_instrumentation_custom_mappings, :rails_instrumentation_ignore_events,
|
|
29
|
+
:logger_bridge_enabled, :logger_bridge_track_severities, :logger_bridge_ignore_patterns,
|
|
30
|
+
:sidekiq_enabled, :active_job_enabled,
|
|
31
|
+
:ephemeral_buffer_enabled, :ephemeral_buffer_flush_on_error, :ephemeral_buffer_flush_on_statuses,
|
|
32
|
+
:ephemeral_buffer_debug_adapters, :ephemeral_buffer_job_buffer_limit,
|
|
33
|
+
:error_handling_fail_on_error,
|
|
34
|
+
:rate_limiting_enabled, :rate_limiting_global_limit, :rate_limiting_global_window,
|
|
35
|
+
:rate_limiting_per_event_limit, :rate_limiting_per_event_limits,
|
|
36
|
+
:slo_tracking_enabled, :slo_tracking_http_ignore_statuses, :slo_tracking_latency_percentiles,
|
|
37
|
+
:slo_tracking_controller_configs, :slo_tracking_job_configs,
|
|
38
|
+
:security_baggage_protection_enabled, :security_baggage_protection_allowed_keys, :security_baggage_protection_block_mode,
|
|
39
|
+
:tracing_source, :tracing_default_sample_rate, :tracing_respect_parent_sampling,
|
|
40
|
+
:tracing_per_event_sample_rates, :tracing_always_sample_if,
|
|
41
|
+
:opentelemetry_span_creation_patterns,
|
|
42
|
+
:cardinality_protection_max_cardinality_limit, :cardinality_protection_denylist, :cardinality_protection_overflow_strategy,
|
|
43
|
+
:dlq_storage, :dlq_filter
|
|
44
|
+
attr_reader :adapter_mapping, :pipeline
|
|
45
|
+
|
|
46
|
+
def initialize
|
|
47
|
+
initialize_basic_config
|
|
48
|
+
initialize_routing_config
|
|
49
|
+
initialize_feature_configs
|
|
50
|
+
configure_default_pipeline
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Get adapters for given severity
|
|
54
|
+
#
|
|
55
|
+
# @param severity [Symbol] Severity level
|
|
56
|
+
# @return [Array<Symbol>] Adapter names (e.g., [:logs, :errors_tracker])
|
|
57
|
+
def adapters_for_severity(severity)
|
|
58
|
+
@adapter_mapping[severity] || @adapter_mapping[:default] || []
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Get built pipeline (cached after first call)
|
|
62
|
+
#
|
|
63
|
+
# @return [#call] Built middleware pipeline
|
|
64
|
+
def built_pipeline
|
|
65
|
+
@built_pipeline ||= @pipeline.build(->(_event_data) {})
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Add per-event rate limit rule.
|
|
69
|
+
#
|
|
70
|
+
# @param pattern [String] Event name or glob pattern (e.g. "payment.*")
|
|
71
|
+
# @param limit [Integer] Max events per window for this pattern
|
|
72
|
+
# @param window [Numeric] Window size in seconds
|
|
73
|
+
def add_rate_limit_per_event(pattern, limit:, window: 1.0)
|
|
74
|
+
(@rate_limiting_per_event_limits ||= []) << {
|
|
75
|
+
pattern: pattern.to_s,
|
|
76
|
+
limit: limit,
|
|
77
|
+
window: window.to_f
|
|
78
|
+
}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Find the most specific rate limit config for a given event name.
|
|
82
|
+
#
|
|
83
|
+
# @param event_name [String] Event name to look up
|
|
84
|
+
# @return [Hash] { limit:, window: }
|
|
85
|
+
def rate_limit_for(event_name)
|
|
86
|
+
limits = @rate_limiting_per_event_limits || []
|
|
87
|
+
match = limits.find do |rule|
|
|
88
|
+
pattern = rule[:pattern].gsub(".", "\\.").gsub("*", ".*")
|
|
89
|
+
Regexp.new("^#{pattern}$").match?(event_name.to_s)
|
|
90
|
+
end
|
|
91
|
+
m = match || { limit: @rate_limiting_per_event_limit, window: @rate_limiting_global_window }
|
|
92
|
+
{ limit: m[:limit], window: m[:window] }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Add per-controller SLO config.
|
|
96
|
+
def add_slo_controller(name, action: nil, &)
|
|
97
|
+
key = action ? "#{name}##{action}" : name
|
|
98
|
+
cfg = ControllerSLOConfig.new
|
|
99
|
+
cfg.instance_eval(&) if block_given?
|
|
100
|
+
(@slo_tracking_controller_configs ||= {})[key] = cfg
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Add per-job SLO config.
|
|
104
|
+
def add_slo_job(name, &)
|
|
105
|
+
cfg = JobSLOConfig.new
|
|
106
|
+
cfg.instance_eval(&) if block_given?
|
|
107
|
+
(@slo_tracking_job_configs ||= {})[name] = cfg
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Set slo_tracking enabled — accepts Boolean.
|
|
111
|
+
def slo_tracking=(value)
|
|
112
|
+
@slo_tracking_enabled = value if value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Filter baggage hash to only allowed keys (for tracestate/job propagation).
|
|
116
|
+
def filter_baggage_for_propagation(hash)
|
|
117
|
+
return {} if hash.nil? || !hash.is_a?(Hash)
|
|
118
|
+
return hash unless @security_baggage_protection_enabled
|
|
119
|
+
|
|
120
|
+
allowed = (@security_baggage_protection_allowed_keys || E11y::BAGGAGE_PROTECTION_DEFAULT_ALLOWED_KEYS).map(&:to_s)
|
|
121
|
+
hash.select { |k, _| allowed.include?(k.to_s) }
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Register an adapter instance by name.
|
|
125
|
+
def register_adapter(name, instance)
|
|
126
|
+
@adapters[name.to_sym] = instance
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Set the default adapter(s) used when no severity-specific mapping matches.
|
|
130
|
+
def default_adapters=(names)
|
|
131
|
+
@adapter_mapping[:default] = Array(names).map(&:to_sym)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# @return [Array<Symbol>] Default adapter names
|
|
135
|
+
def default_adapters
|
|
136
|
+
@adapter_mapping[:default]
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
private
|
|
140
|
+
|
|
141
|
+
def initialize_basic_config
|
|
142
|
+
@adapters = {}
|
|
143
|
+
@log_level = :info
|
|
144
|
+
@pipeline = E11y::Pipeline::Builder.new
|
|
145
|
+
@enabled = nil
|
|
146
|
+
@environment = nil
|
|
147
|
+
@service_name = nil
|
|
148
|
+
@enable_http_tracing = false
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def initialize_routing_config
|
|
152
|
+
@adapter_mapping = default_adapter_mapping
|
|
153
|
+
@default_retention_period = 30.days
|
|
154
|
+
@routing_rules = []
|
|
155
|
+
@fallback_adapters = [:stdout]
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def initialize_feature_configs
|
|
159
|
+
init_instrumentation_configs
|
|
160
|
+
init_ephemeral_buffer_configs
|
|
161
|
+
init_error_handling_configs
|
|
162
|
+
init_rate_limiting_configs
|
|
163
|
+
init_slo_configs
|
|
164
|
+
init_security_configs
|
|
165
|
+
init_tracing_configs
|
|
166
|
+
init_cardinality_configs
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def init_instrumentation_configs
|
|
170
|
+
@rails_instrumentation_enabled = false
|
|
171
|
+
@rails_instrumentation_custom_mappings = {}
|
|
172
|
+
@rails_instrumentation_ignore_events = []
|
|
173
|
+
@logger_bridge_enabled = false
|
|
174
|
+
@logger_bridge_track_severities = nil
|
|
175
|
+
@logger_bridge_ignore_patterns = []
|
|
176
|
+
@sidekiq_enabled = false
|
|
177
|
+
@active_job_enabled = false
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def init_ephemeral_buffer_configs
|
|
181
|
+
@ephemeral_buffer_enabled = false
|
|
182
|
+
@ephemeral_buffer_flush_on_error = true
|
|
183
|
+
@ephemeral_buffer_flush_on_statuses = []
|
|
184
|
+
@ephemeral_buffer_debug_adapters = nil
|
|
185
|
+
@ephemeral_buffer_job_buffer_limit = nil
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def init_error_handling_configs
|
|
189
|
+
@error_handling_fail_on_error = true
|
|
190
|
+
@dlq_storage = nil
|
|
191
|
+
@dlq_filter = nil
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def init_rate_limiting_configs
|
|
195
|
+
@rate_limiting_enabled = false
|
|
196
|
+
@rate_limiting_global_limit = 10_000
|
|
197
|
+
@rate_limiting_global_window = 1.0
|
|
198
|
+
@rate_limiting_per_event_limit = 1_000
|
|
199
|
+
@rate_limiting_per_event_limits = []
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def init_slo_configs
|
|
203
|
+
@slo_tracking_enabled = true
|
|
204
|
+
@slo_tracking_http_ignore_statuses = []
|
|
205
|
+
@slo_tracking_latency_percentiles = [50, 95, 99]
|
|
206
|
+
@slo_tracking_controller_configs = {}
|
|
207
|
+
@slo_tracking_job_configs = {}
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def init_security_configs
|
|
211
|
+
@security_baggage_protection_enabled = true
|
|
212
|
+
@security_baggage_protection_allowed_keys = E11y::BAGGAGE_PROTECTION_DEFAULT_ALLOWED_KEYS.dup
|
|
213
|
+
@security_baggage_protection_block_mode = :silent
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def init_tracing_configs
|
|
217
|
+
@tracing_source = :e11y
|
|
218
|
+
@tracing_default_sample_rate = 0.1
|
|
219
|
+
@tracing_respect_parent_sampling = true
|
|
220
|
+
@tracing_per_event_sample_rates = {}
|
|
221
|
+
@tracing_always_sample_if = nil
|
|
222
|
+
@opentelemetry_span_creation_patterns = []
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def init_cardinality_configs
|
|
226
|
+
@cardinality_protection_max_cardinality_limit = 1000
|
|
227
|
+
@cardinality_protection_denylist = []
|
|
228
|
+
@cardinality_protection_overflow_strategy = :relabel
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def default_adapter_mapping
|
|
232
|
+
{
|
|
233
|
+
error: %i[logs errors_tracker],
|
|
234
|
+
fatal: %i[logs errors_tracker],
|
|
235
|
+
default: [:logs]
|
|
236
|
+
}
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def configure_default_pipeline
|
|
240
|
+
@pipeline.use E11y::Middleware::TrackLatency
|
|
241
|
+
@pipeline.use E11y::Middleware::TraceContext
|
|
242
|
+
@pipeline.use E11y::Middleware::Validation
|
|
243
|
+
@pipeline.use E11y::Middleware::BaggageProtection
|
|
244
|
+
@pipeline.use E11y::Middleware::AuditSigning
|
|
245
|
+
@pipeline.use E11y::Middleware::PIIFilter
|
|
246
|
+
@pipeline.use E11y::Middleware::RateLimiting
|
|
247
|
+
@pipeline.use E11y::Middleware::Sampling
|
|
248
|
+
@pipeline.use E11y::Middleware::Versioning
|
|
249
|
+
@pipeline.use E11y::Middleware::Routing
|
|
250
|
+
@pipeline.use E11y::Middleware::EventSlo
|
|
251
|
+
@pipeline.use E11y::Middleware::SelfMonitoringEmit
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Per-controller SLO config (used by add_slo_controller).
|
|
256
|
+
class ControllerSLOConfig
|
|
257
|
+
def slo_target(value = nil)
|
|
258
|
+
value ? @slo_target = value : @slo_target
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def latency_target(value = nil)
|
|
262
|
+
value ? @latency_target = value : @latency_target
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Per-job SLO config (used by add_slo_job).
|
|
267
|
+
class JobSLOConfig
|
|
268
|
+
def ignore(value = nil)
|
|
269
|
+
value.nil? ? @ignore : @ignore = value
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
data/lib/e11y/console.rb
CHANGED
|
@@ -51,19 +51,17 @@ module E11y
|
|
|
51
51
|
|
|
52
52
|
# List all registered event classes
|
|
53
53
|
def events
|
|
54
|
-
|
|
55
|
-
puts " (Waiting for Event registry implementation)"
|
|
56
|
-
[]
|
|
54
|
+
Registry.event_classes.map { |e| e.respond_to?(:event_name) ? e.event_name : e.name }
|
|
57
55
|
end
|
|
58
56
|
|
|
59
|
-
# List all registered adapters
|
|
57
|
+
# List all registered adapters (from config.adapters)
|
|
60
58
|
def adapters
|
|
61
|
-
|
|
59
|
+
config.adapters.map do |name, adapter|
|
|
62
60
|
{
|
|
63
|
-
name:
|
|
61
|
+
name: name,
|
|
64
62
|
class: adapter.class.name,
|
|
65
|
-
healthy: adapter.healthy
|
|
66
|
-
capabilities: adapter.capabilities
|
|
63
|
+
healthy: adapter.respond_to?(:healthy?) ? adapter.healthy? : true,
|
|
64
|
+
capabilities: adapter.respond_to?(:capabilities) ? adapter.capabilities : {}
|
|
67
65
|
}
|
|
68
66
|
end
|
|
69
67
|
end
|
|
@@ -77,8 +75,8 @@ module E11y
|
|
|
77
75
|
private
|
|
78
76
|
|
|
79
77
|
def adapters_info
|
|
80
|
-
|
|
81
|
-
{ name:
|
|
78
|
+
config.adapters.map do |name, adapter|
|
|
79
|
+
{ name: name, class: adapter.class.name, healthy: adapter.respond_to?(:healthy?) ? adapter.healthy? : true }
|
|
82
80
|
end
|
|
83
81
|
end
|
|
84
82
|
|
|
@@ -95,13 +93,8 @@ module E11y
|
|
|
95
93
|
# @return [void]
|
|
96
94
|
def self.configure_for_console
|
|
97
95
|
E11y.configure do |config|
|
|
98
|
-
|
|
99
|
-
config.adapters
|
|
100
|
-
|
|
101
|
-
# Use stdout adapter with pretty printing
|
|
102
|
-
config.adapters&.register :stdout, E11y::Adapters::Stdout.new(
|
|
103
|
-
colorize: true
|
|
104
|
-
)
|
|
96
|
+
config.adapters.clear
|
|
97
|
+
config.adapters[:stdout] = E11y::Adapters::Stdout.new(colorize: true, format: :rich)
|
|
105
98
|
|
|
106
99
|
# Show all severities
|
|
107
100
|
# TODO: Implement severity_threshold config
|
data/lib/e11y/current.rb
CHANGED
|
@@ -37,12 +37,64 @@ module E11y
|
|
|
37
37
|
class Current < ActiveSupport::CurrentAttributes
|
|
38
38
|
attribute :trace_id
|
|
39
39
|
attribute :span_id
|
|
40
|
-
attribute :parent_trace_id #
|
|
40
|
+
attribute :parent_trace_id # Link to parent trace (C17 Resolution)
|
|
41
|
+
attribute :sampled # Trace-consistent sampling (ADR-005 §7)
|
|
42
|
+
attribute :baggage # Key-value metadata for cross-service propagation (ADR-005 §3)
|
|
41
43
|
attribute :request_id
|
|
42
44
|
attribute :user_id
|
|
43
45
|
attribute :ip_address
|
|
44
46
|
attribute :user_agent
|
|
45
47
|
attribute :request_method
|
|
46
48
|
attribute :request_path
|
|
49
|
+
|
|
50
|
+
# Add baggage key-value (propagated via tracestate / job metadata).
|
|
51
|
+
# Respects config.security_baggage_protection_*: blocks PII keys per allowed_keys (ADR-006 §5.5).
|
|
52
|
+
#
|
|
53
|
+
# @param key [String, Symbol]
|
|
54
|
+
# @param value [Object] Converted to string
|
|
55
|
+
# @raise [E11y::BaggagePiiError] when block_mode is :raise and key not allowed
|
|
56
|
+
def self.add_baggage(key, value)
|
|
57
|
+
cfg = E11y.config
|
|
58
|
+
if cfg&.security_baggage_protection_enabled
|
|
59
|
+
allowed = (cfg.security_baggage_protection_allowed_keys || E11y::BAGGAGE_PROTECTION_DEFAULT_ALLOWED_KEYS).map(&:to_s)
|
|
60
|
+
unless allowed.include?(key.to_s)
|
|
61
|
+
message = "[E11y] Blocked PII from E11y baggage: key=#{key.inspect}"
|
|
62
|
+
case cfg.security_baggage_protection_block_mode || :silent
|
|
63
|
+
when :silent then E11y.logger&.debug(message)
|
|
64
|
+
when :warn then E11y.logger&.warn(message)
|
|
65
|
+
when :raise then raise E11y::BaggagePiiError, "#{message}. Only allowed keys: #{allowed.join(', ')}"
|
|
66
|
+
end
|
|
67
|
+
return
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
self.baggage = (baggage || {}).merge(key.to_s => value.to_s)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Get baggage value by key.
|
|
74
|
+
# @param key [String, Symbol]
|
|
75
|
+
# @return [String, nil]
|
|
76
|
+
def self.get_baggage(key)
|
|
77
|
+
baggage&.[](key.to_s)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Returns current attributes as a hash for sampling context (symbol keys, nil values omitted).
|
|
81
|
+
# Callers may merge job-specific keys (job_class, queue) when in job context.
|
|
82
|
+
#
|
|
83
|
+
# @return [Hash{Symbol=>Object}]
|
|
84
|
+
def self.to_context
|
|
85
|
+
{
|
|
86
|
+
trace_id: trace_id,
|
|
87
|
+
span_id: span_id,
|
|
88
|
+
parent_trace_id: parent_trace_id,
|
|
89
|
+
sampled: sampled,
|
|
90
|
+
baggage: baggage,
|
|
91
|
+
request_id: request_id,
|
|
92
|
+
user_id: user_id,
|
|
93
|
+
ip_address: ip_address,
|
|
94
|
+
user_agent: user_agent,
|
|
95
|
+
request_method: request_method,
|
|
96
|
+
request_path: request_path
|
|
97
|
+
}.compact
|
|
98
|
+
end
|
|
47
99
|
end
|
|
48
100
|
end
|