Class: LittleGhost::Events::Reporter

Inherits:
Object
  • Object
show all
Defined in:
lib/little_ghost/events.rb

Overview

Thread-safe event publisher with process-wide and fiber-scoped listeners. Reporters start without listeners so applications opt into their preferred event destination, including ConsoleListener for JSON-line diagnostics.

Instance Method Summary collapse

Constructor Details

#initialize(listeners: []) ⇒ Reporter

Starts with listeners in subscription order.



58
59
60
61
62
# File 'lib/little_ghost/events.rb', line 58

def initialize(listeners: [])
  @mutex = Mutex.new
  @listeners = []
  Array(listeners).each { |listener| subscribe(listener) }
end

Instance Method Details

#contextObject

Copies the event context active in the current execution.



114
115
116
# File 'lib/little_ghost/events.rb', line 114

def context
  deep_copy(ExecutionState[context_key] || {})
end

#emit(level, name, payload = {}) ⇒ Object

Delivers an event and returns a detached copy of its complete hash.

Raises:

  • (ArgumentError)


83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/little_ghost/events.rb', line 83

def emit(level, name, payload = {})
  level = level.to_sym
  raise ArgumentError, "unknown event level: #{level}" unless LEVELS.include?(level)
  raise ArgumentError, "event payload must be a hash" unless payload.is_a?(Hash)

  event = {
    name: normalize_string(name.to_s),
    level:,
    payload: deep_copy(payload),
    context: context,
    timestamp: Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
  }
  listeners.each do |entry|
    listener = entry.fetch(:listener)
    filter = entry[:filter]
    next if filter && !filter.call(deep_copy(event))

    listener.emit(deep_copy(event))
  rescue
    nil
  end
  event
end

#subscribe(listener, &filter) ⇒ Object

Subscribes listener. The optional block filters copied event hashes.



65
66
67
68
69
70
71
72
# File 'lib/little_ghost/events.rb', line 65

def subscribe(listener, &filter)
  unless listener.respond_to?(:emit)
    raise ArgumentError, "event listener must respond to emit"
  end

  @mutex.synchronize { @listeners << {listener:, filter:} }
  listener
end

#unsubscribe(listener) ⇒ Object

Unsubscribes every entry matching listener.



75
76
77
78
79
80
# File 'lib/little_ghost/events.rb', line 75

def unsubscribe(listener)
  @mutex.synchronize do
    @listeners.delete_if { |entry| listener === entry.fetch(:listener) }
  end
  listener
end

#with_context(attributes) ⇒ Object

Adds attributes to events emitted while the block runs.



108
109
110
111
# File 'lib/little_ghost/events.rb', line 108

def with_context(attributes)
  values = context.merge(deep_copy(attributes.compact))
  ExecutionState.with(context_key => values) { yield }
end