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.
- checksums.yaml +7 -0
- data/.rspec +4 -0
- data/.rubocop.yml +69 -0
- data/CHANGELOG.md +26 -0
- data/CODE_OF_CONDUCT.md +64 -0
- data/LICENSE.txt +21 -0
- data/README.md +179 -0
- data/Rakefile +37 -0
- data/benchmarks/run_all.rb +33 -0
- data/config/README.md +83 -0
- data/config/loki-local-config.yaml +35 -0
- data/config/prometheus.yml +15 -0
- data/docker-compose.yml +78 -0
- data/docs/00-ICP-AND-TIMELINE.md +483 -0
- data/docs/01-SCALE-REQUIREMENTS.md +858 -0
- data/docs/ADR-001-architecture.md +2617 -0
- data/docs/ADR-002-metrics-yabeda.md +1395 -0
- data/docs/ADR-003-slo-observability.md +3337 -0
- data/docs/ADR-004-adapter-architecture.md +2385 -0
- data/docs/ADR-005-tracing-context.md +1372 -0
- data/docs/ADR-006-security-compliance.md +4143 -0
- data/docs/ADR-007-opentelemetry-integration.md +1385 -0
- data/docs/ADR-008-rails-integration.md +1911 -0
- data/docs/ADR-009-cost-optimization.md +2993 -0
- data/docs/ADR-010-developer-experience.md +2166 -0
- data/docs/ADR-011-testing-strategy.md +1836 -0
- data/docs/ADR-012-event-evolution.md +958 -0
- data/docs/ADR-013-reliability-error-handling.md +2750 -0
- data/docs/ADR-014-event-driven-slo.md +1533 -0
- data/docs/ADR-015-middleware-order.md +1061 -0
- data/docs/ADR-016-self-monitoring-slo.md +1234 -0
- data/docs/API-REFERENCE-L28.md +914 -0
- data/docs/COMPREHENSIVE-CONFIGURATION.md +2366 -0
- data/docs/IMPLEMENTATION_NOTES.md +2804 -0
- data/docs/IMPLEMENTATION_PLAN.md +1971 -0
- data/docs/IMPLEMENTATION_PLAN_ARCHITECTURE.md +586 -0
- data/docs/PLAN.md +148 -0
- data/docs/QUICK-START.md +934 -0
- data/docs/README.md +296 -0
- data/docs/design/00-memory-optimization.md +593 -0
- data/docs/guides/MIGRATION-L27-L28.md +692 -0
- data/docs/guides/PERFORMANCE-BENCHMARKS.md +434 -0
- data/docs/guides/README.md +44 -0
- data/docs/prd/01-overview-vision.md +440 -0
- data/docs/use_cases/README.md +119 -0
- data/docs/use_cases/UC-001-request-scoped-debug-buffering.md +813 -0
- data/docs/use_cases/UC-002-business-event-tracking.md +1953 -0
- data/docs/use_cases/UC-003-pattern-based-metrics.md +1627 -0
- data/docs/use_cases/UC-004-zero-config-slo-tracking.md +728 -0
- data/docs/use_cases/UC-005-sentry-integration.md +759 -0
- data/docs/use_cases/UC-006-trace-context-management.md +905 -0
- data/docs/use_cases/UC-007-pii-filtering.md +2648 -0
- data/docs/use_cases/UC-008-opentelemetry-integration.md +1153 -0
- data/docs/use_cases/UC-009-multi-service-tracing.md +1043 -0
- data/docs/use_cases/UC-010-background-job-tracking.md +1018 -0
- data/docs/use_cases/UC-011-rate-limiting.md +1906 -0
- data/docs/use_cases/UC-012-audit-trail.md +2301 -0
- data/docs/use_cases/UC-013-high-cardinality-protection.md +2127 -0
- data/docs/use_cases/UC-014-adaptive-sampling.md +1940 -0
- data/docs/use_cases/UC-015-cost-optimization.md +735 -0
- data/docs/use_cases/UC-016-rails-logger-migration.md +785 -0
- data/docs/use_cases/UC-017-local-development.md +867 -0
- data/docs/use_cases/UC-018-testing-events.md +1081 -0
- data/docs/use_cases/UC-019-tiered-storage-migration.md +562 -0
- data/docs/use_cases/UC-020-event-versioning.md +708 -0
- data/docs/use_cases/UC-021-error-handling-retry-dlq.md +956 -0
- data/docs/use_cases/UC-022-event-registry.md +648 -0
- data/docs/use_cases/backlog.md +226 -0
- data/e11y.gemspec +76 -0
- data/lib/e11y/adapters/adaptive_batcher.rb +207 -0
- data/lib/e11y/adapters/audit_encrypted.rb +239 -0
- data/lib/e11y/adapters/base.rb +580 -0
- data/lib/e11y/adapters/file.rb +224 -0
- data/lib/e11y/adapters/in_memory.rb +216 -0
- data/lib/e11y/adapters/loki.rb +333 -0
- data/lib/e11y/adapters/otel_logs.rb +203 -0
- data/lib/e11y/adapters/registry.rb +141 -0
- data/lib/e11y/adapters/sentry.rb +230 -0
- data/lib/e11y/adapters/stdout.rb +108 -0
- data/lib/e11y/adapters/yabeda.rb +370 -0
- data/lib/e11y/buffers/adaptive_buffer.rb +339 -0
- data/lib/e11y/buffers/base_buffer.rb +40 -0
- data/lib/e11y/buffers/request_scoped_buffer.rb +246 -0
- data/lib/e11y/buffers/ring_buffer.rb +267 -0
- data/lib/e11y/buffers.rb +14 -0
- data/lib/e11y/console.rb +122 -0
- data/lib/e11y/current.rb +48 -0
- data/lib/e11y/event/base.rb +894 -0
- data/lib/e11y/event/value_sampling_config.rb +84 -0
- data/lib/e11y/events/base_audit_event.rb +43 -0
- data/lib/e11y/events/base_payment_event.rb +33 -0
- data/lib/e11y/events/rails/cache/delete.rb +21 -0
- data/lib/e11y/events/rails/cache/read.rb +23 -0
- data/lib/e11y/events/rails/cache/write.rb +22 -0
- data/lib/e11y/events/rails/database/query.rb +45 -0
- data/lib/e11y/events/rails/http/redirect.rb +21 -0
- data/lib/e11y/events/rails/http/request.rb +26 -0
- data/lib/e11y/events/rails/http/send_file.rb +21 -0
- data/lib/e11y/events/rails/http/start_processing.rb +26 -0
- data/lib/e11y/events/rails/job/completed.rb +22 -0
- data/lib/e11y/events/rails/job/enqueued.rb +22 -0
- data/lib/e11y/events/rails/job/failed.rb +22 -0
- data/lib/e11y/events/rails/job/scheduled.rb +23 -0
- data/lib/e11y/events/rails/job/started.rb +22 -0
- data/lib/e11y/events/rails/log.rb +56 -0
- data/lib/e11y/events/rails/view/render.rb +23 -0
- data/lib/e11y/events.rb +18 -0
- data/lib/e11y/instruments/active_job.rb +201 -0
- data/lib/e11y/instruments/rails_instrumentation.rb +141 -0
- data/lib/e11y/instruments/sidekiq.rb +175 -0
- data/lib/e11y/logger/bridge.rb +205 -0
- data/lib/e11y/metrics/cardinality_protection.rb +172 -0
- data/lib/e11y/metrics/cardinality_tracker.rb +134 -0
- data/lib/e11y/metrics/registry.rb +234 -0
- data/lib/e11y/metrics/relabeling.rb +226 -0
- data/lib/e11y/metrics.rb +102 -0
- data/lib/e11y/middleware/audit_signing.rb +174 -0
- data/lib/e11y/middleware/base.rb +140 -0
- data/lib/e11y/middleware/event_slo.rb +167 -0
- data/lib/e11y/middleware/pii_filter.rb +266 -0
- data/lib/e11y/middleware/pii_filtering.rb +280 -0
- data/lib/e11y/middleware/rate_limiting.rb +214 -0
- data/lib/e11y/middleware/request.rb +163 -0
- data/lib/e11y/middleware/routing.rb +157 -0
- data/lib/e11y/middleware/sampling.rb +254 -0
- data/lib/e11y/middleware/slo.rb +168 -0
- data/lib/e11y/middleware/trace_context.rb +131 -0
- data/lib/e11y/middleware/validation.rb +118 -0
- data/lib/e11y/middleware/versioning.rb +132 -0
- data/lib/e11y/middleware.rb +12 -0
- data/lib/e11y/pii/patterns.rb +90 -0
- data/lib/e11y/pii.rb +13 -0
- data/lib/e11y/pipeline/builder.rb +155 -0
- data/lib/e11y/pipeline/zone_validator.rb +110 -0
- data/lib/e11y/pipeline.rb +12 -0
- data/lib/e11y/presets/audit_event.rb +65 -0
- data/lib/e11y/presets/debug_event.rb +34 -0
- data/lib/e11y/presets/high_value_event.rb +51 -0
- data/lib/e11y/presets.rb +19 -0
- data/lib/e11y/railtie.rb +138 -0
- data/lib/e11y/reliability/circuit_breaker.rb +216 -0
- data/lib/e11y/reliability/dlq/file_storage.rb +277 -0
- data/lib/e11y/reliability/dlq/filter.rb +117 -0
- data/lib/e11y/reliability/retry_handler.rb +207 -0
- data/lib/e11y/reliability/retry_rate_limiter.rb +117 -0
- data/lib/e11y/sampling/error_spike_detector.rb +225 -0
- data/lib/e11y/sampling/load_monitor.rb +161 -0
- data/lib/e11y/sampling/stratified_tracker.rb +92 -0
- data/lib/e11y/sampling/value_extractor.rb +82 -0
- data/lib/e11y/self_monitoring/buffer_monitor.rb +79 -0
- data/lib/e11y/self_monitoring/performance_monitor.rb +97 -0
- data/lib/e11y/self_monitoring/reliability_monitor.rb +146 -0
- data/lib/e11y/slo/event_driven.rb +150 -0
- data/lib/e11y/slo/tracker.rb +119 -0
- data/lib/e11y/version.rb +9 -0
- data/lib/e11y.rb +283 -0
- 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
|
data/lib/e11y/buffers.rb
ADDED
|
@@ -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
|
data/lib/e11y/console.rb
ADDED
|
@@ -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
|
data/lib/e11y/current.rb
ADDED
|
@@ -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
|