Class: WifiWand::EventLogger

Inherits:
Object
  • Object
show all
Includes:
Timing
Defined in:
lib/wifi_wand/services/event_logger.rb

Overview

EventLogger continuously monitors WiFi status and logs state changes. Each poll captures WiFi power and current SSID, then derives internet events from the explicit connectivity-state model. Internet reachability is checked via internet_connectivity_state on every poll so internet_on/internet_off always reflect the full TCP+DNS+captive-portal path. Internet reachability is checked independently of WiFi association so alternate uplinks such as Ethernet still produce accurate Internet events.

Threading model:

  • run is a blocking loop. If a caller wants to stop it programmatically, run should usually execute in a background thread.
  • The calling thread, often the main thread, can then call stop and wait for the logger thread to exit.
  • If run and stop are both expected to happen on the same thread, then stop cannot interrupt the loop while that thread is blocked waiting for the next poll.
  • Ctrl+C is different because it raises Interrupt, which can break into the blocking wait without requiring another Ruby thread to call stop.

In other words, stop is intended for coordinated shutdown from another thread, while Interrupt covers interactive termination from the console.

Constant Summary collapse

SSID_UNAVAILABLE_LABEL =
NetworkIdentity::SSID_UNAVAILABLE_LABEL
POLLING_BOUNDARY_ERROR =

StandardError excludes process-control and VM-level exceptions like Interrupt, SystemExit, and NoMemoryError.

StandardError

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Timing

monotonic_now, status_deadline, status_timeout_for

Constructor Details

#initialize(model, interval: 5, log_file_path: nil, log_file_manager: nil, internet_probe_timeout_in_secs: nil, runtime_config: nil, **kwargs) ⇒ EventLogger

Returns a new instance of EventLogger.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/wifi_wand/services/event_logger.rb', line 46

def initialize(
  model,
  interval: 5,
  log_file_path: nil,
  log_file_manager: nil,
  internet_probe_timeout_in_secs: nil,
  runtime_config: nil,
  **kwargs
)
  @model = model
  @interval = interval
  @internet_probe_timeout_in_secs = internet_probe_timeout_in_secs
  @runtime_config = runtime_config || RuntimeConfig.new(verbose: kwargs[:verbose], utc: kwargs[:utc])
  @verbose_override = kwargs[:verbose] if kwargs.key?(:verbose)
  @utc_override = !!kwargs[:utc] if kwargs.key?(:utc)
  @out_stream_override = kwargs[:out_stream] if kwargs.key?(:out_stream)
  # Only create LogFileManager if file logging is requested
  @log_file_manager = if log_file_manager
    log_file_manager
  elsif log_file_path
    LogFileManager.new(
      log_file_path:  log_file_path,
      out_stream:     (@out_stream_override if kwargs.key?(:out_stream)),
      verbose:        (@verbose_override if kwargs.key?(:verbose)),
      runtime_config: @runtime_config
    )
  end
  @previous_state = nil
  @wait_mutex = Mutex.new
  @wait_condition = ConditionVariable.new
  @running = false
  @file_logging_warning_emitted = false
  @consecutive_state_fetch_failures = 0
  @state_fetch_warning_emitted = false
end

Instance Attribute Details

#intervalObject (readonly)

Returns the value of attribute interval.



43
44
45
# File 'lib/wifi_wand/services/event_logger.rb', line 43

def interval
  @interval
end

#log_file_managerObject (readonly)

Returns the value of attribute log_file_manager.



43
44
45
# File 'lib/wifi_wand/services/event_logger.rb', line 43

def log_file_manager
  @log_file_manager
end

#modelObject (readonly)

Returns the value of attribute model.



43
44
45
# File 'lib/wifi_wand/services/event_logger.rb', line 43

def model
  @model
end

Instance Method Details

#cleanupObject



155
156
157
158
159
160
# File 'lib/wifi_wand/services/event_logger.rb', line 155

def cleanup
  if @log_file_manager
    @log_file_manager.close
    @log_file_manager = nil
  end
end

#log_initial_state(state) ⇒ Object



145
146
147
148
149
150
151
152
153
# File 'lib/wifi_wand/services/event_logger.rb', line 145

def log_initial_state(state)
  log_message(format_json_line(
    event:      'current_state',
    wifi:       json_boolean_value(state[:wifi_on]),
    connection: json_connection_value(state[:connected]),
    network:    json_network_value(state),
    internet:   internet_state_label(state[:internet_state])
  ))
end

#out_streamObject



169
170
171
172
173
174
175
# File 'lib/wifi_wand/services/event_logger.rb', line 169

def out_stream
  if instance_variable_defined?(:@out_stream_override)
    @out_stream_override
  else
    runtime_config.out_stream
  end
end

#runObject

Start polling loop. This method blocks until stop is called or Ctrl+C is pressed. For programmatic shutdown, callers should usually run this in a background thread so another thread can call stop.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/wifi_wand/services/event_logger.rb', line 85

def run
  start_running
  log_message(format_json_line(event: 'logging_started', interval: @interval))
  polling_error_raised = false

  begin
    while running?
      poll_started_at = monotonic_now
      current_state = fetch_current_state

      if @previous_state
        detect_and_emit_events(current_state)
      else
        log_initial_state(current_state)
      end
      @previous_state = current_state

      break unless running?

      sleep_until(poll_started_at + @interval)
    end
  rescue Interrupt
    stop
  rescue POLLING_BOUNDARY_ERROR => e
    polling_error_raised = true
    log_termination_message(e)
    raise
  ensure
    log_message(format_json_line(event: 'logging_stopped')) unless polling_error_raised
    cleanup
  end
end

#stopObject



162
163
164
165
166
167
# File 'lib/wifi_wand/services/event_logger.rb', line 162

def stop
  @wait_mutex.synchronize do
    @running = false
    @wait_condition.broadcast
  end
end

#verbose?Boolean

Returns:

  • (Boolean)


521
522
523
524
525
526
527
# File 'lib/wifi_wand/services/event_logger.rb', line 521

def verbose?
  if instance_variable_defined?(:@verbose_override)
    @verbose_override
  else
    runtime_config.verbose
  end
end