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.
Files changed (230) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +130 -10
  3. data/CHANGELOG.md +56 -1
  4. data/CLAUDE.md +168 -0
  5. data/CONTRIBUTING.md +640 -0
  6. data/README.md +134 -702
  7. data/RELEASE.md +18 -3
  8. data/Rakefile +108 -29
  9. data/config/README.md +1 -1
  10. data/config/loki-local-config.yaml +12 -0
  11. data/config/otel-collector-config.yaml +44 -0
  12. data/cucumber.yml +1 -0
  13. data/docker-compose.yml +18 -2
  14. data/docs/ADAPTERS.md +76 -0
  15. data/docs/ADAPTIVE_SAMPLING.md +59 -0
  16. data/docs/COMPARISON.md +104 -0
  17. data/docs/CONFIGURATION.md +52 -0
  18. data/docs/DISTRIBUTED_TRACING.md +44 -0
  19. data/docs/LIMITATIONS.md +13 -0
  20. data/docs/METRICS_DSL.md +84 -0
  21. data/docs/PERFORMANCE.md +60 -0
  22. data/docs/PII_FILTERING.md +40 -0
  23. data/docs/PRESETS.md +65 -0
  24. data/docs/QUICK-START.md +546 -587
  25. data/docs/RAILS_INTEGRATION.md +29 -0
  26. data/docs/SCHEMA_VALIDATION.md +63 -0
  27. data/docs/SLO-PROMQL-ALERTS.md +161 -0
  28. data/docs/TESTING.md +69 -0
  29. data/docs/{ADR-001-architecture.md → architecture/ADR-001-architecture.md} +35 -64
  30. data/docs/{ADR-002-metrics-yabeda.md → architecture/ADR-002-metrics-yabeda.md} +62 -236
  31. data/docs/{ADR-003-slo-observability.md → architecture/ADR-003-slo-observability.md} +27 -466
  32. data/docs/{ADR-004-adapter-architecture.md → architecture/ADR-004-adapter-architecture.md} +163 -146
  33. data/docs/{ADR-005-tracing-context.md → architecture/ADR-005-tracing-context.md} +10 -9
  34. data/docs/{ADR-006-security-compliance.md → architecture/ADR-006-security-compliance.md} +184 -191
  35. data/docs/{ADR-007-opentelemetry-integration.md → architecture/ADR-007-opentelemetry-integration.md} +3 -21
  36. data/docs/{ADR-008-rails-integration.md → architecture/ADR-008-rails-integration.md} +209 -339
  37. data/docs/{ADR-009-cost-optimization.md → architecture/ADR-009-cost-optimization.md} +45 -54
  38. data/docs/architecture/ADR-010-developer-experience.md +522 -0
  39. data/docs/{ADR-011-testing-strategy.md → architecture/ADR-011-testing-strategy.md} +41 -83
  40. data/docs/{ADR-013-reliability-error-handling.md → architecture/ADR-013-reliability-error-handling.md} +37 -12
  41. data/docs/{ADR-014-event-driven-slo.md → architecture/ADR-014-event-driven-slo.md} +12 -24
  42. data/docs/{ADR-015-middleware-order.md → architecture/ADR-015-middleware-order.md} +23 -41
  43. data/docs/{ADR-016-self-monitoring-slo.md → architecture/ADR-016-self-monitoring-slo.md} +52 -349
  44. data/docs/{ADR-017-multi-rails-compatibility.md → architecture/ADR-017-multi-rails-compatibility.md} +4 -11
  45. data/docs/architecture/ADR-018-memory-optimization.md +366 -0
  46. data/docs/{ADR-INDEX.md → architecture/ADR-INDEX.md} +11 -6
  47. data/docs/{00-ICP-AND-TIMELINE.md → prd/00-ICP-AND-TIMELINE.md} +6 -6
  48. data/docs/{01-SCALE-REQUIREMENTS.md → prd/01-SCALE-REQUIREMENTS.md} +6 -6
  49. data/docs/prd/01-overview-vision.md +19 -14
  50. data/docs/use_cases/README.md +22 -23
  51. data/docs/use_cases/UC-001-request-scoped-debug-buffering.md +50 -44
  52. data/docs/use_cases/UC-002-business-event-tracking.md +26 -95
  53. data/docs/use_cases/UC-003-event-metrics.md +66 -0
  54. data/docs/use_cases/UC-004-zero-config-slo-tracking.md +42 -101
  55. data/docs/use_cases/UC-005-sentry-integration.md +13 -15
  56. data/docs/use_cases/UC-006-trace-context-management.md +30 -28
  57. data/docs/use_cases/UC-007-pii-filtering.md +35 -87
  58. data/docs/use_cases/UC-008-opentelemetry-integration.md +51 -89
  59. data/docs/use_cases/UC-009-multi-service-tracing.md +4 -4
  60. data/docs/use_cases/UC-010-background-job-tracking.md +5 -5
  61. data/docs/use_cases/UC-011-rate-limiting.md +95 -168
  62. data/docs/use_cases/UC-012-audit-trail.md +21 -46
  63. data/docs/use_cases/UC-013-high-cardinality-protection.md +29 -167
  64. data/docs/use_cases/UC-014-adaptive-sampling.md +2 -2
  65. data/docs/use_cases/UC-015-cost-optimization.md +46 -99
  66. data/docs/use_cases/UC-016-rails-logger-migration.md +39 -213
  67. data/docs/use_cases/UC-017-local-development.md +203 -777
  68. data/docs/use_cases/UC-018-testing-events.md +3 -3
  69. data/docs/use_cases/UC-019-retention-based-routing.md +53 -106
  70. data/docs/use_cases/UC-020-event-versioning.md +8 -9
  71. data/docs/use_cases/UC-021-error-handling-retry-dlq.md +18 -22
  72. data/docs/use_cases/UC-022-event-registry.md +15 -21
  73. data/docs/use_cases/backlog.md +119 -87
  74. data/e11y.gemspec +2 -2
  75. data/gems/e11y-devtools/README.md +136 -0
  76. data/gems/e11y-devtools/config/routes.rb +8 -0
  77. data/gems/e11y-devtools/e11y-devtools.gemspec +25 -0
  78. data/gems/e11y-devtools/exe/e11y +34 -0
  79. data/gems/e11y-devtools/lib/e11y/devtools/mcp/server.rb +96 -0
  80. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tool_base.rb +25 -0
  81. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/clear.rb +31 -0
  82. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/errors.rb +35 -0
  83. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/event_detail.rb +33 -0
  84. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/events_by_trace.rb +33 -0
  85. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/interactions.rb +40 -0
  86. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/recent_events.rb +34 -0
  87. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/search.rb +34 -0
  88. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/stats.rb +30 -0
  89. data/gems/e11y-devtools/lib/e11y/devtools/overlay/assets/overlay.js +115 -0
  90. data/gems/e11y-devtools/lib/e11y/devtools/overlay/controller.rb +54 -0
  91. data/gems/e11y-devtools/lib/e11y/devtools/overlay/engine.rb +26 -0
  92. data/gems/e11y-devtools/lib/e11y/devtools/overlay/middleware.rb +80 -0
  93. data/gems/e11y-devtools/lib/e11y/devtools/overlay/rails_controller.rb +42 -0
  94. data/gems/e11y-devtools/lib/e11y/devtools/tui/app.rb +262 -0
  95. data/gems/e11y-devtools/lib/e11y/devtools/tui/grouping.rb +66 -0
  96. data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/event_detail.rb +62 -0
  97. data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/event_list.rb +70 -0
  98. data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/interaction_list.rb +47 -0
  99. data/gems/e11y-devtools/lib/e11y/devtools/version.rb +8 -0
  100. data/gems/e11y-devtools/lib/e11y/devtools.rb +13 -0
  101. data/gems/e11y-devtools/spec/e11y/devtools/mcp/tools_spec.rb +107 -0
  102. data/gems/e11y-devtools/spec/e11y/devtools/overlay/controller_spec.rb +58 -0
  103. data/gems/e11y-devtools/spec/e11y/devtools/overlay/middleware_spec.rb +46 -0
  104. data/gems/e11y-devtools/spec/e11y/devtools/tui/app_spec.rb +85 -0
  105. data/gems/e11y-devtools/spec/e11y/devtools/tui/grouping_spec.rb +64 -0
  106. data/gems/e11y-devtools/spec/spec_helper.rb +5 -0
  107. data/gems/e11y-devtools/spec/tui/widgets/event_list_spec.rb +44 -0
  108. data/gems/e11y-devtools/spec/tui/widgets/interaction_list_spec.rb +62 -0
  109. data/lib/e11y/adapters/audit_encrypted.rb +53 -11
  110. data/lib/e11y/adapters/base.rb +33 -34
  111. data/lib/e11y/adapters/dev_log/file_store.rb +143 -0
  112. data/lib/e11y/adapters/dev_log/query.rb +219 -0
  113. data/lib/e11y/adapters/dev_log.rb +118 -0
  114. data/lib/e11y/adapters/file.rb +3 -6
  115. data/lib/e11y/adapters/in_memory.rb +52 -5
  116. data/lib/e11y/adapters/in_memory_test.rb +29 -0
  117. data/lib/e11y/adapters/loki.rb +58 -23
  118. data/lib/e11y/adapters/null.rb +82 -0
  119. data/lib/e11y/adapters/opentelemetry_collector.rb +183 -0
  120. data/lib/e11y/adapters/otel_logs.rb +136 -23
  121. data/lib/e11y/adapters/sentry.rb +4 -7
  122. data/lib/e11y/adapters/stdout.rb +73 -7
  123. data/lib/e11y/adapters/yabeda.rb +153 -29
  124. data/lib/e11y/buffers/adaptive_buffer.rb +3 -17
  125. data/lib/e11y/buffers/{request_scoped_buffer.rb → ephemeral_buffer.rb} +72 -58
  126. data/lib/e11y/buffers/ring_buffer.rb +3 -16
  127. data/lib/e11y/configuration.rb +272 -0
  128. data/lib/e11y/console.rb +10 -17
  129. data/lib/e11y/current.rb +53 -1
  130. data/lib/e11y/debug/pipeline_inspector.rb +96 -0
  131. data/lib/e11y/documentation/generator.rb +48 -0
  132. data/lib/e11y/event/base.rb +176 -82
  133. data/lib/e11y/event/value_sampling_config.rb +1 -5
  134. data/lib/e11y/events/rails/database/query.rb +1 -4
  135. data/lib/e11y/events/rails/job/failed.rb +2 -0
  136. data/lib/e11y/instruments/active_job.rb +46 -12
  137. data/lib/e11y/instruments/rails_instrumentation.rb +49 -24
  138. data/lib/e11y/instruments/sidekiq.rb +137 -31
  139. data/lib/e11y/linters/base.rb +11 -0
  140. data/lib/e11y/linters/pii/pii_declaration_linter.rb +120 -0
  141. data/lib/e11y/linters/slo/config_consistency_linter.rb +76 -0
  142. data/lib/e11y/linters/slo/explicit_declaration_linter.rb +36 -0
  143. data/lib/e11y/linters/slo/slo_status_from_linter.rb +41 -0
  144. data/lib/e11y/logger/bridge.rb +26 -7
  145. data/lib/e11y/metrics/cardinality_protection.rb +10 -15
  146. data/lib/e11y/metrics/cardinality_tracker.rb +16 -6
  147. data/lib/e11y/metrics/registry.rb +3 -5
  148. data/lib/e11y/metrics/test_backend.rb +62 -0
  149. data/lib/e11y/metrics.rb +56 -10
  150. data/lib/e11y/middleware/adapter_resolver.rb +40 -0
  151. data/lib/e11y/middleware/audit_signing.rb +43 -6
  152. data/lib/e11y/middleware/baggage_protection.rb +75 -0
  153. data/lib/e11y/middleware/dev_log_source.rb +24 -0
  154. data/lib/e11y/middleware/event_slo.rb +23 -9
  155. data/lib/e11y/middleware/otel_span.rb +23 -0
  156. data/lib/e11y/middleware/pii_filter.rb +104 -75
  157. data/lib/e11y/middleware/rate_limiting.rb +54 -27
  158. data/lib/e11y/middleware/request.rb +70 -23
  159. data/lib/e11y/middleware/routing.rb +78 -21
  160. data/lib/e11y/middleware/sampling.rb +66 -17
  161. data/lib/e11y/middleware/self_monitoring_emit.rb +39 -0
  162. data/lib/e11y/middleware/trace_context.rb +45 -10
  163. data/lib/e11y/middleware/track_latency.rb +34 -0
  164. data/lib/e11y/middleware/validation.rb +7 -16
  165. data/lib/e11y/middleware/versioning.rb +26 -22
  166. data/lib/e11y/opentelemetry/semantic_conventions.rb +109 -0
  167. data/lib/e11y/opentelemetry/span_creator.rb +142 -0
  168. data/lib/e11y/pii/patterns.rb +12 -1
  169. data/lib/e11y/pipeline/builder.rb +1 -1
  170. data/lib/e11y/presets/audit_event.rb +13 -2
  171. data/lib/e11y/railtie.rb +52 -15
  172. data/lib/e11y/registry.rb +306 -0
  173. data/lib/e11y/reliability/circuit_breaker.rb +19 -21
  174. data/lib/e11y/reliability/dlq/base.rb +71 -0
  175. data/lib/e11y/reliability/dlq/file_adapter.rb +301 -0
  176. data/lib/e11y/reliability/dlq/file_storage.rb +63 -34
  177. data/lib/e11y/reliability/dlq/filter.rb +37 -54
  178. data/lib/e11y/reliability/retry_handler.rb +26 -29
  179. data/lib/e11y/reliability/retry_rate_limiter.rb +3 -11
  180. data/lib/e11y/sampling/error_spike_detector.rb +0 -2
  181. data/lib/e11y/sampling/load_monitor.rb +5 -9
  182. data/lib/e11y/sampling/stratified_tracker.rb +18 -0
  183. data/lib/e11y/self_monitoring/buffer_monitor.rb +2 -0
  184. data/lib/e11y/self_monitoring/performance_monitor.rb +19 -61
  185. data/lib/e11y/self_monitoring/reliability_monitor.rb +4 -74
  186. data/lib/e11y/slo/config_loader.rb +40 -0
  187. data/lib/e11y/slo/config_validator.rb +58 -0
  188. data/lib/e11y/slo/dashboard_generator.rb +122 -0
  189. data/lib/e11y/slo/event_driven.rb +8 -0
  190. data/lib/e11y/slo/tracker.rb +31 -4
  191. data/lib/e11y/testing/have_tracked_event_matcher.rb +190 -0
  192. data/lib/e11y/testing/rspec_matchers.rb +21 -0
  193. data/lib/e11y/testing/snapshot_matcher.rb +86 -0
  194. data/lib/e11y/trace_context/sampler.rb +35 -0
  195. data/lib/e11y/tracing/faraday_middleware.rb +31 -0
  196. data/lib/e11y/tracing/net_http_patch.rb +33 -0
  197. data/lib/e11y/tracing/propagator.rb +116 -0
  198. data/lib/e11y/tracing.rb +47 -0
  199. data/lib/e11y/version.rb +1 -1
  200. data/lib/e11y/versioning/version_extractor.rb +32 -0
  201. data/lib/e11y.rb +141 -265
  202. data/lib/generators/e11y/event/event_generator.rb +22 -0
  203. data/lib/generators/e11y/event/templates/event.rb.tt +16 -0
  204. data/lib/generators/e11y/grafana_dashboard/grafana_dashboard_generator.rb +30 -0
  205. data/lib/generators/e11y/grafana_dashboard/templates/e11y_dashboard.json +81 -0
  206. data/lib/generators/e11y/install/install_generator.rb +34 -0
  207. data/lib/generators/e11y/install/templates/e11y.rb +239 -0
  208. data/lib/generators/e11y/prometheus_alerts/prometheus_alerts_generator.rb +29 -0
  209. data/lib/generators/e11y/prometheus_alerts/templates/e11y_alerts.yml +28 -0
  210. data/lib/tasks/e11y_docs.rake +30 -0
  211. data/lib/tasks/e11y_events.rake +71 -0
  212. data/lib/tasks/e11y_lint.rake +91 -0
  213. data/lib/tasks/e11y_slo.rake +29 -0
  214. metadata +129 -39
  215. data/docs/ADR-010-developer-experience.md +0 -2166
  216. data/docs/API-REFERENCE-L28.md +0 -914
  217. data/docs/COMPREHENSIVE-CONFIGURATION.md +0 -2366
  218. data/docs/CONTRIBUTING.md +0 -312
  219. data/docs/IMPLEMENTATION_NOTES.md +0 -2804
  220. data/docs/IMPLEMENTATION_PLAN.md +0 -1971
  221. data/docs/IMPLEMENTATION_PLAN_ARCHITECTURE.md +0 -586
  222. data/docs/PLAN.md +0 -148
  223. data/docs/README.md +0 -296
  224. data/docs/design/00-memory-optimization.md +0 -593
  225. data/docs/guides/MIGRATION-L27-L28.md +0 -692
  226. data/docs/guides/PERFORMANCE-BENCHMARKS.md +0 -434
  227. data/docs/guides/README.md +0 -44
  228. data/docs/use_cases/UC-003-pattern-based-metrics.md +0 -1627
  229. data/lib/e11y/adapters/registry.rb +0 -141
  230. /data/docs/{ADR-012-event-evolution.md → architecture/ADR-012-event-evolution.md} +0 -0
@@ -204,24 +204,12 @@ module E11y
204
204
  next unless E11y.config.enabled
205
205
 
206
206
  # Setup instruments (each can be enabled/disabled separately)
207
- if E11y.config.instruments.active_support_notifications.enabled
208
- E11y::Instruments::ActiveSupportNotifications.setup!
209
- end
210
-
211
- if E11y.config.instruments.sidekiq.enabled
212
- E11y::Instruments::Sidekiq.setup!
213
- end
214
-
215
- if E11y.config.instruments.active_job.enabled
216
- E11y::Instruments::ActiveJob.setup!
217
- end
218
-
219
- if E11y.config.instruments.rack_middleware.enabled
220
- E11y::Instruments::RackMiddleware.setup!
221
- end
207
+ E11y::Railtie.setup_rails_instrumentation if E11y.config.rails_instrumentation&.enabled
208
+ E11y::Railtie.setup_sidekiq if defined?(::Sidekiq) && E11y.config.sidekiq&.enabled
209
+ E11y::Railtie.setup_active_job if defined?(::ActiveJob) && E11y.config.active_job&.enabled
222
210
 
223
211
  # Setup logger bridge
224
- E11y::Logger::Bridge.setup! if E11y.config.logger_bridge.enabled
212
+ E11y::Logger::Bridge.setup! if E11y.config.logger_bridge_enabled
225
213
 
