Class: Tasker::Telemetry::EventRouter

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/tasker/telemetry/event_router.rb

Overview

EventRouter provides intelligent routing of events to appropriate telemetry backends

This is the strategic core of Phase 4.2.1, enabling declarative event→telemetry mapping while preserving all existing TelemetrySubscriber functionality. It follows the singleton pattern established by HandlerFactory and Events::Publisher.

Core Philosophy: PRESERVE all existing 8 TelemetrySubscriber events while adding intelligent routing for 35+ additional lifecycle events.

Examples:

Basic configuration

Tasker::Telemetry::EventRouter.configure do |router|
  # PRESERVE: All current 8 events → both traces AND metrics
  router.map 'task.completed' => [:trace, :metrics]
  router.map 'step.failed' => [:trace, :metrics]

  # ENHANCE: Add missing lifecycle events with intelligent routing
  router.map 'workflow.viable_steps_discovered' => [:trace, :metrics]
  router.map 'observability.task.enqueue' => [:metrics]  # Job queue metrics only
  router.map 'step.before_handle' => [:trace]            # Handler execution spans
end

Production sampling configuration

router.map 'step.before_handle' => [:trace], sampling_rate: 0.1  # 10% sampling
router.map 'database.query_executed' => [:trace, :metrics], priority: :high

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeEventRouter

Initialize the event router with default mappings



48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/tasker/telemetry/event_router.rb', line 48

def initialize
  @mappings = {}
  @trace_events = []
  @metrics_events = []
  @log_events = []

  # Load default mappings that preserve existing TelemetrySubscriber functionality
  load_default_mappings

  # Register this router with MetricsBackend for automatic routing
  MetricsBackend.instance.register_event_router(self)
end

Instance Attribute Details

#log_eventsArray<String> (readonly)

Returns List of events that route to logs.

Returns:

  • (Array<String>)

    List of events that route to logs



45
46
47
# File 'lib/tasker/telemetry/event_router.rb', line 45

def log_events
  @log_events
end

#mappingsHash<String, EventMapping> (readonly)

Returns Registry of event mappings by event name.

Returns:

  • (Hash<String, EventMapping>)

    Registry of event mappings by event name



36
37
38
# File 'lib/tasker/telemetry/event_router.rb', line 36

def mappings
  @mappings
end

#metrics_eventsArray<String> (readonly)

Returns List of events that route to metrics.

Returns:

  • (Array<String>)

    List of events that route to metrics



42
43
44
# File 'lib/tasker/telemetry/event_router.rb', line 42

def metrics_events
  @metrics_events
end

#trace_eventsArray<String> (readonly)

Returns List of events that route to traces.

Returns:

  • (Array<String>)

    List of events that route to traces



39
40
41
# File 'lib/tasker/telemetry/event_router.rb', line 39

def trace_events
  @trace_events
end

Class Method Details

.configure {|EventRouter| ... } ⇒ EventRouter

Configure event routing with a block

Yields:

  • (EventRouter)

    The router instance for configuration

Returns:



65
66
67
# File 'lib/tasker/telemetry/event_router.rb', line 65

def self.configure
  instance.tap { |router| yield(router) if block_given? }
end

Instance Method Details

#bulk_configure(mappings_config) ⇒ Array<EventMapping>

Bulk configure multiple mappings

Parameters:

  • mappings_config (Hash)

    Hash of event_name => backend_config

Returns:

Raises:

  • (ArgumentError)

    If any mapping configuration is invalid



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/tasker/telemetry/event_router.rb', line 232

def bulk_configure(mappings_config)
  return [] if mappings_config.blank?

  mappings_config.map do |event_name, config|
    if config.is_a?(Array)
      map(event_name, backends: config)
    elsif config.is_a?(Hash)
      map(event_name, **config)
    elsif config.respond_to?(:to_sym)
      map(event_name, backends: [config])
    else
      raise ArgumentError, "Invalid config for #{event_name}: #{config.inspect}. Must be Array, Hash, or Symbol"
    end
  end
end

#configured_eventsArray<String>

Get a list of all configured event names

Returns:

  • (Array<String>)

    All configured event names



198
199
200
# File 'lib/tasker/telemetry/event_router.rb', line 198

def configured_events
  mappings.keys
end

#events_for_backend(backend) ⇒ Array<String>

Get all events that should route to a specific backend

Parameters:

  • backend (Symbol)

    Backend type (:trace, :metrics, :logs)

Returns:

  • (Array<String>)

    List of event names

Raises:

  • (ArgumentError)

    If backend type is not recognized



156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/tasker/telemetry/event_router.rb', line 156

def events_for_backend(backend)
  case backend
  when :trace, :traces
    trace_events
  when :metric, :metrics
    metrics_events
  when :log, :logs
    log_events
  else
    raise ArgumentError, "Unknown backend type: #{backend.inspect}. Valid backends: :trace, :metrics, :logs"
  end
end

#map(event_name_or_hash, backends: [:trace]) ⇒ EventMapping

Map an event to specific telemetry backends

