Back to PXL-808
PXL-808Documentation

Session Logging System - Technical Design Document

For task: πŸ“± Track sessions on device with cloud sync

Problem Statement

Debugging sync race conditions (PXL-801) and other issues is extremely difficult with live logs because:

  • ●Issues are intermittent and hard to reproduce
  • ●By the time an issue is noticed, the relevant logs have scrolled away
  • ●No way to capture logs from real-world usage sessions
  • ●Can't debug issues that happen when not connected to Xcode
Solution Overview

Create an on-device session logging system that:

  1. ●Captures all console output to a local log file per app session
  2. ●Syncs logs to CloudKit public database for cross-device access
  3. ●Tracks session status (active/terminated/crashed)
  4. ●Provides UI for viewing and exporting logs
  5. ●Auto-cleans old logs (7 days local, 30 days remote)

System Architecture
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                              APP LAUNCH                                      β”‚
β”‚                                  β”‚                                           β”‚
β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                 β”‚
β”‚         β–Ό                        β–Ό                        β–Ό                 β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”‚
β”‚  β”‚ Create new  β”‚    β”‚ Check for previous   β”‚    β”‚ Cleanup remote   β”‚       β”‚
β”‚  β”‚ session log β”‚    β”‚ incomplete sessions  β”‚    β”‚ logs > 30 days   β”‚       β”‚
β”‚  β”‚ (local)     β”‚    β”‚ (crashed last time?) β”‚    β”‚ for this user    β”‚       β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β”‚
β”‚                                 β”‚                                            β”‚
β”‚                                 β–Ό                                            β”‚
β”‚                     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                β”‚
β”‚                     β”‚ Upload unsynced prev β”‚                                β”‚
β”‚                     β”‚ session as "CRASHED" β”‚                                β”‚
β”‚                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                β”‚
β”‚                                                                              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                              APP RUNNING                                     β”‚
β”‚                                  β”‚                                           β”‚
β”‚         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                 β”‚
β”‚         β–Ό                                                 β–Ό                 β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”       β”‚
β”‚  β”‚ Log to file     β”‚                         β”‚ Every 60 seconds:   β”‚       β”‚
β”‚  β”‚ continuously    β”‚                         β”‚ Upload to CloudKit  β”‚       β”‚
β”‚  β”‚ (local)         β”‚                         β”‚ status: "ACTIVE"    β”‚       β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       β”‚
β”‚                                                                              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                           APP STATE CHANGES                                  β”‚
β”‚                                                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚
β”‚  β”‚  BACKGROUNDED   β”‚    β”‚   FOREGROUNDED  β”‚    β”‚    TERMINATED       β”‚     β”‚
β”‚  β”‚  β€’ Flush buffer β”‚    β”‚  β€’ Log "resumed"β”‚    β”‚  β€’ Mark TERMINATED  β”‚     β”‚
β”‚  β”‚  β€’ Keep logging β”‚    β”‚  β€’ Upload to CK β”‚    β”‚  β€’ Final flush      β”‚     β”‚
β”‚  β”‚                 β”‚    β”‚    immediately  β”‚    β”‚  β€’ Upload to CK     β”‚     β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚
β”‚                                                                              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                              APP CRASH                                       β”‚
β”‚                                  β”‚                                           β”‚
β”‚                                  β–Ό                                           β”‚
β”‚               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                       β”‚
β”‚               β”‚ ❌ No time to handle during crash   β”‚                       β”‚
β”‚               β”‚                                     β”‚                       β”‚
β”‚               β”‚ βœ… On NEXT launch:                  β”‚                       β”‚
β”‚               β”‚    β€’ Detect incomplete session      β”‚                       β”‚
β”‚               β”‚      (no SESSION END marker)        β”‚                       β”‚
β”‚               β”‚    β€’ Upload with status: "CRASHED"  β”‚                       β”‚
β”‚               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

