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
data/README.md CHANGED
@@ -2,18 +2,20 @@
2
2
 
3
3
  # E11y - Easy Telemetry
4
4
 
5
- **Observability for Rails developers who hate noise**
5
+ **Debug production issues in seconds. Zero setup overhead. Own your data.**
6
6
 
7
7
  [![Gem Version](https://badge.fury.io/rb/e11y.svg)](https://badge.fury.io/rb/e11y)
8
8
  [![CI](https://github.com/arturseletskiy/e11y/actions/workflows/ci.yml/badge.svg)](https://github.com/arturseletskiy/e11y/actions/workflows/ci.yml)
9
9
  [![Code Coverage](https://codecov.io/gh/arturseletskiy/e11y/branch/main/graph/badge.svg)](https://codecov.io/gh/arturseletskiy/e11y)
10
10
 
11
- **⚠️ Work in Progress** - Core features implemented, production validation in progress
11
+ [Quick Start](#quick-start) [How it works](#the-e11y-solution) [Docs](#documentation)
12
12
 
13
- [Quick Start](#quick-start) [Why E11y?](#why-e11y) [Documentation](#documentation)
13
+ > v1.0.0 · Actively developed · Production feedback welcome → [open an issue](https://github.com/arturseletskiy/e11y/issues)
14
14
 
15
15
  </div>
16
16
 
17
+ **Contents:** [Quick Look](#quick-look-2-minutes) · [Quick Start](#quick-start) · [Features](#what-makes-e11y-different) · [vs Alternatives](#e11y-vs-alternatives) · [Docs](#documentation)
18
+
17
19
  ---
18
20
 
19
21
  ## The Problem Every Rails Developer Knows
@@ -60,6 +62,46 @@ You enable debug logs in production to catch that one weird bug.
60
62
 
61
63
  ---
62
64
 
65
+ ## Quick Look (2 minutes)
66
+
67
+ ```ruby
68
+ # 1. Configure once
69
+ E11y.configure do |config|
70
+ config.ephemeral_buffer_enabled = true
71
+ config.adapters[:logs] = E11y::Adapters::Loki.new(url: ENV["LOKI_URL"])
72
+ end
73
+
74
+ # 2. Define a business event
75
+ class OrderPaidEvent < E11y::Event::Base
76
+ schema do
77
+ required(:order_id).filled(:string)
78
+ required(:amount).filled(:float, gt?: 0)
79
+ required(:currency).filled(:string, included_in?: %w[USD EUR GBP])
80
+ optional(:user_email).maybe(:string)
81
+ end
82
+
83
+ validation_mode :sampled, sample_rate: 0.01 # 1% validation for hot path
84
+ contains_pii true
85
+ pii_filtering { hashes :user_email }
86
+ sample_by_value :amount, greater_than: 1000 # Always sample large orders
87
+
88
+ metrics do
89
+ counter :orders_total, tags: [:currency]
90
+ histogram :order_amount, value: :amount, tags: [:currency]
91
+ end
92
+ end
93
+
94
+ # 3. Track it
95
+ OrderPaidEvent.track(order_id: "123", amount: 99.99, currency: "USD")
96
+
97
+ ```
98
+
99
+ **Public API:** define events as subclasses of `E11y::Event::Base` and call **`.track(...)`** on the event class (for example `OrderPaidEvent.track(...)`). That is the only supported tracking entry point in application code.
100
+
101
+ → [Full Quick Start guide (5 min)](#quick-start)
102
+
103
+ ---
104
+
63
105
  ## What Makes E11y Different?
64
106
 
65
107
  ### 1. Request-Scoped Debug Buffering (Unique to E11y)
@@ -75,22 +117,40 @@ Rails.logger.debug "rendering view" # → Always written to disk
75
117
 
76
118
  # E11y approach:
77
119
  E11y.configure do |config|
78
- config.request_buffer.enabled = true
120
+ config.ephemeral_buffer_enabled = true
79
121
  end
80
122
 
81
123
  # Debug events buffered in memory during request
82
- # Flushed to storage ONLY on errors
124
+ # Flushed to storage ONLY on 5xx server errors
83
125
  # Cost: -90%, Noise: -99%, Value: 100%
84
126
  ```
85
127
 
128
+ > **Note:** By default the buffer flushes only on **5xx server errors** (`flush_on_error = true`).
129
+ > On 4xx responses the buffer is discarded. Two independent knobs control this:
130
+ >
131
+ > ```ruby
132
+ > # flush_on_error (default: true) — controls 5xx behaviour
133
+ > config.ephemeral_buffer_flush_on_error = false # disable 5xx flush
134
+ >
135
+ > # flush_on_statuses (default: []) — extra statuses, independent of flush_on_error
136
+ > config.ephemeral_buffer_flush_on_statuses = [403] # also flush on 403 Forbidden
137
+ > config.ephemeral_buffer_flush_on_statuses = [401, 403] # multiple codes
138
+ > ```
139
+
86
140
  **Real-world impact:**
87
- - **Storage costs:** $500/month$50/month (Loki/CloudWatch)
141
+ - **Storage costs:** Up to -90% log volume proportional Loki storage savings
88
142
  - **Log search time:** 30 seconds → 3 seconds (90% less data)
89
143
  - **Developer sanity:** Infinite ✨
90
144
 
91
145
  ---
92
146
 
93
- ### 2. Schema-Validated Business Events
147
+ ### 2. retention_until Simple Archival
148
+
149
+ Events carry `retention_until` (ISO8601) in their payload. **Archival happens later** — a separate job (cron, Loki compaction) filters logs by this field. No custom logic: `WHERE retention_until > ?`. Cost savings (export to cheap cold storage) and simplicity (one field to filter).
150
+
151
+ ---
152
+
153
+ ### 3. Schema-Validated Business Events
94
154
 
95
155
  Stop debugging nil values in production:
96
156
 
@@ -126,54 +186,57 @@ OrderPaidEvent.track(order_id: "123", amount: 99.99, currency: "USD")
126
186
 
127
187
  ---
128
188
 
129
- ### 3. Zero-Config SLO Tracking
189
+ ### 4. Zero-Config SLO Tracking
130
190
 
131
191
  Automatic Service Level Objectives for your endpoints and jobs:
132
192
 
133
193
  ```ruby
134
194
  # Enable Rails instrumentation
135
195
  E11y.configure do |config|
136
- config.rails_instrumentation.enabled = true
196
+ config.rails_instrumentation_enabled = true
137
197
  end
138
198
 
139
- # That's it! E11y now tracks SLOs automatically:
199
+ # That's it! E11y now emits SLO metrics automatically:
140
200
  # - HTTP endpoints: success rate, latency percentiles (p50, p95, p99)
141
201
  # - Background jobs: success rate, execution time, retry rate
142
202
  # - Database queries: slow query detection
143
203
  # - Cache operations: hit/miss ratios
144
-
145
- # View SLO status:
146
- E11y::SLO::Tracker.status
147
- # => {
148
- # "POST /orders" => { success_rate: 99.8%, p95_latency: 250ms },
149
- # "OrderProcessor" => { success_rate: 99.5%, avg_time: 1.2s }
150
- # }
204
+ #
205
+ # SLO metrics are collected via Prometheus/Yabeda; calculate SLOs from those metrics.
151
206
  ```
152
207
 
153
208
  **vs. Traditional SLO Tracking:**
154
209
  - ❌ Manual instrumentation of every endpoint
155
210
  - ❌ Complex SLO definitions and calculations
156
211
  - ❌ Separate tools for different SLOs
157
- - ✅ E11y: Zero config, automatic tracking, unified view
212
+ - ✅ E11y: Zero config, automatic tracking, metrics for SLO calculation
158
213
 
159
214
  ---
160
215
 
161
- ### 4. Rails-First Design
216
+ ### 5. Rails-First Design
162
217
 
163
218
  Built for Rails developers, not platform engineers:
164
219
 
165
220
  ```ruby
166
- # 5-minute setup, not 2-week OpenTelemetry migration
221
+ # Fast setup, not 2-week OpenTelemetry migration
167
222
  gem "e11y"
168
223
 
169
224
  E11y.configure do |config|
170
225
  config.adapters[:logs] = E11y::Adapters::Loki.new(url: ENV["LOKI_URL"])
171
226
  config.adapters[:errors_tracker] = E11y::Adapters::Sentry.new(dsn: ENV["SENTRY_DSN"])
172
- end
173
227
 
174
- # Auto-instruments Rails (optional):
175
- config.rails_instrumentation.enabled = true
176
- # → HTTP requests, ActiveRecord, ActiveJob, Cache events
228
+ # ActiveSupport::Notifications E11y (HTTP, DB, cache, job lifecycle, etc.)
229
+ config.rails_instrumentation_enabled = true
230
+
231
+ # Optional: Sidekiq client/server middleware (buffer + job events) — enable if you use Sidekiq
232
+ config.sidekiq_enabled = true
233
+
234
+ # Optional: ActiveJob callbacks (buffer + context) — enable if you use Active Job
235
+ config.active_job_enabled = true
236
+
237
+ # Optional: wrap Rails.logger and emit E11y::Events::Rails::Log::* — opt-in
238
+ # config.logger_bridge_enabled = true
239
+ end
177
240
  ```
178
241
 
179
242
  **vs. Traditional Observability:**
@@ -184,6 +247,19 @@ config.rails_instrumentation.enabled = true
184
247
 
185
248
  ---
186
249
 
250
+ ### 6. Built-in PII Filtering
251
+
252
+ **Built-in PII filtering** — mask, hash, or redact sensitive fields per event class. No other Ruby observability gem provides this out of the box.
253
+
254
+ ```ruby
255
+ class Events::UserSignedIn < E11y::Event::Base
256
+ contains_pii :email, strategy: :hash
257
+ contains_pii :ip_address, strategy: :mask
258
+ end
259
+ ```
260
+
261
+ ---
262
+
187
263
  ## Who Should Use E11y?
188
264
 
189
265
  ### ✅ Perfect For
@@ -204,7 +280,7 @@ config.rails_instrumentation.enabled = true
204
280
 
205
281
  - **Non-Rails Ruby** - Focused on Rails conventions first
206
282
  - **Microservices polyglot** - OpenTelemetry better for multi-language
207
- - **Enterprise compliance** - E11y is WIP, audit trails coming soon
283
+ - **Enterprise compliance requirements** audit trails and compliance reports are not yet available
208
284
  - **Auto-instrumentation only** - E11y requires event definitions (by design)
209
285
 
210
286
  ---
@@ -218,14 +294,15 @@ config.rails_instrumentation.enabled = true
218
294
  | **Schema Validation** | ✅ Implemented | dry-schema validation before sending events |
219
295
  | **Metrics DSL** | ✅ Implemented | Define Prometheus metrics alongside events |
220
296
  | **Adapters** | ✅ 7 adapters | Loki, Sentry, OpenTelemetry, Yabeda, File, Stdout, InMemory |
221
- | **PII Filtering** | ✅ Implemented | Configurable field masking/hashing/redaction |
297
+ | **PII Filtering** | ✅ Implemented | Configurable field masking/hashing/redaction (event-level DSL) |
222
298
  | **Adaptive Sampling** | ✅ Implemented | Error-based, load-based, value-based strategies |
299
+ | **Rate Limiting** | ✅ Implemented | Opt-in — requires `config.pipeline.use E11y::Middleware::RateLimiting` |
223
300
  | **Rails Integration** | ✅ Implemented | Auto-instrument HTTP, ActiveRecord, ActiveJob, Cache |
224
301
  | **Production Testing** | 🚧 In Progress | Validating with real workloads |
225
302
 
226
303
  ---
227
304
 
228
- ## Quick Start in 5 Minutes
305
+ ## Quick Start
229
306
 
230
307
  ### 1. Install
231
308
 
@@ -242,7 +319,9 @@ bundle install
242
319
  # config/initializers/e11y.rb
243
320
  E11y.configure do |config|
244
321
  # Enable request-scoped debug buffering (THE killer feature)
245
- config.request_buffer.enabled = true
322
+ config.ephemeral_buffer_enabled = true
323
+ # config.ephemeral_buffer_flush_on_error = true # default: flush on 5xx
324
+ # config.ephemeral_buffer_flush_on_statuses = [403] # also flush on 403
246
325
 
247
326
  # Configure where events go
248
327
  config.adapters[:logs] = E11y::Adapters::Loki.new(
@@ -254,8 +333,17 @@ E11y.configure do |config|
254
333
  dsn: ENV["SENTRY_DSN"]
255
334
  )
256
335
 
257
- # Optional: Auto-instrument Rails
258
- config.rails_instrumentation.enabled = true
336
+ # ActiveSupport::Notifications E11y (see docs/RAILS_INTEGRATION.md)
337
+ config.rails_instrumentation_enabled = true
338
+
339
+ # Optional: Sidekiq / ActiveJob (ephemeral buffer + instrumentation at job boundaries)
340
+ config.sidekiq_enabled = true # set if you use Sidekiq
341
+ config.active_job_enabled = true # set if you use Active Job (in addition or instead)
342
+
343
+ # Optional: send Rails.logger lines into E11y as structured events
344
+ # config.logger_bridge_enabled = true
345
+ # config.logger_bridge_track_severities = [:warn, :error, :fatal] # nil = all severities
346
+ # config.logger_bridge_ignore_patterns = [%r{\A\[ActiveJob\]}]
259
347
  end
260
348
  ```
261
349
 
@@ -312,7 +400,7 @@ end
312
400
  - ❌ nil values in production
313
401
  - ❌ Manual `Yabeda.increment` everywhere
314
402
  - ❌ Manual SLO definitions and calculations
315
- - ❌ $500/month log storage bills
403
+ - ❌ High log storage bills from storing everything
316
404
 
317
405
  ---
318
406
 
@@ -341,7 +429,7 @@ Rails.logger.error "Payment failed!"
341
429
  # [DEBUG] Cache read... (3 lines)
342
430
  # [ERROR] Payment failed (1 line)
343
431
  # Total: 400 lines, 74% noise ❌
344
- # Cost: $500/month Loki storage
432
+ # Cost: High (all logs stored)
345
433
  # Search time: 30 seconds ❌
346
434
  ```
347
435
 
@@ -349,7 +437,7 @@ Rails.logger.error "Payment failed!"
349
437
 
350
438
  ```ruby
351
439
  # Production with E11y:
352
- E11y.configure { |c| c.request_buffer.enabled = true }
440
+ E11y.configure { |c| c.ephemeral_buffer_enabled = true }
353
441
 
354
442
  # 99 successful requests:
355
443
  # [INFO] User logged in (99 lines)
@@ -361,7 +449,7 @@ E11y.configure { |c| c.request_buffer.enabled = true }
361
449
  # [DEBUG] SQL: SELECT... (context!) ← Flushed!
362
450
  # [DEBUG] Rendered view... (trail!) ← Flushed!
363
451
  # Total: 103 lines, 0% noise ✅
364
- # Cost: $50/month Loki storage ✅ (-90%)
452
+ # Cost: Low (-90% log volume)
365
453
  # Search time: 3 seconds ✅ (-90%)
366
454
  ```
367
455
 
@@ -376,16 +464,16 @@ E11y.configure { |c| c.request_buffer.enabled = true }
376
464
 
377
465
  ### Comparison Matrix
378
466
 
379
- | Solution | Setup Time | Monthly Cost | Request-Scoped Buffering | SLO Tracking | Schema Validation | Auto-Metrics | Data Ownership |
380
- |----------|-----------|--------------|--------------------------|--------------|-------------------|--------------|----------------|
381
- | **E11y** | **5 minutes** | **Infra costs** | **✅ Unique** | **✅ Zero-config** | **✅** | **✅** | **✅ Full** |
382
- | Datadog APM | 2-4 hours | $500-5,000 | ❌ | ✅ Manual | ❌ | ✅ | ❌ SaaS lock-in |
383
- | New Relic | 2-4 hours | $99-658/user | ❌ | ✅ Manual | ❌ | ✅ | ❌ SaaS lock-in |
384
- | Sentry | 1 hour | $26-80/mo | ❌ | ❌ | ❌ | Partial | ❌ SaaS lock-in |
385
- | Semantic Logger | 30 minutes | Infra costs | ❌ | ❌ | ❌ | ❌ | ✅ Full |
386
- | OpenTelemetry | 1-2 weeks | Infra costs | ❌ | Manual setup | ❌ | ✅ | ✅ Full |
387
- | Grafana + Loki | 2-3 days | Infra costs | ❌ | Manual setup | ❌ | Manual | ✅ Full |
388
- | AppSignal | 1 hour | $23-499/mo | ❌ | ✅ Built-in | ❌ | ✅ | ❌ SaaS lock-in |
467
+ | Solution | Setup Time | Monthly Cost | Request-Scoped Buffering | SLO Tracking | Schema Validation | Auto-Metrics | Built-in PII Filtering | Data Ownership | Ecosystem / Managed Infra |
468
+ |----------|-----------|--------------|--------------------------|--------------|-------------------|--------------|------------------------|----------------|--------------------------|
469
+ | **E11y** | **5–30 min*** | **Infra costs** | **✅ Unique** | **✅ Zero-config** | **✅** | **✅** | **✅ Field masking, hashing, redaction** | **✅ Full** | ⚠️ Ruby/Rails only |
470
+ | Datadog APM | 2-4 hours | $500-5,000 | ❌ | ✅ Manual | ❌ | ✅ | ⚠️ Via agent config (limited) | ❌ SaaS lock-in | ✅ Extensive + fully managed |
471
+ | New Relic | 2-4 hours | $99-658/user | ❌ | ✅ Manual | ❌ | ✅ | ⚠️ Via obfuscation rules | ❌ SaaS lock-in | ✅ Extensive + fully managed |
472
+ | Sentry | 1 hour | $26-80/mo | ❌ | ❌ | ❌ | Partial | ⚠️ Data scrubbing rules | ❌ SaaS lock-in | ✅ Managed (error-focused) |
473
+ | Semantic Logger | 30 minutes | Infra costs | ❌ | ❌ | ❌ | ❌ | ❌ None | ✅ Full | ⚠️ Ruby only, self-hosted |
474
+ | OpenTelemetry | 1-2 weeks | Infra costs | ❌ | Manual setup | ❌ | ✅ | ❌ Manual implementation required | ✅ Full | ✅ Polyglot, vendor-neutral |
475
+ | Grafana + Loki | 2-3 days | Infra costs | ❌ | Manual setup | ❌ | Manual | ❌ None | ✅ Full | ✅ Mature, DevOps-friendly |
476
+ | AppSignal | 1 hour | $23-499/mo | ❌ | ✅ Built-in | ❌ | ✅ | ⚠️ Parameter filtering only | ❌ SaaS lock-in | ✅ Managed (Rails-friendly) |
389
477
 
390
478
  **Legend:**
391
479
  - **Setup Time:** From zero to first meaningful data
@@ -394,108 +482,19 @@ E11y.configure { |c| c.request_buffer.enabled = true }
394
482
  - **SLO Tracking:** Automatic Service Level Objectives monitoring
395
483
  - **Schema Validation:** Type-safe event schemas
396
484
  - **Auto-Metrics:** Metrics generated from events automatically
485
+ - **Built-in PII Filtering:** Automatic masking/hashing of sensitive fields (emails, IPs, credit cards, etc.) — no other Ruby observability gem provides this out of the box
397
486
  - **Data Ownership:** Can you host it yourself?
487
+ - **Ecosystem / Managed Infra:** Integration breadth and whether infrastructure is managed for you
398
488
 
399
- ---
400
-
401
- ### Detailed Comparisons
402
-
403
- #### vs. SaaS APM (Datadog, New Relic, Dynatrace)
404
-
405
- **Datadog / New Relic:**
406
- - ✅ **Pros:** Full-stack visibility, mature dashboards, auto-instrumentation
407
- - ❌ **Cons:** $500-5k/month, vendor lock-in, no debug buffering, no schema validation
408
- - **E11y advantage:** 10x cheaper, request-scoped buffering (unique), type-safe events, own your data
409
-
410
- **When to use Datadog/New Relic instead:**
411
- - You need frontend RUM (Real User Monitoring)
412
- - You have polyglot microservices (not just Rails)
413
- - Budget is unlimited, prefer turnkey solution
414
-
415
- ---
416
-
417
- #### vs. Open-Source Logging (Semantic Logger, Lograge)
418
-
419
- **Semantic Logger:**
420
- - ✅ **Pros:** Structured logs (JSON), async writes, Rails integration
421
- - ❌ **Cons:** No debug buffering, no schema validation, no auto-metrics, logs-only
422
- - **E11y advantage:** Request-scoped buffering (unique), schema validation, auto-metrics, unified events
423
-
424
- **Lograge:**
425
- - ✅ **Pros:** Reduces Rails log noise (single-line requests)
426
- - ❌ **Cons:** Filtering only, no buffering, no validation, no metrics
427
- - **E11y advantage:** Request-scoped buffering (selective, not filtering), schema validation, auto-metrics
428
-
429
- **When to use Semantic Logger instead:**
430
- - You only need structured JSON logs (no events/metrics)
431
- - You don't need debug buffering or schema validation
432
-
433
- ---
434
-
435
- #### vs. OpenTelemetry
489
+ *\* 5 min for gem + stdout; 30 min if adding self-hosted Loki/Grafana stack.
436
490
 
437
- **OpenTelemetry:**
438
- - ✅ **Pros:** Industry standard, polyglot, vendor-neutral, mature ecosystem
439
- - ❌ **Cons:** Complex setup (1-2 weeks), no debug buffering, no schema validation, overkill for Rails monolith
440
- - **E11y advantage:** 5-minute setup, Rails-first, request-scoped buffering, schema validation
441
-
442
- **When to use OpenTelemetry instead:**
443
- - You have microservices in multiple languages (Go, Java, Python, etc.)
444
- - You need distributed tracing across services
445
- - You have a platform team to manage complexity
446
-
447
- **Use both:** E11y events can be sent to OpenTelemetry via `E11y::Adapters::OtelLogs`
448
-
449
- ---
450
-
451
- #### vs. Grafana + Loki + Prometheus
452
-
453
- **Grafana Stack:**
454
- - ✅ **Pros:** Open-source, powerful visualizations, mature, self-hosted
455
- - ❌ **Cons:** Complex setup (2-3 days), requires DevOps, no Rails integration, no schema validation
456
- - **E11y advantage:** 5-minute setup, Rails-native, schema validation, no DevOps required
457
-
458
- **When to use Grafana Stack instead:**
459
- - You already have Grafana/Loki infrastructure
460
- - You have a dedicated DevOps team
461
- - You need custom dashboards across multiple systems
462
-
463
- **Use both:** E11y can send events to Loki via `E11y::Adapters::Loki`
464
-
465
- ---
466
-
467
- #### vs. Error Tracking (Sentry, Honeybadger, Rollbar)
468
-
469
- **Sentry:**
470
- - ✅ **Pros:** Excellent error tracking, stack traces, breadcrumbs, release tracking
471
- - ❌ **Cons:** Errors-only, no debug buffering, no schema validation, $26-80/mo
472
- - **E11y advantage:** Events + errors + metrics unified, request-scoped buffering, schema validation
473
-
474
- **When to use Sentry instead:**
475
- - You only need error tracking (not general observability)
476
- - You need frontend JavaScript error tracking
477
-
478
- **Use both:** E11y can send error events to Sentry via `E11y::Adapters::Sentry`
491
+ > Cost estimates assume migration from verbose SaaS logging (Datadog/CloudWatch) to self-hosted Loki. Actual savings depend on your current setup.
479
492
 
480
493
  ---
481
494
 
482
- #### vs. Rails-First APM (AppSignal, Skylight)
483
-
484
- **AppSignal:**
485
- - ✅ **Pros:** Rails-native, beautiful UI, performance monitoring, $23/mo entry
486
- - ❌ **Cons:** SaaS lock-in, no debug buffering, no schema validation, limited to supported languages
487
- - **E11y advantage:** Request-scoped buffering (unique), schema validation, own your data
488
-
489
- **Skylight:**
490
- - ✅ **Pros:** Rails performance profiling, SQL query analysis
491
- - ❌ **Cons:** Performance-only (no logs/events), SaaS lock-in, $20+/mo
492
- - **E11y advantage:** Unified events/logs/metrics, request-scoped buffering, own your data
493
-
494
- **When to use AppSignal/Skylight instead:**
495
- - You want zero-config turnkey solution
496
- - You prefer paying for hosted service over self-hosting
495
+ ### Detailed Comparisons
497
496
 
498
- **Use both:** E11y for events/logs/metrics, AppSignal for performance profiling
497
+ See [docs/COMPARISON.md](docs/COMPARISON.md) for detailed per-tool comparisons.
499
498
 
500
499
  ---
501
500
 
@@ -543,583 +542,48 @@ E11y is optimized for:
543
542
  **Not optimized for:**
544
543
  - Polyglot microservices (use OpenTelemetry)
545
544
  - Frontend-heavy SPAs (use Datadog/Sentry for RUM)
546
- - Enterprise compliance requirements (WIP, coming soon)
547
-
548
- ---
549
-
550
- ## Table of Contents
551
-
552
- - [Quick Start](#quick-start-in-5-minutes)
553
- - [What Makes E11y Different?](#what-makes-e11y-different)
554
- - [Who Should Use E11y?](#who-should-use-e11y)
555
- - [Before and After](#before-and-after-e11y)
556
- - [E11y vs Alternatives](#e11y-vs-alternatives)
557
- - [Schema Validation](#schema-validation)
558
- - [Metrics DSL](#metrics-dsl)
559
- - [Adapters](#adapters)
560
- - [PII Filtering](#pii-filtering)
561
- - [Adaptive Sampling](#adaptive-sampling)
562
- - [Presets](#presets)
563
- - [Rails Integration](#rails-integration)
564
- - [Testing](#testing)
565
- - [Configuration](#configuration)
566
- - [Performance](#performance)
567
- - [Documentation](#documentation)
568
-
569
- ---
570
-
571
- ## Schema Validation
572
-
573
- E11y validates event data using [dry-schema](https://dry-rb.org/gems/dry-schema/).
574
-
575
- ### Basic Example
576
-
577
- ```ruby
578
- class OrderCreatedEvent < E11y::Event::Base
579
- schema do
580
- required(:order_id).filled(:string)
581
- required(:total).filled(:float, gt?: 0)
582
- required(:currency).filled(:string, included_in?: %w[USD EUR GBP])
583
- optional(:coupon_code).maybe(:string)
584
- end
585
- end
586
-
587
- # Valid event
588
- OrderCreatedEvent.track(order_id: "123", total: 99.99, currency: "USD")
589
-
590
- # Invalid event raises E11y::ValidationError
591
- OrderCreatedEvent.track(order_id: nil, total: -10, currency: "INVALID")
592
- # => ValidationError: order_id is missing, total must be > 0
593
- ```
594
-
595
- ### Validation Modes
596
-
597
- For high-frequency events, you can configure validation behavior:
598
-
599
- ```ruby
600
- class HighFrequencyEvent < E11y::Event::Base
601
- # Always validate (default)
602
- validation_mode :always
603
-
604
- # Sampled validation (validate 1% of events)
605
- validation_mode :sampled, sample_rate: 0.01
606
-
607
- # Never validate (use with caution)
608
- validation_mode :never
609
- end
610
- ```
611
-
612
- Use `:always` for user input and critical events. Use `:sampled` for high-frequency internal events. Use `:never` only for trusted, typed input.
613
-
614
- ### Validation Behavior
615
-
616
- By default, invalid events raise `E11y::ValidationError`:
617
-
618
- ```ruby
619
- OrderEvent.track(order_id: nil)
620
- # => E11y::ValidationError
621
- ```
622
-
623
- To handle validation errors gracefully:
624
-
625
- ```ruby
626
- begin
627
- OrderEvent.track(order_id: nil)
628
- rescue E11y::ValidationError => e
629
- Rails.logger.warn "Invalid event: #{e.message}"
630
- end
631
- ```
632
-
633
- ---
634
-
635
- ## Metrics DSL
636
-
637
- Define Prometheus metrics alongside events.
638
-
639
- ### Basic Example
640
-
641
- ```ruby
642
- class OrderPaidEvent < E11y::Event::Base
643
- schema do
644
- required(:order_id).filled(:string)
645
- required(:amount).filled(:float)
646
- required(:currency).filled(:string)
647
- end
648
-
649
- metrics do
650
- # Counter: Track number of paid orders
651
- counter :orders_total, tags: [:currency]
652
-
653
- # Histogram: Track order amount distribution
654
- histogram :order_amount,
655
- value: :amount,
656
- tags: [:currency],
657
- buckets: [10, 50, 100, 500, 1000]
658
-
659
- # Gauge: Track active orders
660
- gauge :active_orders, value: :active_count
661
- end
662
- end
663
-
664
- # One track() call = event + metrics
665
- OrderPaidEvent.track(order_id: "123", amount: 99.99, currency: "USD")
666
- # => orders_total{currency="USD"} +1
667
- # => order_amount{currency="USD"} observe 99.99
668
- ```
669
-
670
- ### Metric Types
671
-
672
- **Counter** - Monotonically increasing value:
673
- ```ruby
674
- metrics do
675
- counter :orders_total, tags: [:currency, :status]
676
- end
677
- # => orders_total{currency="USD", status="paid"} 42
678
- ```
679
-
680
- **Histogram** - Distribution of values:
681
- ```ruby
682
- metrics do
683
- histogram :order_amount,
684
- value: :amount,
685
- tags: [:currency],
686
- buckets: [10, 50, 100, 500, 1000]
687
- end
688
- # => order_amount_bucket{currency="USD", le="100"} 15
689
- ```
690
-
691
- **Gauge** - Arbitrary value that can go up or down:
692
- ```ruby
693
- metrics do
694
- gauge :queue_depth, value: :size, tags: [:queue_name]
695
- end
696
- # => queue_depth{queue_name="emails"} 37
697
- ```
698
-
699
- ### How It Works
700
-
701
- 1. Define metrics in event class
702
- 2. Metrics registered in `E11y::Metrics::Registry` at boot time
703
- 3. When `track()` is called, metrics are automatically updated
704
- 4. Metrics exported via Yabeda adapter (Prometheus format)
705
-
706
- ---
707
-
708
- ## Adapters
709
-
710
- E11y supports multiple adapters for different backends.
711
-
712
- | Adapter | Purpose | Batching | Use Case |
713
- |---------|---------|----------|----------|
714
- | **Loki** | Log aggregation (Grafana) | Yes | Production logs |
715
- | **Sentry** | Error tracking | Via SDK | Error monitoring |
716
- | **OpenTelemetry** | OTLP export | Yes | Distributed tracing |
717
- | **Yabeda** | Prometheus metrics | N/A | Metrics export |
718
- | **File** | Local logs | Yes | Development, CI |
719
- | **Stdout** | Console output | No | Development |
720
- | **InMemory** | Test buffer | No | Testing |
721
-
722
- ### Configuration
723
-
724
- ```ruby
725
- # config/initializers/e11y.rb
726
- E11y.configure do |config|
727
- # Configure adapters
728
- config.adapters[:logs] = E11y::Adapters::Loki.new(
729
- url: ENV["LOKI_URL"],
730
- batch_size: 100,
731
- batch_timeout: 5,
732
- compress: true
733
- )
734
-
735
- config.adapters[:errors_tracker] = E11y::Adapters::Sentry.new(
736
- dsn: ENV["SENTRY_DSN"]
737
- )
738
-
739
- config.adapters[:stdout] = E11y::Adapters::Stdout.new(
740
- format: :pretty
741
- )
742
- end
743
- ```
744
-
745
- ### Adapter Routing by Severity
746
-
747
- Events are routed to adapters based on severity. The default mapping:
748
-
749
- - `error`, `fatal` → `[:logs, :errors_tracker]`
750
- - Other severities → `[:logs]`
751
-
752
- Override routing explicitly:
753
-
754
- ```ruby
755
- class CustomEvent < E11y::Event::Base
756
- adapters :logs, :stdout # Explicit routing
757
- end
758
- ```
759
-
760
- ### Custom Adapters
761
-
762
- Implement the `write` method:
763
-
764
- ```ruby
765
- class MyBackendAdapter < E11y::Adapters::Base
766
- def write(event_data)
767
- # event_data contains event_name, payload, severity, timestamp, etc.
768
- MyBackend.send_event(event_data)
769
- end
770
- end
771
-
772
- E11y.configure do |config|
773
- config.adapters[:my_backend] = MyBackendAdapter.new
774
- end
775
- ```
776
-
777
- ---
778
-
779
- ## PII Filtering
780
-
781
- E11y provides PII filtering capabilities for sensitive data.
782
-
783
- ### Rails Integration
784
-
785
- E11y can respect `Rails.application.config.filter_parameters` when configured:
786
-
787
- ```ruby
788
- # config/application.rb
789
- config.filter_parameters += [:password, :email, :ssn, :credit_card]
790
-
791
- # E11y will filter these fields when PII filtering middleware is enabled
792
- ```
793
-
794
- ### Explicit PII Strategies
795
-
796
- Configure PII filtering per event:
797
-
798
- ```ruby
799
- class PaymentEvent < E11y::Event::Base
800
- contains_pii true
801
-
802
- pii_filtering do
803
- masks :card_number # Replace with "[FILTERED]"
804
- hashes :user_email # SHA256 hash (searchable)
805
- allows :amount # No filtering
806
- end
807
- end
808
- ```
809
-
810
- Available strategies:
811
- - `masks` - Replace with "[FILTERED]"
812
- - `hashes` - SHA256 hash (preserves searchability)
813
- - `partials` - Show first/last characters
814
- - `redacts` - Remove completely
815
- - `allows` - No filtering
816
-
817
- ---
818
-
819
- ## Adaptive Sampling
820
-
821
- E11y supports adaptive sampling to reduce event volume during high load.
822
-
823
- Sampling strategies:
824
- 1. **Error-based** - Increase sampling during error spikes
825
- 2. **Load-based** - Reduce sampling under high throughput
826
- 3. **Value-based** - Always sample high-value events
827
-
828
- ### Configuration
829
-
830
- ```ruby
831
- E11y.configure do |config|
832
- config.pipeline.use E11y::Middleware::Sampling,
833
- default_sample_rate: 0.1,
834
-
835
- # Error-based sampling
836
- error_based_adaptive: true,
837
- error_spike_config: {
838
- window: 60,
839
- absolute_threshold: 100,
840
- relative_threshold: 3.0,
841
- spike_duration: 300
842
- },
843
-
844
- # Load-based sampling
845
- load_based_adaptive: true,
846
- load_monitor_config: {
847
- window: 60,
848
- thresholds: {
849
- normal: 1_000,
850
- high: 10_000,
851
- very_high: 50_000,
852
- overload: 100_000
853
- }
854
- }
855
- end
856
- ```
857
-
858
- ### Value-Based Sampling
859
-
860
- Sample events based on payload values:
861
-
862
- ```ruby
863
- class PaymentEvent < E11y::Event::Base
864
- sample_by_value :amount, greater_than: 1000 # Always sample large payments
865
- end
866
- ```
867
-
868
- ---
869
-
870
- ## Presets
871
-
872
- E11y provides presets for common event types.
873
-
874
- ### HighValueEvent
875
-
876
- For financial transactions and critical business events:
877
-
878
- ```ruby
879
- class PaymentProcessedEvent < E11y::Event::Base
880
- include E11y::Presets::HighValueEvent
881
-
882
- schema do
883
- required(:transaction_id).filled(:string)
884
- required(:amount).filled(:decimal)
885
- end
886
- end
887
-
888
- # Configured with:
889
- # - severity: :success
890
- # - sample_rate: 1.0 (always sampled)
891
- # - adapters: [:logs, :errors_tracker]
892
- # - rate_limit: unlimited
893
- ```
894
-
895
- ### AuditEvent
896
-
897
- For compliance and audit trails:
898
-
899
- ```ruby
900
- class UserDeletedEvent < E11y::Event::Base
901
- include E11y::Presets::AuditEvent
902
-
903
- schema do
904
- required(:user_id).filled(:string)
905
- required(:deleted_by).filled(:string)
906
- end
907
- end
908
-
909
- # Configured with:
910
- # - sample_rate: 1.0 (never sampled)
911
- # - rate_limit: unlimited
912
- # Note: Set severity based on event criticality
913
- ```
914
-
915
- ### DebugEvent
916
-
917
- For development and troubleshooting:
918
-
919
- ```ruby
920
- class SlowQueryEvent < E11y::Event::Base
921
- include E11y::Presets::DebugEvent
922
-
923
- schema do
924
- required(:query).filled(:string)
925
- required(:duration_ms).filled(:integer)
926
- end
927
- end
928
-
929
- # Configured with:
930
- # - severity: :debug
931
- # - adapters: [:logs]
932
- ```
933
-
934
- ---
935
-
936
- ## Rails Integration
937
-
938
- E11y integrates with Rails via Railtie.
939
-
940
- ### Auto-Instrumented Components
941
-
942
- E11y includes event definitions for common Rails components:
943
-
944
- | Component | Event Classes | Location |
945
- |-----------|--------------|----------|
946
- | **HTTP Requests** | Request, StartProcessing, Redirect, SendFile | `lib/e11y/events/rails/http/` |
947
- | **ActiveRecord** | Query | `lib/e11y/events/rails/database/` |
948
- | **ActiveJob** | Enqueued, Started, Completed, Failed, Scheduled | `lib/e11y/events/rails/job/` |
949
- | **Cache** | Read, Write, Delete | `lib/e11y/events/rails/cache/` |
950
- | **View** | Render | `lib/e11y/events/rails/view/` |
951
-
952
- Enable instrumentation in your configuration as needed.
953
-
954
- ### Sidekiq Integration
955
-
956
- E11y includes Sidekiq instrumentation support. Configure in your initializer:
957
-
958
- ```ruby
959
- E11y.configure do |config|
960
- config.rails_instrumentation.enabled = true
961
- end
962
- ```
963
-
964
- ---
965
-
966
- ## Testing
967
-
968
- Use the InMemory adapter for testing.
969
-
970
- ### Setup
971
-
972
- ```ruby
973
- # spec/rails_helper.rb or spec/spec_helper.rb
974
- RSpec.configure do |config|
975
- config.before(:each) do
976
- # Configure InMemory adapter for tests
977
- E11y.configure do |e11y_config|
978
- e11y_config.adapters[:test] = E11y::Adapters::InMemory.new
979
- end
980
- end
981
-
982
- config.after(:each) do
983
- # Clear events after each test
984
- E11y.configuration.adapters[:test]&.clear!
985
- end
986
- end
987
- ```
988
-
989
- ### Test Events
990
-
991
- ```ruby
992
- RSpec.describe OrdersController do
993
- let(:test_adapter) { E11y.configuration.adapters[:test] }
994
-
995
- it "tracks order creation" do
996
- post :create, params: { item: "Book", price: 29.99 }
997
-
998
- events = test_adapter.events
999
- expect(events).to include(
1000
- a_hash_including(
1001
- event_name: "OrderCreatedEvent",
1002
- payload: hash_including(item: "Book", price: 29.99)
1003
- )
1004
- )
1005
- end
1006
-
1007
- it "does not track payment for free orders" do
1008
- post :create, params: { item: "Free Book", price: 0 }
1009
-
1010
- payment_events = test_adapter.events.select { |e| e[:event_name] == "PaymentProcessedEvent" }
1011
- expect(payment_events).to be_empty
1012
- end
1013
- end
1014
- ```
1015
-
1016
- ### InMemory Adapter API
1017
-
1018
- ```ruby
1019
- test_adapter = E11y::Adapters::InMemory.new
1020
-
1021
- # Get all events
1022
- test_adapter.events # => Array<Hash>
1023
-
1024
- # Count events
1025
- test_adapter.event_count # => Integer
1026
-
1027
- # Find last event
1028
- test_adapter.last_event # => Hash
1029
-
1030
- # Clear all events
1031
- test_adapter.clear!
1032
- ```
1033
-
1034
- ---
1035
-
1036
- ## Configuration
1037
-
1038
- ### Basic Configuration
1039
-
1040
- ```ruby
1041
- # config/initializers/e11y.rb
1042
- E11y.configure do |config|
1043
- # Service identification
1044
- config.service_name = "myapp"
1045
- config.environment = Rails.env
1046
-
1047
- # Configure adapters
1048
- config.adapters[:logs] = E11y::Adapters::Loki.new(
1049
- url: ENV["LOKI_URL"],
1050
- batch_size: 100,
1051
- batch_timeout: 5,
1052
- compress: true
1053
- )
1054
-
1055
- config.adapters[:errors_tracker] = E11y::Adapters::Sentry.new(
1056
- dsn: ENV["SENTRY_DSN"]
1057
- )
1058
-
1059
- # Default retention period
1060
- config.default_retention_period = 30.days
1061
- end
1062
- ```
1063
-
1064
- ### Middleware Pipeline
1065
-
1066
- Configure middleware for sampling, PII filtering, and more:
1067
-
1068
- ```ruby
1069
- E11y.configure do |config|
1070
- # Sampling middleware
1071
- config.pipeline.use E11y::Middleware::Sampling,
1072
- default_sample_rate: 0.1,
1073
- error_based_adaptive: true,
1074
- load_based_adaptive: true
1075
-
1076
- # PII filtering middleware
1077
- config.pipeline.use E11y::Middleware::PIIFilter
1078
-
1079
- # Trace context middleware
1080
- config.pipeline.use E11y::Middleware::TraceContext
1081
- end
1082
- ```
545
+ - Enterprise compliance requirements (not yet available)
1083
546
 
1084
547
  ---
1085
548
 
1086
549
  ## Performance
1087
550
 
1088
- ### Design Principles
1089
-
1090
- E11y is designed for performance:
551
+ p99 latency <70µs (`:always`), <10µs (`:sampled`), <50µs (`:never`). Full benchmarks → [docs/PERFORMANCE.md](docs/PERFORMANCE.md)
1091
552
 
1092
- - **Hash-based events** - Events are Hashes, not objects, minimizing allocations
1093
- - **Configurable validation** - Choose validation mode based on performance needs
1094
- - **Batching** - Loki and other adapters support batching to reduce network overhead
1095
- - **Sampling** - Adaptive sampling reduces event volume under high load
553
+ ---
1096
554
 
1097
- See `benchmarks/` directory for detailed performance tests.
555
+ ## Upgrading
1098
556
 
1099
- ### Cardinality Protection
557
+ Breaking renames and migration steps are listed in [CHANGELOG.md](CHANGELOG.md). Common ones:
1100
558
 
1101
- Optional cardinality protection prevents high-cardinality labels from overwhelming metrics systems:
559
+ - **`RequestScopedBuffer` `E11y::Buffers::EphemeralBuffer`** use `initialize!`, `flush_on_error`, and `discard` at request/job boundaries (middleware and instruments already do this).
560
+ - **Sidekiq / Active Job** — enable with `config.sidekiq_enabled` and `config.active_job_enabled`; see [docs/RAILS_INTEGRATION.md](docs/RAILS_INTEGRATION.md).
561
+ - **Logger bridge** — `config.logger_bridge_enabled` plus optional `logger_bridge_track_severities` and `logger_bridge_ignore_patterns` (same file).
1102
562
 
1103
- ```ruby
1104
- E11y::Adapters::Loki.new(
1105
- url: "http://loki:3100",
1106
- enable_cardinality_protection: true,
1107
- max_label_cardinality: 100
1108
- )
1109
- ```
1110
-
1111
- When enabled, high-cardinality labels (e.g., `user_id`, `order_id`) are filtered from metric tags.
563
+ Known tradeoffs and unfinished pieces: [docs/LIMITATIONS.md](docs/LIMITATIONS.md).
1112
564
 
1113
565
  ---
1114
566
 
1115
567
  ## Documentation
1116
568
 
1117
- Additional documentation is available in the `docs/` directory:
1118
-
1119
- - Architecture Decision Records (ADRs)
1120
- - Use Cases
1121
- - Configuration guides
1122
- - Performance benchmarks
569
+ | Topic | Doc |
570
+ |-------|-----|
571
+ | [Schema Validation](docs/SCHEMA_VALIDATION.md) | dry-schema validation, modes, error handling |
572
+ | [Metrics DSL](docs/METRICS_DSL.md) | Counters, histograms, gauges, Yabeda integration |
573
+ | [Adapters](docs/ADAPTERS.md) | Loki, Sentry, OTel, Yabeda, File, Stdout, InMemory |
574
+ | [PII Filtering](docs/PII_FILTERING.md) | Mask, hash, redact sensitive fields |
575
+ | [Adaptive Sampling](docs/ADAPTIVE_SAMPLING.md) | Error-based, load-based, value-based |
576
+ | [Presets](docs/PRESETS.md) | HighValueEvent, AuditEvent, DebugEvent |
577
+ | [Distributed Tracing](docs/DISTRIBUTED_TRACING.md) | W3C Trace Context, manual propagation |
578
+ | [Rails Integration](docs/RAILS_INTEGRATION.md) | Auto-instrumentation, Sidekiq |
579
+ | [Testing](docs/TESTING.md) | InMemoryTest adapter, RSpec setup |
580
+ | [Configuration](docs/CONFIGURATION.md) | Basic config, middleware pipeline |
581
+ | [SLO PromQL & Alerts](docs/SLO-PROMQL-ALERTS.md) | PromQL queries, Prometheus alert rules |
582
+ | [Performance](docs/PERFORMANCE.md) | Benchmarks, validation modes, cardinality |
583
+ | [Limitations](docs/LIMITATIONS.md) | Rails only, Ruby 3.2+, tradeoffs |
584
+ | [Comparison](docs/COMPARISON.md) | vs Datadog, OTel, Sentry, AppSignal, etc. |
585
+
586
+ Also: [ADRs](docs/architecture/ADR-INDEX.md), [Use Cases](docs/use_cases/README.md), [QUICK-START](docs/QUICK-START.md)
1123
587
 
1124
588
  ---
1125
589