e11y 0.1.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.
Files changed (157) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +4 -0
  3. data/.rubocop.yml +69 -0
  4. data/CHANGELOG.md +26 -0
  5. data/CODE_OF_CONDUCT.md +64 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +179 -0
  8. data/Rakefile +37 -0
  9. data/benchmarks/run_all.rb +33 -0
  10. data/config/README.md +83 -0
  11. data/config/loki-local-config.yaml +35 -0
  12. data/config/prometheus.yml +15 -0
  13. data/docker-compose.yml +78 -0
  14. data/docs/00-ICP-AND-TIMELINE.md +483 -0
  15. data/docs/01-SCALE-REQUIREMENTS.md +858 -0
  16. data/docs/ADR-001-architecture.md +2617 -0
  17. data/docs/ADR-002-metrics-yabeda.md +1395 -0
  18. data/docs/ADR-003-slo-observability.md +3337 -0
  19. data/docs/ADR-004-adapter-architecture.md +2385 -0
  20. data/docs/ADR-005-tracing-context.md +1372 -0
  21. data/docs/ADR-006-security-compliance.md +4143 -0
  22. data/docs/ADR-007-opentelemetry-integration.md +1385 -0
  23. data/docs/ADR-008-rails-integration.md +1911 -0
  24. data/docs/ADR-009-cost-optimization.md +2993 -0
  25. data/docs/ADR-010-developer-experience.md +2166 -0
  26. data/docs/ADR-011-testing-strategy.md +1836 -0
  27. data/docs/ADR-012-event-evolution.md +958 -0
  28. data/docs/ADR-013-reliability-error-handling.md +2750 -0
  29. data/docs/ADR-014-event-driven-slo.md +1533 -0
  30. data/docs/ADR-015-middleware-order.md +1061 -0
  31. data/docs/ADR-016-self-monitoring-slo.md +1234 -0
  32. data/docs/API-REFERENCE-L28.md +914 -0
  33. data/docs/COMPREHENSIVE-CONFIGURATION.md +2366 -0
  34. data/docs/IMPLEMENTATION_NOTES.md +2804 -0
  35. data/docs/IMPLEMENTATION_PLAN.md +1971 -0
  36. data/docs/IMPLEMENTATION_PLAN_ARCHITECTURE.md +586 -0
  37. data/docs/PLAN.md +148 -0
  38. data/docs/QUICK-START.md +934 -0
  39. data/docs/README.md +296 -0
  40. data/docs/design/00-memory-optimization.md +593 -0
  41. data/docs/guides/MIGRATION-L27-L28.md +692 -0
  42. data/docs/guides/PERFORMANCE-BENCHMARKS.md +434 -0
  43. data/docs/guides/README.md +44 -0
  44. data/docs/prd/01-overview-vision.md +440 -0
  45. data/docs/use_cases/README.md +119 -0
  46. data/docs/use_cases/UC-001-request-scoped-debug-buffering.md +813 -0
  47. data/docs/use_cases/UC-002-business-event-tracking.md +1953 -0
  48. data/docs/use_cases/UC-003-pattern-based-metrics.md +1627 -0
  49. data/docs/use_cases/UC-004-zero-config-slo-tracking.md +728 -0
  50. data/docs/use_cases/UC-005-sentry-integration.md +759 -0
  51. data/docs/use_cases/UC-006-trace-context-management.md +905 -0
  52. data/docs/use_cases/UC-007-pii-filtering.md +2648 -0
  53. data/docs/use_cases/UC-008-opentelemetry-integration.md +1153 -0
  54. data/docs/use_cases/UC-009-multi-service-tracing.md +1043 -0
  55. data/docs/use_cases/UC-010-background-job-tracking.md +1018 -0
  56. data/docs/use_cases/UC-011-rate-limiting.md +1906 -0
  57. data/docs/use_cases/UC-012-audit-trail.md +2301 -0
  58. data/docs/use_cases/UC-013-high-cardinality-protection.md +2127 -0
  59. data/docs/use_cases/UC-014-adaptive-sampling.md +1940 -0
  60. data/docs/use_cases/UC-015-cost-optimization.md +735 -0
  61. data/docs/use_cases/UC-016-rails-logger-migration.md +785 -0
  62. data/docs/use_cases/UC-017-local-development.md +867 -0
  63. data/docs/use_cases/UC-018-testing-events.md +1081 -0
  64. data/docs/use_cases/UC-019-tiered-storage-migration.md +562 -0
  65. data/docs/use_cases/UC-020-event-versioning.md +708 -0
  66. data/docs/use_cases/UC-021-error-handling-retry-dlq.md +956 -0
  67. data/docs/use_cases/UC-022-event-registry.md +648 -0
  68. data/docs/use_cases/backlog.md +226 -0
  69. data/e11y.gemspec +76 -0
  70. data/lib/e11y/adapters/adaptive_batcher.rb +207 -0
  71. data/lib/e11y/adapters/audit_encrypted.rb +239 -0
  72. data/lib/e11y/adapters/base.rb +580 -0
  73. data/lib/e11y/adapters/file.rb +224 -0
  74. data/lib/e11y/adapters/in_memory.rb +216 -0
  75. data/lib/e11y/adapters/loki.rb +333 -0
  76. data/lib/e11y/adapters/otel_logs.rb +203 -0
  77. data/lib/e11y/adapters/registry.rb +141 -0
  78. data/lib/e11y/adapters/sentry.rb +230 -0
  79. data/lib/e11y/adapters/stdout.rb +108 -0
  80. data/lib/e11y/adapters/yabeda.rb +370 -0
  81. data/lib/e11y/buffers/adaptive_buffer.rb +339 -0
  82. data/lib/e11y/buffers/base_buffer.rb +40 -0
  83. data/lib/e11y/buffers/request_scoped_buffer.rb +246 -0
  84. data/lib/e11y/buffers/ring_buffer.rb +267 -0
  85. data/lib/e11y/buffers.rb +14 -0
  86. data/lib/e11y/console.rb +122 -0
  87. data/lib/e11y/current.rb +48 -0
  88. data/lib/e11y/event/base.rb +894 -0
  89. data/lib/e11y/event/value_sampling_config.rb +84 -0
  90. data/lib/e11y/events/base_audit_event.rb +43 -0
  91. data/lib/e11y/events/base_payment_event.rb +33 -0
  92. data/lib/e11y/events/rails/cache/delete.rb +21 -0
  93. data/lib/e11y/events/rails/cache/read.rb +23 -0
  94. data/lib/e11y/events/rails/cache/write.rb +22 -0
  95. data/lib/e11y/events/rails/database/query.rb +45 -0
  96. data/lib/e11y/events/rails/http/redirect.rb +21 -0
  97. data/lib/e11y/events/rails/http/request.rb +26 -0
  98. data/lib/e11y/events/rails/http/send_file.rb +21 -0
  99. data/lib/e11y/events/rails/http/start_processing.rb +26 -0
  100. data/lib/e11y/events/rails/job/completed.rb +22 -0
  101. data/lib/e11y/events/rails/job/enqueued.rb +22 -0
  102. data/lib/e11y/events/rails/job/failed.rb +22 -0
  103. data/lib/e11y/events/rails/job/scheduled.rb +23 -0
  104. data/lib/e11y/events/rails/job/started.rb +22 -0
  105. data/lib/e11y/events/rails/log.rb +56 -0
  106. data/lib/e11y/events/rails/view/render.rb +23 -0
  107. data/lib/e11y/events.rb +18 -0
  108. data/lib/e11y/instruments/active_job.rb +201 -0
  109. data/lib/e11y/instruments/rails_instrumentation.rb +141 -0
  110. data/lib/e11y/instruments/sidekiq.rb +175 -0
  111. data/lib/e11y/logger/bridge.rb +205 -0
  112. data/lib/e11y/metrics/cardinality_protection.rb +172 -0
  113. data/lib/e11y/metrics/cardinality_tracker.rb +134 -0
  114. data/lib/e11y/metrics/registry.rb +234 -0
  115. data/lib/e11y/metrics/relabeling.rb +226 -0
  116. data/lib/e11y/metrics.rb +102 -0
  117. data/lib/e11y/middleware/audit_signing.rb +174 -0
  118. data/lib/e11y/middleware/base.rb +140 -0
  119. data/lib/e11y/middleware/event_slo.rb +167 -0
  120. data/lib/e11y/middleware/pii_filter.rb +266 -0
  121. data/lib/e11y/middleware/pii_filtering.rb +280 -0
  122. data/lib/e11y/middleware/rate_limiting.rb +214 -0
  123. data/lib/e11y/middleware/request.rb +163 -0
  124. data/lib/e11y/middleware/routing.rb +157 -0
  125. data/lib/e11y/middleware/sampling.rb +254 -0
  126. data/lib/e11y/middleware/slo.rb +168 -0
  127. data/lib/e11y/middleware/trace_context.rb +131 -0
  128. data/lib/e11y/middleware/validation.rb +118 -0
  129. data/lib/e11y/middleware/versioning.rb +132 -0
  130. data/lib/e11y/middleware.rb +12 -0
  131. data/lib/e11y/pii/patterns.rb +90 -0
  132. data/lib/e11y/pii.rb +13 -0
  133. data/lib/e11y/pipeline/builder.rb +155 -0
  134. data/lib/e11y/pipeline/zone_validator.rb +110 -0
  135. data/lib/e11y/pipeline.rb +12 -0
  136. data/lib/e11y/presets/audit_event.rb +65 -0
  137. data/lib/e11y/presets/debug_event.rb +34 -0
  138. data/lib/e11y/presets/high_value_event.rb +51 -0
  139. data/lib/e11y/presets.rb +19 -0
  140. data/lib/e11y/railtie.rb +138 -0
  141. data/lib/e11y/reliability/circuit_breaker.rb +216 -0
  142. data/lib/e11y/reliability/dlq/file_storage.rb +277 -0
  143. data/lib/e11y/reliability/dlq/filter.rb +117 -0
  144. data/lib/e11y/reliability/retry_handler.rb +207 -0
  145. data/lib/e11y/reliability/retry_rate_limiter.rb +117 -0
  146. data/lib/e11y/sampling/error_spike_detector.rb +225 -0
  147. data/lib/e11y/sampling/load_monitor.rb +161 -0
  148. data/lib/e11y/sampling/stratified_tracker.rb +92 -0
  149. data/lib/e11y/sampling/value_extractor.rb +82 -0
  150. data/lib/e11y/self_monitoring/buffer_monitor.rb +79 -0
  151. data/lib/e11y/self_monitoring/performance_monitor.rb +97 -0
  152. data/lib/e11y/self_monitoring/reliability_monitor.rb +146 -0
  153. data/lib/e11y/slo/event_driven.rb +150 -0
  154. data/lib/e11y/slo/tracker.rb +119 -0
  155. data/lib/e11y/version.rb +9 -0
  156. data/lib/e11y.rb +283 -0
  157. metadata +452 -0
