feat: start better logging solution

This commit is contained in:
Michael Freno
2026-01-15 14:02:18 -05:00
parent f261d86024
commit 1b9cbf5f99
5 changed files with 138 additions and 6 deletions

View File

@@ -8,6 +8,7 @@
import AppKit import AppKit
import Combine import Combine
import SwiftUI import SwiftUI
import os.log
@MainActor @MainActor
class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
@@ -18,6 +19,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
private var hasStartedTimers = false private var hasStartedTimers = false
// Logging manager
private let logger = LoggingManager.shared
// Smart Mode services // Smart Mode services
private var fullscreenService: FullscreenDetectionService? private var fullscreenService: FullscreenDetectionService?
private var idleService: IdleMonitoringService? private var idleService: IdleMonitoringService?
@@ -38,6 +42,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
// Set activation policy to hide dock icon // Set activation policy to hide dock icon
NSApplication.shared.setActivationPolicy(.accessory) NSApplication.shared.setActivationPolicy(.accessory)
// Initialize logging
logger.configureLogging()
logger.appLogger.info("🚀 Application did finish launching")
timerEngine = TimerEngine(settingsManager: settingsManager) timerEngine = TimerEngine(settingsManager: settingsManager)
setupSmartModeServices() setupSmartModeServices()
@@ -103,6 +111,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
private func startTimers() { private func startTimers() {
guard !hasStartedTimers else { return } guard !hasStartedTimers else { return }
hasStartedTimers = true hasStartedTimers = true
logger.appLogger.info("Starting timers")
timerEngine?.start() timerEngine?.start()
observeReminderEvents() observeReminderEvents()
} }
@@ -123,6 +132,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
} }
func applicationWillTerminate(_ notification: Notification) { func applicationWillTerminate(_ notification: Notification) {
logger.appLogger.info(" applicationWill terminate")
settingsManager.saveImmediately() settingsManager.saveImmediately()
timerEngine?.stop() timerEngine?.stop()
} }
@@ -144,11 +154,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
} }
@objc private func systemWillSleep() { @objc private func systemWillSleep() {
logger.systemLogger.info("System will sleep")
timerEngine?.handleSystemSleep() timerEngine?.handleSystemSleep()
settingsManager.saveImmediately() settingsManager.saveImmediately()
} }
@objc private func systemDidWake() { @objc private func systemDidWake() {
logger.systemLogger.info("System did wake")
timerEngine?.handleSystemWake() timerEngine?.handleSystemWake()
} }

View File

@@ -0,0 +1,88 @@
//
// LoggingManager.swift
// Gaze
//
// Created by [Your Name] on [Date].
//
import Foundation
import os.log
#if DEBUG
let isLoggingEnabled = true
#else
let isLoggingEnabled = false
#endif
/// A centralized logging manager that provides structured, subsystem-aware logging
/// for the Gaze application to ensure logs are captured by the run script.
final class LoggingManager {
static let shared = LoggingManager()
// MARK: - Private Properties
private let subsystem = "com.mikefreno.Gaze"
// MARK: - Public Loggers
/// Logger for general application events
let appLogger = Logger(subsystem: "com.mikefreno.Gaze", category: "Application")
/// Logger for timer-related events
let timerLogger = Logger(subsystem: "com.mikefreno.Gaze", category: "TimerEngine")
/// Logger for settings and configuration changes
let settingsLogger = Logger(subsystem: "com.mikefreno.Gaze", category: "Settings")
/// Logger for smart mode functionality
let smartModeLogger = Logger(subsystem: "com.mikefreno.Gaze", category: "SmartMode")
/// Logger for UI and window management events
let uiLogger = Logger(subsystem: "com.mikefreno.Gaze", category: "UI")
/// Logger for system events (sleep/wake)
let systemLogger = Logger(subsystem: "com.mikefreno.Gaze", category: "System")
// MARK: - Initialization
private init() {
// Private initializer to enforce singleton pattern
}
// MARK: - Public Methods
/// Configure the logging system for verbose output when needed
func configureLogging() {
// For now, we'll use standard OSLog behavior.
// This can be extended in the future to support runtime log level changes.
}
/// Convenience method for debug logging
func debug(_ message: String, category: String = "General") {
guard isLoggingEnabled else { return }
let logger = Logger(subsystem: subsystem, category: category)
logger.debug("\(message)")
}
/// Convenience method for info logging
func info(_ message: String, category: String = "General") {
guard isLoggingEnabled else { return }
let logger = Logger(subsystem: subsystem, category: category)
logger.info("\(message)")
}
/// Convenience method for error logging
func error(_ message: String, category: String = "General") {
guard isLoggingEnabled else { return }
let logger = Logger(subsystem: subsystem, category: category)
logger.error("\(message)")
}
/// Convenience method for warning logging
func warning(_ message: String, category: String = "General") {
guard isLoggingEnabled else { return }
let logger = Logger(subsystem: subsystem, category: category)
logger.warning("\(message)")
}
}

