feat: start better logging solution
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
import AppKit
|
||||
import Combine
|
||||
import SwiftUI
|
||||
import os.log
|
||||
|
||||
@MainActor
|
||||
class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
@@ -18,6 +19,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
private var hasStartedTimers = false
|
||||
|
||||
// Logging manager
|
||||
private let logger = LoggingManager.shared
|
||||
|
||||
// Smart Mode services
|
||||
private var fullscreenService: FullscreenDetectionService?
|
||||
private var idleService: IdleMonitoringService?
|
||||
@@ -38,6 +42,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
// 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)
|
||||
|
||||
setupSmartModeServices()
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
88
Gaze/Services/LoggingManager.swift
Normal file
88
Gaze/Services/LoggingManager.swift
Normal 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)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import os.log
|
||||
|
||||
@MainActor
|
||||
class TimerEngine: ObservableObject {
|
||||
@@ -28,6 +29,9 @@ class TimerEngine: ObservableObject {
|
||||
private var idleService: IdleMonitoringService?
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
// Logging manager
|
||||
private let logger = LoggingManager.shared.timerLogger
|
||||
|
||||
init(
|
||||
settingsManager: any SettingsProviding,
|
||||
enforceModeService: EnforceModeService? = nil,
|
||||
@@ -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
|
||||
}
|
||||
|
||||
21
run
21
run
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user