Module: SwarmSDK::LogCollector
- Defined in:
- lib/swarm_sdk/log_collector.rb
Overview
LogCollector manages subscriber callbacks for log events with filtering support.
This module acts as an emitter implementation that forwards events to user-registered callbacks. It's designed to be set as the LogStream emitter during swarm execution.
Features
- Filtered Subscriptions: Subscribe to specific agents, event types, or swarm IDs
- Unsubscribe Support: Remove subscriptions by ID to prevent memory leaks
- Error Isolation: One subscriber's error doesn't break others
- Thread Safety: Fiber-local storage for multi-threaded environments
Thread Safety for Multi-Threaded Environments (Puma, Sidekiq)
Subscriptions are stored in Fiber-local storage (Fiber) instead of class instance variables. This ensures callbacks registered in the parent thread/fiber are accessible to child fibers created by Async reactor.
Why: In Puma/Sidekiq, class instance variables (@subscriptions) are thread-isolated and don't properly propagate to child fibers. Using Fiber-local storage ensures events emitted from within Async blocks can reach registered subscriptions.
Child fibers inherit parent fiber-local storage automatically, so events emitted from agent callbacks (on_tool_call, on_end_message, etc.) executing in child fibers can still reach the parent's registered subscriptions.
Usage
# Subscribe to all events
sub_id = LogCollector.subscribe { |event| puts event }
# Subscribe to specific agent
sub_id = LogCollector.subscribe(filter: { agent: :backend }) { |event|
puts "Backend: #{event}"
}
# Subscribe to specific event types
sub_id = LogCollector.subscribe(filter: { type: ["tool_call", "tool_result"] }) { |event|
log_tool_activity(event)
}
# Subscribe with regex matching
sub_id = LogCollector.subscribe(filter: { type: /^tool_/ }) { |event|
track_tool_usage(event)
}
# Unsubscribe when done
LogCollector.unsubscribe(sub_id)
# After execution, reset for next use
LogCollector.reset!
Defined Under Namespace
Classes: Subscription
Class Method Summary collapse
-
.clear_subscriptions ⇒ void
Clear all subscriptions.
-
.emit(entry) ⇒ void
Emit an event to all matching subscribers.
-
.reset! ⇒ void
Reset the collector (clears subscriptions for next execution).
-
.subscribe(filter: {}) {|Hash| ... } ⇒ String
Subscribe to log events with optional filtering.
-
.subscription_count ⇒ Integer
Get current subscription count.
-
.unsubscribe(subscription_id) ⇒ Subscription?
Unsubscribe by ID.
Class Method Details
.clear_subscriptions ⇒ void
This method returns an undefined value.
Clear all subscriptions
Removes all subscriptions. Useful for testing or execution cleanup.
166 167 168 |
# File 'lib/swarm_sdk/log_collector.rb', line 166 def clear_subscriptions subscriptions.clear end |
.emit(entry) ⇒ void
This method returns an undefined value.
Emit an event to all matching subscribers
Automatically adds a timestamp if one doesn't exist. Errors in individual subscribers are isolated - one bad subscriber won't prevent others from receiving events.
185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/swarm_sdk/log_collector.rb', line 185 def emit(entry) = (entry) subscriptions.each do |subscription| next unless subscription.matches?() begin subscription.callback.call() rescue StandardError => e # Error isolation - don't let one subscriber break others RubyLLM.logger.error("SwarmSDK: Subscription #{subscription.id} error: #{e.}") end end end |
.reset! ⇒ void
This method returns an undefined value.
Reset the collector (clears subscriptions for next execution)
203 204 205 |
# File 'lib/swarm_sdk/log_collector.rb', line 203 def reset! Fiber[:log_subscriptions] = [] end |
.subscribe(filter: {}) {|Hash| ... } ⇒ String
Subscribe to log events with optional filtering
Registers a callback that will receive events matching the filter criteria. Returns a subscription ID that can be used to unsubscribe later.
142 143 144 145 146 |
# File 'lib/swarm_sdk/log_collector.rb', line 142 def subscribe(filter: {}, &block) subscription = Subscription.new(filter: filter, &block) subscriptions << subscription subscription.id end |
.subscription_count ⇒ Integer
Get current subscription count
173 174 175 |
# File 'lib/swarm_sdk/log_collector.rb', line 173 def subscription_count subscriptions.size end |
.unsubscribe(subscription_id) ⇒ Subscription?
Unsubscribe by ID
Removes a subscription to prevent memory leaks and stop receiving events.
154 155 156 157 158 159 |
# File 'lib/swarm_sdk/log_collector.rb', line 154 def unsubscribe(subscription_id) index = subscriptions.find_index { |s| s.id == subscription_id } return unless index subscriptions.delete_at(index) end |