feat: start better logging solution
This commit is contained in:
@@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
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 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",
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
21
run
21
run
@@ -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"
|
||||||
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
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user