Atoll/DynamicIsland/utils/Logger.swift
2026-01-29 12:26:04 +05:30

139 lines
No EOL
4.3 KiB
Swift

/*
* Atoll (DynamicIsland)
* Copyright (C) 2024-2026 Atoll Contributors
*
* Originally from boring.notch project
* Modified and adapted for Atoll (DynamicIsland)
* See NOTICE for details.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import Foundation
import OSLog
import SwiftUI
enum LogCategory: String {
case lifecycle = "🔄"
case memory = "💾"
case performance = "⚡️"
case ui = "🎨"
case network = "🌐"
case error = "❌"
case warning = "⚠️"
case success = "✅"
case debug = "🔍"
case extensions = "🧩"
var osCategoryName: String {
switch self {
case .lifecycle: return "lifecycle"
case .memory: return "memory"
case .performance: return "performance"
case .ui: return "ui"
case .network: return "network"
case .error: return "error"
case .warning: return "warning"
case .success: return "success"
case .debug: return "debug"
case .extensions: return "extensions"
}
}
}
struct Logger {
private static let subsystem = "com.ebullioscopic.Atoll"
private static let dateFormatter: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
return formatter
}()
private static var osLoggerCache: [LogCategory: OSLog] = [:]
private static func osLogger(for category: LogCategory) -> OSLog {
if let cached = osLoggerCache[category] {
return cached
}
let logger = OSLog(subsystem: subsystem, category: category.osCategoryName)
osLoggerCache[category] = logger
return logger
}
static func log(
_ message: String,
category: LogCategory,
file: String = #file,
function: String = #function,
line: Int = #line
) {
let fileName = (file as NSString).lastPathComponent
let timestamp = dateFormatter.string(from: Date())
let entry = "\(category.rawValue) [\(timestamp)] [\(fileName):\(line)] \(function) - \(message)"
let logger = osLogger(for: category)
os_log("%{public}@", log: logger, type: .default, entry)
#if DEBUG
Swift.print(entry)
#endif
}
static func trackMemory(
file: String = #file,
function: String = #function,
line: Int = #line
) {
var info = mach_task_basic_info()
var count = mach_msg_type_number_t(MemoryLayout<mach_task_basic_info>.size)/4
let kerr: kern_return_t = withUnsafeMutablePointer(to: &info) {
$0.withMemoryRebound(to: integer_t.self, capacity: 1) {
task_info(mach_task_self_,
task_flavor_t(MACH_TASK_BASIC_INFO),
$0,
&count)
}
}
if kerr == KERN_SUCCESS {
let usedMB = Double(info.resident_size) / 1024.0 / 1024.0
log(String(format: "Memory used: %.2f MB", usedMB),
category: .memory,
file: file,
function: function,
line: line)
}
}
}
extension View {
func trackLifecycle(_ identifier: String) -> some View {
self.modifier(ViewLifecycleTracker(identifier: identifier))
}
}
struct ViewLifecycleTracker: ViewModifier {
let identifier: String
func body(content: Content) -> some View {
content
.onAppear {
Logger.log("\(identifier) appeared", category: .lifecycle)
Logger.trackMemory()
}
.onDisappear {
Logger.log("\(identifier) disappeared", category: .lifecycle)
Logger.trackMemory()
}
}
}