e11y 0.2.0 → 1.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 (288) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +130 -10
  3. data/CHANGELOG.md +80 -1
  4. data/CLAUDE.md +168 -0
  5. data/CONTRIBUTING.md +640 -0
  6. data/README.md +165 -701
  7. data/RELEASE.md +41 -12
  8. data/Rakefile +249 -57
  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 +79 -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} +36 -65
  30. data/docs/{ADR-002-metrics-yabeda.md → architecture/ADR-002-metrics-yabeda.md} +62 -236
  31. data/docs/architecture/ADR-003-slo-observability.md +1402 -0
  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} +182 -743
  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} +44 -86
  40. data/docs/{ADR-012-event-evolution.md → architecture/ADR-012-event-evolution.md} +11 -11
  41. data/docs/{ADR-013-reliability-error-handling.md → architecture/ADR-013-reliability-error-handling.md} +37 -12
  42. data/docs/{ADR-014-event-driven-slo.md → architecture/ADR-014-event-driven-slo.md} +12 -24
  43. data/docs/{ADR-015-middleware-order.md → architecture/ADR-015-middleware-order.md} +43 -59
  44. data/docs/{ADR-016-self-monitoring-slo.md → architecture/ADR-016-self-monitoring-slo.md} +58 -355
  45. data/docs/{ADR-017-multi-rails-compatibility.md → architecture/ADR-017-multi-rails-compatibility.md} +4 -11
  46. data/docs/architecture/ADR-018-memory-optimization.md +366 -0
  47. data/docs/{ADR-INDEX.md → architecture/ADR-INDEX.md} +11 -6
  48. data/docs/plans/2026-03-20-browser-overlay-svelte.md +281 -0
  49. data/docs/{00-ICP-AND-TIMELINE.md → prd/00-ICP-AND-TIMELINE.md} +6 -6
  50. data/docs/{01-SCALE-REQUIREMENTS.md → prd/01-SCALE-REQUIREMENTS.md} +6 -6
  51. data/docs/prd/01-overview-vision.md +19 -14
  52. data/docs/use_cases/README.md +22 -23
  53. data/docs/use_cases/UC-001-request-scoped-debug-buffering.md +50 -44
  54. data/docs/use_cases/UC-002-business-event-tracking.md +26 -95
  55. data/docs/use_cases/UC-003-event-metrics.md +66 -0
  56. data/docs/use_cases/UC-004-zero-config-slo-tracking.md +33 -684
  57. data/docs/use_cases/UC-005-sentry-integration.md +13 -15
  58. data/docs/use_cases/UC-006-trace-context-management.md +30 -28
  59. data/docs/use_cases/UC-007-pii-filtering.md +35 -87
  60. data/docs/use_cases/UC-008-opentelemetry-integration.md +51 -89
  61. data/docs/use_cases/UC-009-multi-service-tracing.md +30 -178
  62. data/docs/use_cases/UC-010-background-job-tracking.md +24 -91
  63. data/docs/use_cases/UC-011-rate-limiting.md +95 -168
  64. data/docs/use_cases/UC-012-audit-trail.md +21 -46
  65. data/docs/use_cases/UC-013-high-cardinality-protection.md +29 -167
  66. data/docs/use_cases/UC-014-adaptive-sampling.md +2 -2
  67. data/docs/use_cases/UC-015-cost-optimization.md +46 -99
  68. data/docs/use_cases/UC-016-rails-logger-migration.md +39 -213
  69. data/docs/use_cases/UC-017-local-development.md +203 -777
  70. data/docs/use_cases/UC-018-testing-events.md +3 -3
  71. data/docs/use_cases/UC-019-retention-based-routing.md +53 -106
  72. data/docs/use_cases/UC-020-event-versioning.md +8 -9
  73. data/docs/use_cases/UC-021-error-handling-retry-dlq.md +18 -22
  74. data/docs/use_cases/UC-022-event-registry.md +15 -21
  75. data/docs/use_cases/backlog.md +119 -87
  76. data/e11y.gemspec +2 -2
  77. data/gems/e11y-devtools/README.md +158 -0
  78. data/gems/e11y-devtools/config/routes.rb +15 -0
  79. data/gems/e11y-devtools/e11y-devtools.gemspec +25 -0
  80. data/gems/e11y-devtools/exe/e11y +34 -0
  81. data/gems/e11y-devtools/frontend/.gitignore +24 -0
  82. data/gems/e11y-devtools/frontend/README.md +51 -0
  83. data/gems/e11y-devtools/frontend/index.html +14 -0
  84. data/gems/e11y-devtools/frontend/package-lock.json +3707 -0
  85. data/gems/e11y-devtools/frontend/package.json +28 -0
  86. data/gems/e11y-devtools/frontend/public/mocks/v1/events/recent.json +4205 -0
  87. data/gems/e11y-devtools/frontend/public/mocks/v1/interactions.json +194 -0
  88. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/0a2e04027cfa22d014bc22e8b27cd913/events.json +86 -0
  89. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/0e1543af6a630fb3af6b52283154b3e0/events.json +169 -0
  90. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/1838b691faa49564f97db8592ff3978d/events.json +78 -0
  91. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/29f198f6588dacffb687777eb5f8f118/events.json +197 -0
  92. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/34bc3c9c0097de28a7a6f99b90a8e7bc/events.json +194 -0
  93. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/3ba6c20d068ab9cee00e51b180e66444/events.json +184 -0
  94. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/435bfd8f17b9009146a79812d7c3726d/events.json +144 -0
  95. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/4c7676e3fe668e99edb2b94d7d5678a9/events.json +222 -0
  96. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/6daf0d47974bedfc55d5de7004a3ea9f/events.json +194 -0
  97. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/8a81ada42834d15f287bb40010043605/events.json +194 -0
  98. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/8c0a98900edaae105469df8daedccf02/events.json +198 -0
  99. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/8e4f645180f8a7d1dce426b07380466b/events.json +222 -0
  100. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/93db346fa5d44a032605a13b627f4b80/events.json +128 -0
  101. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/98ff6146faf7bd9be8bd03a8275817ba/events.json +223 -0
  102. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/9997ddd0247bc7e25f2ca7a5c415c93d/events.json +197 -0
  103. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/99e35f8ef3baedd798cc4fd085980ad9/events.json +194 -0
  104. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/b4f3095c1909924cbc98889a86c83d6d/events.json +131 -0
  105. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/b54b7fc32b7575a7110de809d11ccda0/events.json +128 -0
  106. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/c0b48033fa06746bcc5886745e053cff/events.json +169 -0
  107. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/c44649ac76701b4558927cd2305ab535/events.json +169 -0
  108. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/d601ae3320057580a39dbdac2edfdf4a/events.json +248 -0
  109. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/e67e724bab422d2b52eeb49635e512e1/events.json +194 -0
  110. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/e6c72765a28f158a8485b35fa63f73da/events.json +194 -0
  111. data/gems/e11y-devtools/frontend/public/mocks/v1/traces/f541b87405c9a54819b18ebe529f6419/events.json +194 -0
  112. data/gems/e11y-devtools/frontend/scripts/generate_mocks.rb +397 -0
  113. data/gems/e11y-devtools/frontend/src/App.svelte +827 -0
  114. data/gems/e11y-devtools/frontend/src/components/Fab.svelte +19 -0
  115. data/gems/e11y-devtools/frontend/src/components/FilterBar.svelte +38 -0
  116. data/gems/e11y-devtools/frontend/src/components/FullscreenPanel.svelte +82 -0
  117. data/gems/e11y-devtools/frontend/src/components/InteractionsTimeline.svelte +264 -0
  118. data/gems/e11y-devtools/frontend/src/components/RecentHistogram.svelte +354 -0
  119. data/gems/e11y-devtools/frontend/src/lib/api.ts +37 -0
  120. data/gems/e11y-devtools/frontend/src/lib/eventIdentity.ts +12 -0
  121. data/gems/e11y-devtools/frontend/src/lib/format.ts +37 -0
  122. data/gems/e11y-devtools/frontend/src/lib/listFilter.ts +43 -0
  123. data/gems/e11y-devtools/frontend/src/lib/recentVolume.ts +80 -0
  124. data/gems/e11y-devtools/frontend/src/lib/router.ts +12 -0
  125. data/gems/e11y-devtools/frontend/src/lib/transitions.ts +34 -0
  126. data/gems/e11y-devtools/frontend/src/lib/viewportOrigin.ts +25 -0
  127. data/gems/e11y-devtools/frontend/src/main.ts +8 -0
  128. data/gems/e11y-devtools/frontend/src/overlay-entry.ts +24 -0
  129. data/gems/e11y-devtools/frontend/src/overlay.css +1080 -0
  130. data/gems/e11y-devtools/frontend/svelte.config.js +2 -0
  131. data/gems/e11y-devtools/frontend/test_puppeteer.js +41 -0
  132. data/gems/e11y-devtools/frontend/test_scale.js +3 -0
  133. data/gems/e11y-devtools/frontend/tsconfig.app.json +21 -0
  134. data/gems/e11y-devtools/frontend/tsconfig.json +7 -0
  135. data/gems/e11y-devtools/frontend/tsconfig.node.json +26 -0
  136. data/gems/e11y-devtools/frontend/vite.config.ts +36 -0
  137. data/gems/e11y-devtools/lib/e11y/devtools/mcp/server.rb +96 -0
  138. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tool_base.rb +25 -0
  139. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/clear.rb +31 -0
  140. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/errors.rb +35 -0
  141. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/event_detail.rb +33 -0
  142. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/events_by_trace.rb +33 -0
  143. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/interactions.rb +40 -0
  144. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/recent_events.rb +34 -0
  145. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/search.rb +34 -0
  146. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/stats.rb +30 -0
  147. data/gems/e11y-devtools/lib/e11y/devtools/overlay/assets/overlay.js +20 -0
  148. data/gems/e11y-devtools/lib/e11y/devtools/overlay/controller.rb +94 -0
  149. data/gems/e11y-devtools/lib/e11y/devtools/overlay/engine.rb +26 -0
  150. data/gems/e11y-devtools/lib/e11y/devtools/overlay/middleware.rb +80 -0
  151. data/gems/e11y-devtools/lib/e11y/devtools/overlay/rails_controller.rb +67 -0
  152. data/gems/e11y-devtools/lib/e11y/devtools/tui/app.rb +262 -0
  153. data/gems/e11y-devtools/lib/e11y/devtools/tui/grouping.rb +66 -0
  154. data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/event_detail.rb +62 -0
  155. data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/event_list.rb +70 -0
  156. data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/interaction_list.rb +47 -0
  157. data/gems/e11y-devtools/lib/e11y/devtools/version.rb +8 -0
  158. data/gems/e11y-devtools/lib/e11y/devtools.rb +13 -0
  159. data/gems/e11y-devtools/spec/e11y/devtools/mcp/tools_spec.rb +107 -0
  160. data/gems/e11y-devtools/spec/e11y/devtools/overlay/controller_spec.rb +91 -0
  161. data/gems/e11y-devtools/spec/e11y/devtools/overlay/middleware_spec.rb +46 -0
  162. data/gems/e11y-devtools/spec/e11y/devtools/tui/app_spec.rb +85 -0
  163. data/gems/e11y-devtools/spec/e11y/devtools/tui/grouping_spec.rb +64 -0
  164. data/gems/e11y-devtools/spec/spec_helper.rb +5 -0
  165. data/gems/e11y-devtools/spec/tui/widgets/event_list_spec.rb +44 -0
  166. data/gems/e11y-devtools/spec/tui/widgets/interaction_list_spec.rb +62 -0
  167. data/lib/e11y/adapters/audit_encrypted.rb +53 -11
  168. data/lib/e11y/adapters/base.rb +33 -34
  169. data/lib/e11y/adapters/dev_log/file_store.rb +143 -0
  170. data/lib/e11y/adapters/dev_log/query.rb +219 -0
  171. data/lib/e11y/adapters/dev_log.rb +118 -0
  172. data/lib/e11y/adapters/file.rb +3 -6
  173. data/lib/e11y/adapters/in_memory.rb +52 -5
  174. data/lib/e11y/adapters/in_memory_test.rb +29 -0
  175. data/lib/e11y/adapters/loki.rb +58 -23
  176. data/lib/e11y/adapters/null.rb +82 -0
  177. data/lib/e11y/adapters/opentelemetry_collector.rb +183 -0
  178. data/lib/e11y/adapters/otel_logs.rb +136 -23
  179. data/lib/e11y/adapters/sentry.rb +4 -7
  180. data/lib/e11y/adapters/stdout.rb +73 -7
  181. data/lib/e11y/adapters/yabeda.rb +153 -29
  182. data/lib/e11y/buffers/adaptive_buffer.rb +3 -17
  183. data/lib/e11y/buffers/{request_scoped_buffer.rb → ephemeral_buffer.rb} +72 -58
  184. data/lib/e11y/buffers/ring_buffer.rb +3 -16
  185. data/lib/e11y/configuration.rb +272 -0
  186. data/lib/e11y/console.rb +10 -17
  187. data/lib/e11y/current.rb +53 -1
  188. data/lib/e11y/debug/pipeline_inspector.rb +96 -0
  189. data/lib/e11y/documentation/generator.rb +48 -0
  190. data/lib/e11y/event/base.rb +176 -82
  191. data/lib/e11y/event/value_sampling_config.rb +1 -5
  192. data/lib/e11y/events/rails/database/query.rb +1 -4
  193. data/lib/e11y/events/rails/job/failed.rb +2 -0
  194. data/lib/e11y/instruments/active_job.rb +44 -12
  195. data/lib/e11y/instruments/rails_instrumentation.rb +49 -24
  196. data/lib/e11y/instruments/sidekiq.rb +135 -31
  197. data/lib/e11y/linters/base.rb +11 -0
  198. data/lib/e11y/linters/pii/pii_declaration_linter.rb +120 -0
  199. data/lib/e11y/linters/slo/config_consistency_linter.rb +76 -0
  200. data/lib/e11y/linters/slo/explicit_declaration_linter.rb +36 -0
  201. data/lib/e11y/linters/slo/slo_status_from_linter.rb +41 -0
  202. data/lib/e11y/logger/bridge.rb +26 -7
  203. data/lib/e11y/metrics/cardinality_protection.rb +10 -15
  204. data/lib/e11y/metrics/cardinality_tracker.rb +16 -6
  205. data/lib/e11y/metrics/registry.rb +3 -5
  206. data/lib/e11y/metrics/test_backend.rb +62 -0
  207. data/lib/e11y/metrics.rb +56 -10
  208. data/lib/e11y/middleware/adapter_resolver.rb +40 -0
  209. data/lib/e11y/middleware/audit_signing.rb +43 -6
  210. data/lib/e11y/middleware/baggage_protection.rb +75 -0
  211. data/lib/e11y/middleware/dev_log_source.rb +24 -0
  212. data/lib/e11y/middleware/event_slo.rb +23 -9
  213. data/lib/e11y/middleware/otel_span.rb +23 -0
  214. data/lib/e11y/middleware/pii_filter.rb +104 -75
  215. data/lib/e11y/middleware/rate_limiting.rb +54 -27
  216. data/lib/e11y/middleware/request.rb +70 -23
  217. data/lib/e11y/middleware/routing.rb +78 -21
  218. data/lib/e11y/middleware/sampling.rb +66 -17
  219. data/lib/e11y/middleware/self_monitoring_emit.rb +39 -0
  220. data/lib/e11y/middleware/trace_context.rb +45 -10
  221. data/lib/e11y/middleware/track_latency.rb +34 -0
  222. data/lib/e11y/middleware/validation.rb +7 -16
  223. data/lib/e11y/middleware/versioning.rb +26 -22
  224. data/lib/e11y/opentelemetry/semantic_conventions.rb +109 -0
  225. data/lib/e11y/opentelemetry/span_creator.rb +142 -0
  226. data/lib/e11y/pii/patterns.rb +12 -1
  227. data/lib/e11y/pipeline/builder.rb +4 -4
  228. data/lib/e11y/presets/audit_event.rb +13 -2
  229. data/lib/e11y/railtie.rb +52 -14
  230. data/lib/e11y/registry.rb +306 -0
  231. data/lib/e11y/reliability/circuit_breaker.rb +19 -21
  232. data/lib/e11y/reliability/dlq/base.rb +71 -0
  233. data/lib/e11y/reliability/dlq/file_adapter.rb +301 -0
  234. data/lib/e11y/reliability/dlq/file_storage.rb +63 -34
  235. data/lib/e11y/reliability/dlq/filter.rb +37 -54
  236. data/lib/e11y/reliability/retry_handler.rb +26 -29
  237. data/lib/e11y/reliability/retry_rate_limiter.rb +3 -11
  238. data/lib/e11y/sampling/error_spike_detector.rb +0 -2
  239. data/lib/e11y/sampling/load_monitor.rb +5 -9
  240. data/lib/e11y/sampling/stratified_tracker.rb +18 -0
  241. data/lib/e11y/self_monitoring/buffer_monitor.rb +2 -0
  242. data/lib/e11y/self_monitoring/performance_monitor.rb +19 -61
  243. data/lib/e11y/self_monitoring/reliability_monitor.rb +4 -74
  244. data/lib/e11y/slo/config_loader.rb +40 -0
  245. data/lib/e11y/slo/config_validator.rb +58 -0
  246. data/lib/e11y/slo/dashboard_generator.rb +122 -0
  247. data/lib/e11y/slo/event_driven.rb +8 -0
  248. data/lib/e11y/slo/tracker.rb +31 -4
  249. data/lib/e11y/testing/have_tracked_event_matcher.rb +190 -0
  250. data/lib/e11y/testing/rspec_matchers.rb +21 -0
  251. data/lib/e11y/testing/snapshot_matcher.rb +86 -0
  252. data/lib/e11y/trace_context/sampler.rb +35 -0
  253. data/lib/e11y/tracing/faraday_middleware.rb +31 -0
  254. data/lib/e11y/tracing/net_http_patch.rb +33 -0
  255. data/lib/e11y/tracing/propagator.rb +144 -0
  256. data/lib/e11y/tracing.rb +47 -0
  257. data/lib/e11y/version.rb +1 -1
  258. data/lib/e11y/versioning/version_extractor.rb +32 -0
  259. data/lib/e11y.rb +123 -266
  260. data/lib/generators/e11y/event/event_generator.rb +22 -0
  261. data/lib/generators/e11y/event/templates/event.rb.tt +16 -0
  262. data/lib/generators/e11y/grafana_dashboard/grafana_dashboard_generator.rb +30 -0
  263. data/lib/generators/e11y/grafana_dashboard/templates/e11y_dashboard.json +81 -0
  264. data/lib/generators/e11y/install/install_generator.rb +34 -0
  265. data/lib/generators/e11y/install/templates/e11y.rb +239 -0
  266. data/lib/generators/e11y/prometheus_alerts/prometheus_alerts_generator.rb +29 -0
  267. data/lib/generators/e11y/prometheus_alerts/templates/e11y_alerts.yml +28 -0
  268. data/lib/tasks/e11y_docs.rake +30 -0
  269. data/lib/tasks/e11y_events.rake +71 -0
  270. data/lib/tasks/e11y_lint.rake +91 -0
  271. data/lib/tasks/e11y_slo.rake +29 -0
  272. metadata +186 -39
  273. data/docs/ADR-003-slo-observability.md +0 -3337
  274. data/docs/ADR-010-developer-experience.md +0 -2166
  275. data/docs/API-REFERENCE-L28.md +0 -914
  276. data/docs/COMPREHENSIVE-CONFIGURATION.md +0 -2366
  277. data/docs/CONTRIBUTING.md +0 -312
  278. data/docs/IMPLEMENTATION_NOTES.md +0 -2804
  279. data/docs/IMPLEMENTATION_PLAN.md +0 -1971
  280. data/docs/IMPLEMENTATION_PLAN_ARCHITECTURE.md +0 -586
  281. data/docs/PLAN.md +0 -148
  282. data/docs/README.md +0 -296
  283. data/docs/design/00-memory-optimization.md +0 -593
  284. data/docs/guides/MIGRATION-L27-L28.md +0 -692
  285. data/docs/guides/PERFORMANCE-BENCHMARKS.md +0 -434
  286. data/docs/guides/README.md +0 -44
  287. data/docs/use_cases/UC-003-pattern-based-metrics.md +0 -1627
  288. data/lib/e11y/adapters/registry.rb +0 -141
