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
|
@@ -204,24 +204,12 @@ module E11y
|
|
|
204
204
|
next unless E11y.config.enabled
|
|
205
205
|
|
|
206
206
|
# Setup instruments (each can be enabled/disabled separately)
|
|
207
|
-
if E11y.config.
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if E11y.config.instruments.sidekiq.enabled
|
|
212
|
-
E11y::Instruments::Sidekiq.setup!
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
if E11y.config.instruments.active_job.enabled
|
|
216
|
-
E11y::Instruments::ActiveJob.setup!
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
if E11y.config.instruments.rack_middleware.enabled
|
|
220
|
-
E11y::Instruments::RackMiddleware.setup!
|
|
221
|
-
end
|
|
207
|
+
E11y::Railtie.setup_rails_instrumentation if E11y.config.rails_instrumentation&.enabled
|
|
208
|
+
E11y::Railtie.setup_sidekiq if defined?(::Sidekiq) && E11y.config.sidekiq&.enabled
|
|
209
|
+
E11y::Railtie.setup_active_job if defined?(::ActiveJob) && E11y.config.active_job&.enabled
|
|
222
210
|
|
|
223
211
|
# Setup logger bridge
|
|
224
|
-
E11y::Logger::Bridge.setup! if E11y.config.
|
|
212
|
+
E11y::Logger::Bridge.setup! if E11y.config.logger_bridge_enabled
|
|
225
213
|
|
|
226
214
|
# Setup development tools
|
|
227
215
|
E11y::Console.setup! if Rails.env.development?
|
|
@@ -400,7 +388,7 @@ module E11y
|
|
|
400
388
|
# ========================================
|
|
401
389
|
|
|
402
390
|
def self.setup!
|
|
403
|
-
return unless E11y.config.
|
|
391
|
+
return unless E11y.config.rails_instrumentation&.enabled
|
|
404
392
|
|
|
405
393
|
# Subscribe to Rails events
|
|
406
394
|
event_mapping.each do |asn_pattern, e11y_event_class|
|
|
@@ -433,6 +421,7 @@ module E11y
|
|
|
433
421
|
'enqueue.active_job' => Events::Rails::Job::Enqueued,
|
|
434
422
|
'enqueue_at.active_job' => Events::Rails::Job::Scheduled,
|
|
435
423
|
'perform_start.active_job' => Events::Rails::Job::Started,
|
|
424
|
+
# perform.active_job: Completed on success; Failed when payload has exception
|
|
436
425
|
'perform.active_job' => Events::Rails::Job::Completed
|
|
437
426
|
}.freeze
|
|
438
427
|
|
|
@@ -442,7 +431,7 @@ module E11y
|
|
|
442
431
|
mapping = DEFAULT_RAILS_EVENT_MAPPING.dup
|
|
443
432
|
|
|
444
433
|
# Apply custom mappings from config (Devise-style overrides)
|
|
445
|
-
custom_mappings = E11y.config.
|
|
434
|
+
custom_mappings = E11y.config.rails_instrumentation&.custom_mappings || {}
|
|
446
435
|
mapping.merge!(custom_mappings)
|
|
447
436
|
|
|
448
437
|
mapping
|
|
@@ -450,7 +439,7 @@ module E11y
|
|
|
450
439
|
end
|
|
451
440
|
|
|
452
441
|
def self.ignored?(pattern)
|
|
453
|
-
ignore_list = E11y.config.
|
|
442
|
+
ignore_list = E11y.config.rails_instrumentation&.ignore_events || []
|
|
454
443
|
ignore_list.include?(pattern)
|
|
455
444
|
end
|
|
456
445
|
|
|
@@ -477,62 +466,60 @@ end
|
|
|
477
466
|
```ruby
|
|
478
467
|
# config/initializers/e11y.rb
|
|
479
468
|
E11y.configure do |config|
|
|
480
|
-
|
|
469
|
+
# ========================================
|
|
470
|
+
# Rails Instrumentation (ActiveSupport::Notifications → E11y)
|
|
471
|
+
# ========================================
|
|
472
|
+
config.rails_instrumentation do
|
|
473
|
+
# Enable/disable entire ASN integration
|
|
474
|
+
enabled true # Set to false to completely disable
|
|
475
|
+
|
|
476
|
+
# Built-in event classes (auto-created by E11y)
|
|
477
|
+
# Located in Events::Rails namespace
|
|
478
|
+
use_built_in_events true # If false, no auto-mapping
|
|
479
|
+
|
|
481
480
|
# ========================================
|
|
482
|
-
#
|
|
481
|
+
# OVERRIDE EVENT CLASSES (Devise-style)
|
|
483
482
|
# ========================================
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
track_patterns [
|
|
511
|
-
'sql.active_record',
|
|
512
|
-
'process_action.action_controller',
|
|
513
|
-
'render_template.action_view',
|
|
514
|
-
'cache_*.active_support'
|
|
515
|
-
]
|
|
516
|
-
|
|
517
|
-
# Sampling for high-volume events
|
|
518
|
-
sample_patterns do
|
|
519
|
-
pattern 'sql.active_record', sample_rate: 0.1 # 10% of SQL queries
|
|
520
|
-
pattern 'cache_read.active_support', sample_rate: 0.01 # 1% of cache reads
|
|
521
|
-
end
|
|
522
|
-
|
|
523
|
-
# Enrich with custom data
|
|
524
|
-
enrich do |asn_event|
|
|
525
|
-
{
|
|
526
|
-
controller: asn_event.payload[:controller],
|
|
527
|
-
action: asn_event.payload[:action],
|
|
528
|
-
format: asn_event.payload[:format],
|
|
529
|
-
user_id: Current.user&.id # Add context
|
|
530
|
-
}
|
|
531
|
-
end
|
|
483
|
+
|
|
484
|
+
# Override default event class for specific ASN pattern
|
|
485
|
+
event_class_for 'sql.active_record', MyApp::Events::CustomDatabaseQuery
|
|
486
|
+
event_class_for 'process_action.action_controller', MyApp::Events::CustomHttpRequest
|
|
487
|
+
|
|
488
|
+
# Disable specific events (too noisy or not needed)
|
|
489
|
+
ignore_event 'cache_read.active_support'
|
|
490
|
+
ignore_event 'render_partial.action_view'
|
|
491
|
+
ignore_event 'SCHEMA' # Schema queries
|
|
492
|
+
|
|
493
|
+
# ========================================
|
|
494
|
+
# SELECTIVE INSTRUMENTATION
|
|
495
|
+
# ========================================
|
|
496
|
+
|
|
497
|
+
# Which Rails events to track (glob patterns)
|
|
498
|
+
track_patterns [
|
|
499
|
+
'sql.active_record',
|
|
500
|
+
'process_action.action_controller',
|
|
501
|
+
'render_template.action_view',
|
|
502
|
+
'cache_*.active_support'
|
|
503
|
+
]
|
|
504
|
+
|
|
505
|
+
# Sampling for high-volume events
|
|
506
|
+
sample_patterns do
|
|
507
|
+
pattern 'sql.active_record', sample_rate: 0.1 # 10% of SQL queries
|
|
508
|
+
pattern 'cache_read.active_support', sample_rate: 0.01 # 1% of cache reads
|
|
532
509
|
end
|
|
533
510
|
|
|
534
|
-
#
|
|
511
|
+
# Enrich with custom data
|
|
512
|
+
enrich do |asn_event|
|
|
513
|
+
{
|
|
514
|
+
controller: asn_event.payload[:controller],
|
|
515
|
+
action: asn_event.payload[:action],
|
|
516
|
+
format: asn_event.payload[:format],
|
|
517
|
+
user_id: Current.user&.id # Add context
|
|
518
|
+
}
|
|
519
|
+
end
|
|
535
520
|
end
|
|
521
|
+
|
|
522
|
+
# (Other integrations: config.sidekiq, config.active_job, config.logger_bridge)
|
|
536
523
|
end
|
|
537
524
|
```
|
|
538
525
|
|
|
@@ -580,7 +567,7 @@ end
|
|
|
580
567
|
|
|
581
568
|
# config/initializers/e11y.rb
|
|
582
569
|
E11y.configure do |config|
|
|
583
|
-
config.
|
|
570
|
+
config.rails_instrumentation do
|
|
584
571
|
# Override default event class
|
|
585
572
|
event_class_for 'sql.active_record', MyApp::Events::CustomDatabaseQuery
|
|
586
573
|
end
|
|
@@ -660,71 +647,44 @@ module E11y
|
|
|
660
647
|
end
|
|
661
648
|
```
|
|
662
649
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
# Server middleware (job execution)
|
|
670
|
-
server_middleware do
|
|
671
|
-
enabled true
|
|
672
|
-
track_start true
|
|
673
|
-
track_complete true
|
|
674
|
-
track_failure true
|
|
675
|
-
end
|
|
676
|
-
|
|
677
|
-
# Client middleware (job enqueuing)
|
|
678
|
-
client_middleware do
|
|
679
|
-
enabled true
|
|
680
|
-
track_enqueue true
|
|
681
|
-
end
|
|
682
|
-
|
|
683
|
-
# Trace propagation
|
|
684
|
-
propagate_trace_context true
|
|
685
|
-
trace_context_keys ['e11y_trace_id', 'e11y_span_id', 'e11y_sampled']
|
|
686
|
-
end
|
|
650
|
+
# ========================================
|
|
651
|
+
# Sidekiq
|
|
652
|
+
# ========================================
|
|
653
|
+
config.sidekiq do
|
|
654
|
+
enabled true # Set to false to disable Sidekiq integration
|
|
687
655
|
|
|
688
|
-
#
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
active_job do
|
|
692
|
-
enabled true # Set to false to disable ActiveJob integration
|
|
693
|
-
|
|
694
|
-
track_enqueue true
|
|
656
|
+
# Server middleware (job execution)
|
|
657
|
+
server_middleware do
|
|
658
|
+
enabled true
|
|
695
659
|
track_start true
|
|
696
660
|
track_complete true
|
|
697
661
|
track_failure true
|
|
698
|
-
|
|
699
|
-
# Trace propagation
|
|
700
|
-
propagate_trace_context true
|
|
701
|
-
|
|
702
|
-
# Job-scoped buffer (like request-scoped buffer for HTTP)
|
|
703
|
-
use_job_buffer true
|
|
704
|
-
|
|
705
|
-
job_buffer do
|
|
706
|
-
buffer_severities [:debug]
|
|
707
|
-
flush_on do
|
|
708
|
-
error true # Flush debug events if job fails
|
|
709
|
-
success false # Discard debug events if job succeeds
|
|
710
|
-
end
|
|
711
|
-
max_events 1000
|
|
712
|
-
end
|
|
713
662
|
end
|
|
714
663
|
|
|
715
|
-
#
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
enabled true # Set to false to disable Rack middleware
|
|
720
|
-
|
|
721
|
-
track_request_start true
|
|
722
|
-
track_request_complete true
|
|
723
|
-
track_request_failure true
|
|
724
|
-
|
|
725
|
-
# Request-scoped buffer
|
|
726
|
-
use_request_buffer true
|
|
664
|
+
# Client middleware (job enqueuing)
|
|
665
|
+
client_middleware do
|
|
666
|
+
enabled true
|
|
667
|
+
track_enqueue true
|
|
727
668
|
end
|
|
669
|
+
|
|
670
|
+
# Trace propagation (C17 hybrid: job gets NEW trace_id, links via e11y_parent_trace_id)
|
|
671
|
+
propagate_trace_context true
|
|
672
|
+
trace_context_keys ['e11y_parent_trace_id', 'e11y_span_id', 'e11y_sampled']
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
# ========================================
|
|
676
|
+
# ActiveJob
|
|
677
|
+
# ========================================
|
|
678
|
+
config.active_job do
|
|
679
|
+
enabled true # Set to false to disable ActiveJob integration
|
|
680
|
+
|
|
681
|
+
track_enqueue true
|
|
682
|
+
track_start true
|
|
683
|
+
track_complete true
|
|
684
|
+
track_failure true
|
|
685
|
+
|
|
686
|
+
# Trace propagation
|
|
687
|
+
propagate_trace_context true
|
|
728
688
|
end
|
|
729
689
|
end
|
|
730
690
|
```
|
|
@@ -734,19 +694,16 @@ end
|
|
|
734
694
|
```ruby
|
|
735
695
|
# Disable ASN but keep Sidekiq
|
|
736
696
|
E11y.configure do |config|
|
|
737
|
-
config.
|
|
738
|
-
config.
|
|
739
|
-
config.
|
|
697
|
+
config.rails_instrumentation_enabled = false
|
|
698
|
+
config.sidekiq_enabled = true
|
|
699
|
+
config.active_job_enabled = true
|
|
740
700
|
end
|
|
741
701
|
|
|
742
702
|
# Minimal setup: only Sidekiq
|
|
743
703
|
E11y.configure do |config|
|
|
744
|
-
config.
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
active_job { enabled false }
|
|
748
|
-
rack_middleware { enabled false }
|
|
749
|
-
end
|
|
704
|
+
config.rails_instrumentation_enabled = false
|
|
705
|
+
config.sidekiq_enabled = true
|
|
706
|
+
config.active_job_enabled = false
|
|
750
707
|
end
|
|
751
708
|
```
|
|
752
709
|
|
|
@@ -860,7 +817,7 @@ end
|
|
|
860
817
|
```ruby
|
|
861
818
|
# config/initializers/e11y.rb
|
|
862
819
|
E11y.configure do |config|
|
|
863
|
-
config.
|
|
820
|
+
config.rails_instrumentation do
|
|
864
821
|
# Disable built-in events
|
|
865
822
|
use_built_in_events false
|
|
866
823
|
|
|
@@ -877,6 +834,8 @@ end
|
|
|
877
834
|
|
|
878
835
|
## 5. Sidekiq Integration
|
|
879
836
|
|
|
837
|
+
**Implementation Note:** Sidekiq middleware emits `Events::Rails::Job::Enqueued`, `Started`, `Completed`, `Failed` for **raw Sidekiq jobs only** (`include Sidekiq::Worker`). When Sidekiq is the queue adapter for ActiveJob, jobs use `ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper`; we skip event emission in Sidekiq middleware to avoid double emission — ActiveJob events come from RailsInstrumentation (ASN).
|
|
838
|
+
|
|
880
839
|
### 5.1. Server Middleware (Job Execution)
|
|
881
840
|
|
|
882
841
|
```ruby
|
|
@@ -886,22 +845,25 @@ module E11y
|
|
|
886
845
|
module Sidekiq
|
|
887
846
|
class ServerMiddleware
|
|
888
847
|
def call(worker, job, queue)
|
|
889
|
-
#
|
|
890
|
-
|
|
848
|
+
# C17 Hybrid: Job gets NEW trace_id, parent_trace_id links to enqueuing request
|
|
849
|
+
parent_trace_id = job['e11y_parent_trace_id']
|
|
850
|
+
trace_id = E11y::TraceContext.generate_id # NEW trace per job
|
|
891
851
|
parent_span_id = job['e11y_span_id']
|
|
892
852
|
|
|
893
|
-
# Restore trace context
|
|
894
853
|
E11y::Current.set(
|
|
895
854
|
trace_id: trace_id,
|
|
855
|
+
parent_trace_id: parent_trace_id,
|
|
896
856
|
parent_span_id: parent_span_id,
|
|
897
857
|
job_id: job['jid'],
|
|
898
858
|
job_class: worker.class.name,
|
|
899
859
|
queue: queue
|
|
900
860
|
)
|
|
901
861
|
|
|
902
|
-
# Start
|
|
903
|
-
if E11y.config.
|
|
904
|
-
E11y
|
|
862
|
+
# Start request-scoped buffer (same as HTTP; config.ephemeral_buffer_enabled)
|
|
863
|
+
if E11y.config.ephemeral_buffer_enabled
|
|
864
|
+
limit = E11y.config.ephemeral_buffer_job_buffer_limit ||
|
|
865
|
+
E11y::Buffers::EphemeralBuffer::DEFAULT_BUFFER_LIMIT
|
|
866
|
+
E11y::EphemeralBuffer.initialize!(buffer_limit: limit)
|
|
905
867
|
end
|
|
906
868
|
|
|
907
869
|
# Track job start
|
|
@@ -926,8 +888,8 @@ module E11y
|
|
|
926
888
|
queue: queue
|
|
927
889
|
)
|
|
928
890
|
|
|
929
|
-
#
|
|
930
|
-
E11y::
|
|
891
|
+
# Discard buffer on success (same as HTTP)
|
|
892
|
+
E11y::EphemeralBuffer.discard if E11y.config.ephemeral_buffer_enabled
|
|
931
893
|
|
|
932
894
|
result
|
|
933
895
|
rescue => error
|
|
@@ -942,8 +904,8 @@ module E11y
|
|
|
942
904
|
backtrace: error.backtrace&.first(10)
|
|
943
905
|
)
|
|
944
906
|
|
|
945
|
-
# Flush
|
|
946
|
-
E11y::
|
|
907
|
+
# Flush buffer on error (includes debug events)
|
|
908
|
+
E11y::EphemeralBuffer.flush_on_error if E11y.config.ephemeral_buffer_enabled
|
|
947
909
|
|
|
948
910
|
raise
|
|
949
911
|
ensure
|
|
@@ -976,8 +938,8 @@ module E11y
|
|
|
976
938
|
module Sidekiq
|
|
977
939
|
class ClientMiddleware
|
|
978
940
|
def call(worker_class, job, queue, redis_pool)
|
|
979
|
-
# Propagate trace
|
|
980
|
-
job['
|
|
941
|
+
# C17 Hybrid: Propagate parent trace (job will create NEW trace_id)
|
|
942
|
+
job['e11y_parent_trace_id'] = E11y::Current.trace_id if E11y::Current.trace_id
|
|
981
943
|
job['e11y_span_id'] = E11y::TraceContext.generate_span_id
|
|
982
944
|
job['e11y_sampled'] = E11y::Current.sampled # Trace-consistent sampling
|
|
983
945
|
|
|
@@ -997,89 +959,22 @@ module E11y
|
|
|
997
959
|
end
|
|
998
960
|
```
|
|
999
961
|
|
|
1000
|
-
### 5.3.
|
|
962
|
+
### 5.3. Buffer for Jobs
|
|
1001
963
|
|
|
1002
|
-
**Design Decision:**
|
|
964
|
+
**Design Decision:** Jobs reuse the same `EphemeralBuffer` as HTTP requests. Same semantics: buffer debug events, flush on error, discard on success. Optional job-specific config allows tuning for longer-running jobs.
|
|
1003
965
|
|
|
1004
966
|
```ruby
|
|
1005
967
|
# config/initializers/e11y.rb
|
|
1006
968
|
E11y.configure do |config|
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
use_job_buffer true
|
|
1010
|
-
|
|
1011
|
-
job_buffer do
|
|
1012
|
-
# Buffer debug events during job execution
|
|
1013
|
-
buffer_severities [:debug]
|
|
1014
|
-
|
|
1015
|
-
# Flush conditions
|
|
1016
|
-
flush_on do
|
|
1017
|
-
error true # Flush debug events if job fails
|
|
1018
|
-
success false # Discard debug events if job succeeds
|
|
1019
|
-
interval 5.seconds # Or flush every 5 seconds
|
|
1020
|
-
end
|
|
1021
|
-
|
|
1022
|
-
# Max buffer size per job
|
|
1023
|
-
max_events 1000
|
|
1024
|
-
end
|
|
1025
|
-
end
|
|
1026
|
-
end
|
|
1027
|
-
```
|
|
1028
|
-
|
|
1029
|
-
**How it works:**
|
|
1030
|
-
|
|
1031
|
-
```ruby
|
|
1032
|
-
class InvoiceGenerationWorker
|
|
1033
|
-
include Sidekiq::Worker
|
|
969
|
+
# Shared buffer for HTTP and jobs
|
|
970
|
+
config.ephemeral_buffer_enabled = true
|
|
1034
971
|
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
Events::Debug::FetchOrder.track(order_id: order_id)
|
|
1038
|
-
|
|
1039
|
-
order = Order.find(order_id)
|
|
1040
|
-
|
|
1041
|
-
Events::Debug::ValidateOrder.track(order_id: order_id, valid: order.valid?)
|
|
1042
|
-
|
|
1043
|
-
if order.invalid?
|
|
1044
|
-
# Job fails → debug events are flushed to adapters
|
|
1045
|
-
raise "Invalid order"
|
|
1046
|
-
end
|
|
1047
|
-
|
|
1048
|
-
# Job succeeds → debug events are discarded
|
|
1049
|
-
Events::InvoiceGenerated.track(order_id: order_id)
|
|
1050
|
-
end
|
|
972
|
+
# Optional: job-specific overrides (jobs can run longer → more debug events)
|
|
973
|
+
config.ephemeral_buffer_job_buffer_limit = 500 # nil = use default (100)
|
|
1051
974
|
end
|
|
1052
975
|
```
|
|
1053
976
|
|
|
1054
|
-
**
|
|
1055
|
-
|
|
1056
|
-
```mermaid
|
|
1057
|
-
sequenceDiagram
|
|
1058
|
-
participant Worker as Sidekiq Worker
|
|
1059
|
-
participant JobBuffer as Job Buffer
|
|
1060
|
-
participant MainBuffer as Main Buffer
|
|
1061
|
-
participant Adapters as Adapters
|
|
1062
|
-
|
|
1063
|
-
Worker->>JobBuffer: Start job buffer
|
|
1064
|
-
|
|
1065
|
-
Note over Worker: Job execution starts
|
|
1066
|
-
|
|
1067
|
-
Worker->>JobBuffer: Track debug event
|
|
1068
|
-
JobBuffer->>JobBuffer: Buffer (not flushed)
|
|
1069
|
-
|
|
1070
|
-
Worker->>MainBuffer: Track info event
|
|
1071
|
-
MainBuffer->>Adapters: Flush immediately (normal flow)
|
|
1072
|
-
|
|
1073
|
-
alt Job succeeds
|
|
1074
|
-
Worker->>JobBuffer: flush! (success)
|
|
1075
|
-
JobBuffer->>JobBuffer: Discard debug events
|
|
1076
|
-
Note over JobBuffer: Debug events never sent
|
|
1077
|
-
else Job fails
|
|
1078
|
-
Worker->>JobBuffer: flush_on_error!
|
|
1079
|
-
JobBuffer->>MainBuffer: Move debug events to main buffer
|
|
1080
|
-
MainBuffer->>Adapters: Flush all events (including debug)
|
|
1081
|
-
end
|
|
1082
|
-
```
|
|
977
|
+
**Rationale:** Single buffer implementation, single config. Jobs may need higher `job_buffer_limit` when they process many items and emit more debug events than a typical HTTP request.
|
|
1083
978
|
|
|
1084
979
|
---
|
|
1085
980
|
|
|
@@ -1100,21 +995,21 @@ sequenceDiagram
|
|
|
1100
995
|
ClientMW->>ClientMW: Extract trace_id from Current
|
|
1101
996
|
ClientMW->>Redis: Store job + trace metadata
|
|
1102
997
|
|
|
1103
|
-
Note over Redis: job['
|
|
998
|
+
Note over Redis: job['e11y_parent_trace_id'] = 'abc123'<br/>job['e11y_span_id'] = 'span002'<br/>job['e11y_sampled'] = true
|
|
1104
999
|
|
|
1105
1000
|
ClientMW->>E11y: Track Enqueued event
|
|
1106
1001
|
|
|
1107
1002
|
Note over ServerMW: Later... job dequeued
|
|
1108
1003
|
|
|
1109
1004
|
Redis->>ServerMW: Fetch job
|
|
1110
|
-
ServerMW->>ServerMW:
|
|
1005
|
+
ServerMW->>ServerMW: C17: new trace_id, parent_trace_id from job
|
|
1111
1006
|
ServerMW->>E11y: Restore Current context
|
|
1112
1007
|
|
|
1113
|
-
Note over E11y: Current.trace_id = 'abc123'<br/>Current.parent_span_id = 'span002'
|
|
1008
|
+
Note over E11y: Current.trace_id = NEW<br/>Current.parent_trace_id = 'abc123'<br/>Current.parent_span_id = 'span002'
|
|
1114
1009
|
|
|
1115
1010
|
ServerMW->>E11y: Track Started event
|
|
1116
1011
|
ServerMW->>Worker: perform
|
|
1117
|
-
Worker->>E11y: Track
|
|
1012
|
+
Worker->>E11y: Track events (linked via parent_trace_id!)
|
|
1118
1013
|
ServerMW->>E11y: Track Completed event
|
|
1119
1014
|
```
|
|
1120
1015
|
|
|
@@ -1122,6 +1017,13 @@ sequenceDiagram
|
|
|
1122
1017
|
|
|
1123
1018
|
## 6. ActiveJob Integration
|
|
1124
1019
|
|
|
1020
|
+
**Implementation Note:** Job lifecycle events (`Enqueued`, `Started`, `Completed`, `Failed`) come from **RailsInstrumentation** (ASN), not from ActiveJob callbacks. ActiveJob callbacks handle trace context propagation, request-scoped buffer, and SLO tracking only. This design:
|
|
1021
|
+
- Uses ASN as single source for all queue adapters (Sidekiq, Resque, Solid Queue, etc.)
|
|
1022
|
+
- Avoids duplicate emission when ActiveJob uses Sidekiq as adapter
|
|
1023
|
+
- Keeps callbacks focused on context/buffer/SLO
|
|
1024
|
+
|
|
1025
|
+
**perform.active_job routing:** When payload contains `exception`, RailsInstrumentation routes to `Events::Rails::Job::Failed` (with `error_class`, `error_message`); otherwise to `Completed`.
|
|
1026
|
+
|
|
1125
1027
|
### 6.1. Callbacks Integration
|
|
1126
1028
|
|
|
1127
1029
|
```ruby
|
|
@@ -1140,20 +1042,24 @@ module E11y
|
|
|
1140
1042
|
private
|
|
1141
1043
|
|
|
1142
1044
|
def e11y_track_job_execution
|
|
1143
|
-
#
|
|
1144
|
-
|
|
1045
|
+
# C17 Hybrid: Job gets NEW trace_id, parent_trace_id links to enqueuer
|
|
1046
|
+
parent_trace_id = job_metadata['e11y_parent_trace_id']
|
|
1047
|
+
trace_id = E11y::TraceContext.generate_id
|
|
1145
1048
|
parent_span_id = job_metadata['e11y_span_id']
|
|
1146
1049
|
|
|
1147
1050
|
E11y::Current.set(
|
|
1148
1051
|
trace_id: trace_id,
|
|
1052
|
+
parent_trace_id: parent_trace_id,
|
|
1149
1053
|
parent_span_id: parent_span_id,
|
|
1150
1054
|
job_id: job_id,
|
|
1151
1055
|
job_class: self.class.name
|
|
1152
1056
|
)
|
|
1153
1057
|
|
|
1154
|
-
# Start
|
|
1155
|
-
if E11y.config.
|
|
1156
|
-
E11y
|
|
1058
|
+
# Start request-scoped buffer (same as HTTP; config.ephemeral_buffer_enabled)
|
|
1059
|
+
if E11y.config.ephemeral_buffer_enabled
|
|
1060
|
+
limit = E11y.config.ephemeral_buffer_job_buffer_limit ||
|
|
1061
|
+
E11y::Buffers::EphemeralBuffer::DEFAULT_BUFFER_LIMIT
|
|
1062
|
+
E11y::EphemeralBuffer.initialize!(buffer_limit: limit)
|
|
1157
1063
|
end
|
|
1158
1064
|
|
|
1159
1065
|
Events::Rails::Job::Started.track(
|
|
@@ -1174,8 +1080,8 @@ module E11y
|
|
|
1174
1080
|
duration: (Time.now - start_time) * 1000
|
|
1175
1081
|
)
|
|
1176
1082
|
|
|
1177
|
-
#
|
|
1178
|
-
E11y::
|
|
1083
|
+
# Discard buffer on success (same as HTTP)
|
|
1084
|
+
E11y::EphemeralBuffer.discard if E11y.config.ephemeral_buffer_enabled
|
|
1179
1085
|
rescue => error
|
|
1180
1086
|
Events::Rails::Job::Failed.track(
|
|
1181
1087
|
job_class: self.class.name,
|
|
@@ -1185,8 +1091,8 @@ module E11y
|
|
|
1185
1091
|
error_message: error.message
|
|
1186
1092
|
)
|
|
1187
1093
|
|
|
1188
|
-
# Flush
|
|
1189
|
-
E11y::
|
|
1094
|
+
# Flush buffer on error (includes debug events)
|
|
1095
|
+
E11y::EphemeralBuffer.flush_on_error if E11y.config.ephemeral_buffer_enabled
|
|
1190
1096
|
|
|
1191
1097
|
raise
|
|
1192
1098
|
ensure
|
|
@@ -1195,8 +1101,8 @@ module E11y
|
|
|
1195
1101
|
end
|
|
1196
1102
|
|
|
1197
1103
|
def e11y_track_job_enqueued
|
|
1198
|
-
# Store trace
|
|
1199
|
-
job_metadata['
|
|
1104
|
+
# C17 Hybrid: Store parent trace (job will create NEW trace_id)
|
|
1105
|
+
job_metadata['e11y_parent_trace_id'] = E11y::Current.trace_id if E11y::Current.trace_id
|
|
1200
1106
|
job_metadata['e11y_span_id'] = E11y::TraceContext.generate_span_id
|
|
1201
1107
|
job_metadata['e11y_sampled'] = E11y::Current.sampled
|
|
1202
1108
|
|
|
@@ -1235,7 +1141,7 @@ module E11y
|
|
|
1235
1141
|
module Logger
|
|
1236
1142
|
class Bridge
|
|
1237
1143
|
def self.setup!
|
|
1238
|
-
return unless E11y.config.
|
|
1144
|
+
return unless E11y.config.logger_bridge_enabled
|
|
1239
1145
|
|
|
1240
1146
|
# Replace Rails.logger
|
|
1241
1147
|
Rails.logger = Bridge.new(Rails.logger)
|
|
@@ -1304,17 +1210,10 @@ module E11y
|
|
|
1304
1210
|
# Extract message
|
|
1305
1211
|
msg = message || (block_given? ? block.call : nil)
|
|
1306
1212
|
|
|
1307
|
-
# Track via E11y
|
|
1308
|
-
|
|
1309
|
-
severity: severity,
|
|
1310
|
-
message: msg.to_s,
|
|
1311
|
-
caller_location: extract_caller_location
|
|
1312
|
-
)
|
|
1213
|
+
# Track via E11y (filtered by track_severities, ignore_patterns)
|
|
1214
|
+
event_class_for_severity(severity).track(message: msg.to_s, caller_location: extract_caller_location)
|
|
1313
1215
|
|
|
1314
|
-
#
|
|
1315
|
-
if @original_logger && E11y.config.logger_bridge.dual_logging
|
|
1316
|
-
@original_logger.public_send(severity, msg)
|
|
1317
|
-
end
|
|
1216
|
+
# Always delegate to original logger (SimpleDelegator super)
|
|
1318
1217
|
end
|
|
1319
1218
|
|
|
1320
1219
|
def extract_caller_location
|
|
@@ -1330,50 +1229,39 @@ module E11y
|
|
|
1330
1229
|
end
|
|
1331
1230
|
```
|
|
1332
1231
|
|
|
1333
|
-
### 7.2.
|
|
1232
|
+
### 7.2. Configuration
|
|
1334
1233
|
|
|
1335
1234
|
```ruby
|
|
1336
1235
|
# config/initializers/e11y.rb
|
|
1337
1236
|
E11y.configure do |config|
|
|
1338
|
-
config.
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
/Started GET/,
|
|
1350
|
-
/Completed \d+ OK/,
|
|
1351
|
-
/CACHE/
|
|
1352
|
-
]
|
|
1353
|
-
|
|
1354
|
-
# Sample high-volume logs
|
|
1355
|
-
sample_rate 0.1 # 10% of logs
|
|
1356
|
-
|
|
1357
|
-
# Enrich with Rails context
|
|
1358
|
-
enrich_with_context true
|
|
1359
|
-
context_fields [:controller, :action, :request_id]
|
|
1360
|
-
end
|
|
1237
|
+
config.logger_bridge_enabled = true
|
|
1238
|
+
|
|
1239
|
+
# Which severities to track (nil = all)
|
|
1240
|
+
config.logger_bridge_track_severities = [:info, :warn, :error, :fatal]
|
|
1241
|
+
|
|
1242
|
+
# Skip noisy log messages (regex or string)
|
|
1243
|
+
config.logger_bridge_ignore_patterns = [
|
|
1244
|
+
/Started GET/,
|
|
1245
|
+
/Completed \d+ OK/,
|
|
1246
|
+
/CACHE/
|
|
1247
|
+
]
|
|
1361
1248
|
end
|
|
1362
1249
|
```
|
|
1363
1250
|
|
|
1251
|
+
**Context:** Events are enriched with `trace_id`, `request_id`, `span_id`, `user_id` from `E11y::Current` via `Event::Base.build_context` — no separate config needed.
|
|
1252
|
+
|
|
1364
1253
|
---
|
|
1365
1254
|
|
|
1366
1255
|
## 8. Middleware Integration
|
|
1367
1256
|
|
|
1368
|
-
### 8.0.
|
|
1257
|
+
### 8.0. Buffer Types (Summary)
|
|
1369
1258
|
|
|
1370
|
-
**E11y
|
|
1259
|
+
**E11y uses a single EphemeralBuffer for both HTTP and jobs:**
|
|
1371
1260
|
|
|
1372
|
-
| Buffer
|
|
1373
|
-
|
|
1374
|
-
| **
|
|
1375
|
-
| **
|
|
1376
|
-
| **Job Buffer** | Debug events in background jobs | Per-job, flush on error | Background job debugging |
|
|
1261
|
+
| Buffer | Purpose | Lifecycle | Config |
|
|
1262
|
+
|--------|---------|-----------|--------|
|
|
1263
|
+
| **Request Buffer** | Debug events (HTTP + jobs) | Per-request/job, flush on error, discard on success | `config.ephemeral_buffer_enabled`, `ephemeral_buffer_job_buffer_limit` |
|
|
1264
|
+
| **Main Buffer** | All events (info+) | Global, flush every 200ms | — |
|
|
1377
1265
|
|
|
1378
1266
|
**Diagram:**
|
|
1379
1267
|
|
|
@@ -1381,20 +1269,20 @@ end
|
|
|
1381
1269
|
graph TB
|
|
1382
1270
|
subgraph "HTTP Request"
|
|
1383
1271
|
HTTPEvent[Event tracked] --> Decision1{Severity?}
|
|
1384
|
-
Decision1 -->|:debug|
|
|
1272
|
+
Decision1 -->|:debug| [Request Buffer]
|
|
1385
1273
|
Decision1 -->|:info+| MainBuffer[Main Buffer]
|
|
1386
1274
|
|
|
1387
|
-
|
|
1275
|
+
--> OnError1{Request failed?}
|
|
1388
1276
|
OnError1 -->|Yes| MainBuffer
|
|
1389
1277
|
OnError1 -->|No| Discard1[Discard]
|
|
1390
1278
|
end
|
|
1391
1279
|
|
|
1392
1280
|
subgraph "Background Job"
|
|
1393
1281
|
JobEvent[Event tracked] --> Decision2{Severity?}
|
|
1394
|
-
Decision2 -->|:debug|
|
|
1282
|
+
Decision2 -->|:debug|
|
|
1395
1283
|
Decision2 -->|:info+| MainBuffer2[Main Buffer]
|
|
1396
1284
|
|
|
1397
|
-
|
|
1285
|
+
--> OnError2{Job failed?}
|
|
1398
1286
|
OnError2 -->|Yes| MainBuffer2
|
|
1399
1287
|
OnError2 -->|No| Discard2[Discard]
|
|
1400
1288
|
end
|
|
@@ -1405,8 +1293,7 @@ graph TB
|
|
|
1405
1293
|
Interval --> Adapters[Flush to Adapters]
|
|
1406
1294
|
end
|
|
1407
1295
|
|
|
1408
|
-
style
|
|
1409
|
-
style JobBuffer fill:#d4edda
|
|
1296
|
+
style fill:#fff3cd
|
|
1410
1297
|
style MainBuffer fill:#d1ecf1
|
|
1411
1298
|
style MainBuffer2 fill:#d1ecf1
|
|
1412
1299
|
```
|
|
@@ -1415,16 +1302,9 @@ graph TB
|
|
|
1415
1302
|
|
|
1416
1303
|
```ruby
|
|
1417
1304
|
E11y.configure do |config|
|
|
1418
|
-
#
|
|
1419
|
-
config.
|
|
1420
|
-
config.
|
|
1421
|
-
|
|
1422
|
-
# Request-scoped buffer (HTTP only)
|
|
1423
|
-
config.instruments.rack_middleware.use_request_buffer = true
|
|
1424
|
-
|
|
1425
|
-
# Job-scoped buffer (Sidekiq + ActiveJob)
|
|
1426
|
-
config.instruments.sidekiq.use_job_buffer = true
|
|
1427
|
-
config.instruments.active_job.use_job_buffer = true
|
|
1305
|
+
# Request-scoped buffer (shared for HTTP and jobs)
|
|
1306
|
+
config.ephemeral_buffer_enabled = true
|
|
1307
|
+
config.ephemeral_buffer_job_buffer_limit = 500 # Optional: higher limit for jobs (nil = default 100)
|
|
1428
1308
|
end
|
|
1429
1309
|
```
|
|
1430
1310
|
|
|
@@ -1458,12 +1338,8 @@ module E11y
|
|
|
1458
1338
|
user_agent: request.user_agent
|
|
1459
1339
|
)
|
|
1460
1340
|
|
|
1461
|
-
# Start request-scoped buffer (for debug events)
|
|
1462
|
-
|
|
1463
|
-
# Jobs have their own JobBuffer (see Sidekiq/ActiveJob sections)
|
|
1464
|
-
if E11y.config.instruments.rack_middleware.use_request_buffer
|
|
1465
|
-
E11y::RequestBuffer.start!
|
|
1466
|
-
end
|
|
1341
|
+
# Start request-scoped buffer (for debug events; shared with jobs)
|
|
1342
|
+
E11y::EphemeralBuffer.initialize! if E11y.config.ephemeral_buffer&.enabled
|
|
1467
1343
|
|
|
1468
1344
|
# Track request start
|
|
1469
1345
|
start_time = Time.now
|
|
@@ -1501,17 +1377,13 @@ module E11y
|
|
|
1501
1377
|
error_message: error.message
|
|
1502
1378
|
)
|
|
1503
1379
|
|
|
1504
|
-
# Flush
|
|
1505
|
-
if E11y.config.
|
|
1506
|
-
E11y::RequestBuffer.flush_on_error!
|
|
1507
|
-
end
|
|
1380
|
+
# Flush buffer on error (includes debug events)
|
|
1381
|
+
E11y::EphemeralBuffer.flush_on_error if E11y.config.ephemeral_buffer&.enabled
|
|
1508
1382
|
|
|
1509
1383
|
raise
|
|
1510
1384
|
ensure
|
|
1511
|
-
#
|
|
1512
|
-
if E11y.config.
|
|
1513
|
-
E11y::RequestBuffer.flush!
|
|
1514
|
-
end
|
|
1385
|
+
# Discard buffer on success (not on error; already flushed in rescue)
|
|
1386
|
+
E11y::EphemeralBuffer.discard if !$ERROR_INFO && E11y.config.ephemeral_buffer&.enabled
|
|
1515
1387
|
|
|
1516
1388
|
# Reset context
|
|
1517
1389
|
Current.reset
|
|
@@ -1563,10 +1435,10 @@ module E11y
|
|
|
1563
1435
|
# E11y.stats
|
|
1564
1436
|
def E11y.stats
|
|
1565
1437
|
{
|
|
1566
|
-
events_tracked: Registry.
|
|
1438
|
+
events_tracked: Registry.event_classes.sum { |e| e.track_count },
|
|
1567
1439
|
events_in_buffer: Buffer.size,
|
|
1568
|
-
adapters:
|
|
1569
|
-
{ name:
|
|
1440
|
+
adapters: config.adapters.map { |name, a|
|
|
1441
|
+
{ name: name, healthy: a.respond_to?(:healthy?) ? a.healthy? : true }
|
|
1570
1442
|
},
|
|
1571
1443
|
rate_limiter: {
|
|
1572
1444
|
current_rate: RateLimiter.current_rate,
|
|
@@ -1588,17 +1460,17 @@ module E11y
|
|
|
1588
1460
|
|
|
1589
1461
|
# E11y.events
|
|
1590
1462
|
def E11y.events
|
|
1591
|
-
Registry.
|
|
1463
|
+
Registry.event_classes.map(&:name).sort
|
|
1592
1464
|
end
|
|
1593
1465
|
|
|
1594
1466
|
# E11y.adapters
|
|
1595
1467
|
def E11y.adapters
|
|
1596
|
-
|
|
1468
|
+
config.adapters.map do |name, adapter|
|
|
1597
1469
|
{
|
|
1598
|
-
name:
|
|
1470
|
+
name: name,
|
|
1599
1471
|
class: adapter.class.name,
|
|
1600
|
-
healthy: adapter.healthy
|
|
1601
|
-
capabilities: adapter.capabilities
|
|
1472
|
+
healthy: adapter.respond_to?(:healthy?) ? adapter.healthy? : true,
|
|
1473
|
+
capabilities: adapter.respond_to?(:capabilities) ? adapter.capabilities : {}
|
|
1602
1474
|
}
|
|
1603
1475
|
end
|
|
1604
1476
|
end
|
|
@@ -1606,7 +1478,7 @@ module E11y
|
|
|
1606
1478
|
# E11y.reset!
|
|
1607
1479
|
def E11y.reset!
|
|
1608
1480
|
Buffer.clear!
|
|
1609
|
-
|
|
1481
|
+
.clear!
|
|
1610
1482
|
puts "✅ Buffers cleared"
|
|
1611
1483
|
end
|
|
1612
1484
|
end
|
|
@@ -1621,7 +1493,7 @@ module E11y
|
|
|
1621
1493
|
)
|
|
1622
1494
|
|
|
1623
1495
|
# Disable rate limiting in console
|
|
1624
|
-
config.
|
|
1496
|
+
config.rate_limiting_enabled = false
|
|
1625
1497
|
|
|
1626
1498
|
# Show all severities
|
|
1627
1499
|
config.severity_threshold = :debug
|
|
@@ -1772,7 +1644,7 @@ RSpec.describe OrdersController, type: :controller do
|
|
|
1772
1644
|
}.to have_enqueued_job(SendOrderEmailJob)
|
|
1773
1645
|
|
|
1774
1646
|
job = ActiveJob::Base.queue_adapter.enqueued_jobs.last
|
|
1775
|
-
expect(job[:args].first['
|
|
1647
|
+
expect(job[:args].first['e11y_parent_trace_id']).to be_present
|
|
1776
1648
|
end
|
|
1777
1649
|
end
|
|
1778
1650
|
end
|
|
@@ -1819,10 +1691,10 @@ end
|
|
|
1819
1691
|
|
|
1820
1692
|
```ruby
|
|
1821
1693
|
E11y.configure do |config|
|
|
1822
|
-
config.
|
|
1823
|
-
config.
|
|
1824
|
-
config.
|
|
1825
|
-
config.
|
|
1694
|
+
config.rails_instrumentation_enabled = false # Disable ASN
|
|
1695
|
+
config.sidekiq_enabled = true # Keep Sidekiq
|
|
1696
|
+
config.active_job_enabled = true # Keep ActiveJob
|
|
1697
|
+
config.logger_bridge_enabled = true # Keep logger bridge
|
|
1826
1698
|
end
|
|
1827
1699
|
```
|
|
1828
1700
|
|
|
@@ -1845,7 +1717,7 @@ end
|
|
|
1845
1717
|
**No!** It's configurable:
|
|
1846
1718
|
|
|
1847
1719
|
```ruby
|
|
1848
|
-
config.
|
|
1720
|
+
config.rails_instrumentation do
|
|
1849
1721
|
enabled false # Completely disable ASN integration
|
|
1850
1722
|
end
|
|
1851
1723
|
```
|
|
@@ -1853,26 +1725,24 @@ end
|
|
|
1853
1725
|
You can also filter which ASN events to track:
|
|
1854
1726
|
|
|
1855
1727
|
```ruby
|
|
1856
|
-
config.
|
|
1728
|
+
config.rails_instrumentation do
|
|
1857
1729
|
track_patterns ['sql.active_record', 'process_action.*']
|
|
1858
|
-
|
|
1730
|
+
ignore_events ['render_partial.*', 'SCHEMA']
|
|
1859
1731
|
end
|
|
1860
1732
|
```
|
|
1861
1733
|
|
|
1862
1734
|
### Q4: Does request-scoped buffer work for Sidekiq/ActiveJob?
|
|
1863
1735
|
|
|
1864
|
-
**
|
|
1736
|
+
**Yes. HTTP and jobs share the same EphemeralBuffer.**
|
|
1865
1737
|
|
|
1866
|
-
- **Request Buffer** → HTTP requests
|
|
1867
|
-
- **Job Buffer** → Sidekiq + ActiveJob (separate buffer per job)
|
|
1738
|
+
- **Request Buffer** → HTTP requests and jobs (same buffer, same semantics)
|
|
1868
1739
|
- **Main Buffer** → Global buffer for all info+ events
|
|
1869
1740
|
|
|
1870
|
-
|
|
1741
|
+
Config:
|
|
1871
1742
|
|
|
1872
1743
|
```ruby
|
|
1873
|
-
config.
|
|
1874
|
-
config.
|
|
1875
|
-
config.instruments.active_job.use_job_buffer = true # ActiveJob
|
|
1744
|
+
config.ephemeral_buffer_enabled = true
|
|
1745
|
+
config.ephemeral_buffer_job_buffer_limit = 500 # Optional: higher limit for jobs (nil = default 100)
|
|
1876
1746
|
```
|
|
1877
1747
|
|
|
1878
1748
|
### Q5: How do I customize built-in Rails events?
|
|
@@ -1880,7 +1750,7 @@ config.instruments.active_job.use_job_buffer = true # ActiveJob
|
|
|
1880
1750
|
**Option A: Override with custom event class:**
|
|
1881
1751
|
|
|
1882
1752
|
```ruby
|
|
1883
|
-
config.
|
|
1753
|
+
config.rails_instrumentation do
|
|
1884
1754
|
custom_mappings do
|
|
1885
1755
|
map 'sql.active_record', to: MyApp::Events::CustomDatabaseQuery
|
|
1886
1756
|
end
|
|
@@ -1890,7 +1760,7 @@ end
|
|
|
1890
1760
|
**Option B: Disable built-in events entirely:**
|
|
1891
1761
|
|
|
1892
1762
|
```ruby
|
|
1893
|
-
config.
|
|
1763
|
+
config.rails_instrumentation do
|
|
1894
1764
|
use_built_in_events false # No automatic mapping
|
|
1895
1765
|
|
|
1896
1766
|
# Manually handle ASN events
|