CloudKit Record Structure
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Record Type: SessionLog                                        β”‚
β”‚  Database: Public (developer-accessible from any device)        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                 β”‚
β”‚  Fields:                                                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
β”‚  β”‚ Field             β”‚ Type        β”‚ Example                  β”‚β”‚
β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚
β”‚  β”‚ userId            β”‚ String      β”‚ "abc123-def456-..."      β”‚β”‚
β”‚  β”‚ cloudKitUserId    β”‚ String      β”‚ "_abc123xyz..."          β”‚β”‚
β”‚  β”‚ sessionId         β”‚ String      β”‚ "2025-12-30_14-32-05"    β”‚β”‚
β”‚  β”‚ filename          β”‚ String      β”‚ "abc123_session_..."     β”‚β”‚
β”‚  β”‚ logFile           β”‚ CKAsset     β”‚ (the .log file)          β”‚β”‚
β”‚  β”‚ status            β”‚ String      β”‚ "active"/"terminated"/   β”‚β”‚
β”‚  β”‚                   β”‚             β”‚ "crashed"                β”‚β”‚
β”‚  β”‚ startedAt         β”‚ Date        β”‚ 2025-12-30T14:32:05Z     β”‚β”‚
β”‚  β”‚ lastUpdatedAt     β”‚ Date        β”‚ 2025-12-30T15:45:00Z     β”‚β”‚
β”‚  β”‚ endedAt           β”‚ Date?       β”‚ null (if active)         β”‚β”‚
β”‚  β”‚ deviceModel       β”‚ String      β”‚ "iPhone 15 Pro"          β”‚β”‚
β”‚  β”‚ iosVersion        β”‚ String      β”‚ "18.2"                   β”‚β”‚
β”‚  β”‚ appVersion        β”‚ String      β”‚ "1.2.3 (456)"            β”‚β”‚
β”‚  β”‚ fileSizeBytes     β”‚ Int64       β”‚ 1048576                  β”‚β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
β”‚                                                                 β”‚
β”‚  Record ID: {userId}_{sessionId}                                β”‚
β”‚  (allows upsert on each upload)                                 β”‚
β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Log File Format
================================================================================
SESSION: {userId}_{sessionId}
STATUS: ACTIVE
LAST UPDATED: 2025-12-30 15:45:00.123
================================================================================
Device: iPhone 15 Pro
iOS: 18.2
App: NutriKit 1.2.3 (456)
User ID: abc123-def456-...
CloudKit ID: _abc123xyz...
================================================================================

[14:32:05.001] [INFO] [App] πŸ“± Session started
[14:32:05.045] [INFO] [Sync] Starting full sync...
[14:35:20.234] [INFO] [MealCreate] New meal: "Breakfast"
[14:40:05.567] [INFO] [App] ⏸️ Entering background
[15:15:30.001] [INFO] [App] ▢️ Returning to foreground
[15:15:30.002] [INFO] [App] ☁️ Uploading log to CloudKit...
[15:20:45.123] [INFO] [MealCreate] New meal: "Lunch"
...

================================================================================
SESSION END: 2025-12-30 18:45:12.789
STATUS: TERMINATED
DURATION: 4h 13m 7s
================================================================================

UI Design

