From 22e02633b5d99f18759a7628976f8f878e7bb4e2 Mon Sep 17 00:00:00 2001 From: jack Date: Fri, 4 Jul 2025 19:38:08 +0200 Subject: [PATCH] Add comprehensive logging system for debugging - Create LoggingService that writes to Documents/Logs directory - Log to both console and file with timestamps - Replace debug print statements with bitchatLog() - Add log file location display in AppInfoView - Enable opening log file in Finder on macOS - Track room member operations and view state - Persist logs for debugging room member display issues --- bitchat.xcodeproj/project.pbxproj | 31 ++++----- bitchat/Services/LoggingService.swift | 93 ++++++++++++++++++++++++++ bitchat/ViewModels/ChatViewModel.swift | 10 ++- bitchat/Views/AppInfoView.swift | 50 ++++++++++++++ bitchat/Views/ContentView.swift | 8 +-- 5 files changed, 167 insertions(+), 25 deletions(-) create mode 100644 bitchat/Services/LoggingService.swift diff --git a/bitchat.xcodeproj/project.pbxproj b/bitchat.xcodeproj/project.pbxproj index 7493a94b..e9074598 100644 --- a/bitchat.xcodeproj/project.pbxproj +++ b/bitchat.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 63; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -11,8 +11,10 @@ 1D9674FA5F998503831DC281 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A08E03AA0C63E97C91749AEC /* ContentView.swift */; }; 4B747085D07A1BCE0F5BA612 /* BinaryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A2136C3E22D02D4A8DBE7EAB /* BinaryProtocol.swift */; }; 61C81ED5F679D5E973EE0C07 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3448F84BF86A42A3CC4A9379 /* NotificationService.swift */; }; + 6A624D9359BCCAAE58CE0D64 /* LoggingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96CDF6D0AF0F2052A6C7E634 /* LoggingService.swift */; }; 6DE056E1EE9850E9FBF50157 /* BitchatProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 229F17B68CFF7AB1BC91C847 /* BitchatProtocol.swift */; }; 6E7761E21C99F28AE2F9BE5F /* BitchatApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF625BB3AD919322C01A46B2 /* BitchatApp.swift */; }; + 72BDC660D445E2D24AB90326 /* LoggingService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96CDF6D0AF0F2052A6C7E634 /* LoggingService.swift */; }; 739429DFDE5C5829CF70DA7D /* EncryptionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DC1563390A15C042D059CF9 /* EncryptionService.swift */; }; 749D8CF8A362B6CD0786782D /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3448F84BF86A42A3CC4A9379 /* NotificationService.swift */; }; 7576A357B278E5733E9D9F33 /* ChatViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6B8F7B7D55092C2540A7996 /* ChatViewModel.swift */; }; @@ -36,7 +38,8 @@ 6DC1563390A15C042D059CF9 /* EncryptionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionService.swift; sourceTree = ""; }; 763E0DBA9492A654FC0CDCB9 /* AppInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppInfoView.swift; sourceTree = ""; }; 7EEBDA723E1CFD88758DA4AC /* bitchat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = bitchat.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 997D512074C64904D75DDD40 /* bitchat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = bitchat.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 96CDF6D0AF0F2052A6C7E634 /* LoggingService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingService.swift; sourceTree = ""; }; + 997D512074C64904D75DDD40 /* bitchat.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = bitchat.app; sourceTree = BUILT_PRODUCTS_DIR; }; A08E03AA0C63E97C91749AEC /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; A2136C3E22D02D4A8DBE7EAB /* BinaryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BinaryProtocol.swift; sourceTree = ""; }; D5C3D880FF8AE1673B20E1E3 /* BluetoothMeshService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothMeshService.swift; sourceTree = ""; }; @@ -108,6 +111,7 @@ children = ( D5C3D880FF8AE1673B20E1E3 /* BluetoothMeshService.swift */, 6DC1563390A15C042D059CF9 /* EncryptionService.swift */, + 96CDF6D0AF0F2052A6C7E634 /* LoggingService.swift */, 3448F84BF86A42A3CC4A9379 /* NotificationService.swift */, ); path = Services; @@ -179,6 +183,7 @@ ); mainGroup = 18198ED912AAF495D8AF7763; minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 54; projectDirPath = ""; projectRoot = ""; targets = ( @@ -220,6 +225,7 @@ D450CF41F207BDE1A1AAA56E /* ChatViewModel.swift in Sources */, 92D34E7A07C990C8A815B0CE /* ContentView.swift in Sources */, 739429DFDE5C5829CF70DA7D /* EncryptionService.swift in Sources */, + 72BDC660D445E2D24AB90326 /* LoggingService.swift in Sources */, 749D8CF8A362B6CD0786782D /* NotificationService.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -236,6 +242,7 @@ 7576A357B278E5733E9D9F33 /* ChatViewModel.swift in Sources */, 1D9674FA5F998503831DC281 /* ContentView.swift in Sources */, DDA1DFAB1FF7AADE52DC0F53 /* EncryptionService.swift in Sources */, + 6A624D9359BCCAAE58CE0D64 /* LoggingService.swift in Sources */, 61C81ED5F679D5E973EE0C07 /* NotificationService.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -252,11 +259,8 @@ CODE_SIGNING_REQUIRED = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = L3N5LHJD5Y; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = bitchat/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = bitchat; - INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -266,12 +270,9 @@ PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat; PRODUCT_NAME = bitchat; SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; }; @@ -284,11 +285,8 @@ CODE_SIGNING_REQUIRED = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = L3N5LHJD5Y; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = bitchat/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = bitchat; - INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -298,12 +296,9 @@ PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat; PRODUCT_NAME = bitchat; SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; - SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; + TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; }; diff --git a/bitchat/Services/LoggingService.swift b/bitchat/Services/LoggingService.swift new file mode 100644 index 00000000..84740149 --- /dev/null +++ b/bitchat/Services/LoggingService.swift @@ -0,0 +1,93 @@ +// +// LoggingService.swift +// bitchat +// +// This is free and unencumbered software released into the public domain. +// For more information, see +// + +import Foundation +import os + +class LoggingService { + static let shared = LoggingService() + + private let logger = Logger(subsystem: "com.bitchat", category: "general") + private let fileURL: URL + private let dateFormatter: DateFormatter + private let queue = DispatchQueue(label: "bitchat.logging", qos: .background) + + private init() { + // Set up date formatter + dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS" + + // Get documents directory + let documentsPath = FileManager.default.urls(for: .documentDirectory, + in: .userDomainMask).first! + + // Create logs directory + let logsDirectory = documentsPath.appendingPathComponent("Logs") + try? FileManager.default.createDirectory(at: logsDirectory, + withIntermediateDirectories: true) + + // Create log file with today's date + let dateString = DateFormatter.localizedString(from: Date(), + dateStyle: .short, + timeStyle: .none) + .replacingOccurrences(of: "/", with: "-") + fileURL = logsDirectory.appendingPathComponent("bitchat-\(dateString).log") + + // Create file if it doesn't exist + if !FileManager.default.fileExists(atPath: fileURL.path) { + FileManager.default.createFile(atPath: fileURL.path, contents: nil) + } + + log("=== BitChat Started ===") + log("Log file: \(fileURL.path)") + } + + func log(_ message: String, category: String = "general") { + let timestamp = dateFormatter.string(from: Date()) + let logLine = "[\(timestamp)] [\(category)] \(message)\n" + + // Log to console for debugging + print(logLine.trimmingCharacters(in: .newlines)) + + // Log to system logger + logger.log("\(message, privacy: .public)") + + // Write to file asynchronously + queue.async { [weak self] in + guard let self = self else { return } + + if let data = logLine.data(using: .utf8) { + do { + let fileHandle = try FileHandle(forWritingTo: self.fileURL) + fileHandle.seekToEndOfFile() + fileHandle.write(data) + fileHandle.closeFile() + } catch { + print("Failed to write to log file: \(error)") + } + } + } + } + + func getLogFileURL() -> URL { + return fileURL + } + + func clearLogs() { + queue.async { [weak self] in + guard let self = self else { return } + try? "".write(to: self.fileURL, atomically: true, encoding: .utf8) + self.log("=== Logs Cleared ===") + } + } +} + +// Global logging function for convenience +func bitchatLog(_ message: String, category: String = "general") { + LoggingService.shared.log(message, category: category) +} \ No newline at end of file diff --git a/bitchat/ViewModels/ChatViewModel.swift b/bitchat/ViewModels/ChatViewModel.swift index 852b10f3..9a52bc79 100644 --- a/bitchat/ViewModels/ChatViewModel.swift +++ b/bitchat/ViewModels/ChatViewModel.swift @@ -59,6 +59,10 @@ class ChatViewModel: ObservableObject { loadJoinedRooms() meshService.delegate = self + // Log startup info + bitchatLog("ChatViewModel initialized", category: "startup") + bitchatLog("Nickname: \(nickname)", category: "startup") + // Start mesh service immediately meshService.startServices() @@ -251,7 +255,7 @@ class ChatViewModel: ObservableObject { roomMembers[room] = [] } roomMembers[room]?.insert(meshService.myPeerID) - print("[DEBUG-ROOM] Added self \(meshService.myPeerID) to room \(room), total members: \(roomMembers[room]?.count ?? 0)") + bitchatLog("Added self \(meshService.myPeerID) to room \(room), total members: \(roomMembers[room]?.count ?? 0)", category: "room") } else { // Add to main messages messages.append(message) @@ -677,9 +681,9 @@ extension ChatViewModel: BitchatDelegate { } if let senderPeerID = message.senderPeerID { roomMembers[room]?.insert(senderPeerID) - print("[DEBUG-ROOM] Added member \(senderPeerID) to room \(room), total members: \(roomMembers[room]?.count ?? 0)") + bitchatLog("Added member \(senderPeerID) to room \(room), total members: \(roomMembers[room]?.count ?? 0)", category: "room") } else { - print("[DEBUG-ROOM] No senderPeerID for message in room \(room)") + bitchatLog("No senderPeerID for message in room \(room)", category: "room") } // Update unread count if not currently viewing this room diff --git a/bitchat/Views/AppInfoView.swift b/bitchat/Views/AppInfoView.swift index a8a57512..54254672 100644 --- a/bitchat/Views/AppInfoView.swift +++ b/bitchat/Views/AppInfoView.swift @@ -111,6 +111,31 @@ struct AppInfoView: View { .foregroundColor(textColor) } + // Debug Info + VStack(alignment: .leading, spacing: 16) { + SectionHeader("Debug") + + VStack(alignment: .leading, spacing: 8) { + Text("Log Location:") + .font(.system(size: 14, weight: .semibold, design: .monospaced)) + .foregroundColor(textColor) + + Text(LoggingService.shared.getLogFileURL().path) + .font(.system(size: 12, design: .monospaced)) + .foregroundColor(secondaryTextColor) + .textSelection(.enabled) + + #if os(macOS) + Button("Open in Finder") { + NSWorkspace.shared.selectFile(LoggingService.shared.getLogFileURL().path, inFileViewerRootedAtPath: "") + } + .buttonStyle(.plain) + .foregroundColor(.blue) + .font(.system(size: 12, design: .monospaced)) + #endif + } + } + // Version HStack { Spacer() @@ -208,6 +233,31 @@ struct AppInfoView: View { .foregroundColor(textColor) } + // Debug Info + VStack(alignment: .leading, spacing: 16) { + SectionHeader("Debug") + + VStack(alignment: .leading, spacing: 8) { + Text("Log Location:") + .font(.system(size: 14, weight: .semibold, design: .monospaced)) + .foregroundColor(textColor) + + Text(LoggingService.shared.getLogFileURL().path) + .font(.system(size: 12, design: .monospaced)) + .foregroundColor(secondaryTextColor) + .textSelection(.enabled) + + #if os(macOS) + Button("Open in Finder") { + NSWorkspace.shared.selectFile(LoggingService.shared.getLogFileURL().path, inFileViewerRootedAtPath: "") + } + .buttonStyle(.plain) + .foregroundColor(.blue) + .font(.system(size: 12, design: .monospaced)) + #endif + } + } + // Version HStack { Spacer() diff --git a/bitchat/Views/ContentView.swift b/bitchat/Views/ContentView.swift index 9ba1d6d4..d24d7c39 100644 --- a/bitchat/Views/ContentView.swift +++ b/bitchat/Views/ContentView.swift @@ -601,9 +601,9 @@ struct ContentView: View { if let currentRoom = viewModel.currentRoom, let roomMemberIDs = viewModel.roomMembers[currentRoom] { // Show only peers who have sent messages to this room (including self) - print("[DEBUG-VIEW] Room \(currentRoom) has members: \(roomMemberIDs)") - print("[DEBUG-VIEW] Connected peers: \(viewModel.connectedPeers)") - print("[DEBUG-VIEW] My peer ID: \(myPeerID)") + bitchatLog("Room \(currentRoom) has members: \(roomMemberIDs)", category: "view") + bitchatLog("Connected peers: \(viewModel.connectedPeers)", category: "view") + bitchatLog("My peer ID: \(myPeerID)", category: "view") // Start with room members who are also connected var memberPeers = viewModel.connectedPeers.filter { roomMemberIDs.contains($0) } @@ -613,7 +613,7 @@ struct ContentView: View { memberPeers.append(myPeerID) } - print("[DEBUG-VIEW] Peers to show in room: \(memberPeers)") + bitchatLog("Peers to show in room: \(memberPeers)", category: "view") return memberPeers } else { // Show all connected peers in main chat