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
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module E11y
|
|
4
|
+
# Configuration class for E11y.
|
|
5
|
+
#
|
|
6
|
+
# Adapters are referenced by name (e.g., :logs, :errors_tracker).
|
|
7
|
+
# The actual implementation (Loki, Sentry, etc.) is configured separately.
|
|
8
|
+
#
|
|
9
|
+
# @example Configure adapters
|
|
10
|
+
# E11y.configure do |config|
|
|
11
|
+
# config.adapters[:logs] = E11y::Adapters::Loki.new(url: "...")
|
|
12
|
+
# config.adapters[:errors_tracker] = E11y::Adapters::Sentry.new(dsn: "...")
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# @example Configure severity => adapter mapping
|
|
16
|
+
# E11y.configure do |config|
|
|
17
|
+
# config.adapter_mapping[:error] = [:logs, :errors_tracker]
|
|
18
|
+
# config.adapter_mapping[:info] = [:logs]
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# @example Configure middleware pipeline
|
|
22
|
+
# E11y.configure do |config|
|
|
23
|
+
# config.pipeline.use E11y::Middleware::Sampling, default_sample_rate: 0.1
|
|
24
|
+
# end
|
|
25
|
+
class Configuration
|
|
26
|
+
attr_accessor :adapters, :log_level, :logger, :enabled, :environment, :service_name, :default_retention_period,
|
|
27
|
+
:routing_rules, :fallback_adapters, :enable_http_tracing,
|
|
28
|
+
:rails_instrumentation_enabled, :rails_instrumentation_custom_mappings, :rails_instrumentation_ignore_events,
|
|
29
|
+
:logger_bridge_enabled, :logger_bridge_track_severities, :logger_bridge_ignore_patterns,
|
|
30
|
+
:sidekiq_enabled, :active_job_enabled,
|
|
31
|
+
:ephemeral_buffer_enabled, :ephemeral_buffer_flush_on_error, :ephemeral_buffer_flush_on_statuses,
|
|
32
|
+
:ephemeral_buffer_debug_adapters, :ephemeral_buffer_job_buffer_limit,
|
|
33
|
+
:error_handling_fail_on_error,
|
|
34
|
+
:rate_limiting_enabled, :rate_limiting_global_limit, :rate_limiting_global_window,
|
|
35
|
+
:rate_limiting_per_event_limit, :rate_limiting_per_event_limits,
|
|
36
|
+
:slo_tracking_enabled, :slo_tracking_http_ignore_statuses, :slo_tracking_latency_percentiles,
|
|
37
|
+
:slo_tracking_controller_configs, :slo_tracking_job_configs,
|
|
38
|
+
:security_baggage_protection_enabled, :security_baggage_protection_allowed_keys, :security_baggage_protection_block_mode,
|
|
39
|
+
:tracing_source, :tracing_default_sample_rate, :tracing_respect_parent_sampling,
|
|
40
|
+
:tracing_per_event_sample_rates, :tracing_always_sample_if,
|
|
41
|
+
:opentelemetry_span_creation_patterns,
|
|
42
|
+
:cardinality_protection_max_cardinality_limit, :cardinality_protection_denylist, :cardinality_protection_overflow_strategy,
|
|
43
|
+
:dlq_storage, :dlq_filter
|
|
44
|
+
attr_reader :adapter_mapping, :pipeline
|
|
45
|
+
|
|
46
|
+
def initialize
|
|
47
|
+
initialize_basic_config
|
|
48
|
+
initialize_routing_config
|
|
49
|
+
initialize_feature_configs
|
|
50
|
+
configure_default_pipeline
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Get adapters for given severity
|
|
54
|
+
#
|
|
55
|
+
# @param severity [Symbol] Severity level
|
|
56
|
+
# @return [Array<Symbol>] Adapter names (e.g., [:logs, :errors_tracker])
|
|
57
|
+
def adapters_for_severity(severity)
|
|
58
|
+
@adapter_mapping[severity] || @adapter_mapping[:default] || []
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Get built pipeline (cached after first call)
|
|
62
|
+
#
|
|
63
|
+
# @return [#call] Built middleware pipeline
|
|
64
|
+
def built_pipeline
|
|
65
|
+
@built_pipeline ||= @pipeline.build(->(_event_data) {})
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Add per-event rate limit rule.
|
|
69
|
+
#
|
|
70
|
+
# @param pattern [String] Event name or glob pattern (e.g. "payment.*")
|
|
71
|
+
# @param limit [Integer] Max events per window for this pattern
|
|
72
|
+
# @param window [Numeric] Window size in seconds
|
|
73
|
+
def add_rate_limit_per_event(pattern, limit:, window: 1.0)
|
|
74
|
+
(@rate_limiting_per_event_limits ||= []) << {
|
|
75
|
+
pattern: pattern.to_s,
|
|
76
|
+
limit: limit,
|
|
77
|
+
window: window.to_f
|
|
78
|
+
}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Find the most specific rate limit config for a given event name.
|
|
82
|
+
#
|
|
83
|
+
# @param event_name [String] Event name to look up
|
|
84
|
+
# @return [Hash] { limit:, window: }
|
|
85
|
+
def rate_limit_for(event_name)
|
|
86
|
+
limits = @rate_limiting_per_event_limits || []
|
|
87
|
+
match = limits.find do |rule|
|
|
88
|
+
pattern = rule[:pattern].gsub(".", "\\.").gsub("*", ".*")
|
|
89
|
+
Regexp.new("^#{pattern}$").match?(event_name.to_s)
|
|
90
|
+
end
|
|
91
|
+
m = match || { limit: @rate_limiting_per_event_limit, window: @rate_limiting_global_window }
|
|
92
|
+
{ limit: m[:limit], window: m[:window] }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Add per-controller SLO config.
|
|
96
|
+
def add_slo_controller(name, action: nil, &)
|
|
97
|
+
key = action ? "#{name}##{action}" : name
|
|
98
|
+
cfg = ControllerSLOConfig.new
|
|
99
|
+
cfg.instance_eval(&) if block_given?
|
|
100
|
+
(@slo_tracking_controller_configs ||= {})[key] = cfg
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Add per-job SLO config.
|
|
104
|
+
def add_slo_job(name, &)
|
|
105
|
+
cfg = JobSLOConfig.new
|
|
106
|
+
cfg.instance_eval(&) if block_given?
|
|
107
|
+
(@slo_tracking_job_configs ||= {})[name] = cfg
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Set slo_tracking enabled — accepts Boolean.
|
|
111
|
+
def slo_tracking=(value)
|
|
112
|
+
@slo_tracking_enabled = value if value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Filter baggage hash to only allowed keys (for tracestate/job propagation).
|
|
116
|
+
def filter_baggage_for_propagation(hash)
|
|
117
|
+
return {} if hash.nil? || !hash.is_a?(Hash)
|
|
118
|
+
return hash unless @security_baggage_protection_enabled
|
|
119
|
+
|
|
120
|
+
allowed = (@security_baggage_protection_allowed_keys || E11y::BAGGAGE_PROTECTION_DEFAULT_ALLOWED_KEYS).map(&:to_s)
|
|
121
|
+
hash.select { |k, _| allowed.include?(k.to_s) }
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Register an adapter instance by name.
|
|
125
|
+
def register_adapter(name, instance)
|
|
126
|
+
@adapters[name.to_sym] = instance
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Set the default adapter(s) used when no severity-specific mapping matches.
|
|
130
|
+
def default_adapters=(names)
|
|
131
|
+
@adapter_mapping[:default] = Array(names).map(&:to_sym)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# @return [Array<Symbol>] Default adapter names
|
|
135
|
+
def default_adapters
|
|
136
|
+
@adapter_mapping[:default]
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
private
|
|
140
|
+
|
|
141
|
+
def initialize_basic_config
|
|
142
|
+
@adapters = {}
|
|
143
|
+
@log_level = :info
|
|
144
|
+
@pipeline = E11y::Pipeline::Builder.new
|
|
145
|
+
@enabled = nil
|
|
146
|
+
@environment = nil
|
|
147
|
+
@service_name = nil
|
|
148
|
+
@enable_http_tracing = false
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def initialize_routing_config
|
|
152
|
+
@adapter_mapping = default_adapter_mapping
|
|
153
|
+
@default_retention_period = 30.days
|
|
154
|
+
@routing_rules = []
|
|
155
|
+
@fallback_adapters = [:stdout]
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def initialize_feature_configs
|
|
159
|
+
init_instrumentation_configs
|
|
160
|
+
init_ephemeral_buffer_configs
|
|
161
|
+
init_error_handling_configs
|
|
162
|
+
init_rate_limiting_configs
|
|
163
|
+
init_slo_configs
|
|
164
|
+
init_security_configs
|
|
165
|
+
init_tracing_configs
|
|
166
|
+
init_cardinality_configs
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def init_instrumentation_configs
|
|
170
|
+
@rails_instrumentation_enabled = false
|
|
171
|
+
@rails_instrumentation_custom_mappings = {}
|
|
172
|
+
@rails_instrumentation_ignore_events = []
|
|
173
|
+
@logger_bridge_enabled = false
|
|
174
|
+
@logger_bridge_track_severities = nil
|
|
175
|
+
@logger_bridge_ignore_patterns = []
|
|
176
|
+
@sidekiq_enabled = false
|
|
177
|
+
@active_job_enabled = false
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def init_ephemeral_buffer_configs
|
|
181
|
+
@ephemeral_buffer_enabled = false
|
|
182
|
+
@ephemeral_buffer_flush_on_error = true
|
|
183
|
+
@ephemeral_buffer_flush_on_statuses = []
|
|
184
|
+
@ephemeral_buffer_debug_adapters = nil
|
|
185
|
+
@ephemeral_buffer_job_buffer_limit = nil
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def init_error_handling_configs
|
|
189
|
+
@error_handling_fail_on_error = true
|
|
190
|
+
@dlq_storage = nil
|
|
191
|
+
@dlq_filter = nil
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def init_rate_limiting_configs
|
|
195
|
+
@rate_limiting_enabled = false
|
|
196
|
+
@rate_limiting_global_limit = 10_000
|
|
197
|
+
@rate_limiting_global_window = 1.0
|
|
198
|
+
@rate_limiting_per_event_limit = 1_000
|
|
199
|
+
@rate_limiting_per_event_limits = []
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def init_slo_configs
|
|
203
|
+
@slo_tracking_enabled = true
|
|
204
|
+
@slo_tracking_http_ignore_statuses = []
|
|
205
|
+
@slo_tracking_latency_percentiles = [50, 95, 99]
|
|
206
|
+
@slo_tracking_controller_configs = {}
|
|
207
|
+
@slo_tracking_job_configs = {}
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def init_security_configs
|
|
211
|
+
@security_baggage_protection_enabled = true
|
|
212
|
+
@security_baggage_protection_allowed_keys = E11y::BAGGAGE_PROTECTION_DEFAULT_ALLOWED_KEYS.dup
|
|
213
|
+
@security_baggage_protection_block_mode = :silent
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def init_tracing_configs
|
|
217
|
+
@tracing_source = :e11y
|
|
218
|
+
@tracing_default_sample_rate = 0.1
|
|
219
|
+
@tracing_respect_parent_sampling = true
|
|
220
|
+
@tracing_per_event_sample_rates = {}
|
|
221
|
+
@tracing_always_sample_if = nil
|
|
222
|
+
@opentelemetry_span_creation_patterns = []
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def init_cardinality_configs
|
|
226
|
+
@cardinality_protection_max_cardinality_limit = 1000
|
|
227
|
+
@cardinality_protection_denylist = []
|
|
228
|
+
@cardinality_protection_overflow_strategy = :relabel
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def default_adapter_mapping
|
|
232
|
+
{
|
|
233
|
+
error: %i[logs errors_tracker],
|
|
234
|
+
fatal: %i[logs errors_tracker],
|
|
235
|
+
default: [:logs]
|
|
236
|
+
}
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def configure_default_pipeline
|
|
240
|
+
@pipeline.use E11y::Middleware::TrackLatency
|
|
241
|
+
@pipeline.use E11y::Middleware::TraceContext
|
|
242
|
+
@pipeline.use E11y::Middleware::Validation
|
|
243
|
+
@pipeline.use E11y::Middleware::BaggageProtection
|
|
244
|
+
@pipeline.use E11y::Middleware::AuditSigning
|
|
245
|
+
@pipeline.use E11y::Middleware::PIIFilter
|
|
246
|
+
@pipeline.use E11y::Middleware::RateLimiting
|
|
247
|
+
@pipeline.use E11y::Middleware::Sampling
|
|
248
|
+
@pipeline.use E11y::Middleware::Versioning
|
|
249
|
+
@pipeline.use E11y::Middleware::Routing
|
|
250
|
+
@pipeline.use E11y::Middleware::EventSlo
|
|
251
|
+
@pipeline.use E11y::Middleware::SelfMonitoringEmit
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Per-controller SLO config (used by add_slo_controller).
|
|
256
|
+
class ControllerSLOConfig
|
|
257
|
+
def slo_target(value = nil)
|
|
258
|
+
value ? @slo_target = value : @slo_target
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def latency_target(value = nil)
|
|
262
|
+
value ? @latency_target = value : @latency_target
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Per-job SLO config (used by add_slo_job).
|
|
267
|
+
class JobSLOConfig
|
|
268
|
+
def ignore(value = nil)
|
|
269
|
+
value.nil? ? @ignore : @ignore = value
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
end
|
data/lib/e11y/console.rb
CHANGED
|
@@ -51,19 +51,17 @@ module E11y
|
|
|
51
51
|
|
|
52
52
|
# List all registered event classes
|
|
53
53
|
def events
|
|
54
|
-
|
|
55
|
-
puts " (Waiting for Event registry implementation)"
|
|
56
|
-
[]
|
|
54
|
+
Registry.event_classes.map { |e| e.respond_to?(:event_name) ? e.event_name : e.name }
|
|
57
55
|
end
|
|
58
56
|
|
|
59
|
-
# List all registered adapters
|
|
57
|
+
# List all registered adapters (from config.adapters)
|
|
60
58
|
def adapters
|
|
61
|
-
|
|
59
|
+
config.adapters.map do |name, adapter|
|
|
62
60
|
{
|
|
63
|
-
name:
|
|
61
|
+
name: name,
|
|
64
62
|
class: adapter.class.name,
|
|
65
|
-
healthy: adapter.healthy
|
|
66
|
-
capabilities: adapter.capabilities
|
|
63
|
+
healthy: adapter.respond_to?(:healthy?) ? adapter.healthy? : true,
|
|
64
|
+
capabilities: adapter.respond_to?(:capabilities) ? adapter.capabilities : {}
|
|
67
65
|
}
|
|
68
66
|
end
|
|
69
67
|
end
|
|
@@ -77,8 +75,8 @@ module E11y
|
|
|
77
75
|
private
|
|
78
76
|
|
|
79
77
|
def adapters_info
|
|
80
|
-
|
|
81
|
-
{ name:
|
|
78
|
+
config.adapters.map do |name, adapter|
|
|
79
|
+
{ name: name, class: adapter.class.name, healthy: adapter.respond_to?(:healthy?) ? adapter.healthy? : true }
|
|
82
80
|
end
|
|
83
81
|
end
|
|
84
82
|
|
|
@@ -95,13 +93,8 @@ module E11y
|
|
|
95
93
|
# @return [void]
|
|
96
94
|
def self.configure_for_console
|
|
97
95
|
E11y.configure do |config|
|
|
98
|
-
|
|
99
|
-
config.adapters
|
|
100
|
-
|
|
101
|
-
# Use stdout adapter with pretty printing
|
|
102
|
-
config.adapters&.register :stdout, E11y::Adapters::Stdout.new(
|
|
103
|
-
colorize: true
|
|
104
|
-
)
|
|
96
|
+
config.adapters.clear
|
|
97
|
+
config.adapters[:stdout] = E11y::Adapters::Stdout.new(colorize: true, format: :rich)
|
|
105
98
|
|
|
106
99
|
# Show all severities
|
|
107
100
|
# TODO: Implement severity_threshold config
|
data/lib/e11y/current.rb
CHANGED
|
@@ -37,12 +37,64 @@ module E11y
|
|
|
37
37
|
class Current < ActiveSupport::CurrentAttributes
|
|
38
38
|
attribute :trace_id
|
|
39
39
|
attribute :span_id
|
|
40
|
-
attribute :parent_trace_id #
|
|
40
|
+
attribute :parent_trace_id # Link to parent trace (C17 Resolution)
|
|
41
|
+
attribute :sampled # Trace-consistent sampling (ADR-005 §7)
|
|
42
|
+
attribute :baggage # Key-value metadata for cross-service propagation (ADR-005 §3)
|
|
41
43
|
attribute :request_id
|
|
42
44
|
attribute :user_id
|
|
43
45
|
attribute :ip_address
|
|
44
46
|
attribute :user_agent
|
|
45
47
|
attribute :request_method
|
|
46
48
|
attribute :request_path
|
|
49
|
+
|
|
50
|
+
# Add baggage key-value (propagated via tracestate / job metadata).
|
|
51
|
+
# Respects config.security_baggage_protection_*: blocks PII keys per allowed_keys (ADR-006 §5.5).
|
|
52
|
+
#
|
|
53
|
+
# @param key [String, Symbol]
|
|
54
|
+
# @param value [Object] Converted to string
|
|
55
|
+
# @raise [E11y::BaggagePiiError] when block_mode is :raise and key not allowed
|
|
56
|
+
def self.add_baggage(key, value)
|
|
57
|
+
cfg = E11y.config
|
|
58
|
+
if cfg&.security_baggage_protection_enabled
|
|
59
|
+
allowed = (cfg.security_baggage_protection_allowed_keys || E11y::BAGGAGE_PROTECTION_DEFAULT_ALLOWED_KEYS).map(&:to_s)
|
|
60
|
+
unless allowed.include?(key.to_s)
|
|
61
|
+
message = "[E11y] Blocked PII from E11y baggage: key=#{key.inspect}"
|
|
62
|
+
case cfg.security_baggage_protection_block_mode || :silent
|
|
63
|
+
when :silent then E11y.logger&.debug(message)
|
|
64
|
+
when :warn then E11y.logger&.warn(message)
|
|
65
|
+
when :raise then raise E11y::BaggagePiiError, "#{message}. Only allowed keys: #{allowed.join(', ')}"
|
|
66
|
+
end
|
|
67
|
+
return
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
self.baggage = (baggage || {}).merge(key.to_s => value.to_s)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Get baggage value by key.
|
|
74
|
+
# @param key [String, Symbol]
|
|
75
|
+
# @return [String, nil]
|
|
76
|
+
def self.get_baggage(key)
|
|
77
|
+
baggage&.[](key.to_s)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Returns current attributes as a hash for sampling context (symbol keys, nil values omitted).
|
|
81
|
+
# Callers may merge job-specific keys (job_class, queue) when in job context.
|
|
82
|
+
#
|
|
83
|
+
# @return [Hash{Symbol=>Object}]
|
|
84
|
+
def self.to_context
|
|
85
|
+
{
|
|
86
|
+
trace_id: trace_id,
|
|
87
|
+
span_id: span_id,
|
|
88
|
+
parent_trace_id: parent_trace_id,
|
|
89
|
+
sampled: sampled,
|
|
90
|
+
baggage: baggage,
|
|
91
|
+
request_id: request_id,
|
|
92
|
+
user_id: user_id,
|
|
93
|
+
ip_address: ip_address,
|
|
94
|
+
user_agent: user_agent,
|
|
95
|
+
request_method: request_method,
|
|
96
|
+
request_path: request_path
|
|
97
|
+
}.compact
|
|
98
|
+
end
|
|
47
99
|
end
|
|
48
100
|
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/core_ext/numeric/time"
|
|
4
|
+
|
|
5
|
+
module E11y
|
|
6
|
+
module Debug
|
|
7
|
+
# Debug utility to trace events through the pipeline with per-middleware logging.
|
|
8
|
+
#
|
|
9
|
+
# Runs the full pipeline including adapter writes. For debugging, use Stdout or
|
|
10
|
+
# InMemory adapter. Stub adapters in tests if needed.
|
|
11
|
+
#
|
|
12
|
+
# @see E11y.trace
|
|
13
|
+
class PipelineInspector
|
|
14
|
+
# Wraps a middleware to log enter/exit for pipeline tracing.
|
|
15
|
+
class TracingWrapper
|
|
16
|
+
def initialize(middleware_class, next_app, name, args: [], options: {})
|
|
17
|
+
@middleware_class = middleware_class
|
|
18
|
+
@next_app = next_app
|
|
19
|
+
@name = name
|
|
20
|
+
@args = args
|
|
21
|
+
@options = options
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def call(event_data)
|
|
25
|
+
log_enter(@name)
|
|
26
|
+
result = @middleware_class.new(@next_app, *@args, **@options).call(event_data)
|
|
27
|
+
log_exit(@name)
|
|
28
|
+
result
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def log_enter(name)
|
|
34
|
+
prefix = $stdout.tty? ? "\e[33m" : ""
|
|
35
|
+
suffix = $stdout.tty? ? "\e[0m" : ""
|
|
36
|
+
print " #{prefix}#{name}#{suffix}... "
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def log_exit(_name)
|
|
40
|
+
mark = $stdout.tty? ? "\e[32m✓\e[0m" : "✓"
|
|
41
|
+
puts mark
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class << self
|
|
46
|
+
# Traces an event through the pipeline with per-middleware logging.
|
|
47
|
+
# Note: adapters WILL receive the event.
|
|
48
|
+
#
|
|
49
|
+
# @param event_class [Class] Event class
|
|
50
|
+
# @param payload [Hash] Event payload
|
|
51
|
+
# @return [Hash] event_data after pipeline
|
|
52
|
+
def trace_event(event_class, **payload)
|
|
53
|
+
event_name = event_class.respond_to?(:event_name) ? event_class.event_name : event_class.name
|
|
54
|
+
puts "\n🔍 Tracing Event Pipeline: #{event_name}\n\n"
|
|
55
|
+
event_data = build_event_data(event_class, payload)
|
|
56
|
+
pipeline = build_tracing_pipeline
|
|
57
|
+
result = pipeline.call(event_data)
|
|
58
|
+
puts "\n✅ Pipeline trace complete\n"
|
|
59
|
+
result
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def build_event_data(event_class, payload)
|
|
65
|
+
{
|
|
66
|
+
event_class: event_class,
|
|
67
|
+
event_name: event_class.respond_to?(:event_name) ? event_class.event_name : event_class.name,
|
|
68
|
+
payload: payload,
|
|
69
|
+
severity: event_class.respond_to?(:severity) ? event_class.severity : :info,
|
|
70
|
+
version: event_class.respond_to?(:version) ? event_class.version : 1,
|
|
71
|
+
adapters: event_class.respond_to?(:adapters) ? event_class.adapters : nil,
|
|
72
|
+
timestamp: Time.now.utc,
|
|
73
|
+
retention_period: event_class.respond_to?(:retention_period) ? event_class.retention_period : 30.days,
|
|
74
|
+
context: {}
|
|
75
|
+
}
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def build_tracing_pipeline
|
|
79
|
+
builder = E11y.configuration.pipeline
|
|
80
|
+
final_app = ->(event_data) { event_data }
|
|
81
|
+
|
|
82
|
+
builder.middlewares.reverse.reduce(final_app) do |next_app, entry|
|
|
83
|
+
name = entry.middleware_class.name.split("::").last
|
|
84
|
+
TracingWrapper.new(
|
|
85
|
+
entry.middleware_class,
|
|
86
|
+
next_app,
|
|
87
|
+
name,
|
|
88
|
+
args: entry.args,
|
|
89
|
+
options: entry.options
|
|
90
|
+
)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module E11y
|
|
4
|
+
module Documentation
|
|
5
|
+
# Generates Markdown documentation for registered E11y events.
|
|
6
|
+
class Generator
|
|
7
|
+
def self.generate(output_dir, criteria: {}, grep: nil)
|
|
8
|
+
classes = criteria.any? ? E11y::Registry.where(**criteria) : E11y::Registry.event_classes
|
|
9
|
+
classes = classes.select { |c| (c.respond_to?(:event_name) ? c.event_name : c.name).to_s.include?(grep) } if grep
|
|
10
|
+
|
|
11
|
+
FileUtils.mkdir_p(output_dir)
|
|
12
|
+
write_index(output_dir, classes)
|
|
13
|
+
classes.each { |klass| write_event_doc(output_dir, klass) }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.write_index(output_dir, classes)
|
|
17
|
+
lines = ["# E11y Events", "", "| Event | Class | Severity |", "|-------|-------|----------|"]
|
|
18
|
+
classes.each do |klass|
|
|
19
|
+
name = klass.respond_to?(:event_name) ? klass.event_name : klass.name
|
|
20
|
+
sev = klass.respond_to?(:severity) ? klass.severity : "—"
|
|
21
|
+
lines << "| #{name} | #{klass.name} | #{sev} |"
|
|
22
|
+
end
|
|
23
|
+
File.write(File.join(output_dir, "README.md"), "#{lines.join("\n")}\n")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.write_event_doc(output_dir, klass)
|
|
27
|
+
name = klass.respond_to?(:event_name) ? klass.event_name : klass.name
|
|
28
|
+
schema_keys = extract_schema_keys(klass)
|
|
29
|
+
sev = klass.respond_to?(:severity) ? klass.severity : "—"
|
|
30
|
+
lines = ["# #{name}", "", "- **Class:** #{klass.name}", "- **Severity:** #{sev}"]
|
|
31
|
+
lines << "- **Schema keys:** #{schema_keys.join(', ')}" if schema_keys&.any?
|
|
32
|
+
lines << ""
|
|
33
|
+
File.write(File.join(output_dir, "#{name.to_s.tr('.', '_')}.md"), "#{lines.join("\n")}\n")
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.extract_schema_keys(klass)
|
|
37
|
+
return nil unless klass.respond_to?(:compiled_schema)
|
|
38
|
+
|
|
39
|
+
schema = klass.compiled_schema
|
|
40
|
+
return nil if schema.nil? || !schema.respond_to?(:key_map)
|
|
41
|
+
|
|
42
|
+
schema.key_map.keys.map(&:name)
|
|
43
|
+
rescue StandardError
|
|
44
|
+
nil
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|