226
214
  # Setup development tools
227
215
  E11y::Console.setup! if Rails.env.development?
@@ -400,7 +388,7 @@ module E11y
400
388
  # ========================================
401
389
 
402
390
  def self.setup!
403
- return unless E11y.config.instruments.rails_instrumentation.enabled
391
+ return unless E11y.config.rails_instrumentation&.enabled
404
392
 
405
393
  # Subscribe to Rails events
406
394
  event_mapping.each do |asn_pattern, e11y_event_class|
@@ -433,6 +421,7 @@ module E11y
433
421
  'enqueue.active_job' => Events::Rails::Job::Enqueued,
434
422
  'enqueue_at.active_job' => Events::Rails::Job::Scheduled,
435
423
  'perform_start.active_job' => Events::Rails::Job::Started,
424
+ # perform.active_job: Completed on success; Failed when payload has exception
436
425
  'perform.active_job' => Events::Rails::Job::Completed
437
426
  }.freeze
438
427
 
@@ -442,7 +431,7 @@ module E11y
442
431
  mapping = DEFAULT_RAILS_EVENT_MAPPING.dup
443
432
 
444
433
  # Apply custom mappings from config (Devise-style overrides)
445
- custom_mappings = E11y.config.instruments.rails_instrumentation.custom_mappings || {}
434
+ custom_mappings = E11y.config.rails_instrumentation&.custom_mappings || {}
446
435
  mapping.merge!(custom_mappings)
447
436
 
448
437
  mapping
@@ -450,7 +439,7 @@ module E11y
450
439
  end
451
440
 
452
441
  def self.ignored?(pattern)
453
- ignore_list = E11y.config.instruments.rails_instrumentation.ignore_events || []
442
+ ignore_list = E11y.config.rails_instrumentation&.ignore_events || []
454
443
  ignore_list.include?(pattern)
455
444
  end
456
445
 
