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.
- checksums.yaml +4 -4
- data/.rubocop.yml +130 -10
- data/CHANGELOG.md +80 -1
- data/CLAUDE.md +168 -0
- data/CONTRIBUTING.md +640 -0
- data/README.md +165 -701
- data/RELEASE.md +41 -12
- data/Rakefile +249 -57
- data/config/README.md +1 -1
- data/config/loki-local-config.yaml +12 -0
- data/config/otel-collector-config.yaml +44 -0
- data/cucumber.yml +1 -0
- data/docker-compose.yml +18 -2
- data/docs/ADAPTERS.md +76 -0
- data/docs/ADAPTIVE_SAMPLING.md +59 -0
- data/docs/COMPARISON.md +104 -0
- data/docs/CONFIGURATION.md +52 -0
- data/docs/DISTRIBUTED_TRACING.md +44 -0
- data/docs/LIMITATIONS.md +13 -0
- data/docs/METRICS_DSL.md +84 -0
- data/docs/PERFORMANCE.md +60 -0
- data/docs/PII_FILTERING.md +40 -0
- data/docs/PRESETS.md +65 -0
- data/docs/QUICK-START.md +546 -587
- data/docs/RAILS_INTEGRATION.md +79 -0
- data/docs/SCHEMA_VALIDATION.md +63 -0
- data/docs/SLO-PROMQL-ALERTS.md +161 -0
- data/docs/TESTING.md +69 -0
- data/docs/{ADR-001-architecture.md → architecture/ADR-001-architecture.md} +36 -65
- data/docs/{ADR-002-metrics-yabeda.md → architecture/ADR-002-metrics-yabeda.md} +62 -236
- data/docs/architecture/ADR-003-slo-observability.md +1402 -0
- data/docs/{ADR-004-adapter-architecture.md → architecture/ADR-004-adapter-architecture.md} +163 -146
- data/docs/{ADR-005-tracing-context.md → architecture/ADR-005-tracing-context.md} +10 -9
- data/docs/{ADR-006-security-compliance.md → architecture/ADR-006-security-compliance.md} +184 -191
- data/docs/{ADR-007-opentelemetry-integration.md → architecture/ADR-007-opentelemetry-integration.md} +3 -21
- data/docs/{ADR-008-rails-integration.md → architecture/ADR-008-rails-integration.md} +182 -743
- data/docs/{ADR-009-cost-optimization.md → architecture/ADR-009-cost-optimization.md} +45 -54
- data/docs/architecture/ADR-010-developer-experience.md +522 -0
- data/docs/{ADR-011-testing-strategy.md → architecture/ADR-011-testing-strategy.md} +44 -86
- data/docs/{ADR-012-event-evolution.md → architecture/ADR-012-event-evolution.md} +11 -11
- data/docs/{ADR-013-reliability-error-handling.md → architecture/ADR-013-reliability-error-handling.md} +37 -12
- data/docs/{ADR-014-event-driven-slo.md → architecture/ADR-014-event-driven-slo.md} +12 -24
- data/docs/{ADR-015-middleware-order.md → architecture/ADR-015-middleware-order.md} +43 -59
- data/docs/{ADR-016-self-monitoring-slo.md → architecture/ADR-016-self-monitoring-slo.md} +58 -355
- data/docs/{ADR-017-multi-rails-compatibility.md → architecture/ADR-017-multi-rails-compatibility.md} +4 -11
- data/docs/architecture/ADR-018-memory-optimization.md +366 -0
- data/docs/{ADR-INDEX.md → architecture/ADR-INDEX.md} +11 -6
- data/docs/plans/2026-03-20-browser-overlay-svelte.md +281 -0
- data/docs/{00-ICP-AND-TIMELINE.md → prd/00-ICP-AND-TIMELINE.md} +6 -6
- data/docs/{01-SCALE-REQUIREMENTS.md → prd/01-SCALE-REQUIREMENTS.md} +6 -6
- data/docs/prd/01-overview-vision.md +19 -14
- data/docs/use_cases/README.md +22 -23
- data/docs/use_cases/UC-001-request-scoped-debug-buffering.md +50 -44
- data/docs/use_cases/UC-002-business-event-tracking.md +26 -95
- data/docs/use_cases/UC-003-event-metrics.md +66 -0
- data/docs/use_cases/UC-004-zero-config-slo-tracking.md +33 -684
- data/docs/use_cases/UC-005-sentry-integration.md +13 -15
- data/docs/use_cases/UC-006-trace-context-management.md +30 -28
- data/docs/use_cases/UC-007-pii-filtering.md +35 -87
- data/docs/use_cases/UC-008-opentelemetry-integration.md +51 -89
- data/docs/use_cases/UC-009-multi-service-tracing.md +30 -178
- data/docs/use_cases/UC-010-background-job-tracking.md +24 -91
- data/docs/use_cases/UC-011-rate-limiting.md +95 -168
- data/docs/use_cases/UC-012-audit-trail.md +21 -46
- data/docs/use_cases/UC-013-high-cardinality-protection.md +29 -167
- data/docs/use_cases/UC-014-adaptive-sampling.md +2 -2
- data/docs/use_cases/UC-015-cost-optimization.md +46 -99
- data/docs/use_cases/UC-016-rails-logger-migration.md +39 -213
- data/docs/use_cases/UC-017-local-development.md +203 -777
- data/docs/use_cases/UC-018-testing-events.md +3 -3
- data/docs/use_cases/UC-019-retention-based-routing.md +53 -106
- data/docs/use_cases/UC-020-event-versioning.md +8 -9
- data/docs/use_cases/UC-021-error-handling-retry-dlq.md +18 -22
- data/docs/use_cases/UC-022-event-registry.md +15 -21
- data/docs/use_cases/backlog.md +119 -87
- data/e11y.gemspec +2 -2
- data/gems/e11y-devtools/README.md +158 -0
- data/gems/e11y-devtools/config/routes.rb +15 -0
- data/gems/e11y-devtools/e11y-devtools.gemspec +25 -0
- data/gems/e11y-devtools/exe/e11y +34 -0
- data/gems/e11y-devtools/frontend/.gitignore +24 -0
- data/gems/e11y-devtools/frontend/README.md +51 -0
- data/gems/e11y-devtools/frontend/index.html +14 -0
- data/gems/e11y-devtools/frontend/package-lock.json +3707 -0
- data/gems/e11y-devtools/frontend/package.json +28 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/events/recent.json +4205 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/interactions.json +194 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/0a2e04027cfa22d014bc22e8b27cd913/events.json +86 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/0e1543af6a630fb3af6b52283154b3e0/events.json +169 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/1838b691faa49564f97db8592ff3978d/events.json +78 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/29f198f6588dacffb687777eb5f8f118/events.json +197 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/34bc3c9c0097de28a7a6f99b90a8e7bc/events.json +194 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/3ba6c20d068ab9cee00e51b180e66444/events.json +184 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/435bfd8f17b9009146a79812d7c3726d/events.json +144 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/4c7676e3fe668e99edb2b94d7d5678a9/events.json +222 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/6daf0d47974bedfc55d5de7004a3ea9f/events.json +194 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/8a81ada42834d15f287bb40010043605/events.json +194 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/8c0a98900edaae105469df8daedccf02/events.json +198 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/8e4f645180f8a7d1dce426b07380466b/events.json +222 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/93db346fa5d44a032605a13b627f4b80/events.json +128 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/98ff6146faf7bd9be8bd03a8275817ba/events.json +223 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/9997ddd0247bc7e25f2ca7a5c415c93d/events.json +197 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/99e35f8ef3baedd798cc4fd085980ad9/events.json +194 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/b4f3095c1909924cbc98889a86c83d6d/events.json +131 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/b54b7fc32b7575a7110de809d11ccda0/events.json +128 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/c0b48033fa06746bcc5886745e053cff/events.json +169 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/c44649ac76701b4558927cd2305ab535/events.json +169 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/d601ae3320057580a39dbdac2edfdf4a/events.json +248 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/e67e724bab422d2b52eeb49635e512e1/events.json +194 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/e6c72765a28f158a8485b35fa63f73da/events.json +194 -0
- data/gems/e11y-devtools/frontend/public/mocks/v1/traces/f541b87405c9a54819b18ebe529f6419/events.json +194 -0
- data/gems/e11y-devtools/frontend/scripts/generate_mocks.rb +397 -0
- data/gems/e11y-devtools/frontend/src/App.svelte +827 -0
- data/gems/e11y-devtools/frontend/src/components/Fab.svelte +19 -0
- data/gems/e11y-devtools/frontend/src/components/FilterBar.svelte +38 -0
- data/gems/e11y-devtools/frontend/src/components/FullscreenPanel.svelte +82 -0
- data/gems/e11y-devtools/frontend/src/components/InteractionsTimeline.svelte +264 -0
- data/gems/e11y-devtools/frontend/src/components/RecentHistogram.svelte +354 -0
- data/gems/e11y-devtools/frontend/src/lib/api.ts +37 -0
- data/gems/e11y-devtools/frontend/src/lib/eventIdentity.ts +12 -0
- data/gems/e11y-devtools/frontend/src/lib/format.ts +37 -0
- data/gems/e11y-devtools/frontend/src/lib/listFilter.ts +43 -0
- data/gems/e11y-devtools/frontend/src/lib/recentVolume.ts +80 -0
- data/gems/e11y-devtools/frontend/src/lib/router.ts +12 -0
- data/gems/e11y-devtools/frontend/src/lib/transitions.ts +34 -0
- data/gems/e11y-devtools/frontend/src/lib/viewportOrigin.ts +25 -0
- data/gems/e11y-devtools/frontend/src/main.ts +8 -0
- data/gems/e11y-devtools/frontend/src/overlay-entry.ts +24 -0
- data/gems/e11y-devtools/frontend/src/overlay.css +1080 -0
- data/gems/e11y-devtools/frontend/svelte.config.js +2 -0
- data/gems/e11y-devtools/frontend/test_puppeteer.js +41 -0
- data/gems/e11y-devtools/frontend/test_scale.js +3 -0
- data/gems/e11y-devtools/frontend/tsconfig.app.json +21 -0
- data/gems/e11y-devtools/frontend/tsconfig.json +7 -0
- data/gems/e11y-devtools/frontend/tsconfig.node.json +26 -0
- data/gems/e11y-devtools/frontend/vite.config.ts +36 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/server.rb +96 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tool_base.rb +25 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/clear.rb +31 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/errors.rb +35 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/event_detail.rb +33 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/events_by_trace.rb +33 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/interactions.rb +40 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/recent_events.rb +34 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/search.rb +34 -0
- data/gems/e11y-devtools/lib/e11y/devtools/mcp/tools/stats.rb +30 -0
- data/gems/e11y-devtools/lib/e11y/devtools/overlay/assets/overlay.js +20 -0
- data/gems/e11y-devtools/lib/e11y/devtools/overlay/controller.rb +94 -0
- data/gems/e11y-devtools/lib/e11y/devtools/overlay/engine.rb +26 -0
- data/gems/e11y-devtools/lib/e11y/devtools/overlay/middleware.rb +80 -0
- data/gems/e11y-devtools/lib/e11y/devtools/overlay/rails_controller.rb +67 -0
- data/gems/e11y-devtools/lib/e11y/devtools/tui/app.rb +262 -0
- data/gems/e11y-devtools/lib/e11y/devtools/tui/grouping.rb +66 -0
- data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/event_detail.rb +62 -0
- data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/event_list.rb +70 -0
- data/gems/e11y-devtools/lib/e11y/devtools/tui/widgets/interaction_list.rb +47 -0
- data/gems/e11y-devtools/lib/e11y/devtools/version.rb +8 -0
- data/gems/e11y-devtools/lib/e11y/devtools.rb +13 -0
- data/gems/e11y-devtools/spec/e11y/devtools/mcp/tools_spec.rb +107 -0
- data/gems/e11y-devtools/spec/e11y/devtools/overlay/controller_spec.rb +91 -0
- data/gems/e11y-devtools/spec/e11y/devtools/overlay/middleware_spec.rb +46 -0
- data/gems/e11y-devtools/spec/e11y/devtools/tui/app_spec.rb +85 -0
- data/gems/e11y-devtools/spec/e11y/devtools/tui/grouping_spec.rb +64 -0
- data/gems/e11y-devtools/spec/spec_helper.rb +5 -0
- data/gems/e11y-devtools/spec/tui/widgets/event_list_spec.rb +44 -0
- data/gems/e11y-devtools/spec/tui/widgets/interaction_list_spec.rb +62 -0
- data/lib/e11y/adapters/audit_encrypted.rb +53 -11
- data/lib/e11y/adapters/base.rb +33 -34
- data/lib/e11y/adapters/dev_log/file_store.rb +143 -0
- data/lib/e11y/adapters/dev_log/query.rb +219 -0
- data/lib/e11y/adapters/dev_log.rb +118 -0
- data/lib/e11y/adapters/file.rb +3 -6
- data/lib/e11y/adapters/in_memory.rb +52 -5
- data/lib/e11y/adapters/in_memory_test.rb +29 -0
- data/lib/e11y/adapters/loki.rb +58 -23
- data/lib/e11y/adapters/null.rb +82 -0
- data/lib/e11y/adapters/opentelemetry_collector.rb +183 -0
- data/lib/e11y/adapters/otel_logs.rb +136 -23
- data/lib/e11y/adapters/sentry.rb +4 -7
- data/lib/e11y/adapters/stdout.rb +73 -7
- data/lib/e11y/adapters/yabeda.rb +153 -29
- data/lib/e11y/buffers/adaptive_buffer.rb +3 -17
- data/lib/e11y/buffers/{request_scoped_buffer.rb → ephemeral_buffer.rb} +72 -58
- data/lib/e11y/buffers/ring_buffer.rb +3 -16
- data/lib/e11y/configuration.rb +272 -0
- data/lib/e11y/console.rb +10 -17
- data/lib/e11y/current.rb +53 -1
- data/lib/e11y/debug/pipeline_inspector.rb +96 -0
- data/lib/e11y/documentation/generator.rb +48 -0
- data/lib/e11y/event/base.rb +176 -82
- data/lib/e11y/event/value_sampling_config.rb +1 -5
- data/lib/e11y/events/rails/database/query.rb +1 -4
- data/lib/e11y/events/rails/job/failed.rb +2 -0
- data/lib/e11y/instruments/active_job.rb +44 -12
- data/lib/e11y/instruments/rails_instrumentation.rb +49 -24
- data/lib/e11y/instruments/sidekiq.rb +135 -31
- data/lib/e11y/linters/base.rb +11 -0
- data/lib/e11y/linters/pii/pii_declaration_linter.rb +120 -0
- data/lib/e11y/linters/slo/config_consistency_linter.rb +76 -0
- data/lib/e11y/linters/slo/explicit_declaration_linter.rb +36 -0
- data/lib/e11y/linters/slo/slo_status_from_linter.rb +41 -0
- data/lib/e11y/logger/bridge.rb +26 -7
- data/lib/e11y/metrics/cardinality_protection.rb +10 -15
- data/lib/e11y/metrics/cardinality_tracker.rb +16 -6
- data/lib/e11y/metrics/registry.rb +3 -5
- data/lib/e11y/metrics/test_backend.rb +62 -0
- data/lib/e11y/metrics.rb +56 -10
- data/lib/e11y/middleware/adapter_resolver.rb +40 -0
- data/lib/e11y/middleware/audit_signing.rb +43 -6
- data/lib/e11y/middleware/baggage_protection.rb +75 -0
- data/lib/e11y/middleware/dev_log_source.rb +24 -0
- data/lib/e11y/middleware/event_slo.rb +23 -9
- data/lib/e11y/middleware/otel_span.rb +23 -0
- data/lib/e11y/middleware/pii_filter.rb +104 -75
- data/lib/e11y/middleware/rate_limiting.rb +54 -27
- data/lib/e11y/middleware/request.rb +70 -23
- data/lib/e11y/middleware/routing.rb +78 -21
- data/lib/e11y/middleware/sampling.rb +66 -17
- data/lib/e11y/middleware/self_monitoring_emit.rb +39 -0
- data/lib/e11y/middleware/trace_context.rb +45 -10
- data/lib/e11y/middleware/track_latency.rb +34 -0
- data/lib/e11y/middleware/validation.rb +7 -16
- data/lib/e11y/middleware/versioning.rb +26 -22
- data/lib/e11y/opentelemetry/semantic_conventions.rb +109 -0
- data/lib/e11y/opentelemetry/span_creator.rb +142 -0
- data/lib/e11y/pii/patterns.rb +12 -1
- data/lib/e11y/pipeline/builder.rb +4 -4
- data/lib/e11y/presets/audit_event.rb +13 -2
- data/lib/e11y/railtie.rb +52 -14
- data/lib/e11y/registry.rb +306 -0
- data/lib/e11y/reliability/circuit_breaker.rb +19 -21
- data/lib/e11y/reliability/dlq/base.rb +71 -0
- data/lib/e11y/reliability/dlq/file_adapter.rb +301 -0
- data/lib/e11y/reliability/dlq/file_storage.rb +63 -34
- data/lib/e11y/reliability/dlq/filter.rb +37 -54
- data/lib/e11y/reliability/retry_handler.rb +26 -29
- data/lib/e11y/reliability/retry_rate_limiter.rb +3 -11
- data/lib/e11y/sampling/error_spike_detector.rb +0 -2
- data/lib/e11y/sampling/load_monitor.rb +5 -9
- data/lib/e11y/sampling/stratified_tracker.rb +18 -0
- data/lib/e11y/self_monitoring/buffer_monitor.rb +2 -0
- data/lib/e11y/self_monitoring/performance_monitor.rb +19 -61
- data/lib/e11y/self_monitoring/reliability_monitor.rb +4 -74
- data/lib/e11y/slo/config_loader.rb +40 -0
- data/lib/e11y/slo/config_validator.rb +58 -0
- data/lib/e11y/slo/dashboard_generator.rb +122 -0
- data/lib/e11y/slo/event_driven.rb +8 -0
- data/lib/e11y/slo/tracker.rb +31 -4
- data/lib/e11y/testing/have_tracked_event_matcher.rb +190 -0
- data/lib/e11y/testing/rspec_matchers.rb +21 -0
- data/lib/e11y/testing/snapshot_matcher.rb +86 -0
- data/lib/e11y/trace_context/sampler.rb +35 -0
- data/lib/e11y/tracing/faraday_middleware.rb +31 -0
- data/lib/e11y/tracing/net_http_patch.rb +33 -0
- data/lib/e11y/tracing/propagator.rb +144 -0
- data/lib/e11y/tracing.rb +47 -0
- data/lib/e11y/version.rb +1 -1
- data/lib/e11y/versioning/version_extractor.rb +32 -0
- data/lib/e11y.rb +123 -266
- data/lib/generators/e11y/event/event_generator.rb +22 -0
- data/lib/generators/e11y/event/templates/event.rb.tt +16 -0
- data/lib/generators/e11y/grafana_dashboard/grafana_dashboard_generator.rb +30 -0
- data/lib/generators/e11y/grafana_dashboard/templates/e11y_dashboard.json +81 -0
- data/lib/generators/e11y/install/install_generator.rb +34 -0
- data/lib/generators/e11y/install/templates/e11y.rb +239 -0
- data/lib/generators/e11y/prometheus_alerts/prometheus_alerts_generator.rb +29 -0
- data/lib/generators/e11y/prometheus_alerts/templates/e11y_alerts.yml +28 -0
- data/lib/tasks/e11y_docs.rake +30 -0
- data/lib/tasks/e11y_events.rake +71 -0
- data/lib/tasks/e11y_lint.rake +91 -0
- data/lib/tasks/e11y_slo.rake +29 -0
- metadata +186 -39
- data/docs/ADR-003-slo-observability.md +0 -3337
- data/docs/ADR-010-developer-experience.md +0 -2166
- data/docs/API-REFERENCE-L28.md +0 -914
- data/docs/COMPREHENSIVE-CONFIGURATION.md +0 -2366
- data/docs/CONTRIBUTING.md +0 -312
- data/docs/IMPLEMENTATION_NOTES.md +0 -2804
- data/docs/IMPLEMENTATION_PLAN.md +0 -1971
- data/docs/IMPLEMENTATION_PLAN_ARCHITECTURE.md +0 -586
- data/docs/PLAN.md +0 -148
- data/docs/README.md +0 -296
- data/docs/design/00-memory-optimization.md +0 -593
- data/docs/guides/MIGRATION-L27-L28.md +0 -692
- data/docs/guides/PERFORMANCE-BENCHMARKS.md +0 -434
- data/docs/guides/README.md +0 -44
- data/docs/use_cases/UC-003-pattern-based-metrics.md +0 -1627
- 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
|
-
**
|
|
5
|
+
**Debug production issues in seconds. Zero setup overhead. Own your data.**
|
|
6
6
|
|
|
7
7
|
[](https://badge.fury.io/rb/e11y)
|
|
8
8
|
[](https://github.com/arturseletskiy/e11y/actions/workflows/ci.yml)
|
|
9
9
|
[](https://codecov.io/gh/arturseletskiy/e11y)
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
[Quick Start](#quick-start) • [How it works](#the-e11y-solution) • [Docs](#documentation)
|
|
12
12
|
|
|
13
|
-
|
|
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.
|
|
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:**
|
|
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.
|
|
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
|
-
###
|
|
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.
|
|
196
|
+
config.rails_instrumentation_enabled = true
|
|
137
197
|
end
|
|
138
198
|
|
|
139
|
-
# That's it! E11y now
|
|
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
|
-
#
|
|
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,
|
|
212
|
+
- ✅ E11y: Zero config, automatic tracking, metrics for SLO calculation
|
|
158
213
|
|
|
159
214
|
---
|
|
160
215
|
|
|
161
|
-
###
|
|
216
|
+
### 5. Rails-First Design
|
|
162
217
|
|
|
163
218
|
Built for Rails developers, not platform engineers:
|
|
164
219
|
|
|
165
220
|
```ruby
|
|
166
|
-
#
|
|
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
|
-
#
|
|
175
|
-
config.
|
|
176
|
-
|
|
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**
|
|
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
|
|
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.
|
|
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
|
-
#
|
|
258
|
-
config.
|
|
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
|
-
- ❌
|
|
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:
|
|
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.
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
555
|
+
## Upgrading
|
|
1098
556
|
|
|
1099
|
-
|
|
557
|
+
Breaking renames and migration steps are listed in [CHANGELOG.md](CHANGELOG.md). Common ones:
|
|
1100
558
|
|
|
1101
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1118
|
-
|
|
1119
|
-
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
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
|
|