Admin Sheet (existing, with new row)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  ←  Admin                                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                             β”‚
β”‚  DATA MANAGEMENT                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚ πŸ”„  Restore Meals from Backup    >  β”‚   β”‚
β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€   β”‚
β”‚  β”‚ 🎲  Generate Dummy Data          >  β”‚   β”‚
β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€   β”‚
β”‚  β”‚ πŸ—‘οΈ  Delete Data                   >  β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                             β”‚
β”‚  DIAGNOSTICS                                β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚ πŸ“‹  Session Logs                 >  β”‚   β”‚
β”‚  β”‚     12 sessions β€’ 2.4 MB            β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Session Logs List View

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  ←  Session Logs                     Edit   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                             β”‚
β”‚  [Local β–Ό] [Cloud]        ← Segment picker  β”‚
β”‚                                             β”‚
β”‚  🟒 CURRENT SESSION (this device)           β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚ πŸ“± iPhone 15 Pro                 >  β”‚   β”‚
β”‚  β”‚ Today, 2:32 PM β€’ Running β€’ 1.8 MB   β”‚   β”‚
β”‚  β”‚ ☁️ Last synced: 2 min ago           β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                             β”‚
β”‚  CLOUD SESSIONS (all devices, 30 days)      β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚ πŸ“± iPhone 15 Pro          ACTIVE >  β”‚   β”‚
β”‚  β”‚ Today, 2:32 PM β€’ 1.8 MB             β”‚   β”‚
β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€   β”‚
β”‚  β”‚ πŸ“± iPhone 15 Pro       TERMINATED>  β”‚   β”‚
β”‚  β”‚ Yesterday, 8:30 AM β€’ 3.2 MB         β”‚   β”‚
β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€   β”‚
β”‚  β”‚ πŸ’» iPad Pro            TERMINATED>  β”‚   β”‚
β”‚  β”‚ Dec 28, 10:15 AM β€’ 2.1 MB           β”‚   β”‚
β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€   β”‚
β”‚  β”‚ πŸ“± iPhone 15 Pro         CRASHED >  β”‚   β”‚
β”‚  β”‚ Dec 27, 3:45 PM β€’ 1.5 MB            β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  πŸ—‘οΈ  Delete All Cloud Logs          β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                             β”‚
β”‚  Cloud logs auto-delete after 30 days      β”‚
β”‚                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Log Detail View

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  ←  Yesterday, 8:30 AM           β†— Share   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                             β”‚
β”‚  SESSION INFO                               β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚ Started    Dec 29, 8:30:15 AM       β”‚   β”‚
β”‚  β”‚ Ended      Dec 29, 10:52:30 PM      β”‚   β”‚
β”‚  β”‚ Duration   14h 22m 15s              β”‚   β”‚
β”‚  β”‚ File Size  3.2 MB                   β”‚   β”‚
β”‚  β”‚ Device     iPhone 15 Pro            β”‚   β”‚
β”‚  β”‚ iOS        18.2                     β”‚   β”‚
β”‚  β”‚ App Ver    1.2.3 (456)              β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                             β”‚
β”‚  LOG CONTENT                    [Search πŸ”] β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚ [08:30:15] πŸ“± Session started       β”‚   β”‚
β”‚  β”‚ [08:30:16] [Sync] Starting...       β”‚   β”‚
β”‚  β”‚ [08:35:20] [MealCreate] Breakfast   β”‚   β”‚
β”‚  β”‚ [08:40:05] ⏸️ Entering background   β”‚   β”‚
β”‚  β”‚ [12:15:30] ▢️ Returning foreground  β”‚   β”‚
β”‚  β”‚ [12:15:32] [Sync] Starting...       β”‚   β”‚
β”‚  β”‚ [12:20:45] [MealCreate] Lunch       β”‚   β”‚
β”‚  β”‚ [12:25:10] ⏸️ Entering background   β”‚   β”‚
β”‚  β”‚ [18:30:00] ▢️ Returning foreground  β”‚   β”‚
β”‚  β”‚ [18:35:22] [MealCreate] Dinner      β”‚   β”‚
β”‚  β”‚ [18:40:15] ⏸️ Entering background   β”‚   β”‚
β”‚  β”‚ [22:52:30] πŸ›‘ Session terminated    β”‚   β”‚
β”‚  β”‚                            β–Ό scroll β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚  πŸ“€  Export Log File                β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Sync Timeline
Time     Event                          Local              CloudKit
─────────────────────────────────────────────────────────────────────
14:32    App launches                   πŸ“„ Created         
14:32    Session starts                 ✍️ Writing         
14:33    +60 sec timer fires            ✍️ Writing         ☁️ Upload (ACTIVE)
14:34    +60 sec timer fires            ✍️ Writing         ☁️ Upload (ACTIVE)
14:35    User backgrounds app           ✍️ Flush           
14:40    User foregrounds               ✍️ Writing         ☁️ Upload (ACTIVE)
14:41    +60 sec timer fires            ✍️ Writing         ☁️ Upload (ACTIVE)
...
18:45    User terminates app            πŸ“„ Complete        ☁️ Upload (TERMINATED)
─────────────────────────────────────────────────────────────────────