View File

@@ -9,6 +9,7 @@ import AppKit
import Combine import Combine
import CoreGraphics import CoreGraphics
import Foundation import Foundation
import os.log
public enum ScreenCaptureAuthorizationStatus: Equatable { public enum ScreenCaptureAuthorizationStatus: Equatable {
case authorized case authorized
@@ -71,6 +72,7 @@ final class ScreenCapturePermissionManager: ObservableObject, ScreenCapturePermi
} }
func openSystemSettings() { func openSystemSettings() {
LoggingManager.shared.uiLogger.log("sup")
// Try different variations // Try different variations
let possibleUrls = [ let possibleUrls = [
"x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenRecording", "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenRecording",

View File

@@ -7,6 +7,7 @@
import Combine import Combine
import Foundation import Foundation
import os.log
@MainActor @MainActor
class TimerEngine: ObservableObject { class TimerEngine: ObservableObject {
@@ -28,6 +29,9 @@ class TimerEngine: ObservableObject {
private var idleService: IdleMonitoringService? private var idleService: IdleMonitoringService?
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
// Logging manager
private let logger = LoggingManager.shared.timerLogger
init( init(
settingsManager: any SettingsProviding, settingsManager: any SettingsProviding,
enforceModeService: EnforceModeService? = nil, enforceModeService: EnforceModeService? = nil,
@@ -73,10 +77,10 @@ class TimerEngine: ObservableObject {
if isFullscreen { if isFullscreen {
pauseAllTimers(reason: .fullscreen) pauseAllTimers(reason: .fullscreen)
print("⏸️ Timers paused: fullscreen detected") logger.info("⏸️ Timers paused: fullscreen detected")
} else { } else {
resumeAllTimers(reason: .fullscreen) resumeAllTimers(reason: .fullscreen)
print("▶️ Timers resumed: fullscreen exited") logger.info("▶️ Timers resumed: fullscreen exited")
} }
} }
@@ -85,10 +89,10 @@ class TimerEngine: ObservableObject {
if isIdle { if isIdle {
pauseAllTimers(reason: .idle) pauseAllTimers(reason: .idle)
print("⏸️ Timers paused: user idle") logger.info("⏸️ Timers paused: user idle")
} else { } else {
resumeAllTimers(reason: .idle) resumeAllTimers(reason: .idle)
print("▶️ Timers resumed: user active") logger.info("▶️ Timers resumed: user active")
} }
} }
@@ -163,6 +167,7 @@ class TimerEngine: ObservableObject {
} }
private func updateConfigurations() { private func updateConfigurations() {
logger.debug("Updating timer configurations")
var newStates: [TimerIdentifier: TimerState] = [:] var newStates: [TimerIdentifier: TimerState] = [:]
// Update built-in timers // Update built-in timers
@@ -175,6 +180,7 @@ class TimerEngine: ObservableObject {
// Timer exists - check if interval changed // Timer exists - check if interval changed
if existingState.originalIntervalSeconds != config.intervalSeconds { if existingState.originalIntervalSeconds != config.intervalSeconds {
// Interval changed - reset with new interval // Interval changed - reset with new interval
logger.debug("Timer interval changed")
newStates[identifier] = TimerState( newStates[identifier] = TimerState(
identifier: identifier, identifier: identifier,
intervalSeconds: config.intervalSeconds, intervalSeconds: config.intervalSeconds,
@@ -187,6 +193,7 @@ class TimerEngine: ObservableObject {
} }
} else { } else {
// Timer was just enabled - create new state // Timer was just enabled - create new state
logger.debug("Timer enabled")
newStates[identifier] = TimerState( newStates[identifier] = TimerState(
identifier: identifier, identifier: identifier,
intervalSeconds: config.intervalSeconds, intervalSeconds: config.intervalSeconds,
@@ -208,6 +215,7 @@ class TimerEngine: ObservableObject {
// Check if interval changed // Check if interval changed
if existingState.originalIntervalSeconds != newIntervalSeconds { if existingState.originalIntervalSeconds != newIntervalSeconds {
// Interval changed - reset with new interval // Interval changed - reset with new interval
logger.debug("User timer interval changed")
newStates[identifier] = TimerState( newStates[identifier] = TimerState(
identifier: identifier, identifier: identifier,
intervalSeconds: newIntervalSeconds, intervalSeconds: newIntervalSeconds,
@@ -220,6 +228,7 @@ class TimerEngine: ObservableObject {
} }
} else { } else {
// New timer - create state // New timer - create state
logger.debug("User timer created")
newStates[identifier] = TimerState( newStates[identifier] = TimerState(
identifier: identifier, identifier: identifier,
intervalSeconds: newIntervalSeconds, intervalSeconds: newIntervalSeconds,
@@ -373,6 +382,7 @@ class TimerEngine: ObservableObject {
/// - Saves current time for elapsed calculation /// - Saves current time for elapsed calculation
/// - Pauses all active timers /// - Pauses all active timers
func handleSystemSleep() { func handleSystemSleep() {
logger.debug("System going to sleep")
sleepStartTime = timeProvider.now() sleepStartTime = timeProvider.now()
for (id, var state) in timerStates { for (id, var state) in timerStates {
state.pauseReasons.insert(.system) state.pauseReasons.insert(.system)
@@ -387,6 +397,7 @@ class TimerEngine: ObservableObject {
/// - Timers that expired during sleep will trigger immediately (1s delay) /// - Timers that expired during sleep will trigger immediately (1s delay)
/// - Resumes all timers /// - Resumes all timers
func handleSystemWake() { func handleSystemWake() {
logger.debug("System waking up")
guard let sleepStart = sleepStartTime else { guard let sleepStart = sleepStartTime else {
return return
} }

19
run
View File

@@ -129,7 +129,26 @@ elif [ "$ACTION" = "run" ]; then
if [ -d "$APP_PATH" ]; then if [ -d "$APP_PATH" ]; then
echo "🚀 Launching: $APP_PATH" echo "🚀 Launching: $APP_PATH"
if [ "$VERBOSE" = true ]; then
echo "📝 Capturing application logs in terminal (Ctrl+C to stop)..."
# Launch the app and capture its logs
open "$APP_PATH" &
APP_PID=$!
# Wait a moment for app to start, then capture logs
sleep 2
# Capture logs from the application using log stream
echo "Logs from Gaze.app will appear below (Ctrl+C to stop):"
echo "================================================================"
/usr/bin/log stream --predicate 'subsystem contains "com.mikefreno.Gaze"' --style compact 2>/dev/null | head -100
echo "================================================================"
echo "Application runtime logging stopped."
else
# Standard launch without logging
open "$APP_PATH" open "$APP_PATH"
fi
else else
echo "⚠️ App not found at expected location, trying fallback..." echo "⚠️ App not found at expected location, trying fallback..."
# Fallback to derived data location # Fallback to derived data location