@@ -12,7 +12,7 @@
12
12
  - ✅ §4.3: Loki Adapter - Implemented (34 tests)
13
13
  - ✅ §4.4: Sentry Adapter - Implemented (39 tests)
14
14
  - ❌ §4.5: Elasticsearch Adapter - Cancelled (not needed now)
15
- - ✅ §5: Adapter Registry - Implemented (26 tests)
15
+ - ✅ §5: Adapter storage (config.adapters)
16
16
  - ✅ §9.1: InMemory Test Adapter - Implemented (51 tests)
17
17
  - ✅ AuditEncrypted Adapter - Updated to new contract (13 tests)
18
18
 
@@ -31,7 +31,7 @@
31
31
  - 4.3. [Loki Adapter](#43-loki-adapter)
32
32
  - 4.4. [Sentry Adapter](#44-sentry-adapter)
33
33
  - 4.5. [Elasticsearch Adapter](#45-elasticsearch-adapter)
34
- 5. [Adapter Registry](#5-adapter-registry)
34
+ 5. [Adapter Storage (config.adapters)](#5-adapter-storage-configadapters)
35
35
  6. [Connection Management](#6-connection-management)
36
36
  7. [Error Handling & Retry](#7-error-handling--retry)
37
37
  8. [Performance & Batching](#8-performance--batching)
@@ -1064,17 +1064,17 @@ end
1064
1064
 
1065
1065
  ---
1066
1066
 
1067
- ## 5. Adapter Registry
1067
+ ## 5. Adapter Storage (config.adapters)
1068
1068
 
1069
- ### 5.1. Registry Architecture
1069
+ ### 5.1. Architecture
1070
1070
 
1071
1071
  ```mermaid
1072
1072
  graph TB
1073
1073
  subgraph "Configuration Phase (Boot Time)"
1074
- Config[Configuration] --> Register[Registry.register]
1075
- Register --> Instance1[Loki Instance]
1076
- Register --> Instance2[Sentry Instance]
1077
- Register --> Instance3[File Instance]
1074
+ Config[Configuration] --> Adapters[config.adapters]
1075
+ Adapters --> Instance1[Loki Instance]
1076
+ Adapters --> Instance2[Sentry Instance]
1077
+ Adapters --> Instance3[File Instance]
1078
1078
 
1079
1079
  Instance1 --> Pool1[Connection Pool]
1080
1080
  Instance2 --> Pool2[Connection Pool]
@@ -1082,79 +1082,33 @@ graph TB
1082
1082
  end
1083
1083
 
1084
1084
  subgraph "Runtime Phase"
1085
- Event[Event.track] --> Resolve[Registry.resolve]
1085
+ Event[Event.track] --> Resolve[config.adapters[name]]
1086
1086
  Resolve -->|:loki| Instance1
1087
1087
  Resolve -->|:sentry| Instance2
1088
1088
  Resolve -->|:file| Instance3
1089
1089
  end
1090
1090
 
1091
- style Register fill:#d1ecf1
1091
+ style Adapters fill:#d1ecf1
1092
1092
  style Resolve fill:#fff3cd
1093
1093
  style Pool1 fill:#d4edda
1094
1094
  style Pool2 fill:#d4edda
1095
1095
  style Pool3 fill:#d4edda
1096
1096
  ```
1097
1097
 
1098
- ### 5.2. Registry Implementation
1098
+ ### 5.2. config.adapters (Adapter Storage)
1099
+
1100
+ Adapters are stored in `E11y.configuration.adapters` (Hash). Routing middleware resolves by name: `config.adapters[adapter_name]`.
1099
1101
 
1100
1102
  ```ruby
1101
- module E11y
1102
- module Adapters
1103
- class Registry
1104
- class << self
1105
- def register(name, adapter_instance)
1106
- validate_adapter!(adapter_instance)
1107
-
1108
- adapters[name] = adapter_instance
1109
-
1110
- # Register cleanup hook
1111
- at_exit { adapter_instance.close }
1112
- end
1113
-
1114
- def resolve(name)
1115
- adapters.fetch(name) do
1116
- raise AdapterNotFoundError, "Adapter not found: #{name}"
1117
- end
1118
- end
1119
-
1120
- def resolve_all(names)
1121
- names.map { |name| resolve(name) }
1122
- end
1123
-
1124
- def all
1125
- adapters.values
1126
- end
1127
-
1128
- def names
1129
- adapters.keys
1130
- end
1131
-
1132
- def clear!
1133
- adapters.each_value(&:close)
1134
- adapters.clear
1135
- end
1136
-
1137
- private
1138
-
1139
- def adapters
1140
- @adapters ||= {}
1141
- end
1142
-
1143
- def validate_adapter!(adapter)
1144
- unless adapter.respond_to?(:write)
1145
- raise ArgumentError, "Adapter must respond to #write"
1146
- end
1147
-
1148
- unless adapter.respond_to?(:write_batch)
1149
- raise ArgumentError, "Adapter must respond to #write_batch"
1150
- end
1151
- end
1152
- end
1153
- end
1154
-
1155
- class AdapterNotFoundError < StandardError; end
1156
- end
1103
+ # Registration (config phase)
1104
+ E11y.configure do |config|
1105
+ config.adapters[:loki] = E11y::Adapters::Loki.new(url: ENV["LOKI_URL"])
1106
+ config.adapters[:sentry] = E11y::Adapters::Sentry.new(dsn: ENV["SENTRY_DSN"])
1157
1107
  end
1108
+
1109
+ # Resolution (runtime, in Routing middleware)
1110
+ adapter = E11y.configuration.adapters[:loki]
1111
+ adapter.write(event_data)
1158
1112
  ```
1159
1113
 
1160
1114
  ### 5.3. Usage in Events
@@ -1714,20 +1668,20 @@ E11y.configure do |config|
1714
1668
  # Register adapters (infrastructure)
1715
1669
  config.register_adapter :loki, Loki.new(url: ENV['LOKI_URL'])
1716
1670
  config.register_adapter :sentry, Sentry.new(dsn: ENV['SENTRY_DSN'])
1717
- config.register_adapter :s3, S3Adapter.new(bucket: 'events-archive')
1671
+ # Archival: external jobs filter by retention_until
1718
1672
  config.register_adapter :audit_encrypted, AuditAdapter.new(...)
1719
1673
 
1720
1674
  # ❌ PROBLEM: Routing for EVERY event in global config
1721
1675
  config.events do
1722
1676
  # Payment events → multiple adapters
1723
1677
  event 'Events::PaymentSucceeded' do
1724
- adapters [:loki, :sentry, :s3]
1678
+ adapters [:loki, :sentry]
1725
1679
  end
1726
1680
  event 'Events::PaymentFailed' do
1727
- adapters [:loki, :sentry, :s3]
1681
+ adapters [:loki, :sentry]
1728
1682
  end
1729
1683
  event 'Events::PaymentRefunded' do
1730
- adapters [:loki, :sentry, :s3]
1684
+ adapters [:loki, :sentry]
1731
1685
  end
1732
1686
 
1733
1687
  # Audit events → encrypted adapter
@@ -1764,7 +1718,7 @@ E11y.configure do |config|
1764
1718
  # ONLY infrastructure (adapter registration)
1765
1719
  config.register_adapter :loki, Loki.new(url: ENV['LOKI_URL'])
1766
1720
  config.register_adapter :sentry, Sentry.new(dsn: ENV['SENTRY_DSN'])
1767
- config.register_adapter :s3, S3Adapter.new(bucket: 'events-archive')
1721
+ # Archival: external jobs filter by retention_until
1768
1722
  config.register_adapter :audit_encrypted, AuditAdapter.new(...)
1769
1723
 
1770
1724
  # Optional: default adapters (convention)
@@ -1777,7 +1731,7 @@ module Events
1777
1731
  schema do; required(:transaction_id).filled(:string); end
1778
1732
 
1779
1733
  # ✅ Adapters right next to schema!
1780
- adapters [:loki, :sentry, :s3]
1734
+ adapters [:loki, :sentry]
1781
1735
  end
1782
1736
  end
1783
1737
 
@@ -1817,7 +1771,7 @@ end
1817
1771
  module Events
1818
1772
  class BasePaymentEvent < E11y::Event::Base
1819
1773
  # Common adapters for ALL payment events
1820
- adapters [:loki, :sentry, :s3]
1774
+ adapters [:loki, :sentry]
1821
1775
 
1822
1776
  # Common config
1823
1777
  severity :success
@@ -1829,18 +1783,18 @@ end
1829
1783
  # Inherit from base (1-2 lines per event!)
1830
1784
  class Events::PaymentSucceeded < Events::BasePaymentEvent
1831
1785
  schema do; required(:transaction_id).filled(:string); end
1832
- # ← Inherits: adapters [:loki, :sentry, :s3]
1786
+ # ← Inherits: adapters [:loki, :sentry]
1833
1787
  end
1834
1788
 
1835
1789
  class Events::PaymentFailed < Events::BasePaymentEvent
1836
1790
  severity :error # ← Override severity
1837
1791
  schema do; required(:error_code).filled(:string); end
1838
- # ← Inherits: adapters [:loki, :sentry, :s3]
1792
+ # ← Inherits: adapters [:loki, :sentry]
1839
1793
  end
1840
1794
 
1841
1795
  class Events::PaymentRefunded < Events::BasePaymentEvent
1842
1796
  schema do; required(:refund_id).filled(:string); end
1843
- # ← Inherits: adapters [:loki, :sentry, :s3]
1797
+ # ← Inherits: adapters [:loki, :sentry]
1844
1798
  end
1845
1799
  ```
1846
1800
 
@@ -1882,7 +1836,7 @@ module E11y
1882
1836
  module HighValueEvent
1883
1837
  extend ActiveSupport::Concern
1884
1838
  included do
1885
- adapters [:loki, :sentry, :s3_archive]
1839
+ adapters [:loki, :sentry]
1886
1840
  sample_rate 1.0
1887
1841
  retention 7.years
1888
1842
  end
@@ -1936,7 +1890,7 @@ end
1936
1890
  # Override convention:
1937
1891
  class Events::OrderCreated < E11y::Event::Base
1938
1892
  severity :success
1939
- adapters [:loki, :elasticsearch, :s3] # ← Override
1893
+ adapters [:loki, :elasticsearch] # ← Override
1940
1894
  schema do; required(:order_id).filled(:string); end
1941
1895
  end
1942
1896
  ```
@@ -1946,13 +1900,13 @@ end
1946
1900
  ```ruby
1947
1901
  # Replace strategy (default): Override parent/convention
1948
1902
  class Events::PaymentSucceeded < Events::BasePaymentEvent
1949
- adapters [:loki, :sentry] # ← Replaces base [:loki, :sentry, :s3]
1903
+ adapters [:loki, :sentry] # ← Replaces base
1950
1904
  end
1951
1905
 
1952
1906
  # Append strategy: Add to parent/convention
1953
1907
  class Events::PaymentSucceeded < Events::BasePaymentEvent
1954
1908
  adapters_strategy :append
1955
- adapters [:slack_business] # ← Adds to base (result: [:loki, :sentry, :s3, :slack_business])
1909
+ adapters [:slack_business] # ← Adds to base (result: [:loki, :sentry, :slack_business])
1956
1910
  end
1957
1911
  ```
1958
1912
 
@@ -1976,7 +1930,7 @@ class Events::BasePaymentEvent < E11y::Event::Base
1976
1930
  end
1977
1931
 
1978
1932
  class Events::PaymentSucceeded < Events::BasePaymentEvent
1979
- include E11y::Presets::HighValueEvent # 2. Preset (adds :s3)
1933
+ include E11y::Presets::HighValueEvent # 2. Preset
1980
1934
  adapters [:loki, :sentry, :pagerduty] # 1. Event-level (WINS!)
1981
1935
  end
1982
1936
 
@@ -2009,7 +1963,7 @@ end
2009
1963
  ```ruby
2010
1964
  # Migrate high-value events first:
2011
1965
  class Events::PaymentSucceeded < E11y::Event::Base
2012
- adapters [:loki, :sentry, :s3] # ← Migrated
1966
+ adapters [:loki, :sentry] # ← Migrated
2013
1967
  end
2014
1968
 
2015
1969
  # Keep others in global config (temporary):
@@ -2124,7 +2078,7 @@ class Events::PaymentFailed < E11y::Event::Base
2124
2078
 
2125
2079
  # Multi-environment routing
2126
2080
  adapters case Rails.env
2127
- when 'production' then [:loki, :sentry, :s3_archive]
2081
+ when 'production' then [:loki, :sentry]
2128
2082
  when 'staging' then [:loki, :sentry]
2129
2083
  else [:file]
2130
2084
  end
@@ -2164,7 +2118,7 @@ end
2164
2118
  module E11y::Presets::HighValueEvent
2165
2119
  extend ActiveSupport::Concern
2166
2120
  included do
2167
- adapters [:loki, :sentry, :s3_archive] # Add S3
2121
+ adapters [:loki, :sentry]
2168
2122
  end
2169
2123
  end
2170
2124
 
@@ -2172,9 +2126,9 @@ end
2172
2126
  class Events::CriticalPayment < Events::BasePaymentEvent
2173
2127
  include E11y::Presets::HighValueEvent
2174
2128
 
2175
- adapters [:loki, :sentry, :s3_archive, :datadog] # Add Datadog
2129
+ adapters [:loki, :sentry, :datadog]
2176
2130
 
2177
- # Final: [:loki, :sentry, :s3_archive, :datadog] (event-level wins)
2131
+ # Final: [:loki, :sentry, :datadog] (event-level wins)
2178
2132
  end
2179
2133
  ```
2180
2134
 
@@ -2189,64 +2143,40 @@ end
2189
2143
  require 'e11y'
2190
2144
 
2191
2145
  E11y.configure do |config|
2192
- # Register Loki for centralized logging
2193
- E11y::Adapters::Registry.register(
2194
- :loki,
2195
- E11y::Adapters::Loki.new(
2196
- url: ENV['LOKI_URL'] || 'http://loki:3100',
2197
- labels: {
2198
- app: 'my_app',
2199
- env: Rails.env,
2200
- host: Socket.gethostname
2201
- },
2202
- batch_size: 100,
2203
- batch_timeout: 5,
2204
- compress: true,
2205
- tenant_id: ENV['LOKI_TENANT_ID']
2206
- )
2146
+ # Loki for centralized logging
2147
+ config.adapters[:loki] = E11y::Adapters::Loki.new(
2148
+ url: ENV['LOKI_URL'] || 'http://loki:3100',
2149
+ labels: { app: 'my_app', env: Rails.env, host: Socket.gethostname },
2150
+ batch_size: 100,
2151
+ batch_timeout: 5,
2152
+ compress: true,
2153
+ tenant_id: ENV['LOKI_TENANT_ID']
2207
2154
  )
2208
2155
 
2209
- # Register Sentry for error tracking
2210
- E11y::Adapters::Registry.register(
2211
- :sentry,
2212
- E11y::Adapters::Sentry.new(
2213
- dsn: ENV['SENTRY_DSN'],
2214
- environment: Rails.env,
2215
- severity_threshold: :warn,
2216
- breadcrumbs: true
2217
- )
2156
+ # Sentry for error tracking
2157
+ config.adapters[:sentry] = E11y::Adapters::Sentry.new(
2158
+ dsn: ENV['SENTRY_DSN'],
2159
+ environment: Rails.env,
2160
+ severity_threshold: :warn,
2161
+ breadcrumbs: true
2218
2162
  )
2219
2163
 
2220
- # Register File adapter for local development
2221
- E11y::Adapters::Registry.register(
2222
- :file,
2223
- E11y::Adapters::File.new(
2224
- path: Rails.root.join('log', 'e11y.log'),
2225
- rotation: :daily,
2226
- max_size: 100 * 1024 * 1024, # 100MB
2227
- compress: true
2228
- )
2164
+ # File adapter for local development
2165
+ config.adapters[:file] = E11y::Adapters::File.new(
2166
+ path: Rails.root.join('log', 'e11y.log'),
2167
+ rotation: :daily,
2168
+ max_size: 100 * 1024 * 1024,
2169
+ compress: true
2229
2170
  )
2230
2171
 
2231
- # Register Stdout for console output
2232
- E11y::Adapters::Registry.register(
2233
- :stdout,
2234
- E11y::Adapters::Stdout.new(
2235
- pretty: Rails.env.development?,
2236
- colorize: true
2237
- )
2172
+ # Stdout for console output
2173
+ config.adapters[:stdout] = E11y::Adapters::Stdout.new(
2174
+ pretty: Rails.env.development?,
2175
+ colorize: true
2238
2176
  )
2239
2177
 
2240
- # Register InMemory for testing
2241
- if Rails.env.test?
2242
- E11y::Adapters::Registry.register(
2243
- :test,
2244
- E11y::Adapters::InMemory.new(
2245
- max_events: 1000,
2246
- max_batches: 100
2247
- )
2248
- )
2249
- end
2178
+ # InMemory for testing
2179
+ config.adapters[:test] = E11y::Adapters::InMemory.new(max_events: 1000, max_batches: 100) if Rails.env.test?
2250
2180
  end
2251
2181
  ```
2252
2182
 
@@ -2307,7 +2237,7 @@ Events::PaymentFailed.track(
2307
2237
  ```ruby
2308
2238
  # spec/events/order_placed_spec.rb
2309
2239
  RSpec.describe Events::OrderPlaced do
2310
- let(:adapter) { E11y::Adapters::Registry.resolve(:test) }
2240
+ let(:adapter) { E11y.config.adapters[:test] }
2311
2241
 
2312
2242
  before do
2313
2243
  adapter.clear!
@@ -2342,7 +2272,7 @@ end
2342
2272
 
2343
2273
  ```ruby
2344
2274
  # Check adapter capabilities before using
2345
- loki = E11y::Adapters::Registry.resolve(:loki)
2275
+ loki = E11y.config.adapters[:loki]
2346
2276
  puts loki.capabilities
2347
2277
  # => {
2348
2278
  # batching: true,
@@ -2366,8 +2296,8 @@ end
2366
2296
  # config/initializers/health_check.rb
2367
2297
  HealthCheck.setup do |config|
2368
2298
  config.add_custom_check('e11y_adapters') do
2369
- adapters = E11y::Adapters::Registry.all
2370
- unhealthy = adapters.reject { |name, adapter| adapter.healthy? }
2299
+ adapters = E11y.config.adapters
2300
+ unhealthy = adapters.select { |_name, adapter| !adapter.respond_to?(:healthy?) || !adapter.healthy? }
2371
2301
 
2372
2302
  if unhealthy.any?
2373
2303
  "Unhealthy adapters: #{unhealthy.keys.join(', ')}"
@@ -2416,7 +2346,7 @@ E11y.configure do |config|
2416
2346
  ->(event) { :audit_encrypted if event[:audit_event] },
2417
2347
  ->(event) {
2418
2348
  days = (Time.parse(event[:retention_until]) - Time.now) / 86400
2419
- days > 90 ? :s3_glacier : :loki
2349
+ days > 90 ? :archive : :loki
2420
2350
  }
2421
2351
  ]
2422
2352
  end
@@ -2495,6 +2425,93 @@ module E11y
2495
2425
  end
2496
2426
  ```
2497
2427
 
2428
+ ### 14.4.1. Routing Decision Matrix
2429
+
2430
+ What happens to an event based on its type and configuration:
2431
+
2432
+ ```mermaid
2433
+ flowchart TD
2434
+ Start{What type of event?}
2435
+
2436
+ Start -->|Regular Event| R1{Has explicit adapters?}
2437
+ Start -->|Audit Event| A1{Has explicit adapters?}
2438
+
2439
+ R1 -->|YES| R2[Use explicit adapters]
2440
+ R1 -->|NO| R3[Use severity mapping]
2441
+
2442
+ R3 --> R4[info → logs]
2443
+ R3 --> R5[error → logs + errors_tracker]
2444
+
2445
+ R2 --> R6[Write to adapters]
2446
+ R4 --> R6
2447
+ R5 --> R6
2448
+ R6 --> R7[SUCCESS]
2449
+
2450
+ A1 -->|YES| A2[Use explicit adapters]
2451
+ A1 -->|NO| A3[Return empty array]
2452
+
2453
+ A3 --> A4{Routing rule matches?}
2454
+ A4 -->|YES| A5[Use rule result]
2455
+ A4 -->|NO| A6[Would use fallback]
2456
+
2457
+ A2 --> A7[Validation: PASS explicit]
2458
+ A5 --> A8[Validation: PASS rule matched]
2459
+ A6 --> A9[Validation: FAIL]
2460
+
2461
+ A7 --> A10[Write to adapters]
2462
+ A8 --> A10
2463
+ A9 --> A11[RAISE ERROR]
2464
+
2465
+ A10 --> A12[SUCCESS encrypted + signed]
2466
+ A11 --> A13[FAILURE event not stored]
2467
+
2468
+ style R7 fill:#c8e6c9
2469
+ style A12 fill:#c8e6c9
2470
+ style A13 fill:#ffcdd2
2471
+ style A11 fill:#ef5350
2472
+ ```
2473
+
2474
+ **Note:** Audit events without explicit adapters return `[]` so routing rules are evaluated. If no rule matches, fallback is used — but `validate_audit_routing!` raises (audit events MUST NOT use fallback).
2475
+
2476
+ ### 14.4.2. Adapter Resolution Logic
2477
+
2478
+ How `Event::Base#adapters` resolves target adapters:
2479
+
2480
+ ```mermaid
2481
+ flowchart TD
2482
+ A[Event.track called] --> B[Event::Base#adapters method]
2483
+
2484
+ B --> C{Check @adapters instance variable}
2485
+ C -->|SET| D[Return @adapters]
2486
+ C -->|NIL| E{Check parent class @adapters}
2487
+
2488
+ E -->|SET| F[Return parent.adapters]
2489
+ E -->|NIL| G{Call resolved_adapters}
2490
+
2491
+ G --> H{Is audit_event?}
2492
+ H -->|YES| I[Return empty array]
2493
+ H -->|NO| J[Call adapters_for_severity]
2494
+
2495
+ J --> K[Check severity level]
2496
+ K --> L[severity :info → logs]
2497
+ K --> M[severity :error → logs + errors_tracker]
2498
+ K --> N[severity :fatal → logs + errors_tracker]
2499
+ K --> O[default → logs]
2500
+
2501
+ D --> P[Result: adapters array]
2502
+ F --> P
2503
+ I --> P
2504
+ L --> P
2505
+ M --> P
2506
+ N --> P
2507
+ O --> P
2508
+
2509
+ style I fill:#fff9c4
2510
+ style P fill:#e0e0e0
2511
+ ```
2512
+
2513
+ **Key:** Audit events without explicit adapters return `[]` — routing middleware then applies `routing_rules` (e.g. `->(e) { :audit_encrypted if e[:audit_event] }`).
2514
+
2498
2515
  ### 14.5. Configuration
2499
2516
 
2500
2517
  ```ruby
@@ -2508,13 +2525,13 @@ E11y.configure do |config|
2508
2525
  # Rule 2: Long retention → cold storage
2509
2526
  ->(event) {
2510
2527
  days = (Time.parse(event[:retention_until]) - Time.now) / 86400
2511
- :s3_glacier if days > 90
2528
+ :archive if days > 90
2512
2529
  },
2513
2530
 
2514
2531
  # Rule 3: Medium retention → warm storage
2515
2532
  ->(event) {
2516
2533
  days = (Time.parse(event[:retention_until]) - Time.now) / 86400
2517
- :s3_standard if days.between?(30, 90)
2534
+ :warm if days.between?(30, 90)
2518
2535
  },
2519
2536
 
2520
2537
  # Rule 4: Short retention → hot storage
@@ -2566,7 +2583,7 @@ end
2566
2583
  **After:** Automatic routing
2567
2584
  - Debug logs (7 days) → stdout: $0/month
2568
2585
  - Business (30 days) → Loki: $100/month
2569
- - Audit (7 years) → S3 Glacier: $5/month
2586
+ - Audit (7 years) → cold tier: $5/month
2570
2587
  - **Total: $105/month (80% savings!)**
2571
2588
 
2572
2589
  ### 14.8. Advantages vs TieredStorage
@@ -2619,7 +2636,7 @@ RSpec.describe E11y::Middleware::Routing do
2619
2636
  it 'respects explicit adapters' do
2620
2637
  event = { adapters: [:sentry], retention_until: 1.year.from_now.iso8601 }
2621
2638
  expect(adapters[:sentry]).to receive(:write)
2622
- expect(adapters[:s3_glacier]).not_to receive(:write)
2639
+ expect(adapters[:archive]).not_to receive(:write)
2623
2640
  routing.call(event)
2624
2641
  end
2625
2642
  end
@@ -682,11 +682,11 @@ Already implemented in ADR-008, but here's the core logic:
682
682
  module E11y
683
683
  module TraceContext
684
684
  class JobPropagator
685
- # Inject trace context into job metadata
685
+ # C17 Hybrid: Inject parent trace into job metadata (job will create NEW trace_id)
686
686
  def self.inject(job_metadata = {})
687
687
  return job_metadata unless E11y::Current.traced?
688
688
 
689
- job_metadata['e11y_trace_id'] = E11y::Current.trace_id
689
+ job_metadata['e11y_parent_trace_id'] = E11y::Current.trace_id
690
690
  job_metadata['e11y_span_id'] = E11y::Current.span_id
691
691
  job_metadata['e11y_sampled'] = E11y::Current.sampled
692
692
 
@@ -702,14 +702,15 @@ module E11y
702
702
  job_metadata
703
703
  end
704
704
 
705
- # Extract trace context from job metadata
705
+ # C17 Hybrid: Extract parent context; job gets NEW trace_id, links via parent_trace_id
706
706
  def self.extract(job_metadata)
707
- return {} unless job_metadata['e11y_trace_id']
707
+ return {} unless job_metadata['e11y_parent_trace_id']
708
708
 
709
709
  {
710
- trace_id: job_metadata['e11y_trace_id'],
710
+ trace_id: IDGenerator.generate_trace_id, # NEW trace per job
711
+ parent_trace_id: job_metadata['e11y_parent_trace_id'],
711
712
  parent_span_id: job_metadata['e11y_span_id'],
712
- span_id: IDGenerator.generate_span_id, # New span for job
713
+ span_id: IDGenerator.generate_span_id,
713
714
  sampled: job_metadata['e11y_sampled'],
714
715
  baggage: job_metadata['e11y_baggage'],
715
716
  user_id: job_metadata['e11y_user_id'],
@@ -1055,7 +1056,7 @@ module E11y
1055
1056
 
1056
1057
  def extract_parent_context(job)
1057
1058
  {
1058
- trace_id: job['e11y_trace_id'],
1059
+ trace_id: job['e11y_parent_trace_id'],
1059
1060
  span_id: job['e11y_span_id'],
1060
1061
  sampled: job['e11y_sampled'],
1061
1062
  baggage: job['e11y_baggage'],
@@ -1085,7 +1086,7 @@ module E11y
1085
1086
  def call(worker_class, job, queue, redis_pool)
1086
1087
  # Inject current trace context into job metadata
1087
1088
  if E11y::Current.traced?
1088
- job['e11y_trace_id'] = E11y::Current.trace_id
1089
+ job['e11y_parent_trace_id'] = E11y::Current.trace_id
1089
1090
  job['e11y_span_id'] = E11y::Current.span_id
1090
1091
  job['e11y_sampled'] = E11y::Current.sampled
1091
1092
  job['e11y_baggage'] = E11y::Current.baggage if E11y::Current.baggage&.any?
@@ -1184,7 +1185,7 @@ ORDER BY created_at;
1184
1185
  # 1. Web request (trace_id: abc-123)
1185
1186
  POST /orders
1186
1187
  → Events::OrderCreated (trace_id: abc-123, span_id: span-001)
1187
- → Enqueue SendOrderEmailJob (metadata: {e11y_trace_id: 'abc-123'})
1188
+ → Enqueue SendOrderEmailJob (metadata: {e11y_parent_trace_id: 'abc-123'})
1188
1189
 
1189
1190
  # 2. Sidekiq job execution (NEW trace_id: xyz-789)
1190
1191
  SendOrderEmailJob#perform