CRASH SCENARIO:
14:32    App launches                   πŸ“„ Created         
14:33    +60 sec timer fires            ✍️ Writing         ☁️ Upload (ACTIVE)
14:34    πŸ’₯ CRASH                       πŸ“„ Incomplete      ☁️ Still "ACTIVE"
         (next launch)
14:40    App launches                   πŸ“„ Detect orphan   ☁️ Upload (CRASHED)

Storage Estimates

Per-Session Size Analysis

Log VerbosityLines/HourBytes/LineSize/Hour8hr Session
Light use~500~100~50 KB~400 KB
Normal use~2,000~120~240 KB~2 MB
Heavy use~5,000~150~750 KB~6 MB
Debug sync~10,000~150~1.5 MB~12 MB

Realistic estimate: 2-4 MB per session (normal use)

30-Day Storage per User

Usage PatternSessions/DaySize/Session30-Day Total
Casual12 MB~60 MB
Regular1-23 MB~120 MB
Power user2-34 MB~300 MB
Debug mode2-310 MB~900 MB

Typical user: ~100-200 MB per 30 days

CloudKit Public Database Capacity

With 1 PB total storage:

  • ●1 user: 200 MB = 0.00002% of capacity
  • ●100 users: 20 GB = 0.002% of capacity
  • ●1,000 users: 200 GB = 0.02% of capacity
  • ●10,000 users: 2 TB = 0.2% of capacity
  • ●100,000 users: 20 TB = 2% of capacity

Verdict: Storage is not a concern with 1 PB capacity and 30-day cleanup


File Storage Structure
Documents/
└── Logs/
    β”œβ”€β”€ session_2025-12-30_14-32-05.log  ← Current (in progress)
    β”œβ”€β”€ session_2025-12-30_10-15-32.log  ← 312 KB
    β”œβ”€β”€ session_2025-12-29_20-30-15.log  ← 1.2 MB
    β”œβ”€β”€ session_2025-12-29_12-45-00.log  ← 245 KB
    └── session_2025-12-28_15-20-30.log  ← 567 KB
    
    (Auto-cleanup: files older than 7 days deleted on app launch)

Files to Create
  1. ●Packages/App/Sources/App/Services/SessionLogger.swift
    • ●Core logging service with stdout/stderr capture
    • ●File creation at Documents/Logs/
    • ●Timestamp formatting
    • ●Session header/footer writing
    • ●Flush mechanism
  2. ●Packages/App/Sources/App/Services/SessionLogCloudKitManager.swift
    • ●CloudKit sync with public database
    • ●Upload/fetch/delete operations
    • ●Crash detection logic
  3. ●NutriKit/Views/Debug/SessionLogsView.swift
    • ●List view with Local/Cloud segments
    • ●Session rows with status badges
  4. ●NutriKit/Views/Debug/SessionLogDetailView.swift
    • ●Detail view with session info
    • ●Scrollable log content
    • ●Search and export functionality
Files to Modify
  1. ●NutriKit/NutriKitApp.swift
    • ●Initialize SessionLogger on launch
    • ●Handle scene phase changes
    • ●Register for termination notification
  2. ●NutriKit/Views/Debug/AdminSheet.swift
    • ●Add "DIAGNOSTICS" section
    • ●Add "Session Logs" navigation row
  3. ●Packages/App/Sources/App/Extensions/Logger.swift
    • ●Integrate OSLog with session file writing

Key Implementation Notes
  • ●Session continues through backgrounding, only ends on true termination
  • ●Crash detection relies on missing SESSION END marker
  • ●CloudKit uploads use CKAsset for efficient large file handling
  • ●60-second upload interval balances freshness vs. battery/network
  • ●Public database allows developer access from any device
  • ●Buffer flush on background ensures no data loss if iOS kills app
Created Dec 30, 2025, 1:33 PM Β· Updated Dec 30, 2025, 1:36 PM