@@ -477,62 +466,60 @@ end
477
466
  ```ruby
478
467
  # config/initializers/e11y.rb
479
468
  E11y.configure do |config|
480
- config.instruments do
469
+ # ========================================
470
+ # Rails Instrumentation (ActiveSupport::Notifications → E11y)
471
+ # ========================================
472
+ config.rails_instrumentation do
473
+ # Enable/disable entire ASN integration
474
+ enabled true # Set to false to completely disable
475
+
476
+ # Built-in event classes (auto-created by E11y)
477
+ # Located in Events::Rails namespace
478
+ use_built_in_events true # If false, no auto-mapping
479
+
481
480
  # ========================================
482
- # Rails Instrumentation (ActiveSupport::Notifications → E11y)
481
+ # OVERRIDE EVENT CLASSES (Devise-style)
483
482
  # ========================================
484
- rails_instrumentation do
485
- # Enable/disable entire ASN integration
486
- enabled true # Set to false to completely disable
487
-
488
- # Built-in event classes (auto-created by E11y)
489
- # Located in Events::Rails namespace
490
- use_built_in_events true # If false, no auto-mapping
491
-
492
- # ========================================
493
- # OVERRIDE EVENT CLASSES (Devise-style)
494
- # ========================================
495
-
496
- # Override default event class for specific ASN pattern
497
- event_class_for 'sql.active_record', MyApp::Events::CustomDatabaseQuery
498
- event_class_for 'process_action.action_controller', MyApp::Events::CustomHttpRequest
499
-
500
- # Disable specific events (too noisy or not needed)
501
- ignore_event 'cache_read.active_support'
502
- ignore_event 'render_partial.action_view'
503
- ignore_event 'SCHEMA' # Schema queries
504
-
505
- # ========================================
506
- # SELECTIVE INSTRUMENTATION
507
- # ========================================
508
-
509
- # Which Rails events to track (glob patterns)
510
- track_patterns [
511
- 'sql.active_record',
512
- 'process_action.action_controller',
513
- 'render_template.action_view',
514
- 'cache_*.active_support'
515
- ]
516
-
517
- # Sampling for high-volume events
518
- sample_patterns do
519
- pattern 'sql.active_record', sample_rate: 0.1 # 10% of SQL queries
520
- pattern 'cache_read.active_support', sample_rate: 0.01 # 1% of cache reads
521
- end
522
-
523
- # Enrich with custom data
524
- enrich do |asn_event|
525
- {
526
- controller: asn_event.payload[:controller],
527
- action: asn_event.payload[:action],
528
- format: asn_event.payload[:format],
529
- user_id: Current.user&.id # Add context
530
- }
531
- end
483
+
484
+ # Override default event class for specific ASN pattern
485
+ event_class_for 'sql.active_record', MyApp::Events::CustomDatabaseQuery
486
+ event_class_for 'process_action.action_controller', MyApp::Events::CustomHttpRequest
487
+
488
+ # Disable specific events (too noisy or not needed)
489
+ ignore_event 'cache_read.active_support'
490
+ ignore_event 'render_partial.action_view'
491
+ ignore_event 'SCHEMA' # Schema queries
492
+
493
+ # ========================================
494
+ # SELECTIVE INSTRUMENTATION
495
+ # ========================================
496
+
497
+ # Which Rails events to track (glob patterns)
498
+ track_patterns [
499
+ 'sql.active_record',
500
+ 'process_action.action_controller',
501
+ 'render_template.action_view',
502
+ 'cache_*.active_support'
503
+ ]
504
+
505
+ # Sampling for high-volume events
506
+ sample_patterns do
507
+ pattern 'sql.active_record', sample_rate: 0.1 # 10% of SQL queries
508
+ pattern 'cache_read.active_support', sample_rate: 0.01 # 1% of cache reads
532
509
  end
533
510
 
534
- # (Other integrations: Sidekiq, ActiveJob, etc.)
511
+ # Enrich with custom data
512
+ enrich do |asn_event|
513
+ {
514
+ controller: asn_event.payload[:controller],
515
+ action: asn_event.payload[:action],
516
+ format: asn_event.payload[:format],
517
+ user_id: Current.user&.id # Add context
518
+ }
519
+ end
535
520
  end
521
+
522
+ # (Other integrations: config.sidekiq, config.active_job, config.logger_bridge)
536
523
  end
537
524
  ```
538
525
 
@@ -580,7 +567,7 @@ end
580
567
 
581
568
  # config/initializers/e11y.rb
582
569
  E11y.configure do |config|
583
- config.instruments.rails_instrumentation do
570
+ config.rails_instrumentation do
584
571
  # Override default event class
585
572
  event_class_for 'sql.active_record', MyApp::Events::CustomDatabaseQuery
586
573
  end
@@ -660,71 +647,44 @@ module E11y
660
647
  end
661
648
  ```
662
649
 
663
- # ========================================
664
- # Sidekiq
665
- # ========================================
666
- sidekiq do
667
- enabled true # Set to false to disable Sidekiq integration
668
-
669
- # Server middleware (job execution)
670
- server_middleware do
671
- enabled true
672
- track_start true
673
- track_complete true
674
- track_failure true
675
- end
676
-
677
- # Client middleware (job enqueuing)
678
- client_middleware do
679
- enabled true
680
- track_enqueue true
681
- end
682
-
683
- # Trace propagation
684
- propagate_trace_context true
685
- trace_context_keys ['e11y_trace_id', 'e11y_span_id', 'e11y_sampled']
686
- end
650
+ # ========================================
651
+ # Sidekiq
652
+ # ========================================
653
+ config.sidekiq do
654
+ enabled true # Set to false to disable Sidekiq integration
687
655
 
688
- # ========================================
689
- # ActiveJob
690
- # ========================================
691
- active_job do
692
- enabled true # Set to false to disable ActiveJob integration
693
-
694
- track_enqueue true
656
+ # Server middleware (job execution)
657
+ server_middleware do
658
+ enabled true
695
659
  track_start true
696
660
  track_complete true
697
661
  track_failure true
698
-
699
- # Trace propagation
700
- propagate_trace_context true
701
-
702
- # Job-scoped buffer (like request-scoped buffer for HTTP)
703
- use_job_buffer true
704
-
705
- job_buffer do
706
- buffer_severities [:debug]
707
- flush_on do
708
- error true # Flush debug events if job fails
709
- success false # Discard debug events if job succeeds
710
- end
711
- max_events 1000
712
- end
713
662
  end
714
663
 
715
- # ========================================
716
- # Rack Middleware
717
- # ========================================
718
- rack_middleware do
719
- enabled true # Set to false to disable Rack middleware
720
-
721
- track_request_start true
722
- track_request_complete true
723
- track_request_failure true
724
-
725
- # Request-scoped buffer
726
- use_request_buffer true
664
+ # Client middleware (job enqueuing)
665
+ client_middleware do
666
+ enabled true
667
+ track_enqueue true
727
668
  end
669
+
670
+ # Trace propagation (C17 hybrid: job gets NEW trace_id, links via e11y_parent_trace_id)
671
+ propagate_trace_context true
672
+ trace_context_keys ['e11y_parent_trace_id', 'e11y_span_id', 'e11y_sampled']
673
+ end
674
+
675
+ # ========================================
676
+ # ActiveJob
677
+ # ========================================
678
+ config.active_job do
679
+ enabled true # Set to false to disable ActiveJob integration
680
+
681
+ track_enqueue true
682
+ track_start true
683
+ track_complete true
684
+ track_failure true
685
+
686
+ # Trace propagation
687
+ propagate_trace_context true
728
688
  end
729
689
  end
730
690
  ```
@@ -734,19 +694,16 @@ end
734
694
  ```ruby
735
695
  # Disable ASN but keep Sidekiq
736
696
  E11y.configure do |config|
737
- config.instruments.active_support_notifications.enabled = false
738
- config.instruments.sidekiq.enabled = true
739
- config.instruments.active_job.enabled = true
697
+ config.rails_instrumentation_enabled = false
698
+ config.sidekiq_enabled = true
699
+ config.active_job_enabled = true
740
700
  end
741
701
 
742
702
  # Minimal setup: only Sidekiq
743
703
  E11y.configure do |config|
744
- config.instruments do
745
- active_support_notifications { enabled false }
746
- sidekiq { enabled true }
747
- active_job { enabled false }
748
- rack_middleware { enabled false }
749
- end
704
+ config.rails_instrumentation_enabled = false
705
+ config.sidekiq_enabled = true
706
+ config.active_job_enabled = false
750
707
  end
751
708
  ```
752
709
 
