diff --git a/Ice/Bridging/Bridging.swift b/Ice/Bridging/Bridging.swift index edab15dd..d35d7a79 100644 --- a/Ice/Bridging/Bridging.swift +++ b/Ice/Bridging/Bridging.swift @@ -4,7 +4,6 @@ // import Cocoa -import OSLog /// A namespace for bridged functionality. enum Bridging { } @@ -25,7 +24,7 @@ extension Bridging { value as CFTypeRef ) if result != .success { - logError(to: .bridging, "CGSSetConnectionProperty failed with error \(result.logString)") + Logger.bridging.error("CGSSetConnectionProperty failed with error \(result.logString)") } } @@ -42,7 +41,7 @@ extension Bridging { &value ) if result != .success { - logError(to: .bridging, "CGSCopyConnectionProperty failed with error \(result.logString)") + Logger.bridging.error("CGSCopyConnectionProperty failed with error \(result.logString)") } return value?.takeRetainedValue() } @@ -60,7 +59,7 @@ extension Bridging { var rect = CGRect.zero let result = CGSGetScreenRectForWindow(CGSMainConnectionID(), windowID, &rect) guard result == .success else { - logError(to: .bridging, "CGSGetScreenRectForWindow failed with error \(result.logString)") + Logger.bridging.error("CGSGetScreenRectForWindow failed with error \(result.logString)") return nil } return rect @@ -73,7 +72,7 @@ extension Bridging { var count: Int32 = 0 let result = CGSGetWindowCount(CGSMainConnectionID(), 0, &count) if result != .success { - logError(to: .bridging, "CGSGetWindowCount failed with error \(result.logString)") + Logger.bridging.error("CGSGetWindowCount failed with error \(result.logString)") } return Int(count) } @@ -82,7 +81,7 @@ extension Bridging { var count: Int32 = 0 let result = CGSGetOnScreenWindowCount(CGSMainConnectionID(), 0, &count) if result != .success { - logError(to: .bridging, "CGSGetOnScreenWindowCount failed with error \(result.logString)") + Logger.bridging.error("CGSGetOnScreenWindowCount failed with error \(result.logString)") } return Int(count) } @@ -99,7 +98,7 @@ extension Bridging { &realCount ) guard result == .success else { - logError(to: .bridging, "CGSGetWindowList failed with error \(result.logString)") + Logger.bridging.error("CGSGetWindowList failed with error \(result.logString)") return [] } return [CGWindowID](list[.. [KeyCombination] { let status = CopySymbolicHotKeys(&symbolicHotkeys) guard status == noErr else { - logError(to: .keyCombination, "CopySymbolicHotKeys returned invalid status: \(status)") + Logger.error("CopySymbolicHotKeys returned invalid status: \(status)") return [] } guard let reservedHotkeys = symbolicHotkeys?.takeRetainedValue() as? [[String: Any]] else { - logError(to: .keyCombination, "Failed to serialize symbolic hotkeys") + Logger.error("Failed to serialize symbolic hotkeys") return [] } @@ -84,8 +83,3 @@ extension KeyCombination: Codable { try container.encode(modifiers.rawValue) } } - -// MARK: - Logger -private extension Logger { - static let keyCombination = Logger(category: "KeyCombination") -} diff --git a/Ice/Main/AppDelegate.swift b/Ice/Main/AppDelegate.swift index f68c7cf1..935efcc0 100644 --- a/Ice/Main/AppDelegate.swift +++ b/Ice/Main/AppDelegate.swift @@ -3,7 +3,6 @@ // Ice // -import OSLog import SwiftUI @MainActor @@ -14,7 +13,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { func applicationWillFinishLaunching(_ notification: Notification) { guard let appState else { - logWarning(to: .appDelegate, "Missing app state in applicationWillFinishLaunching") + Logger.appDelegate.warning("Missing app state in applicationWillFinishLaunching") return } @@ -30,7 +29,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ notification: Notification) { guard let appState else { - logWarning(to: .appDelegate, "Missing app state in applicationDidFinishLaunching") + Logger.appDelegate.warning("Missing app state in applicationDidFinishLaunching") return } @@ -57,7 +56,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { permissionsWindow.center() permissionsWindow.makeKeyAndOrderFront(nil) } else { - logError(to: .appDelegate, "Failed to open permissions window") + Logger.appDelegate.error("Failed to open permissions window") } } } @@ -78,7 +77,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { /// Assigns the app state to the delegate. func assignAppState(_ appState: AppState) { guard self.appState == nil else { - logWarning(to: .appDelegate, "Multiple attempts made to assign app state") + Logger.appDelegate.warning("Multiple attempts made to assign app state") return } self.appState = appState @@ -90,7 +89,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { let appState, let settingsWindow = appState.settingsWindow else { - logError(to: .appDelegate, "Failed to open settings window") + Logger.appDelegate.error("Failed to open settings window") return } // Small delay makes this more reliable. diff --git a/Ice/Main/AppState.swift b/Ice/Main/AppState.swift index 1bfabfd6..de8fd8e7 100644 --- a/Ice/Main/AppState.swift +++ b/Ice/Main/AppState.swift @@ -4,7 +4,6 @@ // import Combine -import OSLog import SwiftUI /// The model for app-wide state. @@ -127,7 +126,7 @@ final class AppState: ObservableObject { } .store(in: &c) } else { - logWarning(to: .appState, "No settings window!") + Logger.appState.warning("No settings window!") } Publishers.Merge( @@ -185,7 +184,7 @@ final class AppState: ObservableObject { /// Assigns the app delegate to the app state. func assignAppDelegate(_ appDelegate: AppDelegate) { guard self.appDelegate == nil else { - logWarning(to: .appState, "Multiple attempts made to assign app delegate") + Logger.appState.warning("Multiple attempts made to assign app delegate") return } self.appDelegate = appDelegate @@ -212,7 +211,7 @@ final class AppState: ObservableObject { activate() } else { Context.hasActivated[self] = true - logDebug(to: .appState, "First time activating app, so going through Dock") + Logger.appState.debug("First time activating app, so going through Dock") // Hack to make sure the app properly activates for the first time. NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.dock").first?.activate() DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { diff --git a/Ice/MenuBar/Appearance/MenuBarAppearanceManager.swift b/Ice/MenuBar/Appearance/MenuBarAppearanceManager.swift index ac14003a..70c35306 100644 --- a/Ice/MenuBar/Appearance/MenuBarAppearanceManager.swift +++ b/Ice/MenuBar/Appearance/MenuBarAppearanceManager.swift @@ -5,7 +5,6 @@ import Cocoa import Combine -import OSLog /// A manager for the appearance of the menu bar. @MainActor @@ -43,7 +42,7 @@ final class MenuBarAppearanceManager: ObservableObject { do { configuration = try .migrate(encoder: encoder, decoder: decoder) } catch { - logError(to: .appearanceManager, "Error decoding configuration: \(error)") + Logger.appearanceManager.error("Error decoding configuration: \(error)") } } @@ -70,7 +69,7 @@ final class MenuBarAppearanceManager: ObservableObject { .encode(encoder: encoder) .sink { completion in if case .failure(let error) = completion { - logError(to: .appearanceManager, "Error encoding configuration: \(error)") + Logger.appearanceManager.error("Error encoding configuration: \(error)") } } receiveValue: { data in Defaults.set(data, forKey: .menuBarAppearanceConfiguration) diff --git a/Ice/MenuBar/Appearance/MenuBarOverlayPanel.swift b/Ice/MenuBar/Appearance/MenuBarOverlayPanel.swift index 9acd5c04..bd73b264 100644 --- a/Ice/MenuBar/Appearance/MenuBarOverlayPanel.swift +++ b/Ice/MenuBar/Appearance/MenuBarOverlayPanel.swift @@ -5,7 +5,6 @@ import Cocoa import Combine -import OSLog // MARK: - Overlay Panel @@ -260,20 +259,20 @@ final class MenuBarOverlayPanel: NSPanel { case .updates: "Preventing overlay panel from updating." } guard let appState else { - logDebug(to: .overlayPanel, "No app state. \(actionMessage)") + Logger.overlayPanel.debug("No app state. \(actionMessage)") return nil } guard !appState.menuBarManager.isMenuBarHiddenBySystemUserDefaults else { - logDebug(to: .overlayPanel, "Menu bar is hidden by system. \(actionMessage)") + Logger.overlayPanel.debug("Menu bar is hidden by system. \(actionMessage)") return nil } guard !appState.isActiveSpaceFullscreen else { - logDebug(to: .overlayPanel, "Active space is fullscreen. \(actionMessage)") + Logger.overlayPanel.debug("Active space is fullscreen. \(actionMessage)") return nil } let owningDisplay = owningScreen.displayID guard appState.menuBarManager.hasValidMenuBar(in: windows, for: owningDisplay) else { - logDebug(to: .overlayPanel, "No valid menu bar found. \(actionMessage)") + Logger.overlayPanel.debug("No valid menu bar found. \(actionMessage)") return nil } return owningDisplay @@ -325,7 +324,7 @@ final class MenuBarOverlayPanel: NSPanel { } guard appState.menuBarManager.appearanceManager.overlayPanels.contains(self) else { - logWarning(to: .overlayPanel, "Overlay panel \(self) not retained") + Logger.overlayPanel.warning("Overlay panel \(self) not retained") return } diff --git a/Ice/MenuBar/MenuBarItemImageCache.swift b/Ice/MenuBar/MenuBarItemImageCache.swift index cb7bbed3..7bbffe3b 100644 --- a/Ice/MenuBar/MenuBarItemImageCache.swift +++ b/Ice/MenuBar/MenuBarItemImageCache.swift @@ -5,7 +5,6 @@ import Cocoa import Combine -import OSLog /// Cache for menu bar item images. final class MenuBarItemImageCache: ObservableObject { @@ -142,7 +141,7 @@ final class MenuBarItemImageCache: ObservableObject { images[itemInfo] = itemImage } } else { - logWarning(to: .imageCache, "Composite image capture failed. Attempting to capturing items individually.") + Logger.imageCache.warning("Composite image capture failed. Attempting to capturing items individually.") for windowID in windowIDs { guard @@ -199,7 +198,7 @@ final class MenuBarItemImageCache: ObservableObject { } let sectionImages = await createImages(for: section, screen: screen) guard !sectionImages.isEmpty else { - logWarning(to: .imageCache, "Update image cache failed for \(section.logString)") + Logger.imageCache.warning("Update image cache failed for \(section.logString)") continue } await context.merge(sectionImages) @@ -226,19 +225,19 @@ final class MenuBarItemImageCache: ObservableObject { if !isIceBarPresented && !isSearchPresented { guard await appState.navigationState.isAppFrontmost else { - logDebug(to: .imageCache, "Skipping image cache as Ice Bar not visible, app not frontmost") + Logger.imageCache.debug("Skipping image cache as Ice Bar not visible, app not frontmost") return } isSettingsPresented = await appState.navigationState.isSettingsPresented guard isSettingsPresented else { - logDebug(to: .imageCache, "Skipping image cache as Ice Bar not visible, Settings not visible") + Logger.imageCache.debug("Skipping image cache as Ice Bar not visible, Settings not visible") return } guard case .menuBarLayout = await appState.navigationState.settingsNavigationIdentifier else { - logDebug(to: .imageCache, "Skipping image cache as Ice Bar not visible, Settings visible but not on Menu Bar Layout pane") + Logger.imageCache.debug("Skipping image cache as Ice Bar not visible, Settings visible but not on Menu Bar Layout pane") return } } else { @@ -247,7 +246,7 @@ final class MenuBarItemImageCache: ObservableObject { if let lastItemMoveStartDate = await appState.itemManager.lastItemMoveStartDate { guard Date.now.timeIntervalSince(lastItemMoveStartDate) > 3 else { - logDebug(to: .imageCache, "Skipping image cache as an item was recently moved") + Logger.imageCache.debug("Skipping image cache as an item was recently moved") return } } diff --git a/Ice/MenuBar/MenuBarItemManager.swift b/Ice/MenuBar/MenuBarItemManager.swift index 928684dd..5da0b104 100644 --- a/Ice/MenuBar/MenuBarItemManager.swift +++ b/Ice/MenuBar/MenuBarItemManager.swift @@ -5,7 +5,6 @@ import Cocoa import Combine -import OSLog /// Manager for menu bar items. @MainActor @@ -178,10 +177,10 @@ extension MenuBarItemManager { /// Caches the given menu bar items, without checking whether the control items are in the correct order. private func uncheckedCacheItems(hiddenControlItem: MenuBarItem, alwaysHiddenControlItem: MenuBarItem?, otherItems: [MenuBarItem]) { func logNotAddedWarning(for item: MenuBarItem) { - logWarning(to: .itemManager, "\(item.logString) doesn't seem to be in a section, so it wasn't cached") + Logger.itemManager.warning("\(item.logString) doesn't seem to be in a section, so it wasn't cached") } - logDebug(to: .itemManager, "Caching menu bar items") + Logger.itemManager.debug("Caching menu bar items") update(&itemCache) { cache in cache.clear() @@ -218,20 +217,20 @@ extension MenuBarItemManager { /// Caches the current menu bar items if needed, ensuring that the control items are in the correct order. private func cacheItemsIfNeeded() async { guard tempShownItemContexts.isEmpty else { - logDebug(to: .itemManager, "Skipping item cache as items are temporarily shown") + Logger.itemManager.debug("Skipping item cache as items are temporarily shown") return } if let lastItemMoveStartDate { guard Date.now.timeIntervalSince(lastItemMoveStartDate) > 3 else { - logDebug(to: .itemManager, "Skipping item cache as an item was recently moved") + Logger.itemManager.debug("Skipping item cache as an item was recently moved") return } } let itemWindowIDs = Bridging.getWindowList(option: [.menuBarItems, .activeSpace]) if cachedItemWindowIDs == itemWindowIDs { - logDebug(to: .itemManager, "Skipping item cache as item windows have not changed") + Logger.itemManager.debug("Skipping item cache as item windows have not changed") return } else { cachedItemWindowIDs = itemWindowIDs @@ -250,8 +249,8 @@ extension MenuBarItemManager { .map { items.remove(at: $0) } guard let hiddenControlItem else { - logWarning(to: .itemManager, "Missing control item for hidden section") - logDebug(to: .itemManager, "Clearing item cache") + Logger.itemManager.warning("Missing control item for hidden section") + Logger.itemManager.debug("Clearing item cache") itemCache.clear() return } @@ -269,8 +268,8 @@ extension MenuBarItemManager { otherItems: items ) } catch { - logError(to: .itemManager, "Error enforcing control item order: \(error)") - logDebug(to: .itemManager, "Clearing item cache") + Logger.itemManager.error("Error enforcing control item order: \(error)") + Logger.itemManager.debug("Clearing item cache") itemCache.clear() } } @@ -410,7 +409,7 @@ extension MenuBarItemManager { /// - Parameter item: The item to return the current frame for. private func getCurrentFrame(for item: MenuBarItem) -> CGRect? { guard let frame = Bridging.getWindowFrame(for: item.window.windowID) else { - logError(to: .itemManager, "Couldn't get current frame for \(item.logString)") + Logger.itemManager.error("Couldn't get current frame for \(item.logString)") return nil } return frame @@ -502,7 +501,7 @@ extension MenuBarItemManager { /// - event: The event to post. /// - location: The event tap location to post the event to. private nonisolated func postEvent(_ event: CGEvent, to location: EventTap.Location) { - logDebug(to: .itemManager, "Posting \(event.type.logString) to \(location.logString)") + Logger.itemManager.debug("Posting \(event.type.logString) to \(location.logString)") switch location { case .hidEventTap: event.post(tap: .cghidEventTap) @@ -548,11 +547,11 @@ extension MenuBarItemManager { // Ensure the tap is enabled, preventing multiple calls to resume(). guard proxy.isEnabled else { - logDebug(to: .itemManager, "Event tap \"\(proxy.label)\" is disabled (item: \(item.logString))") + Logger.itemManager.debug("Event tap \"\(proxy.label)\" is disabled (item: \(item.logString))") return nil } - logDebug(to: .itemManager, "Received \(type.logString) at \(location.logString) (item: \(item.logString))") + Logger.itemManager.debug("Received \(type.logString) at \(location.logString) (item: \(item.logString))") proxy.disable() continuation.resume() @@ -561,7 +560,7 @@ extension MenuBarItemManager { } eventTap.enable(timeout: .milliseconds(50)) { - logError(to: .itemManager, "Event tap \"\(eventTap.label)\" timed out (item: \(item.logString))") + Logger.itemManager.error("Event tap \"\(eventTap.label)\" timed out (item: \(item.logString))") eventTap.disable() continuation.resume(throwing: EventError(code: .eventOperationTimeout, item: item)) } @@ -650,7 +649,7 @@ extension MenuBarItemManager { // Ensure the tap is enabled, preventing multiple calls to resume(). guard proxy.isEnabled else { - logDebug(to: .itemManager, "Event tap \"\(proxy.label)\" is disabled (item: \(item.logString))") + Logger.itemManager.debug("Event tap \"\(proxy.label)\" is disabled (item: \(item.logString))") return nil } @@ -664,7 +663,7 @@ extension MenuBarItemManager { // Enable both taps, with a timeout on the second tap. eventTap1.enable() eventTap2.enable(timeout: .milliseconds(50)) { - logError(to: .itemManager, "Event tap \"\(eventTap2.label)\" timed out (item: \(item.logString))") + Logger.itemManager.error("Event tap \"\(eventTap2.label)\" timed out (item: \(item.logString))") eventTap1.disable() eventTap2.disable() continuation.resume(throwing: EventError(code: .eventOperationTimeout, item: item)) @@ -692,7 +691,7 @@ extension MenuBarItemManager { ) async throws { guard let currentFrame = getCurrentFrame(for: item) else { try await scrombleEvent(event, from: firstLocation, to: secondLocation, item: item) - logWarning(to: .itemManager, "Couldn't get menu bar item frame for \(item.logString), so using fixed delay") + Logger.itemManager.warning("Couldn't get menu bar item frame for \(item.logString), so using fixed delay") // This will be slow, but subsequent events will have a better chance of succeeding. try await Task.sleep(for: .milliseconds(50)) return @@ -717,7 +716,7 @@ extension MenuBarItemManager { throw FrameCheckCancellationError() } if currentFrame != initialFrame { - logDebug(to: .itemManager, "Menu bar item frame for \(item.logString) has changed to \(NSStringFromRect(currentFrame))") + Logger.itemManager.debug("Menu bar item frame for \(item.logString) has changed to \(NSStringFromRect(currentFrame))") return } } @@ -726,7 +725,7 @@ extension MenuBarItemManager { do { try await frameCheckTask.value } catch is FrameCheckCancellationError { - logWarning(to: .itemManager, "Menu bar item frame check for \(item.logString) was cancelled, so using fixed delay") + Logger.itemManager.warning("Menu bar item frame check for \(item.logString) was cancelled, so using fixed delay") // This will be slow, but subsequent events will have a better chance of succeeding. try await Task.sleep(for: .milliseconds(50)) } catch is TaskTimeoutError { @@ -753,7 +752,7 @@ extension MenuBarItemManager { /// Tries to wake up the given item if it is not responding to events. private func wakeUpItem(_ item: MenuBarItem) async throws { - logDebug(to: .itemManager, "Attempting to wake up \(item.logString)") + Logger.itemManager.debug("Attempting to wake up \(item.logString)") guard let source = CGEventSource(stateID: .hidSystemState) else { throw EventError(code: .invalidEventSource, item: item) @@ -847,11 +846,11 @@ extension MenuBarItemManager { try await scrombleEvent(mouseUpEvent, from: .pid(item.ownerPID), to: .sessionEventTap, waitingForFrameChangeOf: item) } catch { do { - logDebug(to: .itemManager, "Posting fallback event for moving \(item.logString)") + Logger.itemManager.debug("Posting fallback event for moving \(item.logString)") // Catch this, as we still want to throw the existing error if the fallback fails. try await postEventAndWaitToReceive(fallbackEvent, to: .sessionEventTap, item: item) } catch { - logError(to: .itemManager, "Failed to post fallback event for moving \(item.logString)") + Logger.itemManager.error("Failed to post fallback event for moving \(item.logString)") } throw error } @@ -864,11 +863,11 @@ extension MenuBarItemManager { /// - destination: A destination to move the menu bar item. func move(item: MenuBarItem, to destination: MoveDestination) async throws { if try itemHasCorrectPosition(item: item, for: destination) { - logDebug(to: .itemManager, "\(item.logString) is already in the correct position") + Logger.itemManager.debug("\(item.logString) is already in the correct position") return } - logInfo(to: .itemManager, "Moving \(item.logString) to \(destination.logString)") + Logger.itemManager.info("Moving \(item.logString) to \(destination.logString)") guard let appState else { throw EventError(code: .invalidAppState, item: item) @@ -901,15 +900,15 @@ extension MenuBarItemManager { throw EventError(code: .invalidItem, item: item) } if newFrame != initialFrame { - logInfo(to: .itemManager, "Successfully moved \(item.logString)") + Logger.itemManager.info("Successfully moved \(item.logString)") break } else { throw EventError(code: .couldNotComplete, item: item) } } catch where n < 5 { - logWarning(to: .itemManager, "Attempt \(n) to move \(item.logString) failed (error: \(error))") + Logger.itemManager.warning("Attempt \(n) to move \(item.logString) failed (error: \(error))") try await wakeUpItem(item) - logInfo(to: .itemManager, "Retrying move of \(item.logString)") + Logger.itemManager.info("Retrying move of \(item.logString)") continue } } @@ -1008,11 +1007,11 @@ extension MenuBarItemManager { try await postEventAndWaitToReceive(mouseUpEvent, to: .sessionEventTap, item: item) } catch { do { - logDebug(to: .itemManager, "Posting fallback event for clicking \(item.logString)") + Logger.itemManager.debug("Posting fallback event for clicking \(item.logString)") // Catch this, as we still want to throw the existing error if the fallback fails. try await postEventAndWaitToReceive(fallbackEvent, to: .sessionEventTap, item: item) } catch { - logError(to: .itemManager, "Failed to post fallback event for clicking \(item.logString)") + Logger.itemManager.error("Failed to post fallback event for clicking \(item.logString)") } throw error } @@ -1020,19 +1019,19 @@ extension MenuBarItemManager { /// Clicks the given menu bar item with the left mouse button. func leftClick(item: MenuBarItem) async throws { - logInfo(to: .itemManager, "Left clicking \(item.logString)") + Logger.itemManager.info("Left clicking \(item.logString)") try await click(item: item, mouseDownButtonState: .leftMouseDown, mouseUpButtonState: .leftMouseUp) } /// Clicks the given menu bar item with the right mouse button. func rightClick(item: MenuBarItem) async throws { - logInfo(to: .itemManager, "Right clicking \(item.logString)") + Logger.itemManager.info("Right clicking \(item.logString)") try await click(item: item, mouseDownButtonState: .rightMouseDown, mouseUpButtonState: .rightMouseUp) } /// Clicks the given menu bar item with the center mouse button. func centerClick(item: MenuBarItem) async throws { - logInfo(to: .itemManager, "Center clicking \(item.logString)") + Logger.itemManager.info("Center clicking \(item.logString)") try await click(item: item, mouseDownButtonState: .otherMouseDown, mouseUpButtonState: .otherMouseUp) } } @@ -1056,14 +1055,14 @@ extension MenuBarItemManager { /// Schedules a timer for the given interval, attempting to rehide the current temporarily shown /// items when the timer fires. private func runTempShownItemTimer(for interval: TimeInterval) { - logDebug(to: .itemManager, "Running rehide timer for temporarily shown items with interval: \(interval)") + Logger.itemManager.debug("Running rehide timer for temporarily shown items with interval: \(interval)") tempShownItemsTimer?.invalidate() tempShownItemsTimer = .scheduledTimer(withTimeInterval: interval, repeats: false) { [weak self] timer in guard let self else { timer.invalidate() return } - logDebug(to: .itemManager, "Rehide timer fired") + Logger.itemManager.debug("Rehide timer fired") Task { await self.rehideTempShownItems() } @@ -1099,7 +1098,7 @@ extension MenuBarItemManager { assertionFailure("Unknown mouse button \(mouseButton)") } } catch { - logError(to: .itemManager, "ERROR: \(error)") + Logger.itemManager.error("ERROR: \(error)") } } } @@ -1111,16 +1110,16 @@ extension MenuBarItemManager { let screen = NSScreen.main, let applicationMenuFrame = appState.menuBarManager.getApplicationMenuFrame(for: screen.displayID) else { - logWarning(to: .itemManager, "No application menu frame, so not showing \(item.logString)") + Logger.itemManager.warning("No application menu frame, so not showing \(item.logString)") return } - logInfo(to: .itemManager, "Temporarily showing \(item.logString)") + Logger.itemManager.info("Temporarily showing \(item.logString)") var items = MenuBarItem.getMenuBarItemsPrivateAPI(onScreenOnly: false, activeSpaceOnly: true) guard let destination = getReturnDestination(for: item, in: items) else { - logError(to: .itemManager, "No return destination for \(item.logString)") + Logger.itemManager.warning("No return destination for \(item.logString)") return } @@ -1141,7 +1140,7 @@ extension MenuBarItemManager { } guard let targetItem = items.first else { - logError(to: .itemManager, "Not enough room to show \(item.logString)") + Logger.itemManager.warning("Not enough room to show \(item.logString)") return } @@ -1162,13 +1161,13 @@ extension MenuBarItemManager { assertionFailure("Unknown mouse button \(mouseButton)") } } catch { - logError(to: .itemManager, "ERROR: \(error)") + Logger.itemManager.error("ERROR: \(error)") } } else { do { try await move(item: item, to: .leftOfItem(targetItem)) } catch { - logError(to: .itemManager, "ERROR: \(error)") + Logger.itemManager.error("ERROR: \(error)") } } @@ -1207,14 +1206,14 @@ extension MenuBarItemManager { do { try await interfaceCheckTask.value } catch is TaskTimeoutError { - logDebug(to: .itemManager, "Menu check task timed out, switching to timer") + Logger.itemManager.debug("Menu check task timed out, switching to timer") runTempShownItemTimer(for: 3) return } catch { - logError(to: .itemManager, "ERROR: \(error)") + Logger.itemManager.error("ERROR: \(error)") } - logInfo(to: .itemManager, "Rehiding temporarily shown items") + Logger.itemManager.info("Rehiding temporarily shown items") var failedContexts = [TempShownItemContext]() @@ -1227,7 +1226,7 @@ extension MenuBarItemManager { do { try await move(item: item, to: context.returnDestination) } catch { - logError(to: .itemManager, "Failed to rehide \(item.logString) (error: \(error))") + Logger.itemManager.error("Failed to rehide \(item.logString) (error: \(error))") failedContexts.append(context) } } @@ -1260,15 +1259,15 @@ extension MenuBarItemManager { /// - alwaysHiddenControlItem: A menu bar item that represents the control item for the always-hidden section. func enforceControlItemOrder(hiddenControlItem: MenuBarItem, alwaysHiddenControlItem: MenuBarItem) async throws { guard !isMouseButtonDown else { - logDebug(to: .itemManager, "Mouse button is down, so will not enforce control item order") + Logger.itemManager.debug("Mouse button is down, so will not enforce control item order") return } guard mouseMovedCount <= 0 else { - logDebug(to: .itemManager, "Mouse has recently moved, so will not enforce control item order") + Logger.itemManager.debug("Mouse has recently moved, so will not enforce control item order") return } if hiddenControlItem.frame.maxX <= alwaysHiddenControlItem.frame.minX { - logInfo(to: .itemManager, "Arranging menu bar items") + Logger.itemManager.info("Arranging menu bar items") try await slowMove(item: alwaysHiddenControlItem, to: .leftOfItem(hiddenControlItem)) } } diff --git a/Ice/MenuBar/MenuBarManager.swift b/Ice/MenuBar/MenuBarManager.swift index 6c940874..3f50a3a7 100644 --- a/Ice/MenuBar/MenuBarManager.swift +++ b/Ice/MenuBar/MenuBarManager.swift @@ -5,7 +5,6 @@ import AXSwift import Combine -import OSLog import SwiftUI /// Manager for the state of the menu bar. @@ -75,7 +74,7 @@ final class MenuBarManager: ObservableObject { private func initializeSections() { // Make sure initialization can only happen once. guard sections.isEmpty else { - logWarning(to: .menuBarManager, "Sections already initialized") + Logger.menuBarManager.warning("Sections already initialized") return } @@ -365,20 +364,20 @@ final class MenuBarManager: ObservableObject { func hideApplicationMenus() { guard let appState else { - logError(to: .menuBarManager, "Error hiding application menus: Missing app state") + Logger.menuBarManager.error("Error hiding application menus: Missing app state") return } - logInfo(to: .menuBarManager, "Hiding application menus") + Logger.menuBarManager.info("Hiding application menus") appState.activate(withPolicy: .regular) isHidingApplicationMenus = true } func showApplicationMenus() { guard let appState else { - logError(to: .menuBarManager, "Error showing application menus: Missing app state") + Logger.menuBarManager.error("Error showing application menus: Missing app state") return } - logInfo(to: .menuBarManager, "Showing application menus") + Logger.menuBarManager.info("Showing application menus") appState.deactivate(withPolicy: .accessory) isHidingApplicationMenus = false } @@ -394,7 +393,7 @@ final class MenuBarManager: ObservableObject { /// Shows the appearance editor popover, centered under the menu bar. @objc private func showAppearanceEditorPopover() { guard let appState else { - logError(to: .menuBarManager, "Error showing appearance editor popover: Missing app state") + Logger.menuBarManager.error("Error showing appearance editor popover: Missing app state") return } let panel = MenuBarAppearanceEditorPanel(appState: appState) diff --git a/Ice/MenuBar/MenuBarSection.swift b/Ice/MenuBar/MenuBarSection.swift index 62d0c825..02acca2a 100644 --- a/Ice/MenuBar/MenuBarSection.swift +++ b/Ice/MenuBar/MenuBarSection.swift @@ -4,7 +4,6 @@ // import Cocoa -import OSLog /// A representation of a section in a menu bar. @MainActor @@ -127,7 +126,7 @@ final class MenuBarSection { /// Assigns the section's app state. func assignAppState(_ appState: AppState) { guard self.appState == nil else { - logWarning(to: .menuBarSection, "Multiple attempts made to assign app state") + Logger.menuBarSection.warning("Multiple attempts made to assign app state") return } self.appState = appState diff --git a/Ice/MenuBar/Spacing/MenuBarItemSpacingManager.swift b/Ice/MenuBar/Spacing/MenuBarItemSpacingManager.swift index 80d0d48f..5a1faba6 100644 --- a/Ice/MenuBar/Spacing/MenuBarItemSpacingManager.swift +++ b/Ice/MenuBar/Spacing/MenuBarItemSpacingManager.swift @@ -5,7 +5,6 @@ import Cocoa import Combine -import OSLog /// Manager for menu bar item spacing. @MainActor @@ -77,10 +76,10 @@ final class MenuBarItemSpacingManager { /// Asynchronously signals the given app to quit. private func signalAppToQuit(_ app: NSRunningApplication) async throws { if app.isTerminated { - logDebug(to: .spacing, "Application \"\(logString(for: app))\" is already terminated") + Logger.spacing.debug("Application \"\(logString(for: app))\" is already terminated") return } else { - logDebug(to: .spacing, "Signaling application \"\(logString(for: app))\" to quit") + Logger.spacing.debug("Signaling application \"\(logString(for: app))\" to quit") } app.terminate() @@ -90,7 +89,7 @@ final class MenuBarItemSpacingManager { let timeoutTask = Task { try await Task.sleep(for: .seconds(forceTerminateDelay)) if !app.isTerminated { - logDebug(to: .spacing, "Application \"\(logString(for: app))\" did not terminate within \(forceTerminateDelay) seconds, attempting to force terminate") + Logger.spacing.debug("Application \"\(logString(for: app))\" did not terminate within \(forceTerminateDelay) seconds, attempting to force terminate") app.forceTerminate() } } @@ -104,7 +103,7 @@ final class MenuBarItemSpacingManager { } timeoutTask.cancel() cancellable?.cancel() - logDebug(to: .spacing, "Application \"\(logString(for: app))\" terminated successfully") + Logger.spacing.debug("Application \"\(logString(for: app))\" terminated successfully") continuation.resume() } } @@ -113,7 +112,7 @@ final class MenuBarItemSpacingManager { /// Asynchronously launches the app at the given URL. private nonisolated func launchApp(at applicationURL: URL, bundleIdentifier: String) async throws { if let app = NSWorkspace.shared.runningApplications.first(where: { $0.bundleIdentifier == bundleIdentifier }) { - logDebug(to: .spacing, "Application \"\(logString(for: app))\" is already open, so skipping launch") + Logger.spacing.debug("Application \"\(logString(for: app))\" is already open, so skipping launch") return } let configuration = NSWorkspace.OpenConfiguration() diff --git a/Ice/Settings/SettingsManagers/GeneralSettingsManager.swift b/Ice/Settings/SettingsManagers/GeneralSettingsManager.swift index 3b9ed625..d4d0cd58 100644 --- a/Ice/Settings/SettingsManagers/GeneralSettingsManager.swift +++ b/Ice/Settings/SettingsManagers/GeneralSettingsManager.swift @@ -5,7 +5,6 @@ import Combine import Foundation -import OSLog @MainActor final class GeneralSettingsManager: ObservableObject { @@ -107,7 +106,7 @@ final class GeneralSettingsManager: ObservableObject { do { iceIcon = try decoder.decode(ControlItemImageSet.self, from: data) } catch { - logError(to: .generalSettingsManager, "Error decoding Ice icon: \(error)") + Logger.generalSettingsManager.error("Error decoding Ice icon: \(error)") } if case .custom = iceIcon.name { lastCustomIceIcon = iceIcon @@ -138,7 +137,7 @@ final class GeneralSettingsManager: ObservableObject { let data = try encoder.encode(iceIcon) Defaults.set(data, forKey: .iceIcon) } catch { - logError(to: .generalSettingsManager, "Error encoding Ice icon: \(error)") + Logger.generalSettingsManager.error("Error encoding Ice icon: \(error)") } } .store(in: &c) diff --git a/Ice/Settings/SettingsManagers/HotkeySettingsManager.swift b/Ice/Settings/SettingsManagers/HotkeySettingsManager.swift index e92e06cf..ea8c2cbe 100644 --- a/Ice/Settings/SettingsManagers/HotkeySettingsManager.swift +++ b/Ice/Settings/SettingsManagers/HotkeySettingsManager.swift @@ -4,7 +4,7 @@ // import Combine -import OSLog +import Foundation @MainActor final class HotkeySettingsManager: ObservableObject { @@ -41,7 +41,7 @@ final class HotkeySettingsManager: ObservableObject { do { hotkey.keyCombination = try decoder.decode(KeyCombination?.self, from: data) } catch { - logError(to: .hotkeySettingsManager, "Error decoding hotkey: \(error)") + Logger.hotkeySettingsManager.error("Error decoding hotkey: \(error)") } } } @@ -66,7 +66,7 @@ final class HotkeySettingsManager: ObservableObject { do { dict[hotkey.action.rawValue] = try self.encoder.encode(hotkey.keyCombination) } catch { - logError(to: .hotkeySettingsManager, "Error encoding hotkey: \(error)") + Logger.hotkeySettingsManager.error("Error encoding hotkey: \(error)") } } Defaults.set(dict, forKey: .hotkeys) diff --git a/Ice/UI/IceUI/IceMenu.swift b/Ice/UI/IceUI/IceMenu.swift index 3b5b69d7..988ee63c 100644 --- a/Ice/UI/IceUI/IceMenu.swift +++ b/Ice/UI/IceUI/IceMenu.swift @@ -4,7 +4,6 @@ // import SwiftUI -import OSLog struct IceMenu: View { @State private var isHovering = false @@ -110,7 +109,7 @@ private struct IceMenuLayout: _VariadicView_UnaryViewRoot { private final class IceMenuItemAction: Hashable { static let nullAction = IceMenuItemAction { - logWarning(to: .iceMenu, "No action assigned to menu item") + Logger.warning("No action assigned to menu item") } let body: () -> Void @@ -161,8 +160,3 @@ extension View { preference(key: IceMenuItemActionKey.self, value: IceMenuItemAction(body: action)) } } - -// MARK: - Logger -private extension Logger { - static let iceMenu = Logger(category: "IceMenu") -} diff --git a/Ice/UI/LayoutBar/LayoutBarPaddingView.swift b/Ice/UI/LayoutBar/LayoutBarPaddingView.swift index 494fa68c..4317e398 100644 --- a/Ice/UI/LayoutBar/LayoutBarPaddingView.swift +++ b/Ice/UI/LayoutBar/LayoutBarPaddingView.swift @@ -5,7 +5,6 @@ import Cocoa import Combine -import OSLog /// A Cocoa view that manages the menu bar layout interface. final class LayoutBarPaddingView: NSView { @@ -112,7 +111,7 @@ final class LayoutBarPaddingView: NSView { if let targetItem { move(item: draggingSource.item, to: .leftOfItem(targetItem)) } else { - logError(to: .layoutBar, "No target item for layout bar drag") + Logger.layoutBar.error("No target item for layout bar drag") } } else if arrangedViews.indices.contains(index + 1) { // we have a view to the right of the dragging source @@ -137,7 +136,7 @@ final class LayoutBarPaddingView: NSView { try await appState.itemManager.slowMove(item: item, to: destination) appState.itemManager.removeTempShownItemFromCache(with: item.info) } catch { - logError(to: .layoutBar, "Error moving menu bar item: \(error)") + Logger.layoutBar.error("Error moving menu bar item: \(error)") let alert = NSAlert(error: error) alert.runModal() } diff --git a/Ice/Utilities/EventMonitors/EventTap.swift b/Ice/Utilities/EventMonitors/EventTap.swift index 47067768..01a44bbc 100644 --- a/Ice/Utilities/EventMonitors/EventTap.swift +++ b/Ice/Utilities/EventMonitors/EventTap.swift @@ -4,7 +4,6 @@ // import Cocoa -import OSLog /// A type that receives system events from various locations within the /// event stream. @@ -133,11 +132,11 @@ final class EventTap { callback: handleEvent, userInfo: Unmanaged.passUnretained(self).toOpaque() ) else { - logError(to: .eventTap, "Error creating mach port for event tap \"\(self.label)\"") + Logger.eventTap.error("Error creating mach port for event tap \"\(self.label)\"") return } guard let source = CFMachPortCreateRunLoopSource(nil, machPort, 0) else { - logError(to: .eventTap, "Error creating run loop source for event tap \"\(self.label)\"") + Logger.eventTap.error("Error creating run loop source for event tap \"\(self.label)\"") return } self.machPort = machPort @@ -206,15 +205,15 @@ final class EventTap { @MainActor private func withUnwrappedComponents(body: @MainActor (CFRunLoop, CFRunLoopSource, CFMachPort) -> Void) { guard let runLoop else { - logError(to: .eventTap, "Missing run loop for event tap \"\(self.label)\"") + Logger.eventTap.error("Missing run loop for event tap \"\(self.label)\"") return } guard let source else { - logError(to: .eventTap, "Missing run loop source for event tap \"\(self.label)\"") + Logger.eventTap.error("Missing run loop source for event tap \"\(self.label)\"") return } guard let machPort else { - logError(to: .eventTap, "Missing mach port for event tap \"\(self.label)\"") + Logger.eventTap.error("Missing mach port for event tap \"\(self.label)\"") return } body(runLoop, source, machPort) diff --git a/Ice/Utilities/Logging.swift b/Ice/Utilities/Logging.swift index 70158e6a..f9112159 100644 --- a/Ice/Utilities/Logging.swift +++ b/Ice/Utilities/Logging.swift @@ -5,34 +5,61 @@ import OSLog -/// Logs the given message using the specified logger and level. -func logMessage(to logger: Logger, at level: OSLogType, _ message: String) { - logger.log(level: level, "\(message, privacy: .public)") -} +/// A type that encapsulates logging behavior for Ice. +struct Logger { + /// The unified logger at the base of this logger. + private let base: os.Logger -/// Logs the given informative message using the specified logger. -func logInfo(to logger: Logger, _ message: String) { - logMessage(to: logger, at: .info, message) -} + /// Creates a logger for Ice using the specified category. + init(category: String) { + self.base = os.Logger(subsystem: Constants.bundleIdentifier, category: category) + } -/// Logs the given debug message using the specified logger. -func logDebug(to logger: Logger, _ message: String) { - logMessage(to: logger, at: .debug, message) -} + /// Logs the given informative message to the logger. + func info(_ message: String) { + base.info("\(message, privacy: .public)") + } + + /// Logs the given debug message to the logger. + func debug(_ message: String) { + base.debug("\(message, privacy: .public)") + } + + /// Logs the given error message to the logger. + func error(_ message: String) { + base.error("\(message, privacy: .public)") + } + + /// Logs the given warning message to the logger. + func warning(_ message: String) { + base.warning("\(message, privacy: .public)") + } -/// Logs the given error message using the specified logger. -func logError(to logger: Logger, _ message: String) { - logMessage(to: logger, at: .error, message) } -/// Logs the given warning message using the specified logger. -func logWarning(to logger: Logger, _ message: String) { - logError(to: logger, message) +extension Logger { + /// The default logger. + static let `default` = Logger(category: "Default") } extension Logger { - /// Creates a logger for Ice using the specified category. - init(category: String) { - self.init(subsystem: Constants.bundleIdentifier, category: category) + /// Logs the given informative message to the default logger. + static func info(_ message: String) { + Logger.default.info(message) + } + + /// Logs the given debug message to the default logger. + static func debug(_ message: String) { + Logger.default.debug(message) + } + + /// Logs the given error message to the default logger. + static func error(_ message: String) { + Logger.default.error(message) + } + + /// Logs the given warning message to the default logger. + static func warning(_ message: String) { + Logger.default.warning(message) } } diff --git a/Ice/Utilities/MigrationManager.swift b/Ice/Utilities/MigrationManager.swift index 9825c060..01bfc87a 100644 --- a/Ice/Utilities/MigrationManager.swift +++ b/Ice/Utilities/MigrationManager.swift @@ -4,7 +4,6 @@ // import Cocoa -import OSLog @MainActor struct MigrationManager { @@ -42,7 +41,7 @@ extension MigrationManager { } private func logError(_ error: any Error) { - Ice.logError(to: .migration, "Migration failed with error: \(error)") + Logger.migration.error("Migration failed with error: \(error)") } } @@ -61,7 +60,7 @@ extension MigrationManager { migrateSections0_8_0, ]) Defaults.set(true, forKey: .hasMigrated0_8_0) - logInfo(to: .migration, "Successfully migrated to 0.8.0 settings") + Logger.migration.info("Successfully migrated to 0.8.0 settings") } // MARK: Migrate Hotkeys @@ -186,7 +185,7 @@ extension MigrationManager { migrateControlItems0_10_0, ]) Defaults.set(true, forKey: .hasMigrated0_10_0) - logInfo(to: .migration, "Successfully migrated to 0.10.0 settings") + Logger.migration.info("Successfully migrated to 0.10.0 settings") } private func migrateControlItems0_10_0() throws { @@ -212,7 +211,7 @@ extension MigrationManager { switch result { case .success, .successButShowAlert: Defaults.set(true, forKey: .hasMigrated0_10_1) - logInfo(to: .migration, "Successfully migrated to 0.10.1 settings") + Logger.migration.info("Successfully migrated to 0.10.1 settings") case .failureLoggingError: break } diff --git a/Ice/Utilities/MouseCursor.swift b/Ice/Utilities/MouseCursor.swift index 5aaed8c3..fc12c9a3 100644 --- a/Ice/Utilities/MouseCursor.swift +++ b/Ice/Utilities/MouseCursor.swift @@ -4,7 +4,6 @@ // import CoreGraphics -import OSLog /// A namespace for mouse cursor operations. enum MouseCursor { @@ -12,7 +11,7 @@ enum MouseCursor { static func hide() { let result = CGDisplayHideCursor(CGMainDisplayID()) if result != .success { - logError(to: .mouseCursor, "CGDisplayHideCursor failed with error \(result.logString)") + Logger.error("CGDisplayHideCursor failed with error \(result.logString)") } } @@ -20,7 +19,7 @@ enum MouseCursor { static func show() { let result = CGDisplayShowCursor(CGMainDisplayID()) if result != .success { - logError(to: .mouseCursor, "CGDisplayShowCursor failed with error \(result.logString)") + Logger.error("CGDisplayShowCursor failed with error \(result.logString)") } } @@ -30,7 +29,7 @@ enum MouseCursor { static func warp(to point: CGPoint) { let result = CGWarpMouseCursorPosition(point) if result != .success { - logError(to: .mouseCursor, "CGWarpMouseCursorPosition failed with error \(result.logString)") + Logger.error("CGWarpMouseCursorPosition failed with error \(result.logString)") } } @@ -52,8 +51,3 @@ enum MouseCursor { } } } - -// MARK: - Logger -private extension Logger { - static let mouseCursor = Logger(category: "MouseCursor") -}