e11y 0.2.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +130 -10
  3. data/CHANGELOG.md +56 -1
  4. data/CLAUDE.md +168 -0
  5. data/CONTRIBUTING.md +640 -0
  6. data/README.md +134 -702
  7. data/RELEASE.md +18 -3
  8. data/Rakefile +108 -29
  9. data/config/README.md +1 -1
  10. data/config/loki-local-config.yaml +12 -0
  11. data/config/otel-collector-config.yaml +44 -0
  12. data/cucumber.yml +1 -0
  13. data/docker-compose.yml +18 -2
  14. data/docs/ADAPTERS.md +76 -0
  15. data/docs/ADAPTIVE_SAMPLING.md +59 -0
  16. data/docs/COMPARISON.md +104 -0
  17. data/docs/CONFIGURATION.md +52 -0
  18. data/docs/DISTRIBUTED_TRACING.md +44 -0
  19. data/docs/LIMITATIONS.md +13 -0
  20. data/docs/METRICS_DSL.md +84 -0
  21. data/docs/PERFORMANCE.md +60 -0
  22. data/docs/PII_FILTERING.md +40 -0
  23. data/docs/PRESETS.md +65 -0
  24. data/docs/QUICK-START.md +546 -587
  25. data/docs/RAILS_INTEGRATION.md +29 -0
  26. data/docs/SCHEMA_VALIDATION.md +63 -0
  27. data/docs/SLO-PROMQL-ALERTS.md +161 -0
  28. data/docs/TESTING.md +69 -0
  29. data/docs/{ADR-001-architecture.md → architecture/ADR-001-architecture.md} +35 -64
  30. data/docs/{ADR-002-metrics-yabeda.md → architecture/ADR-002-metrics-yabeda.md} +62 -236
  31. data/docs/{ADR-003-slo-observability.md → architecture/ADR-003-slo-observability.md} +27 -466
  32. data/docs/{ADR-004-adapter-architecture.md → architecture/ADR-004-adapter-architecture.md} +163 -146
  33. data/docs/{ADR-005-tracing-context.md → architecture/ADR-005-tracing-context.md} +10 -9
  34. data/docs/{ADR-006-security-compliance.md → architecture/ADR-006-security-compliance.md} +184 -191
  35. data/docs/{ADR-007-opentelemetry-integration.md → architecture/ADR-007-opentelemetry-integration.md} +3 -21
  36. data/docs/{ADR-008-rails-integration.md → architecture/ADR-008-rails-integration.md} +209 -339
  37. data/docs/{ADR-009-cost-optimization.md → architecture/ADR-009-cost-optimization.md} +45 -54
  38. data/docs/architecture/ADR-010-developer-experience.md +522 -0
  39. data/docs/{ADR-011-testing-strategy.md → architecture/ADR-011-testing-strategy.md} +41 -83
  40. data/docs/{ADR-013-reliability-error-handling.md → architecture/ADR-013-reliability-error-handling.md} +37 -12
  41. data/docs/{ADR-014-event-driven-slo.md → architecture/ADR-014-event-driven-slo.md} +12 -24
  42. data/docs/{ADR-015-middleware-order.md → architecture/ADR-015-middleware-order.md} +23 -41
  43. data/docs/{ADR-016-self-monitoring-slo.md → architecture/ADR-016-self-monitoring-slo.md} +52 -349
  44. data/docs/{ADR-017-multi-rails-compatibility.md → architecture/ADR-017-multi-rails-compatibility.md} +4 -11
  45. data/docs/architecture/ADR-018-memory-optimization.md +366 -0
  46. data/docs/{ADR-INDEX.md → architecture/ADR-INDEX.md} +11 -6
  47. data/docs/{00-ICP-AND-TIMELINE.md → prd/00-ICP-AND-TIMELINE.md} +6 -6
  48. data/docs/{01-SCALE-REQUIREMENTS.md → prd/01-SCALE-REQUIREMENTS.md} +6 -6
  49. data/docs/prd/01-overview-vision.md +19 -14
  50. data/docs/use_cases/README.md +22 -23
  51. data/docs/use_cases/UC-001-request-scoped-debug-buffering.md +50 -44
  52. data/docs/use_cases/UC-002-business-event-tracking.md +26 -95
  53. data/docs/use_cases/UC-003-event-metrics.md +66 -0
  54. data/docs/use_cases/UC-004-zero-config-slo-tracking.md +42 -101
  55. data/docs/use_cases/UC-005-sentry-integration.md +13 -15
  56. data/docs/use_cases/UC-006-trace-context-management.md +30 -28
  57. data/docs/use_cases/UC-007-pii-filtering.md +35 -87
  58. data/docs/use_cases/UC-008-opentelemetry-integration.md +51 -89
  59. data/docs/use_cases/UC-009-multi-service-tracing.md +4 -4
  60. data/docs/use_cases/UC-010-background-job-tracking.md +5 -5
  61. data/docs/use_cases/UC-011-rate-limiting.md +95 -168
  62. data/docs/use_cases/UC-012-audit-trail.md +21 -46
  63. data/docs/use_cases/UC-013-high-cardinality-protection.md +29 -167
  64. data/docs/use_cases/UC-014-adaptive-sampling.md +2 -2
  65. data/docs/use_cases/UC-015-cost-optimization.md +46 -99
  66. data/docs/use_cases/UC-016-rails-logger-migration.md +39 -213
  67. data/docs/use_cases/UC-017-local-development.md +203 -777
  68. data/docs/use_cases/UC-018-testing-events.md +3 -3
  69. data/docs/use_cases/UC-019-retention-based-routing.md +53 -106
  70. data/docs/use_cases/UC-020-event-versioning.md +8 -9
  71. data/docs/use_cases/UC-021-error-handling-retry-dlq.md +18 -22
  72. data/docs/use_cases/UC-022-event-registry.md +15 -21
  73. data/docs/use_cases/backlog.md +119 -87
  74. data/e11y.gemspec +2 -2
  75. data/gems/e11y-devtools/README.md +136 -0
  76. data/gems/e11y-devtools/config/routes.rb +8 -0
  77. data/gems/e11y-devtools/e11y-devtools.gemspec +25 -0
  78. data/gems/e11y-devtools/exe/e11y +34 -0
  79. data/gems/e11y-devtools/lib/e11y/devtools/mcp/server.rb +96 -0
  80. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tool_base.rb +25 -0
  81. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/clear.rb +31 -0
  82. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/errors.rb +35 -0
  83. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/event_detail.rb +33 -0
  84. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/events_by_trace.rb +33 -0
  85. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/interactions.rb +40 -0
  86. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/recent_events.rb +34 -0
  87. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/search.rb +34 -0
  88. data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/stats.rb +30 -0
  89. data/gems/e11y-devtools/lib/e11y/devtools/overlay/assets/overlay.js +115 -0
  90. data/gems/e11y-devtools/lib/e11y/devtools/overlay/controller.rb +54 -0
  91. data/gems/e11y-devtools/lib/e11y/devtools/overlay/engine.rb +26 -0
  92. data/gems/e11y-devtools/lib/e11y/devtools/overlay/middleware.rb +80 -0
  93. data/gems/e11y-devtools/lib/e11y/devtools/overlay/rails_controller.rb +42 -0
  94. data/gems/e11y-devtools/lib/e11y/devtools/tui/app.rb +262 -0
  95. data/gems/e11y-devtools/lib/e11y/devtools/tui/grouping.rb +66 -0
  96. data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/event_detail.rb +62 -0
  97. data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/event_list.rb +70 -0
  98. data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/interaction_list.rb +47 -0
  99. data/gems/e11y-devtools/lib/e11y/devtools/version.rb +8 -0
  100. data/gems/e11y-devtools/lib/e11y/devtools.rb +13 -0
  101. data/gems/e11y-devtools/spec/e11y/devtools/mcp/tools_spec.rb +107 -0
  102. data/gems/e11y-devtools/spec/e11y/devtools/overlay/controller_spec.rb +58 -0
  103. data/gems/e11y-devtools/spec/e11y/devtools/overlay/middleware_spec.rb +46 -0
  104. data/gems/e11y-devtools/spec/e11y/devtools/tui/app_spec.rb +85 -0
  105. data/gems/e11y-devtools/spec/e11y/devtools/tui/grouping_spec.rb +64 -0
  106. data/gems/e11y-devtools/spec/spec_helper.rb +5 -0
  107. data/gems/e11y-devtools/spec/tui/widgets/event_list_spec.rb +44 -0
  108. data/gems/e11y-devtools/spec/tui/widgets/interaction_list_spec.rb +62 -0
  109. data/lib/e11y/adapters/audit_encrypted.rb +53 -11
  110. data/lib/e11y/adapters/base.rb +33 -34
  111. data/lib/e11y/adapters/dev_log/file_store.rb +143 -0
  112. data/lib/e11y/adapters/dev_log/query.rb +219 -0
  113. data/lib/e11y/adapters/dev_log.rb +118 -0
  114. data/lib/e11y/adapters/file.rb +3 -6
  115. data/lib/e11y/adapters/in_memory.rb +52 -5
  116. data/lib/e11y/adapters/in_memory_test.rb +29 -0
  117. data/lib/e11y/adapters/loki.rb +58 -23
  118. data/lib/e11y/adapters/null.rb +82 -0
  119. data/lib/e11y/adapters/opentelemetry_collector.rb +183 -0
  120. data/lib/e11y/adapters/otel_logs.rb +136 -23
  121. data/lib/e11y/adapters/sentry.rb +4 -7
  122. data/lib/e11y/adapters/stdout.rb +73 -7
  123. data/lib/e11y/adapters/yabeda.rb +153 -29
  124. data/lib/e11y/buffers/adaptive_buffer.rb +3 -17
  125. data/lib/e11y/buffers/{request_scoped_buffer.rb → ephemeral_buffer.rb} +72 -58
  126. data/lib/e11y/buffers/ring_buffer.rb +3 -16
  127. data/lib/e11y/configuration.rb +272 -0
  128. data/lib/e11y/console.rb +10 -17
  129. data/lib/e11y/current.rb +53 -1
  130. data/lib/e11y/debug/pipeline_inspector.rb +96 -0
  131. data/lib/e11y/documentation/generator.rb +48 -0
  132. data/lib/e11y/event/base.rb +176 -82
  133. data/lib/e11y/event/value_sampling_config.rb +1 -5
  134. data/lib/e11y/events/rails/database/query.rb +1 -4
  135. data/lib/e11y/events/rails/job/failed.rb +2 -0
  136. data/lib/e11y/instruments/active_job.rb +46 -12
  137. data/lib/e11y/instruments/rails_instrumentation.rb +49 -24
  138. data/lib/e11y/instruments/sidekiq.rb +137 -31
  139. data/lib/e11y/linters/base.rb +11 -0
  140. data/lib/e11y/linters/pii/pii_declaration_linter.rb +120 -0
  141. data/lib/e11y/linters/slo/config_consistency_linter.rb +76 -0
  142. data/lib/e11y/linters/slo/explicit_declaration_linter.rb +36 -0
  143. data/lib/e11y/linters/slo/slo_status_from_linter.rb +41 -0
  144. data/lib/e11y/logger/bridge.rb +26 -7
  145. data/lib/e11y/metrics/cardinality_protection.rb +10 -15
  146. data/lib/e11y/metrics/cardinality_tracker.rb +16 -6
  147. data/lib/e11y/metrics/registry.rb +3 -5
  148. data/lib/e11y/metrics/test_backend.rb +62 -0
  149. data/lib/e11y/metrics.rb +56 -10
  150. data/lib/e11y/middleware/adapter_resolver.rb +40 -0
  151. data/lib/e11y/middleware/audit_signing.rb +43 -6
  152. data/lib/e11y/middleware/baggage_protection.rb +75 -0
  153. data/lib/e11y/middleware/dev_log_source.rb +24 -0
  154. data/lib/e11y/middleware/event_slo.rb +23 -9
  155. data/lib/e11y/middleware/otel_span.rb +23 -0
  156. data/lib/e11y/middleware/pii_filter.rb +104 -75
  157. data/lib/e11y/middleware/rate_limiting.rb +54 -27
  158. data/lib/e11y/middleware/request.rb +70 -23
  159. data/lib/e11y/middleware/routing.rb +78 -21
  160. data/lib/e11y/middleware/sampling.rb +66 -17
  161. data/lib/e11y/middleware/self_monitoring_emit.rb +39 -0
  162. data/lib/e11y/middleware/trace_context.rb +45 -10
  163. data/lib/e11y/middleware/track_latency.rb +34 -0
  164. data/lib/e11y/middleware/validation.rb +7 -16
  165. data/lib/e11y/middleware/versioning.rb +26 -22
  166. data/lib/e11y/opentelemetry/semantic_conventions.rb +109 -0
  167. data/lib/e11y/opentelemetry/span_creator.rb +142 -0
  168. data/lib/e11y/pii/patterns.rb +12 -1
  169. data/lib/e11y/pipeline/builder.rb +1 -1
  170. data/lib/e11y/presets/audit_event.rb +13 -2
  171. data/lib/e11y/railtie.rb +52 -15
  172. data/lib/e11y/registry.rb +306 -0
  173. data/lib/e11y/reliability/circuit_breaker.rb +19 -21
  174. data/lib/e11y/reliability/dlq/base.rb +71 -0
  175. data/lib/e11y/reliability/dlq/file_adapter.rb +301 -0
  176. data/lib/e11y/reliability/dlq/file_storage.rb +63 -34
  177. data/lib/e11y/reliability/dlq/filter.rb +37 -54
  178. data/lib/e11y/reliability/retry_handler.rb +26 -29
  179. data/lib/e11y/reliability/retry_rate_limiter.rb +3 -11
  180. data/lib/e11y/sampling/error_spike_detector.rb +0 -2
  181. data/lib/e11y/sampling/load_monitor.rb +5 -9
  182. data/lib/e11y/sampling/stratified_tracker.rb +18 -0
  183. data/lib/e11y/self_monitoring/buffer_monitor.rb +2 -0
  184. data/lib/e11y/self_monitoring/performance_monitor.rb +19 -61
  185. data/lib/e11y/self_monitoring/reliability_monitor.rb +4 -74
  186. data/lib/e11y/slo/config_loader.rb +40 -0
  187. data/lib/e11y/slo/config_validator.rb +58 -0
  188. data/lib/e11y/slo/dashboard_generator.rb +122 -0
  189. data/lib/e11y/slo/event_driven.rb +8 -0
  190. data/lib/e11y/slo/tracker.rb +31 -4
  191. data/lib/e11y/testing/have_tracked_event_matcher.rb +190 -0
  192. data/lib/e11y/testing/rspec_matchers.rb +21 -0
  193. data/lib/e11y/testing/snapshot_matcher.rb +86 -0
  194. data/lib/e11y/trace_context/sampler.rb +35 -0
  195. data/lib/e11y/tracing/faraday_middleware.rb +31 -0
  196. data/lib/e11y/tracing/net_http_patch.rb +33 -0
  197. data/lib/e11y/tracing/propagator.rb +116 -0
  198. data/lib/e11y/tracing.rb +47 -0
  199. data/lib/e11y/version.rb +1 -1
  200. data/lib/e11y/versioning/version_extractor.rb +32 -0
  201. data/lib/e11y.rb +141 -265
  202. data/lib/generators/e11y/event/event_generator.rb +22 -0
  203. data/lib/generators/e11y/event/templates/event.rb.tt +16 -0
  204. data/lib/generators/e11y/grafana_dashboard/grafana_dashboard_generator.rb +30 -0
  205. data/lib/generators/e11y/grafana_dashboard/templates/e11y_dashboard.json +81 -0
  206. data/lib/generators/e11y/install/install_generator.rb +34 -0
  207. data/lib/generators/e11y/install/templates/e11y.rb +239 -0
  208. data/lib/generators/e11y/prometheus_alerts/prometheus_alerts_generator.rb +29 -0
  209. data/lib/generators/e11y/prometheus_alerts/templates/e11y_alerts.yml +28 -0
  210. data/lib/tasks/e11y_docs.rake +30 -0
  211. data/lib/tasks/e11y_events.rake +71 -0
  212. data/lib/tasks/e11y_lint.rake +91 -0
  213. data/lib/tasks/e11y_slo.rake +29 -0
  214. metadata +129 -39
  215. data/docs/ADR-010-developer-experience.md +0 -2166
  216. data/docs/API-REFERENCE-L28.md +0 -914
  217. data/docs/COMPREHENSIVE-CONFIGURATION.md +0 -2366
  218. data/docs/CONTRIBUTING.md +0 -312
  219. data/docs/IMPLEMENTATION_NOTES.md +0 -2804
  220. data/docs/IMPLEMENTATION_PLAN.md +0 -1971
  221. data/docs/IMPLEMENTATION_PLAN_ARCHITECTURE.md +0 -586
  222. data/docs/PLAN.md +0 -148
  223. data/docs/README.md +0 -296
  224. data/docs/design/00-memory-optimization.md +0 -593
  225. data/docs/guides/MIGRATION-L27-L28.md +0 -692
  226. data/docs/guides/PERFORMANCE-BENCHMARKS.md +0 -434
  227. data/docs/guides/README.md +0 -44
  228. data/docs/use_cases/UC-003-pattern-based-metrics.md +0 -1627
  229. data/lib/e11y/adapters/registry.rb +0 -141
  230. /data/docs/{ADR-012-event-evolution.md → architecture/ADR-012-event-evolution.md} +0 -0
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
+ > v0.2.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,43 @@ 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
+ → [Full Quick Start guide (5 min)](#quick-start)
99
+
100
+ ---
101
+
63
102
  ## What Makes E11y Different?
64
103
 
65
104
  ### 1. Request-Scoped Debug Buffering (Unique to E11y)
@@ -75,22 +114,40 @@ Rails.logger.debug "rendering view" # → Always written to disk
75
114
 
76
115
  # E11y approach:
77
116
  E11y.configure do |config|
78
- config.request_buffer.enabled = true
117
+ config.ephemeral_buffer_enabled = true
79
118
  end
80
119
 
81
120
  # Debug events buffered in memory during request
82
- # Flushed to storage ONLY on errors
121
+ # Flushed to storage ONLY on 5xx server errors
83
122
  # Cost: -90%, Noise: -99%, Value: 100%
84
123
  ```
85
124
 
125
+ > **Note:** By default the buffer flushes only on **5xx server errors** (`flush_on_error = true`).
126
+ > On 4xx responses the buffer is discarded. Two independent knobs control this:
127
+ >
128
+ > ```ruby
129
+ > # flush_on_error (default: true) — controls 5xx behaviour
130
+ > config.ephemeral_buffer_flush_on_error = false # disable 5xx flush
131
+ >
132
+ > # flush_on_statuses (default: []) — extra statuses, independent of flush_on_error
133
+ > config.ephemeral_buffer_flush_on_statuses = [403] # also flush on 403 Forbidden
134
+ > config.ephemeral_buffer_flush_on_statuses = [401, 403] # multiple codes
135
+ > ```
136
+
86
137
  **Real-world impact:**
87
- - **Storage costs:** $500/month$50/month (Loki/CloudWatch)
138
+ - **Storage costs:** Up to -90% log volume proportional Loki storage savings
88
139
  - **Log search time:** 30 seconds → 3 seconds (90% less data)
89
140
  - **Developer sanity:** Infinite ✨
90
141
 
91
142
  ---
92
143
 
93
- ### 2. Schema-Validated Business Events
144
+ ### 2. retention_until Simple Archival
145
+
146
+ 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).
147
+
148
+ ---
149
+
150
+ ### 3. Schema-Validated Business Events
94
151
 
95
152
  Stop debugging nil values in production:
96
153
 
@@ -126,44 +183,39 @@ OrderPaidEvent.track(order_id: "123", amount: 99.99, currency: "USD")
126
183
 
127
184
  ---
128
185
 
129
- ### 3. Zero-Config SLO Tracking
186
+ ### 4. Zero-Config SLO Tracking
130
187
 
131
188
  Automatic Service Level Objectives for your endpoints and jobs:
132
189
 
133
190
  ```ruby
134
191
  # Enable Rails instrumentation
135
192
  E11y.configure do |config|
136
- config.rails_instrumentation.enabled = true
193
+ config.rails_instrumentation_enabled = true
137
194
  end
138
195
 
139
- # That's it! E11y now tracks SLOs automatically:
196
+ # That's it! E11y now emits SLO metrics automatically:
140
197
  # - HTTP endpoints: success rate, latency percentiles (p50, p95, p99)
141
198
  # - Background jobs: success rate, execution time, retry rate
142
199
  # - Database queries: slow query detection
143
200
  # - 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
- # }
201
+ #
202
+ # SLO metrics are collected via Prometheus/Yabeda; calculate SLOs from those metrics.
151
203
  ```
152
204
 
153
205
  **vs. Traditional SLO Tracking:**
154
206
  - ❌ Manual instrumentation of every endpoint
155
207
  - ❌ Complex SLO definitions and calculations
156
208
  - ❌ Separate tools for different SLOs
157
- - ✅ E11y: Zero config, automatic tracking, unified view
209
+ - ✅ E11y: Zero config, automatic tracking, metrics for SLO calculation
158
210
 
159
211
  ---
160
212
 
161
- ### 4. Rails-First Design
213
+ ### 5. Rails-First Design
162
214
 
163
215
  Built for Rails developers, not platform engineers:
164
216
 
165
217
  ```ruby
166
- # 5-minute setup, not 2-week OpenTelemetry migration
218
+ # Fast setup, not 2-week OpenTelemetry migration
167
219
  gem "e11y"
168
220
 
169
221
  E11y.configure do |config|
@@ -172,7 +224,7 @@ E11y.configure do |config|
172
224
  end
173
225
 
174
226
  # Auto-instruments Rails (optional):
175
- config.rails_instrumentation.enabled = true
227
+ config.rails_instrumentation_enabled = true
176
228
  # → HTTP requests, ActiveRecord, ActiveJob, Cache events
177
229
  ```
178
230
 
@@ -184,6 +236,19 @@ config.rails_instrumentation.enabled = true
184
236
 
185
237
  ---
186
238
 
239
+ ### 6. Built-in PII Filtering
240
+
241
+ **Built-in PII filtering** — mask, hash, or redact sensitive fields per event class. No other Ruby observability gem provides this out of the box.
242
+
243
+ ```ruby
244
+ class Events::UserSignedIn < E11y::Event::Base
245
+ contains_pii :email, strategy: :hash
246
+ contains_pii :ip_address, strategy: :mask
247
+ end
248
+ ```
249
+
250
+ ---
251
+
187
252
  ## Who Should Use E11y?
188
253
 
189
254
  ### ✅ Perfect For
@@ -204,7 +269,7 @@ config.rails_instrumentation.enabled = true
204
269
 
205
270
  - **Non-Rails Ruby** - Focused on Rails conventions first
206
271
  - **Microservices polyglot** - OpenTelemetry better for multi-language
207
- - **Enterprise compliance** - E11y is WIP, audit trails coming soon
272
+ - **Enterprise compliance requirements** audit trails and compliance reports are not yet available
208
273
  - **Auto-instrumentation only** - E11y requires event definitions (by design)
209
274
 
210
275
  ---
@@ -218,14 +283,15 @@ config.rails_instrumentation.enabled = true
218
283
  | **Schema Validation** | ✅ Implemented | dry-schema validation before sending events |
219
284
  | **Metrics DSL** | ✅ Implemented | Define Prometheus metrics alongside events |
220
285
  | **Adapters** | ✅ 7 adapters | Loki, Sentry, OpenTelemetry, Yabeda, File, Stdout, InMemory |
221
- | **PII Filtering** | ✅ Implemented | Configurable field masking/hashing/redaction |
286
+ | **PII Filtering** | ✅ Implemented | Configurable field masking/hashing/redaction (event-level DSL) |
222
287
  | **Adaptive Sampling** | ✅ Implemented | Error-based, load-based, value-based strategies |
288
+ | **Rate Limiting** | ✅ Implemented | Opt-in — requires `config.pipeline.use E11y::Middleware::RateLimiting` |
223
289
  | **Rails Integration** | ✅ Implemented | Auto-instrument HTTP, ActiveRecord, ActiveJob, Cache |
224
290
  | **Production Testing** | 🚧 In Progress | Validating with real workloads |
225
291
 
226
292
  ---
227
293
 
228
- ## Quick Start in 5 Minutes
294
+ ## Quick Start
229
295
 
230
296
  ### 1. Install
231
297
 
@@ -242,7 +308,9 @@ bundle install
242
308
  # config/initializers/e11y.rb
243
309
  E11y.configure do |config|
244
310
  # Enable request-scoped debug buffering (THE killer feature)
245
- config.request_buffer.enabled = true
311
+ config.ephemeral_buffer_enabled = true
312
+ # config.ephemeral_buffer_flush_on_error = true # default: flush on 5xx
313
+ # config.ephemeral_buffer_flush_on_statuses = [403] # also flush on 403
246
314
 
247
315
  # Configure where events go
248
316
  config.adapters[:logs] = E11y::Adapters::Loki.new(
@@ -255,7 +323,7 @@ E11y.configure do |config|
255
323
  )
256
324
 
257
325
  # Optional: Auto-instrument Rails
258
- config.rails_instrumentation.enabled = true
326
+ config.rails_instrumentation_enabled = true
259
327
  end
260
328
  ```
261
329
 
@@ -312,7 +380,7 @@ end
312
380
  - ❌ nil values in production
313
381
  - ❌ Manual `Yabeda.increment` everywhere
314
382
  - ❌ Manual SLO definitions and calculations
315
- - ❌ $500/month log storage bills
383
+ - ❌ High log storage bills from storing everything
316
384
 
317
385
  ---
318
386
 
@@ -341,7 +409,7 @@ Rails.logger.error "Payment failed!"
341
409
  # [DEBUG] Cache read... (3 lines)
342
410
  # [ERROR] Payment failed (1 line)
343
411
  # Total: 400 lines, 74% noise ❌
344
- # Cost: $500/month Loki storage
412
+ # Cost: High (all logs stored)
345
413
  # Search time: 30 seconds ❌
346
414
  ```
347
415
 
@@ -349,7 +417,7 @@ Rails.logger.error "Payment failed!"
349
417
 
350
418
  ```ruby
351
419
  # Production with E11y:
352
- E11y.configure { |c| c.request_buffer.enabled = true }
420
+ E11y.configure { |c| c.ephemeral_buffer_enabled = true }
353
421
 
354
422
  # 99 successful requests:
355
423
  # [INFO] User logged in (99 lines)
@@ -361,7 +429,7 @@ E11y.configure { |c| c.request_buffer.enabled = true }
361
429
  # [DEBUG] SQL: SELECT... (context!) ← Flushed!
362
430
  # [DEBUG] Rendered view... (trail!) ← Flushed!
363
431
  # Total: 103 lines, 0% noise ✅
364
- # Cost: $50/month Loki storage ✅ (-90%)
432
+ # Cost: Low (-90% log volume)
365
433
  # Search time: 3 seconds ✅ (-90%)
366
434
  ```
367
435
 
@@ -376,16 +444,16 @@ E11y.configure { |c| c.request_buffer.enabled = true }
376
444
 
377
445
  ### Comparison Matrix
378
446
 
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 |
447
+ | Solution | Setup Time | Monthly Cost | Request-Scoped Buffering | SLO Tracking | Schema Validation | Auto-Metrics | Built-in PII Filtering | Data Ownership | Ecosystem / Managed Infra |
448
+ |----------|-----------|--------------|--------------------------|--------------|-------------------|--------------|------------------------|----------------|--------------------------|
449
+ | **E11y** | **5–30 min*** | **Infra costs** | **✅ Unique** | **✅ Zero-config** | **✅** | **✅** | **✅ Field masking, hashing, redaction** | **✅ Full** | ⚠️ Ruby/Rails only |
450
+ | Datadog APM | 2-4 hours | $500-5,000 | ❌ | ✅ Manual | ❌ | ✅ | ⚠️ Via agent config (limited) | ❌ SaaS lock-in | ✅ Extensive + fully managed |
451
+ | New Relic | 2-4 hours | $99-658/user | ❌ | ✅ Manual | ❌ | ✅ | ⚠️ Via obfuscation rules | ❌ SaaS lock-in | ✅ Extensive + fully managed |
452
+ | Sentry | 1 hour | $26-80/mo | ❌ | ❌ | ❌ | Partial | ⚠️ Data scrubbing rules | ❌ SaaS lock-in | ✅ Managed (error-focused) |
453
+ | Semantic Logger | 30 minutes | Infra costs | ❌ | ❌ | ❌ | ❌ | ❌ None | ✅ Full | ⚠️ Ruby only, self-hosted |
454
+ | OpenTelemetry | 1-2 weeks | Infra costs | ❌ | Manual setup | ❌ | ✅ | ❌ Manual implementation required | ✅ Full | ✅ Polyglot, vendor-neutral |
455
+ | Grafana + Loki | 2-3 days | Infra costs | ❌ | Manual setup | ❌ | Manual | ❌ None | ✅ Full | ✅ Mature, DevOps-friendly |
456
+ | AppSignal | 1 hour | $23-499/mo | ❌ | ✅ Built-in | ❌ | ✅ | ⚠️ Parameter filtering only | ❌ SaaS lock-in | ✅ Managed (Rails-friendly) |
389
457
 
390
458
  **Legend:**
391
459
  - **Setup Time:** From zero to first meaningful data
@@ -394,108 +462,19 @@ E11y.configure { |c| c.request_buffer.enabled = true }
394
462
  - **SLO Tracking:** Automatic Service Level Objectives monitoring
395
463
  - **Schema Validation:** Type-safe event schemas
396
464
  - **Auto-Metrics:** Metrics generated from events automatically
465
+ - **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
466
  - **Data Ownership:** Can you host it yourself?
467
+ - **Ecosystem / Managed Infra:** Integration breadth and whether infrastructure is managed for you
398
468
 
399
- ---
469
+ *\* 5 min for gem + stdout; 30 min if adding self-hosted Loki/Grafana stack.
400
470
 
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
436
-
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`
471
+ > Cost estimates assume migration from verbose SaaS logging (Datadog/CloudWatch) to self-hosted Loki. Actual savings depend on your current setup.
448
472
 
449
473
  ---
450
474
 
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`
479
-
480
- ---
481
-
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
475
+ ### Detailed Comparisons
497
476
 
498
- **Use both:** E11y for events/logs/metrics, AppSignal for performance profiling
477
+ See [docs/COMPARISON.md](docs/COMPARISON.md) for detailed per-tool comparisons.
499
478
 
500
479
  ---
501
480
 
@@ -543,583 +522,36 @@ E11y is optimized for:
543
522
  **Not optimized for:**
544
523
  - Polyglot microservices (use OpenTelemetry)
545
524
  - 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
- ```
525
+ - Enterprise compliance requirements (not yet available)
1083
526
 
1084
527
  ---
1085
528
 
1086
529
  ## Performance
1087
530
 
1088
- ### Design Principles
1089
-
1090
- E11y is designed for performance:
1091
-
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
1096
-
1097
- See `benchmarks/` directory for detailed performance tests.
1098
-
1099
- ### Cardinality Protection
1100
-
1101
- Optional cardinality protection prevents high-cardinality labels from overwhelming metrics systems:
1102
-
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.
531
+ p99 latency <70µs (`:always`), <10µs (`:sampled`), <50µs (`:never`). Full benchmarks → [docs/PERFORMANCE.md](docs/PERFORMANCE.md)
1112
532
 
1113
533
  ---
1114
534
 
1115
535
  ## Documentation
1116
536
 
1117
- Additional documentation is available in the `docs/` directory:
1118
-
1119
- - Architecture Decision Records (ADRs)
1120
- - Use Cases
1121
- - Configuration guides
1122
- - Performance benchmarks
537
+ | Topic | Doc |
538
+ |-------|-----|
539
+ | [Schema Validation](docs/SCHEMA_VALIDATION.md) | dry-schema validation, modes, error handling |
540
+ | [Metrics DSL](docs/METRICS_DSL.md) | Counters, histograms, gauges, Yabeda integration |
541
+ | [Adapters](docs/ADAPTERS.md) | Loki, Sentry, OTel, Yabeda, File, Stdout, InMemory |
542
+ | [PII Filtering](docs/PII_FILTERING.md) | Mask, hash, redact sensitive fields |
543
+ | [Adaptive Sampling](docs/ADAPTIVE_SAMPLING.md) | Error-based, load-based, value-based |
544
+ | [Presets](docs/PRESETS.md) | HighValueEvent, AuditEvent, DebugEvent |
545
+ | [Distributed Tracing](docs/DISTRIBUTED_TRACING.md) | W3C Trace Context, manual propagation |
546
+ | [Rails Integration](docs/RAILS_INTEGRATION.md) | Auto-instrumentation, Sidekiq |
547
+ | [Testing](docs/TESTING.md) | InMemoryTest adapter, RSpec setup |
548
+ | [Configuration](docs/CONFIGURATION.md) | Basic config, middleware pipeline |
549
+ | [SLO PromQL & Alerts](docs/SLO-PROMQL-ALERTS.md) | PromQL queries, Prometheus alert rules |
550
+ | [Performance](docs/PERFORMANCE.md) | Benchmarks, validation modes, cardinality |
551
+ | [Limitations](docs/LIMITATIONS.md) | Rails only, Ruby 3.2+, tradeoffs |
552
+ | [Comparison](docs/COMPARISON.md) | vs Datadog, OTel, Sentry, AppSignal, etc. |
553
+
554
+ Also: [ADRs](docs/architecture/ADR-INDEX.md), [Use Cases](docs/use_cases/README.md), [QUICK-START](docs/QUICK-START.md)
1123
555
 
1124
556
  ---
1125
557