@@ -860,7 +817,7 @@ end
860
817
  ```ruby
861
818
  # config/initializers/e11y.rb
862
819
  E11y.configure do |config|
863
- config.instruments.active_support_notifications do
820
+ config.rails_instrumentation do
864
821
  # Disable built-in events
865
822
  use_built_in_events false
866
823
 
@@ -877,6 +834,8 @@ end
877
834
 
878
835
  ## 5. Sidekiq Integration
879
836
 
837
+ **Implementation Note:** Sidekiq middleware emits `Events::Rails::Job::Enqueued`, `Started`, `Completed`, `Failed` for **raw Sidekiq jobs only** (`include Sidekiq::Worker`). When Sidekiq is the queue adapter for ActiveJob, jobs use `ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper`; we skip event emission in Sidekiq middleware to avoid double emission — ActiveJob events come from RailsInstrumentation (ASN).
838
+
880
839
  ### 5.1. Server Middleware (Job Execution)
881
840
 
882
841
  ```ruby
@@ -886,22 +845,25 @@ module E11y
886
845
  module Sidekiq
887
846
  class ServerMiddleware
888
847
  def call(worker, job, queue)
889
- # Extract trace context from job metadata
890
- trace_id = job['e11y_trace_id'] || E11y::TraceContext.generate_id
848
+ # C17 Hybrid: Job gets NEW trace_id, parent_trace_id links to enqueuing request
849
+ parent_trace_id = job['e11y_parent_trace_id']
850
+ trace_id = E11y::TraceContext.generate_id # NEW trace per job
891
851
  parent_span_id = job['e11y_span_id']
892
852
 
893
- # Restore trace context
894
853
  E11y::Current.set(
895
854
  trace_id: trace_id,
855
+ parent_trace_id: parent_trace_id,
896
856
  parent_span_id: parent_span_id,
897
857
  job_id: job['jid'],
898
858
  job_class: worker.class.name,
899
859
  queue: queue
900
860
  )
901
861
 
902
- # Start job-scoped buffer (optional, configurable)
903
- if E11y.config.instruments.sidekiq.use_job_buffer
904
- E11y::JobBuffer.start!
862
+ # Start request-scoped buffer (same as HTTP; config.ephemeral_buffer_enabled)
863
+ if E11y.config.ephemeral_buffer_enabled
864
+ limit = E11y.config.ephemeral_buffer_job_buffer_limit ||
865
+ E11y::Buffers::EphemeralBuffer::DEFAULT_BUFFER_LIMIT
866
+ E11y::EphemeralBuffer.initialize!(buffer_limit: limit)
905
867
  end
906
868
 
907
869
  # Track job start
@@ -926,8 +888,8 @@ module E11y
926
888
  queue: queue
927
889
  )
928
890
 
929
- # Flush job buffer (success case)
930
- E11y::JobBuffer.flush! if E11y.config.instruments.sidekiq.use_job_buffer
891
+ # Discard buffer on success (same as HTTP)
892
+ E11y::EphemeralBuffer.discard if E11y.config.ephemeral_buffer_enabled
931
893
 
932
894
  result
933
895
  rescue => error
@@ -942,8 +904,8 @@ module E11y
942
904
  backtrace: error.backtrace&.first(10)
943
905
  )
944
906
 
945
- # Flush job buffer on error (includes debug events)
946
- E11y::JobBuffer.flush_on_error! if E11y.config.instruments.sidekiq.use_job_buffer
907
+ # Flush buffer on error (includes debug events)
908
+ E11y::EphemeralBuffer.flush_on_error if E11y.config.ephemeral_buffer_enabled
947
909
 
948
910
  raise
949
911
  ensure
@@ -976,8 +938,8 @@ module E11y
976
938
  module Sidekiq
977
939
  class ClientMiddleware
978
940
  def call(worker_class, job, queue, redis_pool)
979
- # Propagate trace context to job
980
- job['e11y_trace_id'] = E11y::Current.trace_id
941
+ # C17 Hybrid: Propagate parent trace (job will create NEW trace_id)
942
+ job['e11y_parent_trace_id'] = E11y::Current.trace_id if E11y::Current.trace_id
981
943
  job['e11y_span_id'] = E11y::TraceContext.generate_span_id
982
944
  job['e11y_sampled'] = E11y::Current.sampled # Trace-consistent sampling
983
945
 
@@ -997,89 +959,22 @@ module E11y
997
959
  end
998
960
  ```
999
961
 
1000
- ### 5.3. Job-Scoped Buffer (Optional Feature)
962
+ ### 5.3. Buffer for Jobs
1001
963
 
1002
- **Design Decision:** Similar to request-scoped buffer, jobs can have their own buffer for debug events.
964
+ **Design Decision:** Jobs reuse the same `EphemeralBuffer` as HTTP requests. Same semantics: buffer debug events, flush on error, discard on success. Optional job-specific config allows tuning for longer-running jobs.
1003
965
 
1004
966
  ```ruby
1005
967
  # config/initializers/e11y.rb
1006
968
  E11y.configure do |config|
1007
- config.instruments.sidekiq do
1008
- # Enable job-scoped buffer (like request-scoped buffer)
1009
- use_job_buffer true
1010
-
1011
- job_buffer do
1012
- # Buffer debug events during job execution
1013
- buffer_severities [:debug]
1014
-
1015
- # Flush conditions
1016
- flush_on do
1017
- error true # Flush debug events if job fails
1018
- success false # Discard debug events if job succeeds
1019
- interval 5.seconds # Or flush every 5 seconds
1020
- end
1021
-
1022
- # Max buffer size per job
1023
- max_events 1000
1024
- end
1025
- end
1026
- end
1027
- ```
1028
-
1029
- **How it works:**
1030
-
1031
- ```ruby
1032
- class InvoiceGenerationWorker
1033
- include Sidekiq::Worker
969
+ # Shared buffer for HTTP and jobs
970
+ config.ephemeral_buffer_enabled = true
1034
971
 
1035
- def perform(order_id)
1036
- # Debug events are buffered
1037
- Events::Debug::FetchOrder.track(order_id: order_id)
1038
-
1039
- order = Order.find(order_id)
1040
-
1041
- Events::Debug::ValidateOrder.track(order_id: order_id, valid: order.valid?)
1042
-
1043
- if order.invalid?
1044
- # Job fails → debug events are flushed to adapters
1045
- raise "Invalid order"
1046
- end
1047
-
1048
- # Job succeeds → debug events are discarded
1049
- Events::InvoiceGenerated.track(order_id: order_id)
1050
- end
972
+ # Optional: job-specific overrides (jobs can run longer → more debug events)
973
+ config.ephemeral_buffer_job_buffer_limit = 500 # nil = use default (100)
1051
974
  end
1052
975
  ```
1053
976
 
1054
- **Job Buffer Lifecycle:**
1055
-
1056
- ```mermaid
1057
- sequenceDiagram
1058
- participant Worker as Sidekiq Worker
1059
- participant JobBuffer as Job Buffer
1060
- participant MainBuffer as Main Buffer
1061
- participant Adapters as Adapters
1062
-
1063
- Worker->>JobBuffer: Start job buffer
1064
-
1065
- Note over Worker: Job execution starts
1066
-
1067
- Worker->>JobBuffer: Track debug event
1068
- JobBuffer->>JobBuffer: Buffer (not flushed)
1069
-
1070
- Worker->>MainBuffer: Track info event
1071
- MainBuffer->>Adapters: Flush immediately (normal flow)
1072
-
1073
- alt Job succeeds
1074
- Worker->>JobBuffer: flush! (success)
1075
- JobBuffer->>JobBuffer: Discard debug events
1076
- Note over JobBuffer: Debug events never sent
1077
- else Job fails
1078
- Worker->>JobBuffer: flush_on_error!
1079
- JobBuffer->>MainBuffer: Move debug events to main buffer
1080
- MainBuffer->>Adapters: Flush all events (including debug)
1081
- end
1082
- ```
977
+ **Rationale:** Single buffer implementation, single config. Jobs may need higher `job_buffer_limit` when they process many items and emit more debug events than a typical HTTP request.
1083
978
 
1084
979
  ---
1085
980
 
@@ -1100,21 +995,21 @@ sequenceDiagram
1100
995
  ClientMW->>ClientMW: Extract trace_id from Current
1101
996
  ClientMW->>Redis: Store job + trace metadata
1102
997
 
1103
- Note over Redis: job['e11y_trace_id'] = 'abc123'<br/>job['e11y_span_id'] = 'span002'<br/>job['e11y_sampled'] = true
998
+ Note over Redis: job['e11y_parent_trace_id'] = 'abc123'<br/>job['e11y_span_id'] = 'span002'<br/>job['e11y_sampled'] = true
1104
999
 
1105
1000
  ClientMW->>E11y: Track Enqueued event
1106
1001
 
1107
1002
  Note over ServerMW: Later... job dequeued
1108
1003
 
1109
1004
  Redis->>ServerMW: Fetch job
1110
- ServerMW->>ServerMW: Extract trace_id from job
1005
+ ServerMW->>ServerMW: C17: new trace_id, parent_trace_id from job
1111
1006
  ServerMW->>E11y: Restore Current context
1112
1007
 
1113
- Note over E11y: Current.trace_id = 'abc123'<br/>Current.parent_span_id = 'span002'
1008
+ Note over E11y: Current.trace_id = NEW<br/>Current.parent_trace_id = 'abc123'<br/>Current.parent_span_id = 'span002'
1114
1009
 
1115
1010
  ServerMW->>E11y: Track Started event
1116
1011
  ServerMW->>Worker: perform
1117
- Worker->>E11y: Track custom events (same trace_id!)
1012
+ Worker->>E11y: Track events (linked via parent_trace_id!)
1118
1013
  ServerMW->>E11y: Track Completed event
1119
1014
  ```
1120
1015
 
@@ -1122,6 +1017,13 @@ sequenceDiagram
1122
1017
 
1123
1018
  ## 6. ActiveJob Integration
1124
1019
 
1020
+ **Implementation Note:** Job lifecycle events (`Enqueued`, `Started`, `Completed`, `Failed`) come from **RailsInstrumentation** (ASN), not from ActiveJob callbacks. ActiveJob callbacks handle trace context propagation, request-scoped buffer, and SLO tracking only. This design:
1021
+ - Uses ASN as single source for all queue adapters (Sidekiq, Resque, Solid Queue, etc.)
1022
+ - Avoids duplicate emission when ActiveJob uses Sidekiq as adapter
1023
+ - Keeps callbacks focused on context/buffer/SLO
1024
+
1025
+ **perform.active_job routing:** When payload contains `exception`, RailsInstrumentation routes to `Events::Rails::Job::Failed` (with `error_class`, `error_message`); otherwise to `Completed`.
1026
+
1125
1027
  ### 6.1. Callbacks Integration
1126
1028
 
1127
1029
  ```ruby
@@ -1140,20 +1042,24 @@ module E11y
1140
1042
  private
1141
1043
 
1142
1044
  def e11y_track_job_execution
1143
- # Extract trace context
1144
- trace_id = job_metadata['e11y_trace_id'] || E11y::TraceContext.generate_id
1045
+ # C17 Hybrid: Job gets NEW trace_id, parent_trace_id links to enqueuer
1046
+ parent_trace_id = job_metadata['e11y_parent_trace_id']
1047
+ trace_id = E11y::TraceContext.generate_id
1145
1048
  parent_span_id = job_metadata['e11y_span_id']
1146
1049
 
1147
1050
  E11y::Current.set(
1148
1051
  trace_id: trace_id,
1052
+ parent_trace_id: parent_trace_id,
1149
1053
  parent_span_id: parent_span_id,
1150
1054
  job_id: job_id,
1151
1055
  job_class: self.class.name
1152
1056
  )
1153
1057
 
1154
- # Start job-scoped buffer (optional, configurable)
1155
- if E11y.config.instruments.active_job.use_job_buffer
1156
- E11y::JobBuffer.start!
1058
+ # Start request-scoped buffer (same as HTTP; config.ephemeral_buffer_enabled)
1059
+ if E11y.config.ephemeral_buffer_enabled
1060
+ limit = E11y.config.ephemeral_buffer_job_buffer_limit ||
1061
+ E11y::Buffers::EphemeralBuffer::DEFAULT_BUFFER_LIMIT
1062
+ E11y::EphemeralBuffer.initialize!(buffer_limit: limit)
1157
1063
  end
1158
1064
 
1159
1065
  Events::Rails::Job::Started.track(
@@ -1174,8 +1080,8 @@ module E11y
1174
1080
  duration: (Time.now - start_time) * 1000
1175
1081
  )
1176
1082
 
1177
- # Flush job buffer (success case)
1178
- E11y::JobBuffer.flush! if E11y.config.instruments.active_job.use_job_buffer
1083
+ # Discard buffer on success (same as HTTP)
1084
+ E11y::EphemeralBuffer.discard if E11y.config.ephemeral_buffer_enabled
1179
1085
  rescue => error
1180
1086
  Events::Rails::Job::Failed.track(
1181
1087
  job_class: self.class.name,
@@ -1185,8 +1091,8 @@ module E11y
1185
1091
  error_message: error.message
1186
1092
  )
1187
1093
 
1188
- # Flush job buffer on error (includes debug events)
1189
- E11y::JobBuffer.flush_on_error! if E11y.config.instruments.active_job.use_job_buffer
1094
+ # Flush buffer on error (includes debug events)
1095
+ E11y::EphemeralBuffer.flush_on_error if E11y.config.ephemeral_buffer_enabled
1190
1096
 
1191
1097
  raise
1192
1098
  ensure
@@ -1195,8 +1101,8 @@ module E11y
1195
1101
  end
1196
1102
 
1197
1103
  def e11y_track_job_enqueued
1198
- # Store trace context in job metadata
1199
- job_metadata['e11y_trace_id'] = E11y::Current.trace_id
1104
+ # C17 Hybrid: Store parent trace (job will create NEW trace_id)
1105
+ job_metadata['e11y_parent_trace_id'] = E11y::Current.trace_id if E11y::Current.trace_id
1200
1106
  job_metadata['e11y_span_id'] = E11y::TraceContext.generate_span_id
1201
1107
  job_metadata['e11y_sampled'] = E11y::Current.sampled
1202
1108
 
@@ -1235,7 +1141,7 @@ module E11y
1235
1141
  module Logger
1236
1142
  class Bridge
1237
1143
  def self.setup!
1238
- return unless E11y.config.logger_bridge.enabled
1144
+ return unless E11y.config.logger_bridge_enabled
1239
1145
 
1240
1146
  # Replace Rails.logger
1241
1147
  Rails.logger = Bridge.new(Rails.logger)
@@ -1304,17 +1210,10 @@ module E11y
1304
1210
  # Extract message
1305
1211
  msg = message || (block_given? ? block.call : nil)
1306
1212
 
1307
- # Track via E11y
1308
- Events::RailsLogger.track(
1309
- severity: severity,
1310
- message: msg.to_s,
1311
- caller_location: extract_caller_location
1312
- )
1213
+ # Track via E11y (filtered by track_severities, ignore_patterns)
1214
+ event_class_for_severity(severity).track(message: msg.to_s, caller_location: extract_caller_location)
1313
1215
 
1314
- # Also log to original logger (dual logging)
1315
- if @original_logger && E11y.config.logger_bridge.dual_logging
1316
- @original_logger.public_send(severity, msg)
1317
- end
1216
+ # Always delegate to original logger (SimpleDelegator super)
1318
1217
  end
1319
1218
 
1320
1219
  def extract_caller_location
@@ -1330,50 +1229,39 @@ module E11y
1330
1229
  end
1331
1230
  ```
