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,785 @@
1
+ # UC-016: Rails Logger Migration
2
+
3
+ **Status:** MVP Feature
4
+ **Complexity:** Beginner
5
+ **Setup Time:** 15-20 minutes
6
+ **Target Users:** All Developers, DevOps Teams
7
+
8
+ ---
9
+
10
+ ## 📋 Overview
11
+
12
+ ### Problem Statement
13
+
14
+ **The migration challenge:**
15
+ ```ruby
16
+ # ❌ BEFORE: Existing Rails.logger usage everywhere
17
+ # controllers/orders_controller.rb
18
+ Rails.logger.info "Order #{order.id} created by user #{current_user.id}"
19
+
20
+ # services/payment_service.rb
21
+ Rails.logger.debug "Charging card: #{card.last4}"
22
+ Rails.logger.error "Payment failed: #{error.message}"
23
+
24
+ # jobs/process_order_job.rb
25
+ Rails.logger.info "Processing order #{order_id}"
26
+
27
+ # Problems:
28
+ # 1. 1000+ Rails.logger calls across codebase
29
+ # 2. Can't just replace all at once (risky!)
30
+ # 3. Need gradual migration path
31
+ # 4. Must support both systems during transition
32
+ # 5. Don't want to lose existing logs
33
+ ```
34
+
35
+ ### E11y Solution
36
+
37
+ **Gradual, safe migration strategy:**
38
+ ```ruby
39
+ # ✅ AFTER: Coexistence mode (Phase 1)
40
+ E11y.configure do |config|
41
+ config.rails_logger do
42
+ # Intercept Rails.logger calls
43
+ intercept_rails_logger true
44
+
45
+ # Mirror to both systems during migration
46
+ mirror_to_rails_logger true
47
+
48
+ # Convert to structured events
49
+ auto_convert_to_events true
50
+ end
51
+ end
52
+
53
+ # Existing code works unchanged!
54
+ Rails.logger.info "Order created"
55
+ # → Sent to both Rails.logger AND E11y ✅
56
+
57
+ # New code uses E11y directly
58
+ Events::OrderCreated.track(order_id: order.id)
59
+ # → Only E11y (no duplication) ✅
60
+
61
+ # Phase 2: Turn off mirroring
62
+ config.mirror_to_rails_logger = false
63
+ # → All logs now go to E11y only ✅
64
+ ```
65
+
66
+ ---
67
+
68
+ ## 🎯 Migration Strategy
69
+
70
+ > **Implementation:** See [ADR-008 Section 7: Rails Logger Bridge](../ADR-008-rails-integration.md#7-rails-logger-bridge) for Logger::Bridge architecture, dual logging, and 3-phase migration strategy (shadow → conversion → full).
71
+
72
+ ### Phase 1: Shadow Mode (Week 1-2)
73
+
74
+ **E11y runs alongside Rails.logger, doesn't break anything:**
75
+ ```ruby
76
+ # config/initializers/e11y.rb
77
+ E11y.configure do |config|
78
+ # Phase 1: Shadow mode
79
+ config.rails_logger do
80
+ # Intercept Rails.logger (but keep original too!)
81
+ intercept_rails_logger true
82
+ mirror_to_rails_logger true # ← Keep Rails.logger working!
83
+
84
+ # Auto-convert to E11y events
85
+ auto_convert_to_events true
86
+
87
+ # Map severity levels
88
+ severity_mapping do
89
+ debug -> :debug
90
+ info -> :info
91
+ warn -> :warn
92
+ error -> :error
93
+ fatal -> :fatal
94
+ end
95
+ end
96
+
97
+ # Send to both Stdout (Rails.logger) and Loki (E11y)
98
+ config.adapters = [
99
+ E11y::Adapters::StdoutAdapter.new, # Development
100
+ E11y::Adapters::LokiAdapter.new(...) # E11y backend
101
+ ]
102
+ end
103
+
104
+ # Existing code continues to work!
105
+ Rails.logger.info "User logged in"
106
+ # → Goes to BOTH:
107
+ # 1. Rails.logger (stdout, as before)
108
+ # 2. E11y (Loki, new!)
109
+
110
+ # Verification:
111
+ # - Rails logs still appear in stdout ✅
112
+ # - E11y logs appear in Grafana ✅
113
+ # - No errors, no breakage ✅
114
+ ```
115
+
116
+ ---
117
+
118
+ ### Phase 2: Gradual Conversion (Week 3-6)
119
+
120
+ **Replace Rails.logger with E11y events, one feature at a time:**
121
+ ```ruby
122
+ # Step 1: Start with new features (safe!)
123
+ class OrdersController < ApplicationController
124
+ def create
125
+ order = Order.create!(order_params)
126
+
127
+ # ✅ NEW: Use E11y for new code
128
+ Events::OrderCreated.track(
129
+ order_id: order.id,
130
+ user_id: current_user.id,
131
+ amount: order.total
132
+ )
133
+
134
+ render json: order
135
+ end
136
+ end
137
+
138
+ # Step 2: Replace high-value areas (authentication, payments)
139
+ class SessionsController < ApplicationController
140
+ def create
141
+ # ❌ OLD: Rails.logger
142
+ # Rails.logger.info "User #{user.id} logged in from #{request.ip}"
143
+
144
+ # ✅ NEW: E11y structured event
145
+ Events::UserLoggedIn.track(
146
+ user_id: user.id,
147
+ ip_address: request.ip,
148
+ user_agent: request.user_agent
149
+ )
150
+ end
151
+ end
152
+
153
+ # Step 3: Leave low-value areas as-is (for now)
154
+ class HealthController < ApplicationController
155
+ def show
156
+ # Keep Rails.logger for simple health checks (low priority)
157
+ Rails.logger.debug "Health check"
158
+ render json: { status: 'ok' }
159
+ end
160
+ end
161
+
162
+ # Progress tracking:
163
+ # - Week 3: Authentication (5 controllers) ✅
164
+ # - Week 4: Orders & Payments (10 controllers) ✅
165
+ # - Week 5: Background Jobs (15 jobs) ✅
166
+ # - Week 6: Core Services (20 services) ✅
167
+ ```
168
+
169
+ ---
170
+
171
+ ### Phase 3: Full Migration (Week 7+)
172
+
173
+ **Turn off Rails.logger mirroring, E11y only:**
174
+ ```ruby
175
+ # config/initializers/e11y.rb
176
+ E11y.configure do |config|
177
+ config.rails_logger do
178
+ intercept_rails_logger true
179
+ mirror_to_rails_logger false # ← Turn off mirroring!
180
+
181
+ # Auto-convert remaining Rails.logger calls
182
+ auto_convert_to_events true
183
+ end
184
+ end
185
+
186
+ # Now:
187
+ # - E11y events → E11y only ✅
188
+ # - Rails.logger calls → Auto-converted to E11y ✅
189
+ # - No more duplication ✅
190
+
191
+ # Optional: Deprecation warnings for remaining Rails.logger
192
+ config.rails_logger do
193
+ warn_on_rails_logger_usage true
194
+ # → Logs warning when Rails.logger is used
195
+ # "DEPRECATION: Rails.logger at app/controllers/users_controller.rb:42"
196
+ end
197
+ ```
198
+
199
+ ---
200
+
201
+ ## 💻 Implementation Examples
202
+
203
+ ### Example 1: Auto-Conversion (Quick Start)
204
+
205
+ ```ruby
206
+ # config/initializers/e11y.rb
207
+ E11y.configure do |config|
208
+ config.rails_logger do
209
+ # Intercept ALL Rails.logger calls
210
+ intercept_rails_logger true
211
+
212
+ # Auto-convert to E11y events
213
+ auto_convert_to_events true
214
+
215
+ # Extract structured data from log messages
216
+ extract_structured_data do
217
+ # Pattern: "Order 123 created by user 456"
218
+ pattern /Order (\d+) created by user (\d+)/ do |match|
219
+ {
220
+ event_name: 'order.created',
221
+ order_id: match[1],
222
+ user_id: match[2]
223
+ }
224
+ end
225
+
226
+ # Pattern: "Payment failed: Card declined"
227
+ pattern /Payment failed: (.+)/ do |match|
228
+ {
229
+ event_name: 'payment.failed',
230
+ error: match[1],
231
+ severity: :error
232
+ }
233
+ end
234
+
235
+ # Default: Generic log event
236
+ fallback do |message, severity|
237
+ {
238
+ event_name: 'rails.log',
239
+ message: message,
240
+ severity: severity
241
+ }
242
+ end
243
+ end
244
+ end
245
+ end
246
+
247
+ # Existing code (unchanged):
248
+ Rails.logger.info "Order 123 created by user 456"
249
+
250
+ # Auto-converted to:
251
+ Events::OrderCreated.track(
252
+ order_id: '123',
253
+ user_id: '456'
254
+ )
255
+
256
+ # In Grafana:
257
+ # {event_name="order.created",order_id="123",user_id="456"}
258
+ ```
259
+
260
+ ---
261
+
262
+ ### Example 2: Manual Migration (Controllers)
263
+
264
+ ```ruby
265
+ # === BEFORE ===
266
+ # app/controllers/orders_controller.rb
267
+ class OrdersController < ApplicationController
268
+ def create
269
+ Rails.logger.info "Creating order for user #{current_user.id}"
270
+
271
+ order = Order.create!(order_params)
272
+
273
+ Rails.logger.info "Order #{order.id} created with total #{order.total}"
274
+
275
+ render json: order
276
+ rescue => e
277
+ Rails.logger.error "Failed to create order: #{e.message}"
278
+ render json: { error: e.message }, status: :unprocessable_entity
279
+ end
280
+ end
281
+
282
+ # === AFTER ===
283
+ # app/events/order_creation_started.rb
284
+ module Events
285
+ class OrderCreationStarted < E11y::Event::Base
286
+ schema do
287
+ required(:user_id).filled(:string)
288
+ end
289
+ end
290
+ end
291
+
292
+ # app/events/order_created.rb
293
+ module Events
294
+ class OrderCreated < E11y::Event::Base
295
+ severity :success
296
+
297
+ schema do
298
+ required(:order_id).filled(:string)
299
+ required(:user_id).filled(:string)
300
+ required(:total).filled(:decimal)
301
+ required(:items_count).filled(:integer)
302
+ end
303
+
304
+ metric :counter, name: 'orders.created.total', tags: [:user_segment]
305
+ end
306
+ end
307
+
308
+ # app/events/order_creation_failed.rb
309
+ module Events
310
+ class OrderCreationFailed < E11y::Event::Base
311
+ severity :error
312
+
313
+ schema do
314
+ required(:user_id).filled(:string)
315
+ required(:error).filled(:string)
316
+ end
317
+ end
318
+ end
319
+
320
+ # app/controllers/orders_controller.rb
321
+ class OrdersController < ApplicationController
322
+ def create
323
+ # ✅ Structured event (better than Rails.logger!)
324
+ Events::OrderCreationStarted.track(user_id: current_user.id)
325
+
326
+ order = Order.create!(order_params)
327
+
328
+ # ✅ Rich structured data + automatic metrics
329
+ Events::OrderCreated.track(
330
+ order_id: order.id,
331
+ user_id: current_user.id,
332
+ total: order.total,
333
+ items_count: order.items.count
334
+ )
335
+
336
+ render json: order
337
+ rescue => e
338
+ # ✅ Error tracking with context
339
+ Events::OrderCreationFailed.track(
340
+ user_id: current_user.id,
341
+ error: e.message
342
+ )
343
+
344
+ render json: { error: e.message }, status: :unprocessable_entity
345
+ end
346
+ end
347
+
348
+ # Benefits:
349
+ # ✅ Structured data (can query by order_id, user_id)
350
+ # ✅ Automatic metrics (orders.created.total counter)
351
+ # ✅ Type-safe (schema validation)
352
+ # ✅ Searchable in Grafana
353
+ ```
354
+
355
+ ---
356
+
357
+ ### Example 3: Background Jobs Migration
358
+
359
+ ```ruby
360
+ # === BEFORE ===
361
+ # app/jobs/process_order_job.rb
362
+ class ProcessOrderJob < ApplicationJob
363
+ def perform(order_id)
364
+ Rails.logger.info "Starting order processing: #{order_id}"
365
+
366
+ order = Order.find(order_id)
367
+
368
+ Rails.logger.debug "Checking inventory for order #{order_id}"
369
+ check_inventory(order)
370
+
371
+ Rails.logger.debug "Capturing payment for order #{order_id}"
372
+ capture_payment(order)
373
+
374
+ Rails.logger.info "Order #{order_id} processed successfully"
375
+ rescue => e
376
+ Rails.logger.error "Order processing failed: #{order_id} - #{e.message}"
377
+ raise
378
+ end
379
+ end
380
+
381
+ # === AFTER ===
382
+ # app/jobs/process_order_job.rb
383
+ class ProcessOrderJob < ApplicationJob
384
+ def perform(order_id)
385
+ # E11y auto-tracks job start/end (UC-010)
386
+ # Just track business events!
387
+
388
+ order = Order.find(order_id)
389
+
390
+ Events::InventoryCheckStarted.track(order_id: order.id)
391
+ check_inventory(order)
392
+ Events::InventoryCheckCompleted.track(
393
+ order_id: order.id,
394
+ items_available: true
395
+ )
396
+
397
+ Events::PaymentCaptureStarted.track(order_id: order.id)
398
+ capture_payment(order)
399
+ Events::PaymentCaptured.track(
400
+ order_id: order.id,
401
+ amount: order.total,
402
+ severity: :success
403
+ )
404
+
405
+ rescue => e
406
+ Events::OrderProcessingFailed.track(
407
+ order_id: order_id,
408
+ error: e.message,
409
+ severity: :error
410
+ )
411
+ raise
412
+ end
413
+ end
414
+
415
+ # Benefits:
416
+ # ✅ Job lifecycle auto-tracked (start, end, retries)
417
+ # ✅ Trace ID preserved from enqueue
418
+ # ✅ Business events clearly separated
419
+ # ✅ Can build metrics/dashboards easily
420
+ ```
421
+
422
+ ---
423
+
424
+ ### Example 4: Service Objects Migration
425
+
426
+ ```ruby
427
+ # === BEFORE ===
428
+ # app/services/payment_service.rb
429
+ class PaymentService
430
+ def call(order)
431
+ Rails.logger.info "Processing payment for order #{order.id}"
432
+
433
+ Rails.logger.debug "Card: #{order.card.last4}"
434
+ Rails.logger.debug "Amount: #{order.total}"
435
+
436
+ result = StripeGateway.charge(
437
+ amount: order.total,
438
+ card: order.card.token
439
+ )
440
+
441
+ Rails.logger.info "Payment succeeded: #{result.id}"
442
+
443
+ result
444
+ rescue StripeGateway::Error => e
445
+ Rails.logger.error "Payment failed: #{e.message}"
446
+ raise
447
+ end
448
+ end
449
+
450
+ # === AFTER ===
451
+ # app/services/payment_service.rb
452
+ class PaymentService
453
+ def call(order)
454
+ Events::PaymentProcessingStarted.track(
455
+ order_id: order.id,
456
+ amount: order.total,
457
+ payment_method: 'stripe'
458
+ )
459
+
460
+ result = StripeGateway.charge(
461
+ amount: order.total,
462
+ card: order.card.token
463
+ )
464
+
465
+ Events::PaymentSucceeded.track(
466
+ order_id: order.id,
467
+ transaction_id: result.id,
468
+ amount: order.total,
469
+ card_last4: order.card.last4,
470
+ severity: :success
471
+ )
472
+
473
+ result
474
+ rescue StripeGateway::Error => e
475
+ Events::PaymentFailed.track(
476
+ order_id: order.id,
477
+ amount: order.total,
478
+ error_code: e.code,
479
+ error_message: e.message,
480
+ severity: :error
481
+ )
482
+ raise
483
+ end
484
+ end
485
+
486
+ # Benefits:
487
+ # ✅ No sensitive data logged (card details filtered)
488
+ # ✅ Structured data (can aggregate by error_code)
489
+ # ✅ Success tracking (severity: :success)
490
+ # ✅ Automatic metrics
491
+ ```
492
+
493
+ ---
494
+
495
+ ## 🔧 Configuration
496
+
497
+ ### Migration Configuration
498
+
499
+ ```ruby
500
+ # config/initializers/e11y.rb
501
+ E11y.configure do |config|
502
+ config.rails_logger do
503
+ # === PHASE 1: SHADOW MODE ===
504
+ # Intercept Rails.logger but keep original behavior
505
+ intercept_rails_logger true
506
+ mirror_to_rails_logger true # ← Both systems!
507
+
508
+ # === PHASE 2: GRADUAL CONVERSION ===
509
+ # Auto-convert Rails.logger to E11y events
510
+ auto_convert_to_events true
511
+
512
+ # Pattern extraction (parse log messages)
513
+ extract_patterns do
514
+ # Order events
515
+ pattern /Order (\d+) created/, event: 'order.created' do |match|
516
+ { order_id: match[1] }
517
+ end
518
+
519
+ # User events
520
+ pattern /User (\d+) logged in/, event: 'user.logged_in' do |match|
521
+ { user_id: match[1] }
522
+ end
523
+
524
+ # Payment events
525
+ pattern /Payment (\w+) for order (\d+)/, event: 'payment.status' do |match|
526
+ { status: match[1], order_id: match[2] }
527
+ end
528
+
529
+ # Generic fallback
530
+ fallback event: 'rails.log' do |message, severity|
531
+ { message: message, original_severity: severity }
532
+ end
533
+ end
534
+
535
+ # Severity mapping
536
+ severity_mapping do
537
+ debug -> :debug
538
+ info -> :info
539
+ warn -> :warn
540
+ error -> :error
541
+ fatal -> :fatal
542
+ unknown -> :warn
543
+ end
544
+
545
+ # === PHASE 3: FULL MIGRATION ===
546
+ # Turn off mirroring (E11y only!)
547
+ # mirror_to_rails_logger false
548
+
549
+ # Deprecation warnings
550
+ warn_on_rails_logger_usage true
551
+ deprecation_message "Please use E11y events instead of Rails.logger"
552
+
553
+ # Exceptions (still use Rails.logger)
554
+ ignore_callers [
555
+ /vendor\/bundle/, # Gems
556
+ /config\/initializers/, # Initializers
557
+ /health_check/ # Health checks
558
+ ]
559
+ end
560
+ end
561
+ ```
562
+
563
+ ---
564
+
565
+ ## 📊 Migration Progress Tracking
566
+
567
+ ### Built-in Metrics
568
+
569
+ ```ruby
570
+ # Automatic metrics for migration progress
571
+ # e11y_rails_logger_intercepted_total{severity} - Rails.logger calls intercepted
572
+ # e11y_rails_logger_converted_total{pattern} - Auto-converted to events
573
+ # e11y_rails_logger_fallback_total - Calls using fallback (not matched)
574
+ # e11y_direct_events_total{event_name} - Direct E11y.track calls
575
+
576
+ # Grafana Dashboard:
577
+ # Panel 1: Migration Progress
578
+ # (e11y_direct_events_total / (e11y_direct_events_total + e11y_rails_logger_intercepted_total)) * 100
579
+
580
+ # Panel 2: Rails.logger Usage (should decrease over time)
581
+ # sum(rate(e11y_rails_logger_intercepted_total[1h]))
582
+
583
+ # Panel 3: Pattern Coverage (how many logs are structured?)
584
+ # e11y_rails_logger_converted_total / e11y_rails_logger_intercepted_total
585
+ ```
586
+
587
+ ---
588
+
589
+ ## 🧪 Testing
590
+
591
+ ```ruby
592
+ # spec/support/e11y_migration_helper.rb
593
+ RSpec.configure do |config|
594
+ config.around(:each, :e11y_migration) do |example|
595
+ # Test both modes
596
+
597
+ # Test 1: Shadow mode (both systems)
598
+ E11y.configure do |c|
599
+ c.rails_logger.mirror_to_rails_logger = true
600
+ end
601
+ example.run
602
+
603
+ # Test 2: E11y only mode
604
+ E11y.configure do |c|
605
+ c.rails_logger.mirror_to_rails_logger = false
606
+ end
607
+ example.run
608
+ end
609
+ end
610
+
611
+ # spec/controllers/orders_controller_spec.rb
612
+ RSpec.describe OrdersController, :e11y_migration do
613
+ describe 'POST #create' do
614
+ it 'tracks order creation' do
615
+ # Works with both Rails.logger and E11y
616
+ expect {
617
+ post :create, params: { order: order_params }
618
+ }.to track_event('order.created')
619
+ end
620
+ end
621
+ end
622
+
623
+ # spec/migration/rails_logger_coverage_spec.rb
624
+ RSpec.describe 'Rails.logger migration coverage' do
625
+ it 'has converted all critical paths' do
626
+ # Check that critical areas don't use Rails.logger
627
+ critical_files = [
628
+ 'app/controllers/orders_controller.rb',
629
+ 'app/services/payment_service.rb',
630
+ 'app/jobs/process_order_job.rb'
631
+ ]
632
+
633
+ critical_files.each do |file|
634
+ content = File.read(Rails.root.join(file))
635
+ expect(content).not_to match(/Rails\.logger/)
636
+ end
637
+ end
638
+
639
+ it 'tracks migration progress' do
640
+ # Count Rails.logger usage
641
+ rails_logger_count = 0
642
+ e11y_track_count = 0
643
+
644
+ Dir['app/**/*.rb'].each do |file|
645
+ content = File.read(file)
646
+ rails_logger_count += content.scan(/Rails\.logger/).count
647
+ e11y_track_count += content.scan(/Events::\w+\.track/).count
648
+ end
649
+
650
+ # Expect at least 80% migrated
651
+ migration_pct = (e11y_track_count.to_f / (rails_logger_count + e11y_track_count)) * 100
652
+ expect(migration_pct).to be >= 80
653
+ end
654
+ end
655
+ ```
656
+
657
+ ---
658
+
659
+ ## 💡 Best Practices
660
+
661
+ ### ✅ DO
662
+
663
+ **1. Migrate in phases (safe!)**
664
+ ```ruby
665
+ # ✅ GOOD: Gradual migration
666
+ # Week 1-2: Shadow mode (both systems)
667
+ # Week 3-6: Convert high-value areas
668
+ # Week 7+: Turn off mirroring
669
+ ```
670
+
671
+ **2. Start with new features**
672
+ ```ruby
673
+ # ✅ GOOD: New code uses E11y from day 1
674
+ class NewFeatureController < ApplicationController
675
+ def action
676
+ Events::NewFeatureUsed.track(...) # ← E11y!
677
+ end
678
+ end
679
+ ```
680
+
681
+ **3. Convert high-value areas first**
682
+ ```ruby
683
+ # ✅ GOOD: Priority order
684
+ # 1. Authentication (security)
685
+ # 2. Payments (money!)
686
+ # 3. Orders (business critical)
687
+ # 4. Background jobs (async visibility)
688
+ # 5. Everything else
689
+ ```
690
+
691
+ **4. Use auto-conversion for long tail**
692
+ ```ruby
693
+ # ✅ GOOD: Auto-convert remaining Rails.logger
694
+ config.rails_logger do
695
+ auto_convert_to_events true # Handles stragglers
696
+ end
697
+ ```
698
+
699
+ ---
700
+
701
+ ### ❌ DON'T
702
+
703
+ **1. Don't migrate everything at once**
704
+ ```ruby
705
+ # ❌ BAD: Big bang migration (risky!)
706
+ # - Replace all 1000+ Rails.logger calls in one PR
707
+ # - Deploy to production
708
+ # - Hope nothing breaks 🤞
709
+
710
+ # ✅ GOOD: Incremental migration
711
+ # - Week 1: Shadow mode
712
+ # - Week 2: 10 controllers
713
+ # - Week 3: 15 jobs
714
+ # - etc.
715
+ ```
716
+
717
+ **2. Don't break existing functionality**
718
+ ```ruby
719
+ # ❌ BAD: Turn off Rails.logger immediately
720
+ config.rails_logger.mirror_to_rails_logger = false
721
+ # → Existing code breaks! 💥
722
+
723
+ # ✅ GOOD: Keep mirroring during migration
724
+ config.rails_logger.mirror_to_rails_logger = true
725
+ # → Both systems work ✅
726
+ ```
727
+
728
+ **3. Don't lose log context**
729
+ ```ruby
730
+ # ❌ BAD: Unstructured conversion
731
+ Rails.logger.info "Order 123 created by user 456"
732
+ # → Events::RailsLog.track(message: "Order 123 created by user 456")
733
+ # Still unstructured! 😞
734
+
735
+ # ✅ GOOD: Extract structure
736
+ Events::OrderCreated.track(
737
+ order_id: '123',
738
+ user_id: '456'
739
+ )
740
+ # Queryable, structured! 🎉
741
+ ```
742
+
743
+ ---
744
+
745
+ ## 📚 Related Use Cases
746
+
747
+ - **[UC-002: Business Event Tracking](./UC-002-business-event-tracking.md)** - E11y events basics
748
+ - **[UC-017: Local Development](./UC-017-local-development.md)** - Development setup
749
+ - **[UC-018: Testing Events](./UC-018-testing-events.md)** - Testing strategies
750
+
751
+ ---
752
+
753
+ ## 🎯 Summary
754
+
755
+ ### Migration Timeline
756
+
757
+ | Phase | Duration | Risk | Status |
758
+ |-------|----------|------|--------|
759
+ | **Phase 1: Shadow Mode** | 1-2 weeks | Low (no changes) | Both systems run |
760
+ | **Phase 2: Gradual Conversion** | 4-6 weeks | Low (incremental) | Convert high-value areas |
761
+ | **Phase 3: Full Migration** | 1+ weeks | Medium (turn off mirror) | E11y only |
762
+ | **TOTAL** | **6-9 weeks** | **Low overall** | Gradual, safe |
763
+
764
+ ### Benefits After Migration
765
+
766
+ | Before (Rails.logger) | After (E11y) |
767
+ |----------------------|--------------|
768
+ | Unstructured text | Structured events |
769
+ | Hard to search | Easy queries (Grafana) |
770
+ | No metrics | Automatic metrics |
771
+ | No correlation | Trace ID everywhere |
772
+ | Manual parsing | Type-safe schemas |
773
+ | No validation | Schema validation |
774
+
775
+ **Developer Experience:**
776
+ - Migration: 6-9 weeks for typical Rails app
777
+ - Per feature: 15-30 min to convert
778
+ - Testing: Works with both systems
779
+ - Risk: Low (gradual, reversible)
780
+
781
+ ---
782
+
783
+ **Document Version:** 1.0
784
+ **Last Updated:** January 12, 2026
785
+ **Status:** ✅ Complete