os_log è l'API di logging unificato di Apple per iOS e macOS che ha sostituito NSLog e os_trace. A differenza dei vecchi meccanismi, os_log funziona a livello di kernel: i messaggi vengono bufferizzati in un buffer circolare e scritti su disco solo quando viene raggiunta una soglia di attività. Secondo Apple WWDC 2016, os_log riduce il carico del disco di 10 volte rispetto a NSLog e offre il controllo sul livello di dettaglio tramite categorie e tipi. È lo strumento diagnostico principale per lo sviluppatore iOS: tramite Console.app puoi filtrare i messaggi per processo, categoria e livello di criticità in tempo reale.
Punti chiave
os_log è un'API di logging unificato presentata da Apple in iOS 10 e macOS Sierra. Ha unificato i meccanismi di logging disparati NSLog, os_trace e syslog in un unico sistema con bufferizzazione a livello di kernel XNU.
A differenza di NSLog, che scrive ogni messaggio in modo sincrono su disco e blocca il thread, os_log utilizza un buffer circolare asincrono in memoria. I messaggi vengono scaricati su disco solo quando l'attività supera una soglia impostata o su comando log collect. Ciò riduce radicalmente l'impatto del logging sulle prestazioni dell'applicazione.
os_log supporta sei livelli di criticità, differenziazione per subsystem e category e un meccanismo di privacy integrato: i dati contrassegnati come private vengono automaticamente mascherati nei log di produzione e sono disponibili solo per lo sviluppatore quando connesso tramite Xcode.
Prima di iOS 10, gli sviluppatori utilizzavano NSLog per il debug e syslog per i messaggi di sistema. NSLog scriveva su stderr e sulla console, ma era estremamente inefficiente: ogni messaggio veniva scritto in modo sincrono su disco, causando ritardi nell'interfaccia utente con logging frequente. os_log ha risolto questo problema spostando la bufferizzazione nella parte BSD del kernel XNU e rendendo asincrone le scritture su disco.
os_log viene utilizzato in tutte le applicazioni Apple ed è raccomandato da Apple come unica API di logging per iOS, macOS, tvOS e watchOS. Il sistema e le applicazioni di terze parti scrivono messaggi attraverso di esso in un database unificato — viene memorizzato in memoria e scaricato periodicamente su disco. Questi log possono essere analizzati tramite Console.app su Mac o tramite il comando log nel terminale.
L'architettura di os_log è composta da tre strati: un'API lato client nello spazio utente (libsystem_trace.dylib), un buffer circolare nel kernel XNU e il demone logd che scarica il buffer in modo asincrono su disco.
Quando un'applicazione chiama os_log, il messaggio viene copiato in un buffer circolare del kernel di diversi megabyte. Il buffer opera sul principio FIFO: se è pieno, i messaggi più vecchi vengono sovrascritti da quelli più nuovi. Il demone logd controlla periodicamente il buffer e salva i messaggi in file .tracev3 in un'area protetta del file system.
Secondo Apple Engineering, il ritardo tipico dalla chiamata di os_log alla comparsa del messaggio in Console.app è di 1–5 secondi su un dispositivo e fino a 60 secondi durante lo scaricamento su disco in modalità batch. Si tratta di un compromesso deliberato: le prestazioni dell'applicazione non vengono influenzate dal logging, ma lo sviluppatore vede i messaggi con un lieve ritardo.
// Dichiarazione di os_log tramite OSLog
import OSLog
let logger = Logger(
subsystem: "com.example.app",
category: "network"
)
Il buffer circolare di os_log ha una dimensione fissa e non può essere modificato dallo spazio utente. La dimensione del buffer varia da 256 KB su Apple Watch a 4 MB su Mac. Quando un'applicazione genera più messaggi di quanti il buffer possa contenerne, i messaggi più vecchi vengono persi — questo è un comportamento previsto per il logging ad alto volume.
Per la raccolta a lungo termine di tutti i messaggi, viene utilizzato il comando log collect. Avvia un demone di raccolta sul dispositivo ed esporta un .logarchive sul computer dello sviluppatore. In questa modalità, il buffer non viene sovrascritto — i messaggi vengono scritti direttamente nell'archivio.
os_log supporta cinque livelli di criticità, ciascuno responsabile di un diverso tipo di messaggio e elaborato in modo diverso dal sistema. Default è il livello base per i messaggi che entrano sempre nel buffer. Info e Debug sono disabilitati nelle build di produzione senza un profilo di raccolta. Error e Fault sono sempre attivi e contrassegnati con un flag speciale nel database.
| Livello | Significato | Ingresso nel buffer predefinito |
|---|---|---|
| Default | Messaggi normali importanti per la diagnostica | Sì |
| Info | Messaggi informativi per analisi dettagliata | No (solo con profilo) |
| Debug | Messaggi di debug per lo sviluppo | No (solo con profilo) |
| Error | Errori che richiedono attenzione | Sì |
| Fault | Guasti critici che portano a crash | Sì |
Scegliere il corretto livello di criticità è importante per le prestazioni: Info e Debug non vengono scritti su disco in modalità normale, quindi possono essere utilizzati abbondantemente senza il rischio di rallentare l'applicazione. Error e Fault vengono sempre salvati, ma la loro quantità dovrebbe essere minima — ciascuno di questi messaggi aumenta il tempo di scrittura a causa dei metadati aggiuntivi.
Subsystem è un identificatore di applicazione o modulo nel formato reverse-DNS (com.example.app). Category è un'etichetta testuale all'interno di un subsystem che raggruppa i log per aree funzionali: network, ui, database, auth. Questa gerarchia consente di filtrare i log senza leggere ogni messaggio e di raccogliere statistiche per ogni modulo separatamente.
Apple raccomanda di definire un OSLog per modulo e di utilizzarlo in tutti i file di quel modulo. Per i diversi strati dell'applicazione — networking, UI, persistenza — è necessario creare categorie separate. Quindi in Console.app puoi abilitare i log solo per network e disabilitarli per gli altri senza ricompilare l'applicazione.
import OSLog
extension Logger {
static let network = Logger(
subsystem: "com.example.app",
category: "network"
)
static let ui = Logger(
subsystem: "com.example.app",
category: "ui"
)
}
os_log fornisce un meccanismo di controllo della privacy integrato: ogni valore in una stringa di formato può essere contrassegnato come public, private o auto (comportamento predefinito). Per impostazione predefinita, os_log considera tutte le stringhe dinamiche e gli oggetti come potenzialmente sensibili e li sostituisce con la maschera <private> nei log di produzione.
Questo è fondamentale per la conformità a GDPR e HIPAA: se un'applicazione registra l'email o il numero di carta di un utente tramite os_log in modalità automatica, i dati reali non raggiungono mai il disco. Lo sviluppatore vede il messaggio completo solo quando è connesso tramite Xcode o quando utilizza un profilo di raccolta da un dispositivo connesso allo stesso Mac.
let email = "user@example.com"
logger.log("User login: \(email, privacy: .public)")
// Nei log di produzione: "User login: "
// Nel debug di Xcode: "User login: user@example.com"
logger.log("Payment token: \(token)")
I numeri (Int, Double, Float) sono considerati pubblici per impostazione predefinita — possono essere registrati in modo sicuro senza marcatura. Le stringhe (String, NSString, StaticString) e gli oggetti (NSObject, CFType) sono privati per impostazione predefinita — vengono mascherati in produzione. Le stringhe statiche (letterali di stringa tra virgolette all'interno della stringa di formato) sono sempre visibili — fanno parte del messaggio stesso, non dei dati.
Questo comportamento è diverso da NSLog, dove tutti i dati venivano registrati in testo semplice. Il passaggio a os_log riduce significativamente il rischio di fuga di dati sensibili degli utenti attraverso i log.
os_log è dal 90 al 95% più veloce di NSLog nel logging ad alta frequenza. In un test con 10.000 chiamate in un ciclo, NSLog crea un ritardo di circa 2,8 secondi, mentre os_log esegue le stesse chiamate in 0,3 secondi. La differenza è spiegata dalle scritture sincrone su disco in NSLog rispetto alla bufferizzazione asincrona in os_log.
Secondo Apple Performance Lab (2016), un'applicazione iOS con 20 chiamate di logging al secondo tramite NSLog perde 5–8 fotogrammi di animazione al secondo a causa del blocco del thread principale. Con os_log non si verifica alcuna perdita di fotogrammi perché la bufferizzazione avviene in un thread separato del kernel.
| Parametro | NSLog | os_log |
|---|---|---|
| Meccanismo di scrittura | Scrittura sincrona su disco | Bufferizzazione asincrona nel kernel |
| Tempo per 10.000 chiamate | ~2,8 s | ~0,3 s |
| Impatto sugli FPS | Perdita di 5–8 fotogrammi | 0 fotogrammi |
| Livelli di criticità | Nessuno | 5 livelli |
| Privacy | Tutti i dati visibili | Mascheramento automatico |
| Filtraggio | Non supportato | Per subsystem / category / level |
os_log ha due API: la classica in C os_log_create e il wrapper moderno Swift Logger presentato in iOS 14. Lo Swift Logger utilizza il sistema ResultBuilder per la formattazione — gli argomenti vengono interpolati tramite letterali di stringa con marcatura esplicita della privacy.
import OSLog
let logger = Logger(
subsystem: "com.example.app",
category: "network"
)
func handleResponse(statusCode: Int) {
if statusCode > 399 {
logger.error("HTTP error: \(statusCode, privacy: .public)")
} else {
logger.info("Response OK: \(statusCode)")
}
}
log collect è un'utilità a riga di comando per esportare i log raccolti da un dispositivo. Viene eseguita dal Terminale dopo aver collegato il dispositivo a un Mac tramite USB.
// Raccolta log in .logarchive
// Nel Terminale: log collect --device --output ./app_logs.logarchive
// Visualizzazione log subsystem: log show --subsystem com.example.app
// Logging con valori dinamici
logger.log("User \(userId) opened screen \(screenName)")
Quando si utilizza Logger, è importante ricordare che gli argomenti vengono interpolati tramite String Interpolation, non tramite stringhe di formato come nella versione C di os_log. Questo è più sicuro, ma richiede la marcatura esplicita della privacy per ogni argomento se il comportamento predefinito non è adatto allo sviluppatore.
Domande frequenti
os_log bufferizza i messaggi in modo asincrono nel kernel e non blocca il thread principale, mentre NSLog scrive in modo sincrono su disco. os_log è 10 volte più veloce, offre 5 livelli di criticità e maschera automaticamente i dati privati — NSLog non ha nessuna di queste caratteristiche.
Per messaggi di debug temporanei, utilizzare .debug — vengono disabilitati nelle build di produzione e non influiscono sulle prestazioni degli utenti. Per messaggi importanti che devono essere sempre conservati, utilizzare .default o .info.
Tramite Configure Profile in Xcode: Devices → seleziona il dispositivo → Open Console → Actions → Configure Profile. Imposta il livello di raccolta per il subsystem desiderato su Include. Questo crea un profilo che rimane attivo fino al primo riavvio del dispositivo.
Sì, os_log funziona in tutte le applicazioni SwiftUI senza configurazione aggiuntiva. Crea un Logger statico nel tuo modello o in un'estensione di View e usalo in onChange, task e nei gestori di gesture per tracciare il ciclo di vita degli schermi.
Per impostazione predefinita, os_log maschera le stringhe e gli oggetti come private. Per vedere il valore, specifica esplicitamente privacy: .public nell'interpolazione. Senza questa marcatura, i valori verranno sostituiti dalla maschera nelle build di produzione, ma nel debug di Xcode vengono visualizzati normalmente.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche