Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions docs/observability.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,35 @@ series; size your collector/Prometheus retention accordingly.
The metric-name constants are the single source of truth in
`rust/crates/aetower-telemetry/src/lib.rs` (`pub mod metric_names`).

## Local UI refresh diagnostics

Aetower emits a bounded `ui-refresh-slow` diagnostic when a UI refresh takes at
least 250 ms or publication takes at least 120 ms, and a periodic
`ui-refresh-sample` for the intervening refreshes. The sample window records its
actual `window_duration_millis` and `window_refreshes`; counts include updated,
no-change, failed, and cancelled cycles, so a slow-event count can be compared
with the observed denominator.

Per-refresh timings include `full_snapshot_fetch_millis`,
`evaluator_snapshot_fetch_millis`, `monitor_payload_fetch_millis`,
`runtime_lag_fetch_millis`, `operator_state_fetch_millis`,
`worker_total_millis`, `monitor_decode_millis`, `monitor_row_build_millis`,
`publish_millis`, and `ui_refresh_millis`. Window `*_fetches` and
`window_worker_total_attempts` count attempts; their `*_timing_samples` fields
(and `window_worker_samples`) count only calls with measured durations. Means
use timing-sample counts, not attempt counts; when no samples exist, the mean is
`n/a`. An attempted call that throws therefore increments its attempt counter; its
elapsed time is recorded when measurable, otherwise it contributes no fabricated
zero to the mean. Per-refresh phase durations are `n/a` when unavailable, and
`bridge_call_total_millis` is `n/a` if any attempted Bridge phase lacks a
duration.

The existing runtime metric `bridge_fetch_millis` deliberately retains its
original scope: it measures only the full `SystemSnapshot` fetch. It does not
include the compact Monitor delta, runtime-lag metrics, operator-state Bridge
calls, or total refresh time. Use the local per-phase event fields to compare
those costs; do not interpret `bridge_fetch_millis` as the whole Bridge path.

## Collector + Prometheus setup

Minimal `otel-collector-config.yaml` that receives OTLP/HTTP from Aetower and
Expand Down
1 change: 0 additions & 1 deletion macos/Sources/AetowerApp/AetowerApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1137,7 +1137,6 @@ struct AetowerApp: App {
WindowGroup {
TabView(selection: $nav.workspace) {
MainListView(state: state, settings: settings)
.demandsFullSnapshot(from: state, reason: "monitor")
.tabItem {
Label(WorkspaceTab.monitor.title, systemImage: WorkspaceTab.monitor.systemImage)
}
Expand Down
10 changes: 9 additions & 1 deletion macos/Sources/AetowerBindings/aetower_ffi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7653,6 +7653,7 @@ public struct UiHostSummary {
public var gpuPercent: Float
public var gpuMemoryBytes: UInt64
public var thermalState: ThermalState
public var thermalForecast: ThermalForecast?
public var onBattery: Bool
public var lowPowerMode: Bool
public var frontmostAppName: String?
Expand All @@ -7663,7 +7664,7 @@ public struct UiHostSummary {

// Default memberwise initializers are never public by default, so we
// declare one manually.
public init(machineFriction: Float, cpuPercent: Float, memoryUsedBytes: UInt64, memoryTotalBytes: UInt64, compressedMemoryBytes: UInt64, swapUsedBytes: UInt64, diskReadBps: UInt64, diskWriteBps: UInt64, networkReceiveBps: UInt64, networkSendBps: UInt64, wakeupsPerSecond: Float, gpuPercent: Float, gpuMemoryBytes: UInt64, thermalState: ThermalState, onBattery: Bool, lowPowerMode: Bool, frontmostAppName: String?, frontmostWindowTitle: String?, aiAgentCount: UInt32, entityCount: UInt32, processCount: UInt32) {
public init(machineFriction: Float, cpuPercent: Float, memoryUsedBytes: UInt64, memoryTotalBytes: UInt64, compressedMemoryBytes: UInt64, swapUsedBytes: UInt64, diskReadBps: UInt64, diskWriteBps: UInt64, networkReceiveBps: UInt64, networkSendBps: UInt64, wakeupsPerSecond: Float, gpuPercent: Float, gpuMemoryBytes: UInt64, thermalState: ThermalState, thermalForecast: ThermalForecast?, onBattery: Bool, lowPowerMode: Bool, frontmostAppName: String?, frontmostWindowTitle: String?, aiAgentCount: UInt32, entityCount: UInt32, processCount: UInt32) {
self.machineFriction = machineFriction
self.cpuPercent = cpuPercent
self.memoryUsedBytes = memoryUsedBytes
Expand All @@ -7678,6 +7679,7 @@ public struct UiHostSummary {
self.gpuPercent = gpuPercent
self.gpuMemoryBytes = gpuMemoryBytes
self.thermalState = thermalState
self.thermalForecast = thermalForecast
self.onBattery = onBattery
self.lowPowerMode = lowPowerMode
self.frontmostAppName = frontmostAppName
Expand Down Expand Up @@ -7737,6 +7739,9 @@ extension UiHostSummary: Equatable, Hashable {
if lhs.thermalState != rhs.thermalState {
return false
}
if lhs.thermalForecast != rhs.thermalForecast {
return false
}
if lhs.onBattery != rhs.onBattery {
return false
}
Expand Down Expand Up @@ -7776,6 +7781,7 @@ extension UiHostSummary: Equatable, Hashable {
hasher.combine(gpuPercent)
hasher.combine(gpuMemoryBytes)
hasher.combine(thermalState)
hasher.combine(thermalForecast)
hasher.combine(onBattery)
hasher.combine(lowPowerMode)
hasher.combine(frontmostAppName)
Expand Down Expand Up @@ -7809,6 +7815,7 @@ public struct FfiConverterTypeUiHostSummary: FfiConverterRustBuffer {
gpuPercent: FfiConverterFloat.read(from: &buf),
gpuMemoryBytes: FfiConverterUInt64.read(from: &buf),
thermalState: FfiConverterTypeThermalState.read(from: &buf),
thermalForecast: FfiConverterOptionTypeThermalForecast.read(from: &buf),
onBattery: FfiConverterBool.read(from: &buf),
lowPowerMode: FfiConverterBool.read(from: &buf),
frontmostAppName: FfiConverterOptionString.read(from: &buf),
Expand All @@ -7834,6 +7841,7 @@ public struct FfiConverterTypeUiHostSummary: FfiConverterRustBuffer {
FfiConverterFloat.write(value.gpuPercent, into: &buf)
FfiConverterUInt64.write(value.gpuMemoryBytes, into: &buf)
FfiConverterTypeThermalState.write(value.thermalState, into: &buf)
FfiConverterOptionTypeThermalForecast.write(value.thermalForecast, into: &buf)
FfiConverterBool.write(value.onBattery, into: &buf)
FfiConverterBool.write(value.lowPowerMode, into: &buf)
FfiConverterOptionString.write(value.frontmostAppName, into: &buf)
Expand Down
Loading
Loading