e11y 0.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.
Files changed (157) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +4 -0
  3. data/.rubocop.yml +69 -0
  4. data/CHANGELOG.md +26 -0
  5. data/CODE_OF_CONDUCT.md +64 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +179 -0
  8. data/Rakefile +37 -0
  9. data/benchmarks/run_all.rb +33 -0
  10. data/config/README.md +83 -0
  11. data/config/loki-local-config.yaml +35 -0
  12. data/config/prometheus.yml +15 -0
  13. data/docker-compose.yml +78 -0
  14. data/docs/00-ICP-AND-TIMELINE.md +483 -0
  15. data/docs/01-SCALE-REQUIREMENTS.md +858 -0
  16. data/docs/ADR-001-architecture.md +2617 -0
  17. data/docs/ADR-002-metrics-yabeda.md +1395 -0
  18. data/docs/ADR-003-slo-observability.md +3337 -0
  19. data/docs/ADR-004-adapter-architecture.md +2385 -0
  20. data/docs/ADR-005-tracing-context.md +1372 -0
  21. data/docs/ADR-006-security-compliance.md +4143 -0
  22. data/docs/ADR-007-opentelemetry-integration.md +1385 -0
  23. data/docs/ADR-008-rails-integration.md +1911 -0
  24. data/docs/ADR-009-cost-optimization.md +2993 -0
  25. data/docs/ADR-010-developer-experience.md +2166 -0
  26. data/docs/ADR-011-testing-strategy.md +1836 -0
  27. data/docs/ADR-012-event-evolution.md +958 -0
  28. data/docs/ADR-013-reliability-error-handling.md +2750 -0
  29. data/docs/ADR-014-event-driven-slo.md +1533 -0
  30. data/docs/ADR-015-middleware-order.md +1061 -0
  31. data/docs/ADR-016-self-monitoring-slo.md +1234 -0
  32. data/docs/API-REFERENCE-L28.md +914 -0
  33. data/docs/COMPREHENSIVE-CONFIGURATION.md +2366 -0
  34. data/docs/IMPLEMENTATION_NOTES.md +2804 -0
  35. data/docs/IMPLEMENTATION_PLAN.md +1971 -0
  36. data/docs/IMPLEMENTATION_PLAN_ARCHITECTURE.md +586 -0
  37. data/docs/PLAN.md +148 -0
  38. data/docs/QUICK-START.md +934 -0
  39. data/docs/README.md +296 -0
  40. data/docs/design/00-memory-optimization.md +593 -0
  41. data/docs/guides/MIGRATION-L27-L28.md +692 -0
  42. data/docs/guides/PERFORMANCE-BENCHMARKS.md +434 -0
  43. data/docs/guides/README.md +44 -0
  44. data/docs/prd/01-overview-vision.md +440 -0
  45. data/docs/use_cases/README.md +119 -0
  46. data/docs/use_cases/UC-001-request-scoped-debug-buffering.md +813 -0
  47. data/docs/use_cases/UC-002-business-event-tracking.md +1953 -0
  48. data/docs/use_cases/UC-003-pattern-based-metrics.md +1627 -0
  49. data/docs/use_cases/UC-004-zero-config-slo-tracking.md +728 -0
  50. data/docs/use_cases/UC-005-sentry-integration.md +759 -0
  51. data/docs/use_cases/UC-006-trace-context-management.md +905 -0
  52. data/docs/use_cases/UC-007-pii-filtering.md +2648 -0
  53. data/docs/use_cases/UC-008-opentelemetry-integration.md +1153 -0
  54. data/docs/use_cases/UC-009-multi-service-tracing.md +1043 -0
  55. data/docs/use_cases/UC-010-background-job-tracking.md +1018 -0
  56. data/docs/use_cases/UC-011-rate-limiting.md +1906 -0
  57. data/docs/use_cases/UC-012-audit-trail.md +2301 -0
  58. data/docs/use_cases/UC-013-high-cardinality-protection.md +2127 -0
  59. data/docs/use_cases/UC-014-adaptive-sampling.md +1940 -0
  60. data/docs/use_cases/UC-015-cost-optimization.md +735 -0
  61. data/docs/use_cases/UC-016-rails-logger-migration.md +785 -0
  62. data/docs/use_cases/UC-017-local-development.md +867 -0
  63. data/docs/use_cases/UC-018-testing-events.md +1081 -0
  64. data/docs/use_cases/UC-019-tiered-storage-migration.md +562 -0
  65. data/docs/use_cases/UC-020-event-versioning.md +708 -0
  66. data/docs/use_cases/UC-021-error-handling-retry-dlq.md +956 -0
  67. data/docs/use_cases/UC-022-event-registry.md +648 -0
  68. data/docs/use_cases/backlog.md +226 -0
  69. data/e11y.gemspec +76 -0
  70. data/lib/e11y/adapters/adaptive_batcher.rb +207 -0
  71. data/lib/e11y/adapters/audit_encrypted.rb +239 -0
  72. data/lib/e11y/adapters/base.rb +580 -0
  73. data/lib/e11y/adapters/file.rb +224 -0
  74. data/lib/e11y/adapters/in_memory.rb +216 -0
  75. data/lib/e11y/adapters/loki.rb +333 -0
  76. data/lib/e11y/adapters/otel_logs.rb +203 -0
  77. data/lib/e11y/adapters/registry.rb +141 -0
  78. data/lib/e11y/adapters/sentry.rb +230 -0
  79. data/lib/e11y/adapters/stdout.rb +108 -0
  80. data/lib/e11y/adapters/yabeda.rb +370 -0
  81. data/lib/e11y/buffers/adaptive_buffer.rb +339 -0
  82. data/lib/e11y/buffers/base_buffer.rb +40 -0
  83. data/lib/e11y/buffers/request_scoped_buffer.rb +246 -0
  84. data/lib/e11y/buffers/ring_buffer.rb +267 -0
  85. data/lib/e11y/buffers.rb +14 -0
  86. data/lib/e11y/console.rb +122 -0
  87. data/lib/e11y/current.rb +48 -0
  88. data/lib/e11y/event/base.rb +894 -0
  89. data/lib/e11y/event/value_sampling_config.rb +84 -0
  90. data/lib/e11y/events/base_audit_event.rb +43 -0
  91. data/lib/e11y/events/base_payment_event.rb +33 -0
  92. data/lib/e11y/events/rails/cache/delete.rb +21 -0
  93. data/lib/e11y/events/rails/cache/read.rb +23 -0
  94. data/lib/e11y/events/rails/cache/write.rb +22 -0
  95. data/lib/e11y/events/rails/database/query.rb +45 -0
  96. data/lib/e11y/events/rails/http/redirect.rb +21 -0
  97. data/lib/e11y/events/rails/http/request.rb +26 -0
  98. data/lib/e11y/events/rails/http/send_file.rb +21 -0
  99. data/lib/e11y/events/rails/http/start_processing.rb +26 -0
  100. data/lib/e11y/events/rails/job/completed.rb +22 -0
  101. data/lib/e11y/events/rails/job/enqueued.rb +22 -0
  102. data/lib/e11y/events/rails/job/failed.rb +22 -0
  103. data/lib/e11y/events/rails/job/scheduled.rb +23 -0
  104. data/lib/e11y/events/rails/job/started.rb +22 -0
  105. data/lib/e11y/events/rails/log.rb +56 -0
  106. data/lib/e11y/events/rails/view/render.rb +23 -0
  107. data/lib/e11y/events.rb +18 -0
  108. data/lib/e11y/instruments/active_job.rb +201 -0
  109. data/lib/e11y/instruments/rails_instrumentation.rb +141 -0
  110. data/lib/e11y/instruments/sidekiq.rb +175 -0
  111. data/lib/e11y/logger/bridge.rb +205 -0
  112. data/lib/e11y/metrics/cardinality_protection.rb +172 -0
  113. data/lib/e11y/metrics/cardinality_tracker.rb +134 -0
  114. data/lib/e11y/metrics/registry.rb +234 -0
  115. data/lib/e11y/metrics/relabeling.rb +226 -0
  116. data/lib/e11y/metrics.rb +102 -0
  117. data/lib/e11y/middleware/audit_signing.rb +174 -0
  118. data/lib/e11y/middleware/base.rb +140 -0
  119. data/lib/e11y/middleware/event_slo.rb +167 -0
  120. data/lib/e11y/middleware/pii_filter.rb +266 -0
  121. data/lib/e11y/middleware/pii_filtering.rb +280 -0
  122. data/lib/e11y/middleware/rate_limiting.rb +214 -0
  123. data/lib/e11y/middleware/request.rb +163 -0
  124. data/lib/e11y/middleware/routing.rb +157 -0
  125. data/lib/e11y/middleware/sampling.rb +254 -0
  126. data/lib/e11y/middleware/slo.rb +168 -0
  127. data/lib/e11y/middleware/trace_context.rb +131 -0
  128. data/lib/e11y/middleware/validation.rb +118 -0
  129. data/lib/e11y/middleware/versioning.rb +132 -0
  130. data/lib/e11y/middleware.rb +12 -0
  131. data/lib/e11y/pii/patterns.rb +90 -0
  132. data/lib/e11y/pii.rb +13 -0
  133. data/lib/e11y/pipeline/builder.rb +155 -0
  134. data/lib/e11y/pipeline/zone_validator.rb +110 -0
  135. data/lib/e11y/pipeline.rb +12 -0
  136. data/lib/e11y/presets/audit_event.rb +65 -0
  137. data/lib/e11y/presets/debug_event.rb +34 -0
  138. data/lib/e11y/presets/high_value_event.rb +51 -0
  139. data/lib/e11y/presets.rb +19 -0
  140. data/lib/e11y/railtie.rb +138 -0
  141. data/lib/e11y/reliability/circuit_breaker.rb +216 -0
  142. data/lib/e11y/reliability/dlq/file_storage.rb +277 -0
  143. data/lib/e11y/reliability/dlq/filter.rb +117 -0
  144. data/lib/e11y/reliability/retry_handler.rb +207 -0
  145. data/lib/e11y/reliability/retry_rate_limiter.rb +117 -0
  146. data/lib/e11y/sampling/error_spike_detector.rb +225 -0
  147. data/lib/e11y/sampling/load_monitor.rb +161 -0
  148. data/lib/e11y/sampling/stratified_tracker.rb +92 -0
  149. data/lib/e11y/sampling/value_extractor.rb +82 -0
  150. data/lib/e11y/self_monitoring/buffer_monitor.rb +79 -0
  151. data/lib/e11y/self_monitoring/performance_monitor.rb +97 -0
  152. data/lib/e11y/self_monitoring/reliability_monitor.rb +146 -0
  153. data/lib/e11y/slo/event_driven.rb +150 -0
  154. data/lib/e11y/slo/tracker.rb +119 -0
  155. data/lib/e11y/version.rb +9 -0
  156. data/lib/e11y.rb +283 -0
  157. metadata +452 -0
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "e11y/metrics"
4
+
5
+ module E11y
6
+ module SLO
7
+ # Event-Driven SLO for business logic reliability (ADR-014).
8
+ #
9
+ # Provides DSL for Event classes to opt-in to SLO tracking, auto-calculate
10
+ # `slo_status` from payload, and emit metrics for custom business SLO.
11
+ #
12
+ # **Key Features:**
13
+ # - Explicit opt-in via `slo { enabled true }` in Event class
14
+ # - Auto-calculation of `slo_status` from payload (e.g., status == 'completed' → 'success')
15
+ # - Explicit override: `track(status: 'completed', slo_status: 'failure')`
16
+ # - Metrics export: `event_result_total{slo_status="success|failure"}`
17
+ # - Custom SLO configuration in `slo.yml` (optional)
18
+ #
19
+ # **ADR References:**
20
+ # - ADR-014 §3 (Event SLO DSL)
21
+ # - ADR-014 §4 (SLO Status Calculation)
22
+ # - ADR-014 §6 (Metrics Export)
23
+ #
24
+ # **Use Case:** UC-014 (Event-Driven SLO)
25
+ #
26
+ # @example Event with SLO enabled
27
+ # module Events
28
+ # class PaymentProcessed < E11y::Event::Base
29
+ # schema do
30
+ # required(:payment_id).filled(:string)
31
+ # required(:status).filled(:string)
32
+ # optional(:slo_status).filled(:string) # Explicit override
33
+ # end
34
+ #
35
+ # slo do
36
+ # enabled true
37
+ # slo_status_from do |payload|
38
+ # return payload[:slo_status] if payload[:slo_status]
39
+ # case payload[:status]
40
+ # when 'completed' then 'success'
41
+ # when 'failed' then 'failure'
42
+ # else nil # Not counted in SLO
43
+ # end
44
+ # end
45
+ # end
46
+ # end
47
+ # end
48
+ #
49
+ # @example Tracking with auto-calculated slo_status
50
+ # Events::PaymentProcessed.track(
51
+ # payment_id: 'p123',
52
+ # status: 'completed' # → slo_status = 'success'
53
+ # )
54
+ #
55
+ # @example Tracking with explicit override
56
+ # Events::PaymentProcessed.track(
57
+ # payment_id: 'p456',
58
+ # status: 'completed',
59
+ # slo_status: 'failure' # Explicit override (e.g., fraud detected)
60
+ # )
61
+ #
62
+ # @see ADR-014 for complete architecture
63
+ module EventDriven
64
+ # SLO configuration for an Event class.
65
+ class SLOConfig
66
+ attr_reader :slo_status_proc, :contributes_to_value, :group_by_field
67
+
68
+ def initialize
69
+ @enabled = false
70
+ @slo_status_proc = nil
71
+ @contributes_to_value = nil
72
+ @group_by_field = nil
73
+ end
74
+
75
+ # DSL method: Enable or disable SLO tracking.
76
+ #
77
+ # @param value [Boolean] true to enable, false to disable
78
+ def enabled(value = nil)
79
+ return @enabled if value.nil?
80
+
81
+ @enabled = value
82
+ end
83
+
84
+ # Check if SLO is enabled.
85
+ #
86
+ # @return [Boolean]
87
+ def enabled?
88
+ @enabled
89
+ end
90
+
91
+ # DSL method: Define how to calculate slo_status from payload.
92
+ #
93
+ # @yieldparam payload [Hash] Event payload
94
+ # @yieldreturn [String, nil] 'success', 'failure', or nil (not counted)
95
+ def slo_status_from(&block)
96
+ @slo_status_proc = block
97
+ end
98
+
99
+ # DSL method: Define which custom SLO this event contributes to.
100
+ #
101
+ # @param slo_name [String] Name of custom SLO (from slo.yml)
102
+ def contributes_to(slo_name = nil)
103
+ return @contributes_to_value if slo_name.nil?
104
+
105
+ @contributes_to_value = slo_name
106
+ end
107
+
108
+ # DSL method: Group SLO metrics by a specific field.
109
+ #
110
+ # @param field [Symbol] Field name to group by (e.g., :payment_method)
111
+ def group_by(field = nil)
112
+ return @group_by_field if field.nil?
113
+
114
+ @group_by_field = field
115
+ end
116
+ end
117
+
118
+ # DSL methods for Event classes (extend with this module).
119
+ module DSL
120
+ # DSL method: Configure SLO for this Event class.
121
+ #
122
+ # @yieldparam config [SLOConfig] SLO configuration object
123
+ # @return [void]
124
+ #
125
+ # @example Enable SLO
126
+ # slo do
127
+ # enabled true
128
+ # slo_status_from { |payload| payload[:status] == 'success' ? 'success' : 'failure' }
129
+ # end
130
+ #
131
+ # @example Disable SLO (explicit)
132
+ # slo do
133
+ # enabled false
134
+ # end
135
+ def slo(&)
136
+ @slo_config ||= SLOConfig.new
137
+ @slo_config.instance_eval(&) if block_given?
138
+ @slo_config
139
+ end
140
+
141
+ # Get SLO configuration for this Event class.
142
+ #
143
+ # @return [SLOConfig, nil] SLO config or nil if not defined
144
+ def slo_config
145
+ @slo_config
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "e11y/metrics"
4
+
5
+ module E11y
6
+ module SLO
7
+ # Zero-Config SLO Tracker for HTTP requests and background jobs.
8
+ #
9
+ # Automatically tracks Service Level Indicators (SLIs):
10
+ # - HTTP request success rate (availability)
11
+ # - HTTP request latency (p95, p99)
12
+ # - Background job success rate
13
+ # - Background job duration
14
+ #
15
+ # @see UC-004 (Zero-Config SLO Tracking)
16
+ # @see ADR-003 §3 (Multi-Level SLO Strategy)
17
+ #
18
+ # @example Enable SLO tracking
19
+ # E11y.configure do |config|
20
+ # config.slo_tracking.enabled = true
21
+ # end
22
+ #
23
+ # @example Track HTTP request
24
+ # E11y::SLO::Tracker.track_http_request(
25
+ # controller: 'OrdersController',
26
+ # action: 'create',
27
+ # status: 200,
28
+ # duration_ms: 42.5
29
+ # )
30
+ #
31
+ # @note C11 Resolution (Sampling Correction): Not yet implemented.
32
+ # Requires Phase 2.8 (Stratified Sampling) for accurate SLO with sampling.
33
+ module Tracker
34
+ class << self
35
+ # Track HTTP request for SLO metrics.
36
+ #
37
+ # @param controller [String] Controller name
38
+ # @param action [String] Action name
39
+ # @param status [Integer] HTTP status code
40
+ # @param duration_ms [Numeric] Request duration in milliseconds
41
+ # @return [void]
42
+ def track_http_request(controller:, action:, status:, duration_ms:)
43
+ return unless enabled?
44
+
45
+ labels = {
46
+ controller: controller,
47
+ action: action,
48
+ status: normalize_status(status)
49
+ }
50
+
51
+ # Track request count
52
+ E11y::Metrics.increment(:slo_http_requests_total, labels)
53
+
54
+ # Track request duration
55
+ E11y::Metrics.histogram(
56
+ :slo_http_request_duration_seconds,
57
+ duration_ms / 1000.0,
58
+ labels.except(:status),
59
+ buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]
60
+ )
61
+ end
62
+
63
+ # Track background job for SLO metrics.
64
+ #
65
+ # @param job_class [String] Job class name
66
+ # @param status [Symbol] Job status (:success or :failed)
67
+ # @param duration_ms [Numeric] Job duration in milliseconds
68
+ # @param queue [String, nil] Queue name (optional)
69
+ # @return [void]
70
+ def track_background_job(job_class:, status:, duration_ms:, queue: nil)
71
+ return unless enabled?
72
+
73
+ labels = {
74
+ job_class: job_class,
75
+ status: status.to_s
76
+ }
77
+ labels[:queue] = queue if queue
78
+
79
+ # Track job count
80
+ E11y::Metrics.increment(:slo_background_jobs_total, labels)
81
+
82
+ # Track job duration (only for successful jobs)
83
+ return unless status == :success
84
+
85
+ E11y::Metrics.histogram(
86
+ :slo_background_job_duration_seconds,
87
+ duration_ms / 1000.0,
88
+ labels.except(:status),
89
+ buckets: [0.1, 0.5, 1, 5, 10, 30, 60, 300, 600]
90
+ )
91
+ end
92
+
93
+ # Check if SLO tracking is enabled.
94
+ #
95
+ # @return [Boolean] true if enabled
96
+ def enabled?
97
+ E11y.config.respond_to?(:slo_tracking) && E11y.config.slo_tracking&.enabled
98
+ end
99
+
100
+ # Normalize HTTP status code to category (2xx, 3xx, 4xx, 5xx).
101
+ #
102
+ # @param status [Integer] HTTP status code
103
+ # @return [String] Status category
104
+ # @api private
105
+ def normalize_status(status)
106
+ case status
107
+ when 200..299 then "2xx"
108
+ when 300..399 then "3xx"
109
+ when 400..499 then "4xx"
110
+ when 500..599 then "5xx"
111
+ else "unknown"
112
+ end
113
+ end
114
+
115
+ private :normalize_status
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module E11y
4
+ # Semantic versioning: MAJOR.MINOR.PATCH
5
+ # - MAJOR: Breaking changes (incompatible API changes)
6
+ # - MINOR: New features (backwards-compatible)
7
+ # - PATCH: Bug fixes (backwards-compatible)
8
+ VERSION = "0.1.0"
9
+ end
data/lib/e11y.rb ADDED
@@ -0,0 +1,283 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "zeitwerk"
4
+
5
+ # Zeitwerk autoloader setup
6
+ loader = Zeitwerk::Loader.for_gem
7
+ # Configure inflector for acronyms
8
+ loader.inflector.inflect(
9
+ "pii" => "PII",
10
+ "pii_filter" => "PIIFilter"
11
+ )
12
+ loader.setup
13
+
14
+ # E11y - Event-Driven Observability for Ruby on Rails
15
+ #
16
+ # @example Basic usage
17
+ # E11y.configure do |config|
18
+ # config.adapters = [:loki, :sentry]
19
+ # end
20
+ #
21
+ # E11y.track(Events::UserSignup.new(user_id: 123))
22
+ #
23
+ # @see https://e11y.dev Documentation
24
+ module E11y
25
+ class Error < StandardError; end
26
+ class ValidationError < Error; end
27
+ class ZoneViolationError < Error; end
28
+ class InvalidPipelineError < Error; end
29
+
30
+ class << self
31
+ # Configure E11y
32
+ #
33
+ # @yield [Configuration] configuration object
34
+ # @return [void]
35
+ #
36
+ # @example
37
+ # E11y.configure do |config|
38
+ # config.adapters = [:loki, :stdout]
39
+ # config.log_level = :debug
40
+ # end
41
+ def configure
42
+ yield configuration if block_given?
43
+ end
44
+
45
+ # Get current configuration
46
+ #
47
+ # @return [Configuration] current configuration instance
48
+ def configuration
49
+ @configuration ||= Configuration.new
50
+ end
51
+ alias config configuration
52
+
53
+ # Track an event
54
+ #
55
+ # @param event [Event] event instance to track
56
+ # @return [void]
57
+ #
58
+ # @example
59
+ # E11y.track(Events::UserSignup.new(user_id: 123))
60
+ def track(event)
61
+ # TODO: Implement in Phase 1
62
+ raise NotImplementedError, "E11y.track will be implemented in Phase 1"
63
+ end
64
+
65
+ # Get logger instance
66
+ #
67
+ # @return [Logger] logger instance
68
+ def logger
69
+ require "logger"
70
+ @logger ||= ::Logger.new($stdout)
71
+ end
72
+
73
+ # Reset configuration (primarily for testing)
74
+ #
75
+ # @return [void]
76
+ # @api private
77
+ def reset!
78
+ @configuration = nil
79
+ @logger = nil
80
+ end
81
+ end
82
+
83
+ # Configuration class for E11y
84
+ #
85
+ # Adapters are referenced by name (e.g., :logs, :errors_tracker).
86
+ # The actual implementation (Loki, Sentry, etc.) is configured separately.
87
+ #
88
+ # @example Configure adapters
89
+ # E11y.configure do |config|
90
+ # # Register adapter instances
91
+ # config.adapters[:logs] = E11y::Adapters::Loki.new(url: "...")
92
+ # config.adapters[:errors_tracker] = E11y::Adapters::Sentry.new(dsn: "...")
93
+ # end
94
+ #
95
+ # @example Configure severity => adapter mapping
96
+ # E11y.configure do |config|
97
+ # config.adapter_mapping[:error] = [:logs, :errors_tracker]
98
+ # config.adapter_mapping[:info] = [:logs]
99
+ # end
100
+ #
101
+ # @example Configure middleware pipeline
102
+ # E11y.configure do |config|
103
+ # config.pipeline.use E11y::Middleware::Sampling, default_sample_rate: 0.1
104
+ # end
105
+ class Configuration
106
+ attr_accessor :adapters, :log_level, :enabled, :environment, :service_name
107
+ attr_reader :adapter_mapping, :pipeline, :rails_instrumentation, :logger_bridge, :request_buffer, :error_handling,
108
+ :dlq_storage, :dlq_filter, :rate_limiting, :slo_tracking
109
+
110
+ def initialize
111
+ @adapters = {} # Hash of adapter_name => adapter_instance
112
+ @log_level = :info
113
+ @adapter_mapping = default_adapter_mapping
114
+ @pipeline = E11y::Pipeline::Builder.new
115
+ @enabled = true
116
+ @environment = nil
117
+ @service_name = nil
118
+ @rails_instrumentation = RailsInstrumentationConfig.new
119
+ @logger_bridge = LoggerBridgeConfig.new
120
+ @request_buffer = RequestBufferConfig.new
121
+ @error_handling = ErrorHandlingConfig.new # ✅ C18 Resolution
122
+ @dlq_storage = nil # Set by user (e.g., DLQ::FileStorage instance)
123
+ @dlq_filter = nil # Set by user (e.g., DLQ::Filter instance)
124
+ @rate_limiting = RateLimitingConfig.new
125
+ @slo_tracking = SLOTrackingConfig.new # ✅ L3.14.1
126
+ configure_default_pipeline
127
+ end
128
+
129
+ # Get adapters for given severity
130
+ #
131
+ # @param severity [Symbol] Severity level
132
+ # @return [Array<Symbol>] Adapter names (e.g., [:logs, :errors_tracker])
133
+ def adapters_for_severity(severity)
134
+ @adapter_mapping[severity] || @adapter_mapping[:default] || []
135
+ end
136
+
137
+ private
138
+
139
+ # Default adapter mapping (convention-based)
140
+ #
141
+ # Adapter names represent PURPOSE, not implementation:
142
+ # - :logs → centralized logging (implementation: Loki, Elasticsearch, CloudWatch, etc.)
143
+ # - :errors_tracker → error tracking with alerting (implementation: Sentry, Rollbar, Bugsnag, etc.)
144
+ #
145
+ # @return [Hash{Symbol => Array<Symbol>}] Default mapping (severity => adapter names)
146
+ def default_adapter_mapping
147
+ {
148
+ error: %i[logs errors_tracker], # Errors: both logging + alerting
149
+ fatal: %i[logs errors_tracker], # Fatal: both logging + alerting
150
+ default: [:logs] # Others: logging only
151
+ }
152
+ end
153
+
154
+ # Setup default middleware pipeline
155
+ #
156
+ # Default pipeline order (per ADR-015):
157
+ # 1. TraceContext - Add trace_id, span_id, timestamp (zone: :pre_processing)
158
+ # 2. Validation - Schema validation (zone: :pre_processing)
159
+ # 3. PIIFilter - PII filtering (zone: :security)
160
+ # 4. AuditSigning - Audit event signing (zone: :security)
161
+ # 5. Sampling - Adaptive sampling (zone: :routing)
162
+ # 6. Routing - Buffer routing (zone: :adapters)
163
+ #
164
+ # @return [void]
165
+ # @see ADR-015 Middleware Execution Order
166
+ def configure_default_pipeline
167
+ # Zone: :pre_processing
168
+ @pipeline.use E11y::Middleware::TraceContext
169
+ @pipeline.use E11y::Middleware::Validation
170
+
171
+ # Zone: :security
172
+ @pipeline.use E11y::Middleware::PIIFilter
173
+ @pipeline.use E11y::Middleware::AuditSigning
174
+
175
+ # Zone: :routing
176
+ @pipeline.use E11y::Middleware::Sampling
177
+
178
+ # Zone: :adapters
179
+ @pipeline.use E11y::Middleware::Routing
180
+ end
181
+ end
182
+
183
+ # Rails Instrumentation configuration
184
+ class RailsInstrumentationConfig
185
+ attr_accessor :enabled, :custom_mappings, :ignore_events
186
+
187
+ def initialize
188
+ @enabled = false # Disabled by default, enabled by Railtie
189
+ @custom_mappings = {}
190
+ @ignore_events = []
191
+ end
192
+
193
+ # Override event class for specific ASN pattern (Devise-style)
194
+ # @param pattern [String] ActiveSupport::Notifications pattern
195
+ # @param event_class [Class] E11y event class
196
+ # @return [void]
197
+ def event_class_for(pattern, event_class)
198
+ @custom_mappings[pattern] = event_class
199
+ end
200
+
201
+ # Ignore specific ASN event
202
+ # @param pattern [String] ActiveSupport::Notifications pattern
203
+ # @return [void]
204
+ def ignore_event(pattern)
205
+ @ignore_events << pattern
206
+ end
207
+ end
208
+
209
+ # Logger Bridge configuration
210
+ class LoggerBridgeConfig
211
+ attr_accessor :enabled, :dual_logging
212
+
213
+ def initialize
214
+ @enabled = false # Opt-in
215
+ @dual_logging = true # Keep writing to original Rails.logger
216
+ end
217
+ end
218
+
219
+ # Request Buffer configuration
220
+ class RequestBufferConfig
221
+ attr_accessor :enabled
222
+
223
+ def initialize
224
+ @enabled = false # Disabled by default
225
+ end
226
+ end
227
+
228
+ # Error Handling configuration (C18 Resolution)
229
+ #
230
+ # Controls whether event tracking failures should raise exceptions.
231
+ # Default: true (for web requests - fast feedback)
232
+ # Exception: false (for background jobs - don't fail business logic)
233
+ #
234
+ # @see ADR-013 §3.6 (Event Tracking in Background Jobs)
235
+ class ErrorHandlingConfig
236
+ attr_accessor :fail_on_error
237
+
238
+ def initialize
239
+ @fail_on_error = true # Default: raise errors (fast feedback for web requests)
240
+ end
241
+ end
242
+
243
+ # Rate Limiting configuration (UC-011, C02 Resolution)
244
+ #
245
+ # Protects adapters from event floods using token bucket algorithm.
246
+ #
247
+ # @see UC-011 (Rate Limiting - DoS Protection)
248
+ # @see ADR-013 §4.6 (C02 Resolution)
249
+ class RateLimitingConfig
250
+ attr_accessor :enabled, :global_limit, :per_event_limit, :window
251
+
252
+ def initialize
253
+ @enabled = false # Opt-in (enable explicitly)
254
+ @global_limit = 10_000 # Max 10K events/sec globally
255
+ @per_event_limit = 1_000 # Max 1K events/sec per event type
256
+ @window = 1.0 # 1 second window
257
+ end
258
+ end
259
+
260
+ # SLO Tracking configuration (UC-004, ADR-003)
261
+ #
262
+ # Zero-config SLO tracking for HTTP requests and background jobs.
263
+ # Automatically emits SLO metrics (availability, latency, success rate).
264
+ #
265
+ # @see UC-004 (Zero-Config SLO Tracking)
266
+ # @see ADR-003 (SLO & Observability)
267
+ #
268
+ # @note C11 Resolution (Sampling Correction): Requires Phase 2.8 (Stratified Sampling).
269
+ # Without stratified sampling, SLO metrics may be inaccurate when adaptive sampling is enabled.
270
+ class SLOTrackingConfig
271
+ attr_accessor :enabled
272
+
273
+ def initialize
274
+ @enabled = false # Opt-in (enable explicitly)
275
+ end
276
+ end
277
+ end
278
+
279
+ # Load Railtie if Rails is present
280
+ require "e11y/railtie" if defined?(Rails::Railtie)
281
+
282
+ # Eager load for production (optional - uncomment if needed)
283
+ # loader.eager_load if ENV["RAILS_ENV"] == "production"