1332
1231
 
1333
- ### 7.2. Migration Strategy
1232
+ ### 7.2. Configuration
1334
1233
 
1335
1234
  ```ruby
1336
1235
  # config/initializers/e11y.rb
1337
1236
  E11y.configure do |config|
1338
- config.logger_bridge do
1339
- enabled true
1340
-
1341
- # Dual logging (E11y + original Rails.logger)
1342
- dual_logging true # Keep writing to log/production.log
1343
-
1344
- # Which severities to track
1345
- track_severities [:info, :warn, :error, :fatal]
1346
-
1347
- # Skip noisy log messages
1348
- ignore_patterns [
1349
- /Started GET/,
1350
- /Completed \d+ OK/,
1351
- /CACHE/
1352
- ]
1353
-
1354
- # Sample high-volume logs
1355
- sample_rate 0.1 # 10% of logs
1356
-
1357
- # Enrich with Rails context
1358
- enrich_with_context true
1359
- context_fields [:controller, :action, :request_id]
1360
- end
1237
+ config.logger_bridge_enabled = true
1238
+
1239
+ # Which severities to track (nil = all)
1240
+ config.logger_bridge_track_severities = [:info, :warn, :error, :fatal]
1241
+
1242
+ # Skip noisy log messages (regex or string)
1243
+ config.logger_bridge_ignore_patterns = [
1244
+ /Started GET/,
1245
+ /Completed \d+ OK/,
1246
+ /CACHE/
1247
+ ]
1361
1248
  end
1362
1249
  ```
1363
1250
 
1251
+ **Context:** Events are enriched with `trace_id`, `request_id`, `span_id`, `user_id` from `E11y::Current` via `Event::Base.build_context` — no separate config needed.
1252
+
1364
1253
  ---
1365
1254
 
1366
1255
  ## 8. Middleware Integration
1367
1256
 
1368
- ### 8.0. Three Buffer Types (Summary)
1257
+ ### 8.0. Buffer Types (Summary)
1369
1258
 
1370
- **E11y has 3 independent buffer types:**
1259
+ **E11y uses a single EphemeralBuffer for both HTTP and jobs:**
1371
1260
 
1372
- | Buffer Type | Purpose | Lifecycle | Use Case |
1373
- |-------------|---------|-----------|----------|
1374
- | **Main Buffer** | All events (info+) | Global, flush every 200ms | Normal event tracking |
1375
- | **Request Buffer** | Debug events in HTTP requests | Per-request, flush on error | HTTP request debugging |
1376
- | **Job Buffer** | Debug events in background jobs | Per-job, flush on error | Background job debugging |
1261
+ | Buffer | Purpose | Lifecycle | Config |
1262
+ |--------|---------|-----------|--------|
1263
+ | **Request Buffer** | Debug events (HTTP + jobs) | Per-request/job, flush on error, discard on success | `config.ephemeral_buffer_enabled`, `ephemeral_buffer_job_buffer_limit` |
1264
+ | **Main Buffer** | All events (info+) | Global, flush every 200ms | |
1377
1265
 
1378
1266
  **Diagram:**
1379
1267
 
@@ -1381,20 +1269,20 @@ end
1381
1269
  graph TB
1382
1270
  subgraph "HTTP Request"
1383
1271
  HTTPEvent[Event tracked] --> Decision1{Severity?}
1384
- Decision1 -->|:debug| RequestBuffer[Request Buffer]
1272
+ Decision1 -->|:debug| [Request Buffer]
1385
1273
  Decision1 -->|:info+| MainBuffer[Main Buffer]
1386
1274
 
1387
- RequestBuffer --> OnError1{Request failed?}
1275
+ --> OnError1{Request failed?}
1388
1276
  OnError1 -->|Yes| MainBuffer
1389
1277
  OnError1 -->|No| Discard1[Discard]
1390
1278
  end
1391
1279
 
1392
1280
  subgraph "Background Job"
1393
1281
  JobEvent[Event tracked] --> Decision2{Severity?}
1394
- Decision2 -->|:debug| JobBuffer[Job Buffer]
1282
+ Decision2 -->|:debug|
1395
1283
  Decision2 -->|:info+| MainBuffer2[Main Buffer]
1396
1284
 
1397
- JobBuffer --> OnError2{Job failed?}
1285
+ --> OnError2{Job failed?}
1398
1286
  OnError2 -->|Yes| MainBuffer2
1399
1287
  OnError2 -->|No| Discard2[Discard]
1400
1288
  end
@@ -1405,8 +1293,7 @@ graph TB
1405
1293
  Interval --> Adapters[Flush to Adapters]
1406
1294
  end
1407
1295
 
1408
- style RequestBuffer fill:#fff3cd
1409
- style JobBuffer fill:#d4edda
1296
+ style fill:#fff3cd
1410
1297
  style MainBuffer fill:#d1ecf1
1411
1298
  style MainBuffer2 fill:#d1ecf1
1412
1299
  ```
@@ -1415,16 +1302,9 @@ graph TB
1415
1302
 
1416
1303
  ```ruby
1417
1304
  E11y.configure do |config|
1418
- # Main buffer (always enabled)
1419
- config.buffer.flush_interval = 200.milliseconds
1420
- config.buffer.max_size = 10_000
1421
-
1422
- # Request-scoped buffer (HTTP only)
1423
- config.instruments.rack_middleware.use_request_buffer = true
1424
-
1425
- # Job-scoped buffer (Sidekiq + ActiveJob)
1426
- config.instruments.sidekiq.use_job_buffer = true
1427
- config.instruments.active_job.use_job_buffer = true
1305
+ # Request-scoped buffer (shared for HTTP and jobs)
1306
+ config.ephemeral_buffer_enabled = true
1307
+ config.ephemeral_buffer_job_buffer_limit = 500 # Optional: higher limit for jobs (nil = default 100)
1428
1308
  end