@@ -0,0 +1,267 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent"
4
+
5
+ module E11y
6
+ module Buffers
7
+ # Lock-free SPSC (Single-Producer, Single-Consumer) ring buffer
8
+ #
9
+ # Thread-safe ring buffer using atomic operations for high-throughput event buffering.
10
+ # Designed for 100K+ events/sec with minimal contention.
11
+ #
12
+ # Architecture:
13
+ # - Fixed capacity (default: 100,000 events)
14
+ # - Atomic read/write pointers (Concurrent::AtomicFixnum)
15
+ # - Backpressure strategies: :drop_oldest (default), :drop_newest, :block
16
+ # - Zero-copy flush operations
17
+ #
18
+ # Performance:
19
+ # - Target: 100K events/sec throughput
20
+ # - Latency: <10ฮผs per push/pop operation (p99)
21
+ # - Memory: Fixed allocation (capacity * avg_event_size)
22
+ #
23
+ # References:
24
+ # - ADR-001 ยง3.3.1 (Ring Buffer Specification)
25
+ # - UC-001 (Request-Scoped Debug Buffering)
26
+ #
27
+ # @example Basic usage
28
+ # buffer = E11y::Buffers::RingBuffer.new(capacity: 1000)
29
+ # buffer.push(event_hash)
30
+ # events = buffer.pop(100) # Batch pop
31
+ #
32
+ # @example With backpressure
33
+ # buffer = E11y::Buffers::RingBuffer.new(
34
+ # capacity: 1000,
35
+ # overflow_strategy: :drop_oldest
36
+ # )
37
+ # buffer.push(event) # Drops oldest if full
38
+ #
39
+ # @see ADR-001 ยง3.3.1
40
+ class RingBuffer
41
+ # Default buffer capacity (100K events)
42
+ DEFAULT_CAPACITY = 100_000
43
+
44
+ # Available overflow strategies
45
+ OVERFLOW_STRATEGIES = %i[drop_oldest drop_newest block].freeze
46
+
47
+ # Default overflow strategy
48
+ DEFAULT_OVERFLOW_STRATEGY = :drop_oldest
49
+
50
+ # Maximum block time for :block strategy (milliseconds)
51
+ DEFAULT_MAX_BLOCK_TIME_MS = 1000
52
+
53
+ attr_reader :capacity, :overflow_strategy
54
+
55
+ # Initialize a new ring buffer
56
+ #
57
+ # @param capacity [Integer] Maximum number of events (default: 100,000)
58
+ # @param overflow_strategy [Symbol] Strategy when buffer is full
59
+ # - :drop_oldest - Drop oldest event, keep newest (default)
60
+ # - :drop_newest - Drop new event, keep existing
61
+ # - :block - Wait until space available (up to max_block_time)
62
+ # @param max_block_time_ms [Integer] Max wait time for :block strategy (default: 1000ms)
63
+ #
64
+ # @raise [ArgumentError] if capacity <= 0 or invalid overflow_strategy
65
+ #
66
+ # @example
67
+ # buffer = RingBuffer.new(capacity: 10_000, overflow_strategy: :drop_oldest)
68
+ def initialize(capacity: DEFAULT_CAPACITY, overflow_strategy: DEFAULT_OVERFLOW_STRATEGY,
69
+ max_block_time_ms: DEFAULT_MAX_BLOCK_TIME_MS)
70
+ raise ArgumentError, "capacity must be > 0" if capacity <= 0
71
+
72
+ unless OVERFLOW_STRATEGIES.include?(overflow_strategy)
73
+ raise ArgumentError,
74
+ "overflow_strategy must be one of #{OVERFLOW_STRATEGIES.inspect}"
75
+ end
76
+
77
+ @capacity = capacity
78
+ @overflow_strategy = overflow_strategy
79
+ @max_block_time_ms = max_block_time_ms
80
+
81
+ # Fixed-size array for storage
82
+ @buffer = Array.new(capacity)
83
+
84
+ # Atomic pointers (SPSC pattern)
85
+ @write_index = Concurrent::AtomicFixnum.new(0) # Producer writes here
86
+ @read_index = Concurrent::AtomicFixnum.new(0) # Consumer reads here
87
+ @size = Concurrent::AtomicFixnum.new(0) # Current occupancy
88
+ end
89
+
90
+ # Push an event into the buffer (Producer)
91
+ #
92
+ # This is a single-producer operation - only ONE thread should call push().
93
+ # Uses atomic operations to ensure thread-safety with pop() (consumer).
94
+ #
95
+ # Behavior on overflow:
96
+ # - :drop_oldest - Removes oldest event, adds new one (always succeeds)
97
+ # - :drop_newest - Discards new event, keeps buffer unchanged
98
+ # - :block - Waits up to max_block_time for space, then drops
99
+ #
100
+ # @param event [Hash] Event hash to buffer
101
+ # @return [Boolean] true if event was added, false if dropped
102
+ #
103
+ # @example
104
+ # success = buffer.push({ event_name: "test", payload: {} })
105
+ # # => true (or false if dropped)
106
+ def push(event)
107
+ current_size = @size.value
108
+
109
+ if current_size >= @capacity
110
+ # Buffer full - handle backpressure
111
+ return handle_overflow(event)
112
+ end
113
+
114
+ # Write to buffer
115
+ write_pos = @write_index.value % @capacity
116
+ @buffer[write_pos] = event
117
+
118
+ # Increment pointers atomically
119
+ @write_index.increment
120
+ @size.increment
121
+
122
+ true
123
+ end
124
+
125
+ # Pop events from buffer (Consumer)
126
+ #
127
+ # This is a single-consumer operation - only ONE thread should call pop().
128
+ # Returns up to batch_size events in FIFO order.
129
+ #
130
+ # @param batch_size [Integer] Maximum events to pop (default: 100)
131
+ # @return [Array<Hash>] Array of events (may be empty)
132
+ #
133
+ # @example
134
+ # events = buffer.pop(50)
135
+ # # => [{ event_name: "test", ... }, ...]
136
+ def pop(batch_size = 100)
137
+ events = []
138
+ current_size = @size.value
139
+
140
+ # Limit batch size to available events
141
+ actual_batch_size = [batch_size, current_size].min
142
+
143
+ actual_batch_size.times do
144
+ read_pos = @read_index.value % @capacity
145
+ event = @buffer[read_pos]
146
+
147
+ events << event if event
148
+
149
+ # Clear slot to allow GC
150
+ @buffer[read_pos] = nil
151
+
152
+ # Increment pointers atomically
153
+ @read_index.increment
154
+ @size.decrement
155
+ end
156
+
157
+ events
158
+ end
159
+
160
+ # Flush all events from buffer
161
+ #
162
+ # Empties the buffer and returns all events in FIFO order.
163
+ # This is equivalent to pop(size), but more explicit.
164
+ #
165
+ # @return [Array<Hash>] All buffered events
166
+ #
167
+ # @example
168
+ # all_events = buffer.flush_all
169
+ # # => [event1, event2, ...]
170
+ def flush_all
171
+ pop(@size.value)
172
+ end
173
+
174
+ # Current number of events in buffer
175
+ #
176
+ # @return [Integer] Number of buffered events
177
+ def size
178
+ @size.value
179
+ end
180
+
181
+ # Check if buffer is empty
182
+ #
183
+ # @return [Boolean] true if no events buffered
184
+ def empty?
185
+ @size.value.zero?
186
+ end
187
+
188
+ # Check if buffer is full
189
+ #
190
+ # @return [Boolean] true if buffer is at capacity
191
+ def full?
192
+ @size.value >= @capacity
193
+ end
194
+
195
+ # Calculate buffer utilization percentage
196
+ #
197
+ # @return [Float] Utilization (0.0 to 1.0)
198
+ #
199
+ # @example
200
+ # buffer.utilization # => 0.75 (75% full)
201
+ def utilization
202
+ @size.value.to_f / @capacity
203
+ end
204
+
205
+ private
206
+
207
+ # Handle buffer overflow according to strategy
208
+ #
209
+ # @param event [Hash] Event that caused overflow
210
+ # @return [Boolean] true if event was eventually added, false if dropped
211
+ # rubocop:disable Metrics/MethodLength
212
+ def handle_overflow(event)
213
+ case @overflow_strategy
214
+ when :drop_oldest
215
+ # Drop oldest event, add new one
216
+ pop(1) # Remove one old event
217
+ push(event) # Retry push (recursive, but will succeed)
218
+ when :drop_newest
219
+ # Drop new event, keep buffer unchanged
220
+ increment_metric("e11y.buffer.overflow.drop_newest")
221
+ false
222
+ when :block
223
+ # Wait for space, with timeout
224
+ wait_for_space
225
+ if full?
226
+ # Timeout reached, drop event
227
+ increment_metric("e11y.buffer.overflow.block_timeout")
228
+ false
229
+ else
230
+ push(event) # Retry after space freed
231
+ end
232
+ end
233
+ end
234
+ # rubocop:enable Metrics/MethodLength
235
+
236
+ # Wait for buffer space (with timeout)
237
+ #
238
+ # Sleeps briefly until space is available or timeout reached.
239
+ # Used by :block overflow strategy.
240
+ #
241
+ # @return [void]
242
+ def wait_for_space
243
+ start_time = Time.now.to_f
244
+ timeout_seconds = @max_block_time_ms / 1000.0
245
+
246
+ while full?
247
+ elapsed = Time.now.to_f - start_time
248
+ break if elapsed >= timeout_seconds
249
+
250
+ # Brief sleep to avoid busy-wait
251
+ sleep 0.001 # 1ms
252
+ end
253
+ 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
+ end
266
+ end
267
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # E11y::Buffers module - Event buffering implementations
4
+ #
5
+ # This module contains buffer implementations for high-throughput event storage:
6
+ # - RingBuffer: Lock-free SPSC ring buffer (100K+ events/sec)
7
+ # - AdaptiveBuffer: Memory-aware buffer with backpressure (Phase 1.2.2)
8
+ #
9
+ # @see E11y::Buffers::RingBuffer
10
+ # @see ADR-001 ยง3.3 (Buffer Architecture)
11
+ module E11y
12
+ module Buffers
13
+ end
14
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module E11y
4
+ # Console helpers for Rails console
5
+ #
6
+ # Provides convenient methods for debugging and introspection in Rails console.
7
+ #
8
+ # @example Usage in Rails console
9
+ # E11y.stats # Show E11y statistics
10
+ # E11y.test_event # Track a test event
11
+ # E11y.events # List all registered events
12
+ # E11y.adapters # List all adapters
13
+ # E11y.reset! # Clear buffers
14
+ #
15
+ # @see ADR-008 ยง9 (Console & Development)
16
+ module Console
17
+ # Enable console helpers
18
+ #
19
+ # Called automatically by E11y::Railtie when Rails console starts.
20
+ #
21
+ # @return [void]
22
+ def self.enable!
23
+ define_helper_methods
24
+ configure_for_console
25
+ end
26
+
27
+ # Define helper methods on E11y module
28
+ # @return [void]
29
+ def self.define_helper_methods
30
+ E11y.singleton_class.class_eval do
31
+ # Show E11y statistics
32
+ # @return [Hash] Statistics hash
33
+ def stats
34
+ {
35
+ enabled: E11y.config.enabled,
36
+ environment: E11y.config.environment,
37
+ service_name: E11y.config.service_name,
38
+ adapters: E11y::Adapters::Registry.all.map do |a|
39
+ {
40
+ name: a.name,
41
+ class: a.class.name,
42
+ healthy: a.healthy?
43
+ }
44
+ end,
45
+ buffer: {
46
+ size: buffer_size,
47
+ max_size: E11y.config.buffer&.max_size
48
+ }
49
+ }
50
+ end
51
+
52
+ # Track a test event
53
+ # @return [void]
54
+ def test_event
55
+ # TODO: Implement test event tracking
56
+ # For now, just print a message
57
+ puts "โœ… E11y test event would be tracked here"
58
+ puts " (Waiting for Events::Console::Test implementation)"
59
+ end
60
+
61
+ # List all registered event classes
62
+ # @return [Array<String>] Event class names
63
+ def events
64
+ # TODO: Implement event registry
65
+ puts "๐Ÿ“‹ E11y events list"
66
+ puts " (Waiting for Event registry implementation)"
67
+ []
68
+ end
69
+
70
+ # List all registered adapters
71
+ # @return [Array<Hash>] Adapter details
72
+ def adapters
73
+ E11y::Adapters::Registry.all.map do |adapter|
74
+ {
75
+ name: adapter.name,
76
+ class: adapter.class.name,
77
+ healthy: adapter.healthy?,
78
+ capabilities: adapter.capabilities
79
+ }
80
+ end
81
+ end
82
+
83
+ # Reset buffers (clear all buffered events)
84
+ # @return [void]
85
+ def reset!
86
+ # TODO: Implement buffer clearing
87
+ puts "โœ… E11y buffers would be cleared here"
88
+ puts " (Waiting for Buffer#clear! implementation)"
89
+ end
90
+
91
+ private
92
+
93
+ # Get current buffer size
94
+ # @return [Integer] Number of buffered events
95
+ def buffer_size
96
+ # TODO: Implement buffer size tracking
97
+ 0
98
+ end
99
+ end
100
+ end
101
+
102
+ # Configure E11y for console-friendly output
103
+ # @return [void]
104
+ def self.configure_for_console
105
+ E11y.configure do |config|
106
+ # Console-friendly output
107
+ config.adapters&.clear
108
+
109
+ # Use stdout adapter with pretty printing
110
+ config.adapters&.register :stdout, E11y::Adapters::Stdout.new(
111
+ colorize: true
112
+ )
113
+
114
+ # Show all severities
115
+ # TODO: Implement severity_threshold config
116
+ # config.severity_threshold = :debug
117
+ end
118
+ rescue StandardError => e
119
+ warn "[E11y] Failed to configure console: #{e.message}"
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support"
4
+ require "active_support/current_attributes"
5
+
6
+ module E11y
7
+ # Request/job-scoped context using ActiveSupport::CurrentAttributes (Rails Way).
8
+ #
9
+ # Stores trace_id, span_id, parent_trace_id, and other request/job-scoped data.
10
+ # Automatically managed by E11y::Middleware::Request (HTTP) or
11
+ # Sidekiq/ActiveJob middleware (background jobs).
12
+ #
13
+ # **Hybrid Tracing (C17 Resolution)**:
14
+ # - HTTP Requests: new trace_id, parent_trace_id = nil
15
+ # - Background Jobs: new trace_id, parent_trace_id = enqueuing request's trace_id
16
+ #
17
+ # @example Setting request context (HTTP)
18
+ # E11y::Current.trace_id = "abc123"
19
+ # E11y::Current.user_id = 42
20
+ # # parent_trace_id is nil for requests
21
+ #
22
+ # @example Setting context (Background job with parent link)
23
+ # E11y::Current.trace_id = "xyz789" # NEW trace for job
24
+ # E11y::Current.parent_trace_id = "abc123" # Link to parent request
25
+ #
26
+ # @example Accessing context
27
+ # E11y::Current.trace_id # => "xyz789"
28
+ # E11y::Current.parent_trace_id # => "abc123"
29
+ #
30
+ # @example Resetting context
31
+ # E11y::Current.reset
32
+ #
33
+ # @see ADR-005 ยง8.3 (C17 Resolution: Hybrid Background Job Tracing)
34
+ # @see UC-009 (Multi-Service Tracing with parent_trace_id)
35
+ # @see UC-010 (Background Job Tracking)
36
+ # @see https://api.rubyonrails.org/classes/ActiveSupport/CurrentAttributes.html
37
+ class Current < ActiveSupport::CurrentAttributes
38
+ attribute :trace_id
39
+ attribute :span_id
40
+ attribute :parent_trace_id # โœ… NEW: Link to parent trace (C17 Resolution)
41
+ attribute :request_id
42
+ attribute :user_id
43
+ attribute :ip_address
44
+ attribute :user_agent
45
+ attribute :request_method
46
+ attribute :request_path
47
+ end
48
+ end