Parameters:

  • event_name (String, Hash)

    Event name in dot notation, or hash with event => backends

  • backends (Array<Symbol>) (defaults to: [:trace])

    List of backend types (:trace, :metrics, :logs)

  • options (Hash)

    Additional mapping options

Returns:

Raises:

  • (ArgumentError)


79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/tasker/telemetry/event_router.rb', line 79

def map(event_name_or_hash, backends: [:trace], **)
  # Handle hash syntax: map 'event' => [:trace, :metrics]
  if event_name_or_hash.is_a?(Hash)
    # Extract the first (and typically only) hash pair
    event_name, extracted_backends = event_name_or_hash.first
    backends = Array(extracted_backends)
  else
    event_name = event_name_or_hash
  end

  # Ensure we always have an event_name
  raise ArgumentError, 'event_name cannot be nil' if event_name.nil?

  mapping = EventMapping.new(
    event_name: event_name.to_s,
    backends: Array(backends),
    **
  )

  register_mapping(mapping)
  mapping
end

#mapping_exists?(event_name) ⇒ Boolean

Check if a mapping exists for a specific event

Parameters:

  • event_name (String)

    Event name

Returns:

  • (Boolean)

    True if mapping exists



114
115
116
# File 'lib/tasker/telemetry/event_router.rb', line 114

def mapping_exists?(event_name)
  mappings.key?(event_name.to_s)
end

#mapping_for(event_name) ⇒ EventMapping?

Get the mapping for a specific event

Parameters:

  • event_name (String)

    Event name

Returns:



106
107
108
# File 'lib/tasker/telemetry/event_router.rb', line 106

def mapping_for(event_name)
  mappings[event_name.to_s]
end

#reset!void

This method returns an undefined value.

Reset all mappings (primarily for testing)



187
188
189
190
191
192
193
# File 'lib/tasker/telemetry/event_router.rb', line 187

def reset!
  @mappings.clear
  @trace_events.clear
  @metrics_events.clear
  @log_events.clear
  load_default_mappings
end

#route_event(event_name, payload = {}) ⇒ Hash

Route an event to appropriate backends based on configuration

This is the core routing method that directs events to traces, metrics, and logs based on their configured mapping.

Parameters:

  • event_name (String)

    The lifecycle event name

  • payload (Hash) (defaults to: {})

    Event payload data

Returns:

  • (Hash)

    Results from each backend (backend => success boolean)



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/tasker/telemetry/event_router.rb', line 210

def route_event(event_name, payload = {})
  results = {}
  mapping = mapping_for(event_name)
  return results unless mapping&.active?

  # Route to metrics backend if configured
  results[:metrics] = MetricsBackend.instance.handle_event(event_name, payload) if mapping.routes_to_metrics?

  # Route to trace backend if configured
  results[:traces] = TraceBackend.instance.handle_event(event_name, payload) if mapping.routes_to_traces?

  # Route to log backend if configured
  results[:logs] = LogBackend.instance.handle_event(event_name, payload) if mapping.routes_to_logs?

  results
end

#routes_to_logs?(event_name) ⇒ Boolean

Check if an event routes to logs

Parameters:

  • event_name (String)

    Event name

Returns:

  • (Boolean)

    True if the event routes to logs



144
145
146
147
148
149
# File 'lib/tasker/telemetry/event_router.rb', line 144

def routes_to_logs?(event_name)
  mapping = mapping_for(event_name)
  return false unless mapping

  mapping.active? && mapping.routes_to_logs?
end

#routes_to_metrics?(event_name) ⇒ Boolean

Check if an event routes to metrics

Parameters:

  • event_name (String)

    Event name

Returns:

  • (Boolean)

    True if the event routes to metrics



133
134
135
136
137
138
# File 'lib/tasker/telemetry/event_router.rb', line 133

def routes_to_metrics?(event_name)
  mapping = mapping_for(event_name)
  return false unless mapping

  mapping.active? && mapping.routes_to_metrics?
end

#routes_to_traces?(event_name) ⇒ Boolean

Check if an event routes to traces

Parameters:

  • event_name (String)

    Event name

Returns:

  • (Boolean)

    True if the event routes to traces



122
123
124
125
126
127
# File 'lib/tasker/telemetry/event_router.rb', line 122

def routes_to_traces?(event_name)
  mapping = mapping_for(event_name)
  return false unless mapping

  mapping.active? && mapping.routes_to_traces?
end

#routing_statsHash

Get routing statistics for debugging

Returns:

  • (Hash)

    Statistics about current routing configuration



172
173
174
175
176
177
178
179
180
181
182
# File 'lib/tasker/telemetry/event_router.rb', line 172

def routing_stats
  {
    total_mappings: mappings.size,
    trace_events: trace_events.size,
    metrics_events: metrics_events.size,
    log_events: log_events.size,
    enabled_mappings: mappings.values.count(&:enabled),
    high_priority: mappings.values.count { |m| m.priority == :high },
    sampled_mappings: mappings.values.count { |m| m.sampling_rate < 1.0 }
  }
end