1429
1309
  ```
1430
1310
 
@@ -1458,12 +1338,8 @@ module E11y
1458
1338
  user_agent: request.user_agent
1459
1339
  )
1460
1340
 
1461
- # Start request-scoped buffer (for debug events)
1462
- # Note: This is ONLY for HTTP requests, not for jobs
1463
- # Jobs have their own JobBuffer (see Sidekiq/ActiveJob sections)
1464
- if E11y.config.instruments.rack_middleware.use_request_buffer
1465
- E11y::RequestBuffer.start!
1466
- end
1341
+ # Start request-scoped buffer (for debug events; shared with jobs)
1342
+ E11y::EphemeralBuffer.initialize! if E11y.config.ephemeral_buffer&.enabled
1467
1343
 
1468
1344
  # Track request start
1469
1345
  start_time = Time.now
@@ -1501,17 +1377,13 @@ module E11y
1501
1377
  error_message: error.message
1502
1378
  )
1503
1379
 
1504
- # Flush request buffer (includes debug events on error)
1505
- if E11y.config.instruments.rack_middleware.use_request_buffer
1506
- E11y::RequestBuffer.flush_on_error!
1507
- end
1380
+ # Flush buffer on error (includes debug events)
1381
+ E11y::EphemeralBuffer.flush_on_error if E11y.config.ephemeral_buffer&.enabled
1508
1382
 
1509
1383
  raise
1510
1384
  ensure
1511
- # Flush request buffer (success case)
1512
- if E11y.config.instruments.rack_middleware.use_request_buffer && !error
1513
- E11y::RequestBuffer.flush!
1514
- end
1385
+ # Discard buffer on success (not on error; already flushed in rescue)
1386
+ E11y::EphemeralBuffer.discard if !$ERROR_INFO && E11y.config.ephemeral_buffer&.enabled
1515
1387
 
1516
1388
  # Reset context
1517
1389
  Current.reset
@@ -1563,10 +1435,10 @@ module E11y
1563
1435
  # E11y.stats
1564
1436
  def E11y.stats
1565
1437
  {
1566
- events_tracked: Registry.all_events.sum { |e| e.track_count },
1438
+ events_tracked: Registry.event_classes.sum { |e| e.track_count },
1567
1439
  events_in_buffer: Buffer.size,
1568
- adapters: Adapters::Registry.all.map { |a|
1569
- { name: a.name, healthy: a.healthy? }
1440
+ adapters: config.adapters.map { |name, a|
1441
+ { name: name, healthy: a.respond_to?(:healthy?) ? a.healthy? : true }
1570
1442
  },
1571
1443
  rate_limiter: {
1572
1444
  current_rate: RateLimiter.current_rate,
@@ -1588,17 +1460,17 @@ module E11y
1588
1460
 
1589
1461
  # E11y.events
1590
1462
  def E11y.events
1591
- Registry.all_events.map(&:name).sort
1463
+ Registry.event_classes.map(&:name).sort
1592
1464
  end
1593
1465
 
1594
1466
  # E11y.adapters
1595
1467
  def E11y.adapters
1596
- Adapters::Registry.all.map do |adapter|
1468
+ config.adapters.map do |name, adapter|
1597
1469
  {
1598
- name: adapter.name,
1470
+ name: name,
1599
1471
  class: adapter.class.name,
1600
- healthy: adapter.healthy?,
1601
- capabilities: adapter.capabilities
1472
+ healthy: adapter.respond_to?(:healthy?) ? adapter.healthy? : true,
1473
+ capabilities: adapter.respond_to?(:capabilities) ? adapter.capabilities : {}
1602
1474
  }
1603
1475
  end
1604
1476
  end
@@ -1606,7 +1478,7 @@ module E11y
1606
1478
  # E11y.reset!
1607
1479
  def E11y.reset!
1608
1480
  Buffer.clear!
1609
- RequestBuffer.clear!
1481
+ .clear!
1610
1482
  puts "✅ Buffers cleared"
1611
1483
  end
1612
1484
  end
@@ -1621,7 +1493,7 @@ module E11y
1621
1493
  )
1622
1494
 
1623
1495
  # Disable rate limiting in console
1624
- config.rate_limiting.enabled = false
1496
+ config.rate_limiting_enabled = false
1625
1497
 
1626
1498
  # Show all severities
1627
1499
  config.severity_threshold = :debug
@@ -1772,7 +1644,7 @@ RSpec.describe OrdersController, type: :controller do
1772
1644
  }.to have_enqueued_job(SendOrderEmailJob)
1773
1645
 
1774
1646
  job = ActiveJob::Base.queue_adapter.enqueued_jobs.last
1775
- expect(job[:args].first['e11y_trace_id']).to be_present
1647
+ expect(job[:args].first['e11y_parent_trace_id']).to be_present
1776
1648
  end
1777
1649
  end
1778
1650
  end
@@ -1819,10 +1691,10 @@ end
1819
1691
 
1820
1692
  ```ruby
1821
1693
  E11y.configure do |config|
1822
- config.instruments.active_support_notifications.enabled = false # Disable ASN
1823
- config.instruments.sidekiq.enabled = true # Keep Sidekiq
1824
- config.instruments.active_job.enabled = true # Keep ActiveJob
1825
- config.instruments.rack_middleware.enabled = true # Keep HTTP tracking
1694
+ config.rails_instrumentation_enabled = false # Disable ASN
1695
+ config.sidekiq_enabled = true # Keep Sidekiq
1696
+ config.active_job_enabled = true # Keep ActiveJob
1697
+ config.logger_bridge_enabled = true # Keep logger bridge
1826
1698
  end
1827
1699
  ```
1828
1700
 
@@ -1845,7 +1717,7 @@ end
1845
1717
  **No!** It's configurable:
1846
1718
 
1847
1719
  ```ruby
1848
- config.instruments.active_support_notifications do
1720
+ config.rails_instrumentation do
1849
1721
  enabled false # Completely disable ASN integration
1850
1722
  end
1851
1723
  ```
@@ -1853,26 +1725,24 @@ end
1853
1725
  You can also filter which ASN events to track:
1854
1726
 
1855
1727
  ```ruby
1856
- config.instruments.active_support_notifications do
1728
+ config.rails_instrumentation do
1857
1729
  track_patterns ['sql.active_record', 'process_action.*']
1858
- ignore_patterns ['render_partial.*', 'SCHEMA']
1730
+ ignore_events ['render_partial.*', 'SCHEMA']
1859
1731
  end
1860
1732
  ```
1861
1733
 
1862
1734
  ### Q4: Does request-scoped buffer work for Sidekiq/ActiveJob?
1863
1735
 
1864
- **No, they have their own job-scoped buffer!**
1736
+ **Yes. HTTP and jobs share the same EphemeralBuffer.**
1865
1737
 
1866
- - **Request Buffer** → HTTP requests only (Rack middleware)
1867
- - **Job Buffer** → Sidekiq + ActiveJob (separate buffer per job)
1738
+ - **Request Buffer** → HTTP requests and jobs (same buffer, same semantics)
1868
1739
  - **Main Buffer** → Global buffer for all info+ events
1869
1740
 
1870
- All 3 buffers are independent and configurable:
1741
+ Config:
1871
1742
 
1872
1743
  ```ruby
1873
- config.instruments.rack_middleware.use_request_buffer = true # HTTP
1874
- config.instruments.sidekiq.use_job_buffer = true # Sidekiq
1875
- config.instruments.active_job.use_job_buffer = true # ActiveJob
1744
+ config.ephemeral_buffer_enabled = true
1745
+ config.ephemeral_buffer_job_buffer_limit = 500 # Optional: higher limit for jobs (nil = default 100)
1876
1746
  ```
1877
1747
 
1878
1748
  ### Q5: How do I customize built-in Rails events?
@@ -1880,7 +1750,7 @@ config.instruments.active_job.use_job_buffer = true # ActiveJob
1880
1750
  **Option A: Override with custom event class:**
1881
1751
 
1882
1752
  ```ruby
1883
- config.instruments.active_support_notifications do
1753
+ config.rails_instrumentation do
1884
1754
  custom_mappings do
1885
1755
  map 'sql.active_record', to: MyApp::Events::CustomDatabaseQuery
1886
1756
  end
@@ -1890,7 +1760,7 @@ end
1890
1760
  **Option B: Disable built-in events entirely:**
1891
1761
 
1892
1762
  ```ruby
1893
- config.instruments.active_support_notifications do
1763
+ config.rails_instrumentation do
1894
1764
  use_built_in_events false # No automatic mapping
1895
1765
 
1896
1766
  # Manually handle ASN events