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/lib/e11y.rb
CHANGED
|
@@ -7,14 +7,27 @@ require "active_support/core_ext/numeric/time" # For 30.days, 7.years, etc.
|
|
|
7
7
|
loader = Zeitwerk::Loader.for_gem
|
|
8
8
|
# Configure inflector for acronyms
|
|
9
9
|
loader.inflector.inflect(
|
|
10
|
+
"documentation" => "Documentation",
|
|
11
|
+
"debug" => "Debug",
|
|
12
|
+
"opentelemetry_collector" => "OpenTelemetryCollector",
|
|
13
|
+
"otel_span" => "OtelSpan",
|
|
10
14
|
"pii" => "PII",
|
|
11
15
|
"pii_filter" => "PIIFilter",
|
|
12
16
|
"otel_logs" => "OTelLogs",
|
|
13
17
|
"slo" => "SLO",
|
|
14
|
-
"dlq" => "DLQ"
|
|
18
|
+
"dlq" => "DLQ",
|
|
19
|
+
"net_http_patch" => "NetHTTPPatch",
|
|
20
|
+
"rspec_matchers" => "RSpecMatchers",
|
|
21
|
+
"have_tracked_event_matcher" => "HaveTrackedEventMatcher",
|
|
22
|
+
"snapshot_matcher" => "SnapshotMatcher"
|
|
15
23
|
)
|
|
16
24
|
# Don't autoload railtie - it will be required manually when Rails is available
|
|
17
25
|
loader.do_not_eager_load("#{__dir__}/e11y/railtie.rb")
|
|
26
|
+
# Generators live under lib/generators/ — not part of the autoloaded tree
|
|
27
|
+
loader.ignore("#{__dir__}/generators")
|
|
28
|
+
# Optional HTTP tracing files require external gems (faraday, net/http) — loaded on demand only
|
|
29
|
+
loader.ignore("#{__dir__}/e11y/tracing/faraday_middleware.rb")
|
|
30
|
+
loader.ignore("#{__dir__}/e11y/tracing/net_http_patch.rb")
|
|
18
31
|
loader.setup
|
|
19
32
|
|
|
20
33
|
# E11y - Event-Driven Observability for Ruby on Rails
|
|
@@ -24,8 +37,6 @@ loader.setup
|
|
|
24
37
|
# config.adapters = [:loki, :sentry]
|
|
25
38
|
# end
|
|
26
39
|
#
|
|
27
|
-
# E11y.track(Events::UserSignup.new(user_id: 123))
|
|
28
|
-
#
|
|
29
40
|
# @see https://e11y.dev Documentation
|
|
30
41
|
module E11y
|
|
31
42
|
class Error < StandardError; end
|
|
@@ -33,6 +44,9 @@ module E11y
|
|
|
33
44
|
class ZoneViolationError < Error; end
|
|
34
45
|
class InvalidPipelineError < Error; end
|
|
35
46
|
|
|
47
|
+
# Raised when PII key is blocked in baggage (ADR-006 §5.5). Used by BaggageProtection and E11y::Current.add_baggage.
|
|
48
|
+
class BaggagePiiError < Error; end
|
|
49
|
+
|
|
36
50
|
class << self
|
|
37
51
|
# Configure E11y
|
|
38
52
|
#
|
|
@@ -56,301 +70,144 @@ module E11y
|
|
|
56
70
|
end
|
|
57
71
|
alias config configuration
|
|
58
72
|
|
|
59
|
-
#
|
|
73
|
+
# Test adapter for specs (InMemoryTest in unit tests, InMemory in integration).
|
|
74
|
+
# Returns :test adapter (unit tests) or :memory adapter (integration tests from dummy config).
|
|
60
75
|
#
|
|
61
|
-
# @
|
|
62
|
-
|
|
76
|
+
# @return [E11y::Adapters::InMemory, E11y::Adapters::InMemoryTest, nil]
|
|
77
|
+
def test_adapter
|
|
78
|
+
configuration.adapters[:test] || configuration.adapters[:memory]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Trace an event through the pipeline (debug utility).
|
|
82
|
+
# Delegates to PipelineInspector.trace_event. Loads the inspector on demand.
|
|
83
|
+
#
|
|
84
|
+
# @param event_class [Class] event class (e.g., Events::OrderCreated)
|
|
85
|
+
# @param payload [Hash] keyword arguments for the event payload
|
|
86
|
+
# @return [Hash] event_data after pipeline
|
|
63
87
|
#
|
|
64
88
|
# @example
|
|
65
|
-
# E11y.
|
|
66
|
-
def
|
|
67
|
-
|
|
68
|
-
|
|
89
|
+
# E11y.trace(Events::OrderCreated, order_id: "123", amount: 99.99)
|
|
90
|
+
def trace(event_class, **payload)
|
|
91
|
+
require "e11y/debug/pipeline_inspector"
|
|
92
|
+
E11y::Debug::PipelineInspector.trace_event(event_class, **payload)
|
|
69
93
|
end
|
|
70
94
|
|
|
71
|
-
# Get logger instance
|
|
95
|
+
# Get logger instance.
|
|
96
|
+
# Priority: config.logger > Rails.logger (when in Rails) > $stdout.
|
|
97
|
+
# Set config.logger = Logger.new(nil) in tests to suppress output.
|
|
72
98
|
#
|
|
73
99
|
# @return [Logger] logger instance
|
|
74
100
|
def logger
|
|
101
|
+
return configuration.logger if configuration&.logger
|
|
102
|
+
|
|
103
|
+
return @logger if defined?(@logger) && !@logger.nil?
|
|
104
|
+
|
|
75
105
|
require "logger"
|
|
76
|
-
@logger
|
|
106
|
+
@logger = if defined?(Rails) && Rails.respond_to?(:application) && Rails.application
|
|
107
|
+
Rails.logger
|
|
108
|
+
else
|
|
109
|
+
::Logger.new($stdout)
|
|
110
|
+
end
|
|
77
111
|
end
|
|
78
112
|
|
|
79
|
-
#
|
|
113
|
+
# Initialize E11y and all configured adapters.
|
|
114
|
+
# Call after the configure block at application startup.
|
|
80
115
|
#
|
|
81
116
|
# @return [void]
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
@configuration = nil
|
|
85
|
-
@logger = nil
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# Configuration class for E11y
|
|
90
|
-
#
|
|
91
|
-
# Adapters are referenced by name (e.g., :logs, :errors_tracker).
|
|
92
|
-
# The actual implementation (Loki, Sentry, etc.) is configured separately.
|
|
93
|
-
#
|
|
94
|
-
# @example Configure adapters
|
|
95
|
-
# E11y.configure do |config|
|
|
96
|
-
# # Register adapter instances
|
|
97
|
-
# config.adapters[:logs] = E11y::Adapters::Loki.new(url: "...")
|
|
98
|
-
# config.adapters[:errors_tracker] = E11y::Adapters::Sentry.new(dsn: "...")
|
|
99
|
-
# end
|
|
100
|
-
#
|
|
101
|
-
# @example Configure severity => adapter mapping
|
|
102
|
-
# E11y.configure do |config|
|
|
103
|
-
# config.adapter_mapping[:error] = [:logs, :errors_tracker]
|
|
104
|
-
# config.adapter_mapping[:info] = [:logs]
|
|
105
|
-
# end
|
|
106
|
-
#
|
|
107
|
-
# @example Configure middleware pipeline
|
|
108
|
-
# E11y.configure do |config|
|
|
109
|
-
# config.pipeline.use E11y::Middleware::Sampling, default_sample_rate: 0.1
|
|
110
|
-
# end
|
|
111
|
-
class Configuration
|
|
112
|
-
attr_accessor :adapters, :log_level, :enabled, :environment, :service_name, :default_retention_period,
|
|
113
|
-
:routing_rules, :fallback_adapters
|
|
114
|
-
attr_reader :adapter_mapping, :pipeline, :rails_instrumentation, :logger_bridge, :request_buffer, :active_job,
|
|
115
|
-
:sidekiq, :error_handling, :dlq_storage, :dlq_filter, :rate_limiting, :slo_tracking
|
|
116
|
-
|
|
117
|
-
def initialize
|
|
118
|
-
initialize_basic_config
|
|
119
|
-
initialize_routing_config
|
|
120
|
-
initialize_feature_configs
|
|
121
|
-
configure_default_pipeline
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
private
|
|
117
|
+
def start!
|
|
118
|
+
return unless configuration.enabled
|
|
125
119
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
@enabled = true
|
|
131
|
-
@environment = nil
|
|
132
|
-
@service_name = nil
|
|
120
|
+
configuration.adapters.each_value do |adapter|
|
|
121
|
+
adapter.start! if adapter.respond_to?(:start!)
|
|
122
|
+
end
|
|
123
|
+
logger.info("[E11y] Started (#{configuration.adapters.size} adapters)")
|
|
133
124
|
end
|
|
134
125
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
126
|
+
# Gracefully shut down E11y, flushing pending events.
|
|
127
|
+
#
|
|
128
|
+
# @param timeout [Integer] Seconds to wait for each adapter flush (default: 5)
|
|
129
|
+
# @return [void]
|
|
130
|
+
def stop!(timeout: 5)
|
|
131
|
+
require "timeout"
|
|
132
|
+
configuration.adapters.each_value do |adapter|
|
|
133
|
+
if adapter.respond_to?(:stop!)
|
|
134
|
+
adapter.stop!(timeout: timeout)
|
|
135
|
+
elsif adapter.respond_to?(:flush!)
|
|
136
|
+
Timeout.timeout(timeout) { adapter.flush! }
|
|
137
|
+
end
|
|
138
|
+
rescue StandardError => e
|
|
139
|
+
logger.warn("[E11y] Adapter stop error: #{e.message}")
|
|
140
|
+
end
|
|
141
|
+
logger.info("[E11y] Stopped")
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Check whether E11y will process events with the given severity.
|
|
145
|
+
# Returns false if no healthy adapter is registered for that severity.
|
|
146
|
+
#
|
|
147
|
+
# @param severity [Symbol] e.g. :debug, :info, :error
|
|
148
|
+
# @return [Boolean]
|
|
149
|
+
def enabled_for?(severity)
|
|
150
|
+
return false unless configuration.enabled
|
|
141
151
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
@sidekiq = SidekiqConfig.new
|
|
148
|
-
@error_handling = ErrorHandlingConfig.new # ✅ C18 Resolution
|
|
149
|
-
@dlq_storage = nil # Set by user (e.g., DLQ::FileStorage instance)
|
|
150
|
-
@dlq_filter = nil # Set by user (e.g., DLQ::Filter instance)
|
|
151
|
-
@rate_limiting = RateLimitingConfig.new
|
|
152
|
-
@slo_tracking = SLOTrackingConfig.new # ✅ L3.14.1
|
|
152
|
+
configuration.adapters_for_severity(severity).any? do |name|
|
|
153
|
+
configuration.adapters[name]&.healthy?
|
|
154
|
+
end
|
|
155
|
+
rescue StandardError
|
|
156
|
+
false
|
|
153
157
|
end
|
|
154
158
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
# Get adapters for given severity
|
|
159
|
+
# Current size of the request-scoped debug buffer for this thread.
|
|
158
160
|
#
|
|
159
|
-
# @
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
# @return [Integer]
|
|
162
|
+
def buffer_size
|
|
163
|
+
buffer = Thread.current[:e11y_ephemeral_buffer]
|
|
164
|
+
buffer.respond_to?(:size) ? buffer.size : 0
|
|
163
165
|
end
|
|
164
166
|
|
|
165
|
-
#
|
|
167
|
+
# Circuit breaker states for all adapters.
|
|
166
168
|
#
|
|
167
|
-
# @return [
|
|
168
|
-
def
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
169
|
+
# @return [Hash{Symbol => Symbol}] adapter_name => :closed / :open / :half_open
|
|
170
|
+
def circuit_breaker_state
|
|
171
|
+
configuration.adapters.transform_values do |adapter|
|
|
172
|
+
if adapter.respond_to?(:circuit_breaker_state)
|
|
173
|
+
adapter.circuit_breaker_state
|
|
174
|
+
else
|
|
175
|
+
:closed
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Access the global Event Registry singleton.
|
|
175
181
|
#
|
|
176
|
-
#
|
|
177
|
-
#
|
|
178
|
-
# - :errors_tracker → error tracking with alerting (implementation: Sentry, Rollbar, Bugsnag, etc.)
|
|
182
|
+
# The registry auto-populates as event classes are defined (via the `event_name` DSL setter).
|
|
183
|
+
# Useful for introspection, documentation generation, and admin dashboards.
|
|
179
184
|
#
|
|
180
|
-
# @return [
|
|
181
|
-
def default_adapter_mapping
|
|
182
|
-
{
|
|
183
|
-
error: %i[logs errors_tracker], # Errors: both logging + alerting
|
|
184
|
-
fatal: %i[logs errors_tracker], # Fatal: both logging + alerting
|
|
185
|
-
default: [:logs] # Others: logging only
|
|
186
|
-
}
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
# Setup default middleware pipeline
|
|
185
|
+
# @return [E11y::Registry]
|
|
190
186
|
#
|
|
191
|
-
#
|
|
192
|
-
#
|
|
193
|
-
#
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
# 5. Sampling - Adaptive sampling (zone: :routing)
|
|
197
|
-
# 6. Routing - Buffer routing (zone: :adapters)
|
|
198
|
-
#
|
|
199
|
-
# @return [void]
|
|
200
|
-
# @see ADR-015 Middleware Execution Order
|
|
201
|
-
def configure_default_pipeline
|
|
202
|
-
# Zone: :pre_processing
|
|
203
|
-
@pipeline.use E11y::Middleware::TraceContext
|
|
204
|
-
@pipeline.use E11y::Middleware::Validation
|
|
205
|
-
|
|
206
|
-
# Zone: :security
|
|
207
|
-
@pipeline.use E11y::Middleware::PIIFilter
|
|
208
|
-
@pipeline.use E11y::Middleware::AuditSigning
|
|
209
|
-
|
|
210
|
-
# Zone: :routing
|
|
211
|
-
@pipeline.use E11y::Middleware::Sampling
|
|
212
|
-
|
|
213
|
-
# Zone: :adapters
|
|
214
|
-
@pipeline.use E11y::Middleware::Routing
|
|
215
|
-
end
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
# Rails Instrumentation configuration
|
|
219
|
-
class RailsInstrumentationConfig
|
|
220
|
-
attr_accessor :enabled, :custom_mappings, :ignore_events
|
|
221
|
-
|
|
222
|
-
def initialize
|
|
223
|
-
@enabled = false # Disabled by default, enabled by Railtie
|
|
224
|
-
@custom_mappings = {}
|
|
225
|
-
@ignore_events = []
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
# Override event class for specific ASN pattern (Devise-style)
|
|
229
|
-
# @param pattern [String] ActiveSupport::Notifications pattern
|
|
230
|
-
# @param event_class [Class] E11y event class
|
|
231
|
-
# @return [void]
|
|
232
|
-
def event_class_for(pattern, event_class)
|
|
233
|
-
@custom_mappings[pattern] = event_class
|
|
187
|
+
# @example
|
|
188
|
+
# E11y.registry.event_classes
|
|
189
|
+
# E11y.registry.find("order.created")
|
|
190
|
+
def registry
|
|
191
|
+
Registry.instance
|
|
234
192
|
end
|
|
235
193
|
|
|
236
|
-
#
|
|
237
|
-
#
|
|
194
|
+
# Reset configuration (primarily for testing)
|
|
195
|
+
#
|
|
238
196
|
# @return [void]
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
# Logger Bridge configuration
|
|
245
|
-
#
|
|
246
|
-
# Controls Rails.logger integration:
|
|
247
|
-
# - When enabled = true: wraps Rails.logger and sends logs to E11y
|
|
248
|
-
# - When enabled = false: no integration (default)
|
|
249
|
-
#
|
|
250
|
-
# @example Enable logger bridge
|
|
251
|
-
# E11y.configure do |config|
|
|
252
|
-
# config.logger_bridge.enabled = true # Wrap Rails.logger + send to E11y
|
|
253
|
-
# end
|
|
254
|
-
#
|
|
255
|
-
# @see lib/e11y/logger/bridge.rb
|
|
256
|
-
class LoggerBridgeConfig
|
|
257
|
-
attr_accessor :enabled
|
|
258
|
-
|
|
259
|
-
def initialize
|
|
260
|
-
@enabled = false # Opt-in: disabled by default
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
# Request Buffer configuration
|
|
265
|
-
class RequestBufferConfig
|
|
266
|
-
attr_accessor :enabled
|
|
267
|
-
|
|
268
|
-
def initialize
|
|
269
|
-
@enabled = false # Disabled by default
|
|
270
|
-
end
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
# ActiveJob configuration
|
|
274
|
-
#
|
|
275
|
-
# Controls ActiveJob integration (callbacks for event tracking).
|
|
276
|
-
# When enabled, E11y will automatically track job lifecycle events:
|
|
277
|
-
# - job.enqueued
|
|
278
|
-
# - job.started
|
|
279
|
-
# - job.completed
|
|
280
|
-
# - job.failed
|
|
281
|
-
#
|
|
282
|
-
# @see lib/e11y/instruments/active_job.rb
|
|
283
|
-
class ActiveJobConfig
|
|
284
|
-
attr_accessor :enabled
|
|
285
|
-
|
|
286
|
-
def initialize
|
|
287
|
-
@enabled = false # Disabled by default, enabled by Railtie
|
|
288
|
-
end
|
|
289
|
-
end
|
|
290
|
-
|
|
291
|
-
# Sidekiq configuration
|
|
292
|
-
#
|
|
293
|
-
# Controls Sidekiq middleware integration for trace propagation and context setup.
|
|
294
|
-
# Automatically enabled by Railtie when Sidekiq is detected.
|
|
295
|
-
#
|
|
296
|
-
# @see ADR-008 §9 (Sidekiq Integration)
|
|
297
|
-
class SidekiqConfig
|
|
298
|
-
attr_accessor :enabled
|
|
299
|
-
|
|
300
|
-
def initialize
|
|
301
|
-
@enabled = false # Disabled by default, enabled by Railtie when Sidekiq is present
|
|
302
|
-
end
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
# Error Handling configuration (C18 Resolution)
|
|
306
|
-
#
|
|
307
|
-
# Controls whether event tracking failures should raise exceptions.
|
|
308
|
-
# Default: true (for web requests - fast feedback)
|
|
309
|
-
# Exception: false (for background jobs - don't fail business logic)
|
|
310
|
-
#
|
|
311
|
-
# @see ADR-013 §3.6 (Event Tracking in Background Jobs)
|
|
312
|
-
class ErrorHandlingConfig
|
|
313
|
-
attr_accessor :fail_on_error
|
|
314
|
-
|
|
315
|
-
def initialize
|
|
316
|
-
@fail_on_error = true # Default: raise errors (fast feedback for web requests)
|
|
317
|
-
end
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
# Rate Limiting configuration (UC-011, C02 Resolution)
|
|
321
|
-
#
|
|
322
|
-
# Protects adapters from event floods using token bucket algorithm.
|
|
323
|
-
#
|
|
324
|
-
# @see UC-011 (Rate Limiting - DoS Protection)
|
|
325
|
-
# @see ADR-013 §4.6 (C02 Resolution)
|
|
326
|
-
class RateLimitingConfig
|
|
327
|
-
attr_accessor :enabled, :global_limit, :per_event_limit, :window
|
|
328
|
-
|
|
329
|
-
def initialize
|
|
330
|
-
@enabled = false # Opt-in (enable explicitly)
|
|
331
|
-
@global_limit = 10_000 # Max 10K events/sec globally
|
|
332
|
-
@per_event_limit = 1_000 # Max 1K events/sec per event type
|
|
333
|
-
@window = 1.0 # 1 second window
|
|
197
|
+
# @api private
|
|
198
|
+
def reset!
|
|
199
|
+
@configuration = nil
|
|
200
|
+
@logger = nil
|
|
201
|
+
E11y::Metrics.reset_backend!
|
|
334
202
|
end
|
|
335
203
|
end
|
|
336
204
|
|
|
337
|
-
#
|
|
338
|
-
#
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
# @see ADR-003 (SLO & Observability)
|
|
344
|
-
#
|
|
345
|
-
# @note C11 Resolution (Sampling Correction): Requires Phase 2.8 (Stratified Sampling).
|
|
346
|
-
# Without stratified sampling, SLO metrics may be inaccurate when adaptive sampling is enabled.
|
|
347
|
-
class SLOTrackingConfig
|
|
348
|
-
attr_accessor :enabled
|
|
349
|
-
|
|
350
|
-
def initialize
|
|
351
|
-
@enabled = false # Opt-in (enable explicitly)
|
|
352
|
-
end
|
|
353
|
-
end
|
|
205
|
+
# Default allowed keys for baggage protection (ADR-006 §5.5).
|
|
206
|
+
# Used when security_baggage_protection_allowed_keys is not set.
|
|
207
|
+
BAGGAGE_PROTECTION_DEFAULT_ALLOWED_KEYS = %w[
|
|
208
|
+
trace_id span_id environment version service_name deployment_id request_id
|
|
209
|
+
user_id experiment experiment_id tenant feature_flag
|
|
210
|
+
].freeze
|
|
354
211
|
end
|
|
355
212
|
|
|
356
213
|
# Load Railtie if Rails is present
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module E11y
|
|
6
|
+
module Generators
|
|
7
|
+
# Generates an event class under app/events/.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# rails g e11y:event OrderPaid
|
|
11
|
+
# # => creates app/events/events/order_paid.rb
|
|
12
|
+
class EventGenerator < Rails::Generators::NamedBase
|
|
13
|
+
source_root File.expand_path("templates", __dir__)
|
|
14
|
+
|
|
15
|
+
desc "Creates an E11y event class in app/events/."
|
|
16
|
+
|
|
17
|
+
def create_event_file
|
|
18
|
+
template "event.rb.tt", File.join("app/events/events", "#{file_name}.rb")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Events
|
|
4
|
+
class <%= class_name %> < E11y::Event::Base
|
|
5
|
+
# severity :info # auto-inferred from class name; override if needed
|
|
6
|
+
|
|
7
|
+
schema do
|
|
8
|
+
# required(:field_name).filled(:string)
|
|
9
|
+
# optional(:other_field).maybe(:integer)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# metrics do
|
|
13
|
+
# counter :<%= file_name.gsub("/", "_") %>_total
|
|
14
|
+
# end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module E11y
|
|
6
|
+
module Generators
|
|
7
|
+
# Generates a Grafana dashboard JSON for E11y metrics.
|
|
8
|
+
#
|
|
9
|
+
# Requires Yabeda/Prometheus integration.
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# rails g e11y:grafana_dashboard
|
|
13
|
+
# # => creates config/grafana/e11y_dashboard.json
|
|
14
|
+
class GrafanaDashboardGenerator < Rails::Generators::Base
|
|
15
|
+
source_root File.expand_path("templates", __dir__)
|
|
16
|
+
|
|
17
|
+
desc "Creates a Grafana dashboard JSON for E11y metrics in config/grafana/."
|
|
18
|
+
|
|
19
|
+
def create_dashboard
|
|
20
|
+
empty_directory "config/grafana"
|
|
21
|
+
template "e11y_dashboard.json", "config/grafana/e11y_dashboard.json"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def show_readme
|
|
25
|
+
say "\n✅ Grafana dashboard created: config/grafana/e11y_dashboard.json", :green
|
|
26
|
+
say " Import it via Grafana → Dashboards → Import → Upload JSON file\n"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "E11y Observability",
|
|
3
|
+
"uid": "e11y-overview",
|
|
4
|
+
"version": 1,
|
|
5
|
+
"schemaVersion": 36,
|
|
6
|
+
"tags": ["e11y", "ruby", "observability"],
|
|
7
|
+
"panels": [
|
|
8
|
+
{
|
|
9
|
+
"id": 1,
|
|
10
|
+
"type": "stat",
|
|
11
|
+
"title": "Events / sec",
|
|
12
|
+
"targets": [
|
|
13
|
+
{
|
|
14
|
+
"expr": "rate(e11y_events_total[1m])",
|
|
15
|
+
"legendFormat": "{{event_name}}"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"gridPos": { "x": 0, "y": 0, "w": 6, "h": 4 }
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": 2,
|
|
22
|
+
"type": "stat",
|
|
23
|
+
"title": "Error rate",
|
|
24
|
+
"targets": [
|
|
25
|
+
{
|
|
26
|
+
"expr": "rate(e11y_events_total{severity=\"error\"}[1m]) / rate(e11y_events_total[1m])",
|
|
27
|
+
"legendFormat": "error rate"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"gridPos": { "x": 6, "y": 0, "w": 6, "h": 4 }
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": 3,
|
|
34
|
+
"type": "timeseries",
|
|
35
|
+
"title": "Events by severity",
|
|
36
|
+
"targets": [
|
|
37
|
+
{
|
|
38
|
+
"expr": "rate(e11y_events_total[1m])",
|
|
39
|
+
"legendFormat": "{{severity}}"
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"gridPos": { "x": 0, "y": 4, "w": 12, "h": 8 }
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": 4,
|
|
46
|
+
"type": "stat",
|
|
47
|
+
"title": "Rate limit drops",
|
|
48
|
+
"targets": [
|
|
49
|
+
{
|
|
50
|
+
"expr": "rate(e11y_rate_limit_dropped_total[1m])",
|
|
51
|
+
"legendFormat": "dropped"
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"gridPos": { "x": 12, "y": 0, "w": 6, "h": 4 }
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"id": 5,
|
|
58
|
+
"type": "stat",
|
|
59
|
+
"title": "Circuit breaker trips",
|
|
60
|
+
"targets": [
|
|
61
|
+
{
|
|
62
|
+
"expr": "e11y_circuit_breaker_transitions_total{event=\"opened\"}",
|
|
63
|
+
"legendFormat": "{{adapter}}"
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
"gridPos": { "x": 18, "y": 0, "w": 6, "h": 4 }
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"id": 6,
|
|
70
|
+
"type": "timeseries",
|
|
71
|
+
"title": "DLQ queue depth",
|
|
72
|
+
"targets": [
|
|
73
|
+
{
|
|
74
|
+
"expr": "e11y_dlq_size",
|
|
75
|
+
"legendFormat": "DLQ"
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
"gridPos": { "x": 12, "y": 4, "w": 12, "h": 8 }
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
|
|
5
|
+
module E11y
|
|
6
|
+
module Generators
|
|
7
|
+
# Creates config/initializers/e11y.rb and app/events/ directory scaffold.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# rails g e11y:install
|
|
11
|
+
class InstallGenerator < Rails::Generators::Base
|
|
12
|
+
source_root File.expand_path("templates", __dir__)
|
|
13
|
+
|
|
14
|
+
desc "Creates an E11y initializer and the app/events/ directory."
|
|
15
|
+
|
|
16
|
+
def create_initializer
|
|
17
|
+
template "e11y.rb", "config/initializers/e11y.rb"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def create_events_directory
|
|
21
|
+
empty_directory "app/events"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def show_readme
|
|
25
|
+
say "\n✅ E11y installed!", :green
|
|
26
|
+
say " • config/initializers/e11y.rb — configure adapters here"
|
|
27
|
+
say " • app/events/ — put your event classes here"
|
|
28
|
+
say "\nNext steps:"
|
|
29
|
+
say " rails g e11y:event OrderPaid # generate an event class"
|
|
30
|
+
say " E11y.start! # call after configure in production\n"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|