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 Combine
import SwiftUI
import os.log
@MainActor
class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
@@ -17,6 +18,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
private var updateManager: UpdateManager?
private var cancellables = Set<AnyCancellable>()
private var hasStartedTimers = false
// Logging manager
private let logger = LoggingManager.shared
// Smart Mode services
private var fullscreenService: FullscreenDetectionService?
@@ -37,6 +41,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
func applicationDidFinishLaunching(_ notification: Notification) {
// Set activation policy to hide dock icon
NSApplication.shared.setActivationPolicy(.accessory)
// Initialize logging
logger.configureLogging()
logger.appLogger.info("🚀 Application did finish launching")
timerEngine = TimerEngine(settingsManager: settingsManager)
@@ -103,6 +111,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
private func startTimers() {
guard !hasStartedTimers else { return }
hasStartedTimers = true
logger.appLogger.info("Starting timers")
timerEngine?.start()
observeReminderEvents()
}
@@ -123,6 +132,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
}
func applicationWillTerminate(_ notification: Notification) {
logger.appLogger.info(" applicationWill terminate")
settingsManager.saveImmediately()
timerEngine?.stop()
}
@@ -144,11 +154,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
}
@objc private func systemWillSleep() {
logger.systemLogger.info("System will sleep")
timerEngine?.handleSystemSleep()
settingsManager.saveImmediately()
}
@objc private func systemDidWake() {
logger.systemLogger.info("System did wake")
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 CoreGraphics
import Foundation
import os.log
public enum ScreenCaptureAuthorizationStatus: Equatable {
case authorized
@@ -71,6 +72,7 @@ final class ScreenCapturePermissionManager: ObservableObject, ScreenCapturePermi
}
func openSystemSettings() {
LoggingManager.shared.uiLogger.log("sup")
// Try different variations
let possibleUrls = [
"x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenRecording",

View File

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

21
run
View File

@@ -129,7 +129,26 @@ elif [ "$ACTION" = "run" ]; then
if [ -d "$APP_PATH" ]; then
echo "🚀 Launching: $APP_PATH"
open "$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"
fi
else
echo "⚠️ App not found at expected location, trying fallback..."
# Fallback to derived data location