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.
- checksums.yaml +4 -4
- data/.rubocop.yml +130 -10
- data/CHANGELOG.md +56 -1
- data/CLAUDE.md +168 -0
- data/CONTRIBUTING.md +640 -0
- data/README.md +134 -702
- data/RELEASE.md +18 -3
- data/Rakefile +108 -29
- 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 +29 -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} +35 -64
- data/docs/{ADR-002-metrics-yabeda.md → architecture/ADR-002-metrics-yabeda.md} +62 -236
- data/docs/{ADR-003-slo-observability.md → architecture/ADR-003-slo-observability.md} +27 -466
- 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} +209 -339
- 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} +41 -83
- 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} +23 -41
- data/docs/{ADR-016-self-monitoring-slo.md → architecture/ADR-016-self-monitoring-slo.md} +52 -349
- 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/{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 +42 -101
- 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 +4 -4
- data/docs/use_cases/UC-010-background-job-tracking.md +5 -5
- 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 +136 -0
- data/gems/e11y-devtools/config/routes.rb +8 -0
- data/gems/e11y-devtools/e11y-devtools.gemspec +25 -0
- data/gems/e11y-devtools/exe/e11y +34 -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 +115 -0
- data/gems/e11y-devtools/lib/e11y/devtools/overlay/controller.rb +54 -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 +42 -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 +58 -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 +46 -12
- data/lib/e11y/instruments/rails_instrumentation.rb +49 -24
- data/lib/e11y/instruments/sidekiq.rb +137 -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 +1 -1
- data/lib/e11y/presets/audit_event.rb +13 -2
- data/lib/e11y/railtie.rb +52 -15
- 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 +116 -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 +141 -265
- 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 +129 -39
- 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/docs/{ADR-012-event-evolution.md → architecture/ADR-012-event-evolution.md} +0 -0
|
@@ -1,867 +1,293 @@
|
|
|
1
|
-
# UC-017: Local Development
|
|
1
|
+
# UC-017: Local Development with e11y-devtools
|
|
2
2
|
|
|
3
|
-
**Status:**
|
|
4
|
-
**Complexity:** Beginner
|
|
5
|
-
**Setup Time:**
|
|
6
|
-
**Target Users:** All Developers
|
|
3
|
+
**Status:** Implemented
|
|
4
|
+
**Complexity:** Beginner
|
|
5
|
+
**Setup Time:** 2 minutes
|
|
6
|
+
**Target Users:** All Rails Developers
|
|
7
|
+
**Related ADR:** ADR-010
|
|
7
8
|
|
|
8
9
|
---
|
|
9
10
|
|
|
10
|
-
##
|
|
11
|
+
## Overview
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
During local development, e11y automatically registers a **DevLog adapter** that writes all events to a local log file (default: `log/e11y_dev.jsonl`). No configuration is required — the Railtie activates the adapter in `development` and `test` environments on startup.
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
```ruby
|
|
16
|
-
# ❌ BEFORE: Poor development experience
|
|
17
|
-
# - Events go to production backends (Loki, Sentry)
|
|
18
|
-
# - Can't see events in console (hidden in logs)
|
|
19
|
-
# - No colored output (hard to read)
|
|
20
|
-
# - No pretty-printing (JSON blobs)
|
|
21
|
-
# - Debug events flood console
|
|
22
|
-
# - Can't easily filter what you see
|
|
23
|
-
|
|
24
|
-
# Terminal output:
|
|
25
|
-
# {"event":"order.created","order_id":"123","timestamp":"2026-01-12T10:00:00Z"}
|
|
26
|
-
# {"event":"payment.processing","order_id":"123","timestamp":"2026-01-12T10:00:01Z"}
|
|
27
|
-
# {"event":"debug.sql","query":"SELECT...","timestamp":"2026-01-12T10:00:02Z"}
|
|
28
|
-
# → Hard to read! 😞
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
### E11y Solution
|
|
15
|
+
Three complementary interfaces let you inspect those events:
|
|
32
16
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
# Beautiful colored console output
|
|
39
|
-
config.adapters = [
|
|
40
|
-
E11y::Adapters::ConsoleAdapter.new(
|
|
41
|
-
colored: true,
|
|
42
|
-
pretty: true,
|
|
43
|
-
show_payload: true,
|
|
44
|
-
show_context: true
|
|
45
|
-
)
|
|
46
|
-
]
|
|
47
|
-
|
|
48
|
-
# Show all severities (including debug)
|
|
49
|
-
config.severity = :debug
|
|
50
|
-
|
|
51
|
-
# No rate limiting in dev
|
|
52
|
-
config.rate_limiting.enabled = false
|
|
53
|
-
end
|
|
54
|
-
end
|
|
17
|
+
| Interface | How to access | Best for |
|
|
18
|
+
|-----------|--------------|----------|
|
|
19
|
+
| TUI (terminal) | `bundle exec e11y` | Browsing interactions, drilling into traces |
|
|
20
|
+
| Browser Overlay | Included with `e11y-devtools` gem | Checking events for the page you just loaded |
|
|
21
|
+
| MCP Server | `bundle exec e11y mcp` | AI-assisted debugging in Cursor / Claude Code |
|
|
55
22
|
|
|
56
|
-
|
|
57
|
-
# ╭─────────────────────────────────────────────────────────╮
|
|
58
|
-
# │ 🎉 order.created [SUCCESS] 10:00:00 │
|
|
59
|
-
# ├─────────────────────────────────────────────────────────┤
|
|
60
|
-
# │ order_id: 123 │
|
|
61
|
-
# │ user_id: 456 │
|
|
62
|
-
# │ amount: $99.99 │
|
|
63
|
-
# │ trace_id: abc-123-def │
|
|
64
|
-
# ╰─────────────────────────────────────────────────────────╯
|
|
65
|
-
```
|
|
23
|
+
**Debug buffer note:** debug-severity events are held in memory during a request and flushed to the DevLog only when the request fails. A successful request discards the debug buffer, keeping the log free of noise. Error events always write immediately.
|
|
66
24
|
|
|
67
25
|
---
|
|
68
26
|
|
|
69
|
-
##
|
|
70
|
-
|
|
71
|
-
> **Implementation:** See [ADR-010: Developer Experience](../ADR-010-developer-experience.md) for complete architecture, including [Section 3: Console Output](../ADR-010-developer-experience.md#3-console-output), [Section 4: Web UI](../ADR-010-developer-experience.md#4-web-ui), [Section 5: Event Registry](../ADR-010-developer-experience.md#5-event-registry), and [Section 6: Debug Helpers](../ADR-010-developer-experience.md#6-debug-helpers).
|
|
27
|
+
## Setup
|
|
72
28
|
|
|
73
|
-
|
|
29
|
+
Add the devtools gem to your `Gemfile`:
|
|
74
30
|
|
|
75
|
-
**Beautiful colored terminal output:**
|
|
76
31
|
```ruby
|
|
77
|
-
#
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
E11y.configure do |config|
|
|
81
|
-
config.adapters = [
|
|
82
|
-
E11y::Adapters::ConsoleAdapter.new(
|
|
83
|
-
# Colors
|
|
84
|
-
colored: true,
|
|
85
|
-
color_scheme: :solarized, # :default, :solarized, :monokai
|
|
86
|
-
|
|
87
|
-
# Formatting
|
|
88
|
-
pretty: true,
|
|
89
|
-
compact: false,
|
|
90
|
-
|
|
91
|
-
# What to show
|
|
92
|
-
show_payload: true,
|
|
93
|
-
show_context: true,
|
|
94
|
-
show_metadata: false, # timestamps, etc.
|
|
95
|
-
show_trace_id: true,
|
|
96
|
-
|
|
97
|
-
# Filtering
|
|
98
|
-
severity_filter: :debug, # Show all
|
|
99
|
-
event_filter: nil, # Show all events
|
|
100
|
-
|
|
101
|
-
# Grouping
|
|
102
|
-
group_by_trace_id: true, # Group events with same trace_id
|
|
103
|
-
|
|
104
|
-
# Performance
|
|
105
|
-
max_payload_length: 1000, # Truncate long payloads
|
|
106
|
-
max_array_items: 10 # Limit array display
|
|
107
|
-
)
|
|
108
|
-
]
|
|
109
|
-
end
|
|
110
|
-
end
|
|
32
|
+
# Gemfile
|
|
33
|
+
group :development, :test do
|
|
34
|
+
gem "e11y-devtools"
|
|
111
35
|
end
|
|
112
|
-
|
|
113
|
-
# Output examples:
|
|
114
|
-
# ✅ SUCCESS event (green)
|
|
115
|
-
# 🎉 order.created [SUCCESS] 10:00:00
|
|
116
|
-
# order_id: 123
|
|
117
|
-
# amount: $99.99
|
|
118
|
-
# ⚡ Duration: 45ms
|
|
119
|
-
|
|
120
|
-
# ⚠️ WARN event (yellow)
|
|
121
|
-
# ⚠️ payment.retry [WARN] 10:00:05
|
|
122
|
-
# order_id: 123
|
|
123
|
-
# attempt: 2
|
|
124
|
-
# reason: "Card declined"
|
|
125
|
-
|
|
126
|
-
# ❌ ERROR event (red)
|
|
127
|
-
# ❌ payment.failed [ERROR] 10:00:10
|
|
128
|
-
# order_id: 123
|
|
129
|
-
# error: "Insufficient funds"
|
|
130
|
-
# trace_id: abc-123-def
|
|
131
36
|
```
|
|
132
37
|
|
|
133
|
-
|
|
38
|
+
Run `bundle install`. That is all — no `config/environments/development.rb` changes needed.
|
|
134
39
|
|
|
135
|
-
|
|
40
|
+
The Railtie auto-registers the DevLog adapter and respects three ENV vars:
|
|
136
41
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
#
|
|
140
|
-
> E11y::Inspector.start
|
|
141
|
-
E11y Inspector started. Type 'help' for commands.
|
|
142
|
-
|
|
143
|
-
# Watch events in real-time
|
|
144
|
-
e11y> watch
|
|
145
|
-
Watching events... (Ctrl+C to stop)
|
|
146
|
-
[10:00:00] order.created { order_id: 123 }
|
|
147
|
-
[10:00:01] payment.processing { order_id: 123 }
|
|
148
|
-
[10:00:02] payment.succeeded { transaction_id: 'tx_123' }
|
|
149
|
-
|
|
150
|
-
# Filter by pattern
|
|
151
|
-
e11y> watch pattern: 'order.*'
|
|
152
|
-
Watching events matching 'order.*'...
|
|
153
|
-
[10:00:00] order.created { order_id: 123 }
|
|
154
|
-
[10:00:05] order.shipped { order_id: 123, tracking: 'TRACK123' }
|
|
155
|
-
|
|
156
|
-
# Filter by severity
|
|
157
|
-
e11y> watch severity: :error
|
|
158
|
-
Watching ERROR events...
|
|
159
|
-
[10:00:10] payment.failed { error: "Card declined" }
|
|
160
|
-
|
|
161
|
-
# Show last N events
|
|
162
|
-
e11y> last 10
|
|
163
|
-
Showing last 10 events:
|
|
164
|
-
1. [10:00:00] order.created
|
|
165
|
-
2. [10:00:01] payment.processing
|
|
166
|
-
3. [10:00:02] payment.succeeded
|
|
167
|
-
...
|
|
168
|
-
|
|
169
|
-
# Search events
|
|
170
|
-
e11y> search order_id: '123'
|
|
171
|
-
Found 5 events:
|
|
172
|
-
1. [10:00:00] order.created
|
|
173
|
-
2. [10:00:01] payment.processing
|
|
174
|
-
3. [10:00:02] payment.succeeded
|
|
175
|
-
4. [10:00:05] order.shipped
|
|
176
|
-
5. [10:00:10] order.delivered
|
|
177
|
-
|
|
178
|
-
# Show event details
|
|
179
|
-
e11y> show 1
|
|
180
|
-
Event: order.created
|
|
181
|
-
Severity: SUCCESS
|
|
182
|
-
Timestamp: 2026-01-12 10:00:00
|
|
183
|
-
Trace ID: abc-123-def
|
|
184
|
-
Payload:
|
|
185
|
-
order_id: 123
|
|
186
|
-
user_id: 456
|
|
187
|
-
amount: 99.99
|
|
188
|
-
currency: USD
|
|
189
|
-
Context:
|
|
190
|
-
request_id: req-789
|
|
191
|
-
user_agent: Mozilla/5.0...
|
|
192
|
-
Duration: 45ms
|
|
42
|
+
```bash
|
|
43
|
+
bundle exec rails server
|
|
44
|
+
# DevLog active: log/e11y_dev.jsonl (max 10 000 events, 50 MB)
|
|
193
45
|
```
|
|
194
46
|
|
|
195
47
|
---
|
|
196
48
|
|
|
197
|
-
|
|
49
|
+
## TUI — Interactive Log Viewer
|
|
198
50
|
|
|
199
|
-
|
|
200
|
-
```ruby
|
|
201
|
-
# app/controllers/orders_controller.rb
|
|
202
|
-
class OrdersController < ApplicationController
|
|
203
|
-
def create
|
|
204
|
-
# Quick debug (only in development!)
|
|
205
|
-
E11y.debug("Creating order", order_params)
|
|
206
|
-
# → Pretty-printed to console immediately
|
|
207
|
-
|
|
208
|
-
order = Order.create!(order_params)
|
|
209
|
-
|
|
210
|
-
# Breakpoint with context
|
|
211
|
-
E11y.breakpoint(
|
|
212
|
-
"Order created",
|
|
213
|
-
order: order.attributes,
|
|
214
|
-
user: current_user.attributes
|
|
215
|
-
)
|
|
216
|
-
# → Pauses execution, shows data, waits for Enter
|
|
217
|
-
|
|
218
|
-
# Measure block
|
|
219
|
-
result = E11y.measure("Payment processing") do
|
|
220
|
-
process_payment(order)
|
|
221
|
-
end
|
|
222
|
-
# → Logs duration automatically
|
|
223
|
-
|
|
224
|
-
render json: order
|
|
225
|
-
end
|
|
226
|
-
end
|
|
51
|
+
Launch the terminal UI from your project root:
|
|
227
52
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
# user_id: 456
|
|
231
|
-
# items: [...]
|
|
232
|
-
# total: 99.99
|
|
233
|
-
#
|
|
234
|
-
# ⏸️ [BREAKPOINT] Order created
|
|
235
|
-
# order: { id: 123, status: "pending", ... }
|
|
236
|
-
# user: { id: 456, email: "user@example.com", ... }
|
|
237
|
-
# Press Enter to continue...
|
|
238
|
-
#
|
|
239
|
-
# ⏱️ [MEASURE] Payment processing → 1.2s
|
|
53
|
+
```bash
|
|
54
|
+
bundle exec e11y
|
|
240
55
|
```
|
|
241
56
|
|
|
242
|
-
|
|
57
|
+
### Views
|
|
243
58
|
|
|
244
|
-
|
|
59
|
+
The TUI has three nested views. Navigation is always the same two keys: Enter to drill in, Esc or `b` to go back.
|
|
245
60
|
|
|
246
|
-
**Record and replay events for testing:**
|
|
247
|
-
```ruby
|
|
248
|
-
# Record events during a request
|
|
249
|
-
# rails console
|
|
250
|
-
> recorder = E11y::Recorder.new
|
|
251
|
-
> recorder.start
|
|
252
|
-
Recording events...
|
|
253
|
-
|
|
254
|
-
# Make request
|
|
255
|
-
> app.post '/orders', params: { order: {...} }
|
|
256
|
-
|
|
257
|
-
> recorder.stop
|
|
258
|
-
Recorded 15 events
|
|
259
|
-
Saved to tmp/e11y_recordings/2026-01-12_10-00-00.json
|
|
260
|
-
|
|
261
|
-
# Replay events
|
|
262
|
-
> recorder.replay('tmp/e11y_recordings/2026-01-12_10-00-00.json')
|
|
263
|
-
Replaying 15 events...
|
|
264
|
-
[1/15] order.creation.started
|
|
265
|
-
[2/15] inventory.checked
|
|
266
|
-
[3/15] payment.processing
|
|
267
|
-
...
|
|
268
|
-
[15/15] order.created
|
|
269
|
-
|
|
270
|
-
# Compare recordings (regression testing)
|
|
271
|
-
> diff = E11y::Recorder.diff(
|
|
272
|
-
'recordings/baseline.json',
|
|
273
|
-
'recordings/current.json'
|
|
274
|
-
)
|
|
275
|
-
> puts diff
|
|
276
|
-
+ payment.retry (NEW in current)
|
|
277
|
-
- payment.succeeded (MISSING in current)
|
|
278
|
-
~ payment.processing.duration_ms: 120ms → 1500ms (12.5x slower!)
|
|
279
61
|
```
|
|
280
|
-
|
|
281
|
-
---
|
|
282
|
-
|
|
283
|
-
### 5. Visual Timeline (Web UI)
|
|
284
|
-
|
|
285
|
-
**Mini web UI for development:**
|
|
286
|
-
```ruby
|
|
287
|
-
# config/routes.rb (development only)
|
|
288
|
-
Rails.application.routes.draw do
|
|
289
|
-
if Rails.env.development?
|
|
290
|
-
mount E11y::Web => '/e11y'
|
|
291
|
-
end
|
|
292
|
-
end
|
|
293
|
-
|
|
294
|
-
# Visit: http://localhost:3000/e11y
|
|
295
|
-
# Features:
|
|
296
|
-
# - Real-time event stream
|
|
297
|
-
# - Timeline view (Gantt chart)
|
|
298
|
-
# - Filtering by severity, pattern
|
|
299
|
-
# - Trace visualization
|
|
300
|
-
# - Event details modal
|
|
301
|
-
# - Export to JSON/CSV
|
|
302
|
-
# - Search & filter
|
|
303
|
-
|
|
304
|
-
# Example UI:
|
|
305
|
-
# ╔══════════════════════════════════════════════════════════╗
|
|
306
|
-
# ║ E11y Event Dashboard 🔄 Auto-refresh ║
|
|
307
|
-
# ╠══════════════════════════════════════════════════════════╣
|
|
308
|
-
# ║ Filters: [All Severities ▾] [All Events ▾] [Search...] ║
|
|
309
|
-
# ╠══════════════════════════════════════════════════════════╣
|
|
310
|
-
# ║ Timeline (Last 5 minutes) ║
|
|
311
|
-
# ║ ┌────────────────────────────────────────────────────┐ ║
|
|
312
|
-
# ║ │ 10:00:00 ████ order.created │ ║
|
|
313
|
-
# ║ │ 10:00:01 ███████ payment.processing │ ║
|
|
314
|
-
# ║ │ 10:00:02 ██ payment.succeeded │ ║
|
|
315
|
-
# ║ │ 10:00:05 ████ shipment.created │ ║
|
|
316
|
-
# ║ └────────────────────────────────────────────────────┘ ║
|
|
317
|
-
# ╠══════════════════════════════════════════════════════════╣
|
|
318
|
-
# ║ Recent Events ║
|
|
319
|
-
# ║ ✅ order.created 10:00:00 trace: abc-123 ║
|
|
320
|
-
# ║ ⏳ payment.processing 10:00:01 trace: abc-123 ║
|
|
321
|
-
# ║ ✅ payment.succeeded 10:00:02 trace: abc-123 ║
|
|
322
|
-
# ╚══════════════════════════════════════════════════════════╝
|
|
62
|
+
:interactions → :events → :detail
|
|
323
63
|
```
|
|
324
64
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
### 6. Environment-Specific Configuration Recommendations (C14)
|
|
65
|
+
### Interactions view (default)
|
|
328
66
|
|
|
329
|
-
|
|
67
|
+
Parallel traces that start within 500 ms are grouped into one row. A red dot (●) means the interaction contains at least one error; a gray dot (○) means it is clean.
|
|
330
68
|
|
|
331
|
-
|
|
69
|
+
```
|
|
70
|
+
e11y [w] web [j] jobs [a] all r=reload q=quit
|
|
71
|
+
────────────────────────────────────────────────────────────────────
|
|
72
|
+
# time source dur events status
|
|
73
|
+
────────────────────────────────────────────────────────────────────
|
|
74
|
+
1 10:04:12 web 312ms 14 ○ GET /orders
|
|
75
|
+
2 10:03:58 web 89ms 6 ○ GET /orders/123
|
|
76
|
+
3 10:03:41 web 541ms 22 ● POST /checkout
|
|
77
|
+
4 10:02:15 jobs 2.1s 8 ○ OrderFulfillmentJob
|
|
78
|
+
5 10:01:07 web 73ms 3 ○ GET /products
|
|
79
|
+
────────────────────────────────────────────────────────────────────
|
|
80
|
+
↓/↑ navigate Enter drill-in
|
|
81
|
+
```
|
|
332
82
|
|
|
333
|
-
|
|
83
|
+
### Events view (after Enter on an interaction)
|
|
334
84
|
|
|
335
|
-
```
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
#
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
# flush_interval 0.1.seconds # Flush every 100ms
|
|
348
|
-
# end
|
|
349
|
-
|
|
350
|
-
# === SAMPLING: DISABLED (keep all events) ===
|
|
351
|
-
# ✅ See EVERY event for complete debugging
|
|
352
|
-
# ✅ No data loss during development
|
|
353
|
-
# ⚠️ Trade-off: More console noise (filter with ignore_events)
|
|
354
|
-
config.sampling.enabled = false
|
|
355
|
-
|
|
356
|
-
# === RATE LIMITING: DISABLED (no throttling) ===
|
|
357
|
-
# ✅ Rapid testing won't hit limits
|
|
358
|
-
config.rate_limiting.enabled = false
|
|
359
|
-
|
|
360
|
-
# === PII FILTERING: DISABLED (optional) ===
|
|
361
|
-
# ✅ See real data for easier debugging
|
|
362
|
-
# ⚠️ Only disable if you're NOT using production data in dev!
|
|
363
|
-
config.pii_filtering.enabled = false
|
|
364
|
-
|
|
365
|
-
# === FLUSH HELPER: Available for manual testing ===
|
|
366
|
-
# Sometimes you want to force-flush buffered events:
|
|
367
|
-
# E11y.flush # ← Forces immediate flush
|
|
368
|
-
end
|
|
85
|
+
```
|
|
86
|
+
e11y > POST /checkout (trace: f3a9b2c1)
|
|
87
|
+
────────────────────────────────────────────────────────────────────
|
|
88
|
+
# time severity event name
|
|
89
|
+
────────────────────────────────────────────────────────────────────
|
|
90
|
+
1 10:03:41 info order.validation.started
|
|
91
|
+
2 10:03:41 info inventory.checked
|
|
92
|
+
3 10:03:41 debug db.query (buffered — shown because request failed)
|
|
93
|
+
4 10:03:42 warn payment.retry
|
|
94
|
+
5 10:03:42 error payment.failed
|
|
95
|
+
────────────────────────────────────────────────────────────────────
|
|
96
|
+
↓/↑ navigate Enter detail Esc/b back
|
|
369
97
|
```
|
|
370
98
|
|
|
371
|
-
|
|
99
|
+
### Detail view (after Enter on an event)
|
|
372
100
|
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
config.sampling do
|
|
391
|
-
enabled true
|
|
392
|
-
strategy :adaptive
|
|
393
|
-
base_rate 0.1 # Keep 10% of success events
|
|
394
|
-
error_rate 1.0 # Keep 100% of errors
|
|
395
|
-
end
|
|
396
|
-
|
|
397
|
-
# === RATE LIMITING: ENABLED (DDoS protection) ===
|
|
398
|
-
# ✅ Prevents cost explosions
|
|
399
|
-
# ✅ Protects backend from overload
|
|
400
|
-
config.rate_limiting do
|
|
401
|
-
enabled true
|
|
402
|
-
limit 1000
|
|
403
|
-
window 1.minute
|
|
404
|
-
end
|
|
405
|
-
|
|
406
|
-
# === PII FILTERING: ENABLED (GDPR compliance) ===
|
|
407
|
-
# ✅ GDPR/CCPA compliant
|
|
408
|
-
# ✅ Protects sensitive data
|
|
409
|
-
config.pii_filtering.enabled true
|
|
410
|
-
end
|
|
101
|
+
```
|
|
102
|
+
e11y > POST /checkout > payment.failed
|
|
103
|
+
────────────────────────────────────────────────────────────────────
|
|
104
|
+
event_name: payment.failed
|
|
105
|
+
severity: error
|
|
106
|
+
timestamp: 2026-03-18T10:03:42.317Z
|
|
107
|
+
trace_id: f3a9b2c1-...
|
|
108
|
+
duration_ms: 541
|
|
109
|
+
|
|
110
|
+
payload:
|
|
111
|
+
order_id: "ord_8812"
|
|
112
|
+
amount: 99.99
|
|
113
|
+
currency: "USD"
|
|
114
|
+
reason: "Card declined"
|
|
115
|
+
attempt: 2
|
|
116
|
+
────────────────────────────────────────────────────────────────────
|
|
117
|
+
Esc/b back c=copy JSON
|
|
411
118
|
```
|
|
412
119
|
|
|
413
|
-
|
|
120
|
+
### Keyboard reference
|
|
414
121
|
|
|
415
|
-
|
|
|
416
|
-
|
|
417
|
-
|
|
|
418
|
-
|
|
|
419
|
-
|
|
|
420
|
-
|
|
|
421
|
-
|
|
|
122
|
+
| Key | Action |
|
|
123
|
+
|-----|--------|
|
|
124
|
+
| `↓` / `↑` | Navigate list |
|
|
125
|
+
| `Enter` | Drill into interaction or event |
|
|
126
|
+
| `Esc` / `b` | Go back one level |
|
|
127
|
+
| `w` | Filter: web requests only (default) |
|
|
128
|
+
| `j` | Filter: background jobs only |
|
|
129
|
+
| `a` | Filter: all sources |
|
|
130
|
+
| `r` | Reload from log file |
|
|
131
|
+
| `c` | Copy event JSON to clipboard (detail view) |
|
|
132
|
+
| `q` | Quit |
|
|
422
133
|
|
|
423
|
-
|
|
134
|
+
File watching polls `mtime` every 250 ms — new events appear without pressing `r`.
|
|
424
135
|
|
|
425
|
-
|
|
426
|
-
# Scenario: Testing event delivery in specs
|
|
427
|
-
RSpec.describe 'Order creation' do
|
|
428
|
-
it 'tracks order.created event' do
|
|
429
|
-
# Event is buffered (not sent yet)
|
|
430
|
-
post '/orders', params: { order: {...} }
|
|
431
|
-
|
|
432
|
-
# Force immediate flush (for testing)
|
|
433
|
-
E11y.flush
|
|
434
|
-
|
|
435
|
-
# Now event is available in test adapter
|
|
436
|
-
expect(E11y.test_adapter.events).to include(
|
|
437
|
-
hash_including(event_name: 'order.created')
|
|
438
|
-
)
|
|
439
|
-
end
|
|
440
|
-
end
|
|
441
|
-
|
|
442
|
-
# Rails console manual testing:
|
|
443
|
-
> Events::OrderCreated.track(order_id: 123)
|
|
444
|
-
# → Event buffered, not visible yet
|
|
136
|
+
---
|
|
445
137
|
|
|
446
|
-
|
|
447
|
-
# → Forces immediate flush, event now visible in console
|
|
138
|
+
## Browser Overlay
|
|
448
139
|
|
|
449
|
-
|
|
450
|
-
# => 0 (buffer is empty after flush)
|
|
451
|
-
```
|
|
140
|
+
When `e11y-devtools` is present in the Gemfile, a lightweight JavaScript snippet is injected into every development page response. It requires no route configuration.
|
|
452
141
|
|
|
453
|
-
|
|
142
|
+
### Badge
|
|
454
143
|
|
|
455
|
-
|
|
144
|
+
A small badge appears in the bottom-right corner of every page:
|
|
456
145
|
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
# → Appears in console IMMEDIATELY ✅
|
|
462
|
-
|
|
463
|
-
# Production (buffering enabled, 10s flush):
|
|
464
|
-
Events::OrderCreated.track(order_id: 123)
|
|
465
|
-
# → Buffered, appears after 10 seconds ⏱️
|
|
466
|
-
# → Or when buffer full (1000 events)
|
|
467
|
-
# → Or when E11y.flush called manually
|
|
468
|
-
|
|
469
|
-
# Solution: If you need instant delivery in prod (e.g., critical alerts):
|
|
470
|
-
Events::CriticalAlert.track(
|
|
471
|
-
alert_type: 'payment_failure',
|
|
472
|
-
severity: :fatal
|
|
473
|
-
)
|
|
474
|
-
E11y.flush # ← Force immediate delivery (bypasses buffer)
|
|
146
|
+
```
|
|
147
|
+
╭─────────────╮
|
|
148
|
+
│ e11y 14 ● 1│
|
|
149
|
+
╰─────────────╯
|
|
475
150
|
```
|
|
476
151
|
|
|
477
|
-
|
|
152
|
+
- The first number is the total event count for the current page's trace.
|
|
153
|
+
- The second number (after ●) is the error count.
|
|
154
|
+
- The badge border turns red when errors are present.
|
|
478
155
|
|
|
479
|
-
|
|
480
|
-
# ❌ GOTCHA: All events visible in dev, some dropped in prod
|
|
481
|
-
# Development (sampling disabled):
|
|
482
|
-
100.times { Events::UserLogin.track(user_id: rand(1000)) }
|
|
483
|
-
# → See ALL 100 events in console ✅
|
|
484
|
-
|
|
485
|
-
# Production (sampling enabled, 10% rate):
|
|
486
|
-
100.times { Events::UserLogin.track(user_id: rand(1000)) }
|
|
487
|
-
# → Only ~10 events reach Loki (90 dropped) ❌
|
|
488
|
-
# → Errors ALWAYS kept (100% sampling) ✅
|
|
489
|
-
|
|
490
|
-
# Solution: Test sampling behavior in staging:
|
|
491
|
-
# config/environments/staging.rb
|
|
492
|
-
config.sampling do
|
|
493
|
-
enabled true # ← Test production-like sampling
|
|
494
|
-
base_rate 0.1
|
|
495
|
-
end
|
|
496
|
-
```
|
|
156
|
+
### Slide-in panel
|
|
497
157
|
|
|
498
|
-
|
|
158
|
+
Clicking the badge opens a panel on the right side of the screen:
|
|
499
159
|
|
|
500
|
-
```
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
# Sampling: Higher rate (more data than prod)
|
|
512
|
-
config.sampling do
|
|
513
|
-
enabled true
|
|
514
|
-
base_rate 0.5 # vs 0.1 in prod (keep 50% of events)
|
|
515
|
-
error_rate 1.0 # Always keep errors
|
|
516
|
-
end
|
|
517
|
-
|
|
518
|
-
# Rate limiting: Higher limits (easier testing)
|
|
519
|
-
config.rate_limiting do
|
|
520
|
-
enabled true
|
|
521
|
-
limit 10_000 # vs 1000 in prod
|
|
522
|
-
end
|
|
523
|
-
|
|
524
|
-
# PII: Enabled (test GDPR compliance)
|
|
525
|
-
config.pii_filtering.enabled true
|
|
526
|
-
end
|
|
160
|
+
```
|
|
161
|
+
╔══════════════════════════════════════════╗
|
|
162
|
+
║ e11y — trace: f3a9b2c1 ║
|
|
163
|
+
╠══════════════════════════════════════════╣
|
|
164
|
+
║ 10:03:41 info order.validation.start ║
|
|
165
|
+
║ 10:03:41 info inventory.checked ║
|
|
166
|
+
║ 10:03:42 warn payment.retry ║
|
|
167
|
+
║ 10:03:42 error payment.failed ║
|
|
168
|
+
╠══════════════════════════════════════════╣
|
|
169
|
+
║ [clear log] [copy trace_id] ║
|
|
170
|
+
╚══════════════════════════════════════════╝
|
|
527
171
|
```
|
|
528
172
|
|
|
529
|
-
|
|
173
|
+
The panel shows only events that share the current page's trace ID. It auto-polls every 2 seconds.
|
|
530
174
|
|
|
531
|
-
|
|
532
|
-
2. **Production:** Enable buffering & sampling for performance & cost savings
|
|
533
|
-
3. **Staging:** Middle ground - production-like but easier to debug
|
|
534
|
-
4. **Use `E11y.flush`:** For manual testing when buffering is enabled
|
|
535
|
-
5. **Test in staging:** Catch production behavior differences before deployment
|
|
175
|
+
The overlay endpoint returns 404 outside the `development` environment, so it cannot leak into staging or production.
|
|
536
176
|
|
|
537
177
|
---
|
|
538
178
|
|
|
539
|
-
##
|
|
540
|
-
|
|
541
|
-
### Example 1: Full Development Config
|
|
179
|
+
## MCP Server — AI-Assisted Debugging
|
|
542
180
|
|
|
543
|
-
|
|
544
|
-
# config/environments/development.rb
|
|
545
|
-
Rails.application.configure do
|
|
546
|
-
config.after_initialize do
|
|
547
|
-
E11y.configure do |config|
|
|
548
|
-
# === CONSOLE OUTPUT ===
|
|
549
|
-
config.adapters = [
|
|
550
|
-
E11y::Adapters::ConsoleAdapter.new(
|
|
551
|
-
colored: true,
|
|
552
|
-
pretty: true,
|
|
553
|
-
show_payload: true,
|
|
554
|
-
show_context: true,
|
|
555
|
-
show_trace_id: true,
|
|
556
|
-
group_by_trace_id: true
|
|
557
|
-
)
|
|
558
|
-
]
|
|
559
|
-
|
|
560
|
-
# === SEVERITY ===
|
|
561
|
-
# Show everything in development
|
|
562
|
-
config.severity = :debug
|
|
563
|
-
|
|
564
|
-
# === FEATURES ===
|
|
565
|
-
# Disable production features
|
|
566
|
-
config.rate_limiting.enabled = false
|
|
567
|
-
config.sampling.enabled = false
|
|
568
|
-
config.pii_filtering.enabled = false # Easier debugging
|
|
569
|
-
|
|
570
|
-
# === DEBUGGING ===
|
|
571
|
-
# Enable debug helpers
|
|
572
|
-
config.debug_mode = true
|
|
573
|
-
|
|
574
|
-
# === BUFFERING ===
|
|
575
|
-
# Immediate flush (no buffering)
|
|
576
|
-
config.buffer.enabled = false # Or flush_interval: 0.1.seconds
|
|
577
|
-
|
|
578
|
-
# === WEB UI ===
|
|
579
|
-
config.web_ui do
|
|
580
|
-
enabled true
|
|
581
|
-
port 3001 # Or use Rails server
|
|
582
|
-
auto_refresh true
|
|
583
|
-
refresh_interval 2.seconds
|
|
584
|
-
end
|
|
585
|
-
|
|
586
|
-
# === RECORDING ===
|
|
587
|
-
config.recording do
|
|
588
|
-
enabled true
|
|
589
|
-
save_path Rails.root.join('tmp', 'e11y_recordings')
|
|
590
|
-
auto_save_on_error true
|
|
591
|
-
end
|
|
592
|
-
|
|
593
|
-
# === PERFORMANCE ===
|
|
594
|
-
# Verbose self-monitoring
|
|
595
|
-
config.self_monitoring do
|
|
596
|
-
enabled true
|
|
597
|
-
log_internal_events true
|
|
598
|
-
end
|
|
599
|
-
end
|
|
600
|
-
end
|
|
601
|
-
end
|
|
602
|
-
```
|
|
603
|
-
|
|
604
|
-
---
|
|
181
|
+
The MCP server exposes the DevLog over the Model Context Protocol so AI assistants can query your local events directly.
|
|
605
182
|
|
|
606
|
-
###
|
|
183
|
+
### Start the server
|
|
607
184
|
|
|
608
|
-
```
|
|
609
|
-
#
|
|
610
|
-
|
|
611
|
-
def call(order_id)
|
|
612
|
-
# Debug checkpoint
|
|
613
|
-
E11y.debug("Starting order processing", order_id: order_id)
|
|
614
|
-
|
|
615
|
-
order = Order.find(order_id)
|
|
616
|
-
|
|
617
|
-
# Show complex data structure
|
|
618
|
-
E11y.inspect(order, depth: 2)
|
|
619
|
-
# → Pretty-printed with colors, max depth 2
|
|
620
|
-
|
|
621
|
-
# Measure performance
|
|
622
|
-
inventory_result = E11y.measure("Inventory check") do
|
|
623
|
-
check_inventory(order)
|
|
624
|
-
end
|
|
625
|
-
|
|
626
|
-
# Conditional breakpoint
|
|
627
|
-
E11y.breakpoint_if(
|
|
628
|
-
-> { inventory_result.low_stock? },
|
|
629
|
-
"Low stock detected!",
|
|
630
|
-
order: order.attributes,
|
|
631
|
-
inventory: inventory_result
|
|
632
|
-
)
|
|
633
|
-
|
|
634
|
-
# Trace execution
|
|
635
|
-
E11y.trace("Processing payment") do
|
|
636
|
-
process_payment(order)
|
|
637
|
-
end
|
|
638
|
-
# → Logs entry/exit automatically
|
|
639
|
-
|
|
640
|
-
# Count invocations
|
|
641
|
-
E11y.count("order_processing")
|
|
642
|
-
# → Logs: "order_processing called 5 times"
|
|
643
|
-
|
|
644
|
-
# Diff objects
|
|
645
|
-
before = order.attributes
|
|
646
|
-
order.update!(status: 'processed')
|
|
647
|
-
E11y.diff(before, order.attributes)
|
|
648
|
-
# → Shows: { status: "pending" → "processed" }
|
|
649
|
-
|
|
650
|
-
order
|
|
651
|
-
end
|
|
652
|
-
end
|
|
185
|
+
```bash
|
|
186
|
+
# Default port 3099
|
|
187
|
+
bundle exec e11y mcp
|
|
653
188
|
|
|
654
|
-
#
|
|
655
|
-
|
|
656
|
-
# order_id: 123
|
|
657
|
-
#
|
|
658
|
-
# 📦 [INSPECT] Order #123
|
|
659
|
-
# id: 123
|
|
660
|
-
# status: "pending"
|
|
661
|
-
# items: [
|
|
662
|
-
# { id: 1, product_id: 456, quantity: 2 },
|
|
663
|
-
# { id: 2, product_id: 789, quantity: 1 }
|
|
664
|
-
# ]
|
|
665
|
-
# total: 99.99
|
|
666
|
-
#
|
|
667
|
-
# ⏱️ [MEASURE] Inventory check → 45ms
|
|
668
|
-
#
|
|
669
|
-
# ⏸️ [BREAKPOINT] Low stock detected!
|
|
670
|
-
# order: { ... }
|
|
671
|
-
# inventory: { low_stock: true, product_id: 456 }
|
|
672
|
-
# Press Enter to continue...
|
|
673
|
-
#
|
|
674
|
-
# 🔀 [TRACE] Processing payment
|
|
675
|
-
# → Entered at 10:00:01
|
|
676
|
-
# → Exited at 10:00:02 (1.2s)
|
|
677
|
-
#
|
|
678
|
-
# 📊 [COUNT] order_processing called 5 times
|
|
679
|
-
#
|
|
680
|
-
# 🔄 [DIFF] Order #123
|
|
681
|
-
# - status: "pending"
|
|
682
|
-
# + status: "processed"
|
|
189
|
+
# Custom port
|
|
190
|
+
bundle exec e11y mcp --port 3099
|
|
683
191
|
```
|
|
684
192
|
|
|
685
|
-
|
|
193
|
+
### Available tools
|
|
194
|
+
|
|
195
|
+
| Tool | Description |
|
|
196
|
+
|------|-------------|
|
|
197
|
+
| `recent_events` | Return the N most recent events (default 50) |
|
|
198
|
+
| `events_by_trace` | Return all events for a given `trace_id` |
|
|
199
|
+
| `search` | Full-text search across event names and payload fields |
|
|
200
|
+
| `stats` | Event counts by severity and source for a time window |
|
|
201
|
+
| `interactions` | List grouped interactions (same view as TUI :interactions) |
|
|
202
|
+
| `event_detail` | Return full JSON for a single event by ID |
|
|
203
|
+
| `errors` | Return all error/fatal events since a given timestamp |
|
|
204
|
+
| `clear` | Truncate the DevLog (useful before reproducing a bug) |
|
|
205
|
+
|
|
206
|
+
### Cursor configuration
|
|
207
|
+
|
|
208
|
+
Create or update `.cursor/mcp.json` in your project root:
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"mcpServers": {
|
|
213
|
+
"e11y": {
|
|
214
|
+
"command": "bundle",
|
|
215
|
+
"args": ["exec", "e11y", "mcp"]
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
686
220
|
|
|
687
|
-
###
|
|
221
|
+
### Claude Code configuration
|
|
688
222
|
|
|
689
|
-
|
|
690
|
-
# config/environments/development.rb
|
|
691
|
-
E11y.configure do |config|
|
|
692
|
-
config.adapters = [
|
|
693
|
-
E11y::Adapters::ConsoleAdapter.new(
|
|
694
|
-
colored: true,
|
|
695
|
-
pretty: true,
|
|
696
|
-
|
|
697
|
-
# === FILTERING ===
|
|
698
|
-
# Only show events matching patterns
|
|
699
|
-
event_filter: ->(event) {
|
|
700
|
-
# Show orders & payments, hide everything else
|
|
701
|
-
event.event_name.match?(/^(order|payment)\./)
|
|
702
|
-
},
|
|
703
|
-
|
|
704
|
-
# Only show warn/error/success
|
|
705
|
-
severity_filter: [:warn, :error, :fatal, :success],
|
|
706
|
-
|
|
707
|
-
# Hide noisy events
|
|
708
|
-
ignore_events: [
|
|
709
|
-
'health_check',
|
|
710
|
-
'heartbeat',
|
|
711
|
-
'metrics.collected'
|
|
712
|
-
],
|
|
713
|
-
|
|
714
|
-
# Hide debug SQL queries
|
|
715
|
-
ignore_patterns: [
|
|
716
|
-
/^debug\./,
|
|
717
|
-
/\.sql$/
|
|
718
|
-
]
|
|
719
|
-
)
|
|
720
|
-
]
|
|
721
|
-
end
|
|
223
|
+
Create or update `.claude/mcp.json` in your project root:
|
|
722
224
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
225
|
+
```json
|
|
226
|
+
{
|
|
227
|
+
"mcpServers": {
|
|
228
|
+
"e11y": {
|
|
229
|
+
"command": "bundle",
|
|
230
|
+
"args": ["exec", "e11y", "mcp"]
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
726
234
|
```
|
|
727
235
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
## 🔧 Configuration
|
|
236
|
+
### Example prompts
|
|
731
237
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
Rails.application.configure do
|
|
737
|
-
config.after_initialize do
|
|
738
|
-
E11y.configure do |config|
|
|
739
|
-
# === OUTPUT ===
|
|
740
|
-
config.adapters = [
|
|
741
|
-
E11y::Adapters::ConsoleAdapter.new(
|
|
742
|
-
colored: true,
|
|
743
|
-
pretty: true,
|
|
744
|
-
color_scheme: :solarized,
|
|
745
|
-
show_payload: true,
|
|
746
|
-
show_context: true,
|
|
747
|
-
show_trace_id: true,
|
|
748
|
-
group_by_trace_id: true,
|
|
749
|
-
max_payload_length: 1000
|
|
750
|
-
)
|
|
751
|
-
]
|
|
752
|
-
|
|
753
|
-
# === LEVEL ===
|
|
754
|
-
config.severity = :debug
|
|
755
|
-
|
|
756
|
-
# === FEATURES ===
|
|
757
|
-
config.rate_limiting.enabled = false
|
|
758
|
-
config.sampling.enabled = false
|
|
759
|
-
config.buffering.enabled = false
|
|
760
|
-
|
|
761
|
-
# === DEBUG ===
|
|
762
|
-
config.debug_mode = true
|
|
763
|
-
config.debug_helpers.enabled = true
|
|
764
|
-
|
|
765
|
-
# === WEB UI ===
|
|
766
|
-
config.web_ui.enabled = true
|
|
767
|
-
config.web_ui.auto_refresh = true
|
|
768
|
-
|
|
769
|
-
# === RECORDING ===
|
|
770
|
-
config.recording.enabled = true
|
|
771
|
-
config.recording.auto_save_on_error = true
|
|
772
|
-
end
|
|
773
|
-
end
|
|
774
|
-
end
|
|
775
|
-
```
|
|
238
|
+
- "What errors happened in the last request?"
|
|
239
|
+
- "Show me all events for trace f3a9b2c1"
|
|
240
|
+
- "Why is the checkout slow? Look at recent interactions."
|
|
241
|
+
- "Compare event counts from the last two POST /checkout traces."
|
|
776
242
|
|
|
777
243
|
---
|
|
778
244
|
|
|
779
|
-
##
|
|
245
|
+
## Configuration Reference
|
|
780
246
|
|
|
781
|
-
|
|
247
|
+
All settings are controlled via ENV vars. No code changes are needed for the defaults.
|
|
782
248
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
colored: true,
|
|
789
|
-
pretty: true
|
|
790
|
-
)
|
|
791
|
-
]
|
|
792
|
-
```
|
|
249
|
+
| ENV var | Default | Description |
|
|
250
|
+
|---------|---------|-------------|
|
|
251
|
+
| `E11Y_MAX_EVENTS` | `10000` | Maximum number of events retained in the DevLog (oldest are dropped) |
|
|
252
|
+
| `E11Y_MAX_SIZE` | `52428800` (50 MB) | Maximum DevLog file size in bytes before rotation |
|
|
253
|
+
| `E11Y_KEEP_ROTATED` | `5` | Number of rotated log files to retain |
|
|
793
254
|
|
|
794
|
-
|
|
795
|
-
```ruby
|
|
796
|
-
# ✅ GOOD: Hide irrelevant events
|
|
797
|
-
config.adapters = [
|
|
798
|
-
E11y::Adapters::ConsoleAdapter.new(
|
|
799
|
-
ignore_events: ['health_check', 'heartbeat']
|
|
800
|
-
)
|
|
801
|
-
]
|
|
802
|
-
```
|
|
255
|
+
Example: raise the cap for a long debugging session:
|
|
803
256
|
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
# ✅ GOOD: Quick debugging
|
|
807
|
-
E11y.debug("User logged in", user_id: user.id)
|
|
808
|
-
E11y.breakpoint("Check this", data: complex_data)
|
|
257
|
+
```bash
|
|
258
|
+
E11Y_MAX_EVENTS=50000 bundle exec rails server
|
|
809
259
|
```
|
|
810
260
|
|
|
811
|
-
|
|
261
|
+
The DevLog file is written to `log/e11y_dev.jsonl` by default. Add it to `.gitignore`:
|
|
812
262
|
|
|
813
|
-
### ❌ DON'T
|
|
814
|
-
|
|
815
|
-
**1. Don't use production adapters**
|
|
816
|
-
```ruby
|
|
817
|
-
# ❌ BAD: Production adapters in development
|
|
818
|
-
config.adapters = [
|
|
819
|
-
E11y::Adapters::LokiAdapter.new(...) # Slow!
|
|
820
|
-
]
|
|
821
|
-
|
|
822
|
-
# ✅ GOOD: Console adapter
|
|
823
|
-
config.adapters = [
|
|
824
|
-
E11y::Adapters::ConsoleAdapter.new(...)
|
|
825
|
-
]
|
|
826
263
|
```
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
```ruby
|
|
830
|
-
# ❌ BAD: Production features enabled
|
|
831
|
-
config.rate_limiting.enabled = true # Annoying in dev!
|
|
832
|
-
config.sampling.enabled = true # Lose events!
|
|
833
|
-
|
|
834
|
-
# ✅ GOOD: Disable in development
|
|
835
|
-
config.rate_limiting.enabled = false
|
|
836
|
-
config.sampling.enabled = false
|
|
264
|
+
# .gitignore
|
|
265
|
+
log/e11y_dev*.log
|
|
837
266
|
```
|
|
838
267
|
|
|
839
268
|
---
|
|
840
269
|
|
|
841
|
-
##
|
|
842
|
-
|
|
843
|
-
-
|
|
844
|
-
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
**Setup Time:** 5-10 minutes (one-time config)
|
|
270
|
+
## Acceptance Criteria
|
|
271
|
+
|
|
272
|
+
- [x] Railtie auto-registers DevLog adapter in `development` and `test` — no manual configuration required
|
|
273
|
+
- [x] DevLog respects `E11Y_MAX_EVENTS`, `E11Y_MAX_SIZE`, and `E11Y_KEEP_ROTATED` ENV vars
|
|
274
|
+
- [x] TUI launches with `bundle exec e11y` and shows the `:interactions` view by default
|
|
275
|
+
- [x] Interactions group parallel traces within a 500 ms window into a single row
|
|
276
|
+
- [x] Red dot (●) on interactions that contain at least one error; gray dot (○) otherwise
|
|
277
|
+
- [x] Source filter toggles: web requests (`w`), background jobs (`j`), all (`a`)
|
|
278
|
+
- [x] TUI supports drill-down: `:interactions` → `:events` → `:detail`
|
|
279
|
+
- [x] Detail view provides `c` to copy event JSON to clipboard
|
|
280
|
+
- [x] TUI polls log `mtime` every 250 ms and refreshes automatically
|
|
281
|
+
- [x] Browser overlay badge appears in bottom-right corner with event count and error count
|
|
282
|
+
- [x] Badge border turns red when the current trace contains errors
|
|
283
|
+
- [x] Panel shows only events for the current page's trace ID
|
|
284
|
+
- [x] Panel auto-polls every 2 seconds
|
|
285
|
+
- [x] Browser overlay endpoint returns 404 outside `development`
|
|
286
|
+
- [x] MCP server starts with `bundle exec e11y mcp` (default port 3099)
|
|
287
|
+
- [x] MCP server exposes 8 tools: `recent_events`, `events_by_trace`, `search`, `stats`, `interactions`, `event_detail`, `errors`, `clear`
|
|
288
|
+
- [x] Cursor and Claude Code JSON configs work with `command: "bundle", args: ["exec", "e11y", "mcp"]`
|
|
289
|
+
- [x] Debug events are buffered per-request and flushed to DevLog only on request failure
|
|
862
290
|
|
|
863
291
|
---
|
|
864
292
|
|
|
865
|
-
**
|
|
866
|
-
**Last Updated:** January 12, 2026
|
|
867
|
-
**Status:** ✅ Complete
|
|
293
|
+
**Related:** [ADR-010: Developer Experience](../architecture/ADR-010-developer-experience.md) | [UC-018: Testing Events](./UC-018-testing-events.md)
|