os_log — je unified logging API od Apple pro iOS a macOS, které nahradilo NSLog a os_trace. Na rozdíl od starých mechanismů, os_log pracuje na úrovni jádra: zprávy jsou bufferovány v kruhovém bufferu a zapisovány na disk pouze po dosažení prahu aktivity. Podle údajů Apple WWDC 2016, os_log snižuje zatížení disku 10krát ve srovnání s NSLog a poskytuje kontrolu nad úrovní podrobností prostřednictvím kategorií a typů. Je to základní diagnostický nástroj pro iOS vývojáře: prostřednictvím Console.app lze filtrovat zprávy podle procesu, kategorie a úrovně kritičnosti v reálném čase.
Hlavní body
os_log — je unified logging API představené Apple v iOS 10 a macOS Sierra. Sjednotilo roztříštěné mechanismy logování NSLog, os_trace a syslog do jednoho systému s bufferováním na úrovni jádra XNU.
Na rozdíl od NSLog, které každou zprávu synchronně zapisuje na disk a blokuje vlákno, os_log používá asynchronní kruhový buffer v paměti. Zprávy se zapisují na disk pouze když aktivita překročí určitý práh nebo na příkaz log collect. To radikálně snižuje dopad logování na výkon aplikace.
os_log podporuje šest úrovní kritičnosti, rozlišení podle subsystem a category a vestavěný mechanismus soukromí: data označená jako private jsou automaticky maskována v produkčních logách a přístupná pouze vývojáři po připojení Xcode.
Před iOS 10 vývojáři používali NSLog pro ladění a syslog pro systémové zprávy. NSLog zapisoval do stderr a konzole, ale byl extrémně neefektivní: každá zpráva se synchronně zapisovala na disk, což při častém logování způsobovalo zpoždění v UI. os_log tento problém vyřešil přesunutím bufferování na úroveň BSD části jádra XNU a asynchronním zápisem na disk.
os_log se používá ve všech aplikacích Apple a Apple jej doporučuje jako jediné API pro logování pro iOS, macOS, tvOS a watchOS. Systém a aplikace třetích stran přes něj zapisují zprávy do jediné databáze — uložené v paměti a periodicky vypisované na disk. Tyto logy lze analyzovat přes Console.app na Macu nebo přes příkaz log v terminálu.
Architektura os_log se skládá ze tří vrstev: klientské API v uživatelském prostoru (libsystem_trace.dylib), kruhového bufferu v jádře XNU a démona logd, který asynchronně vypisuje buffer na disk.
Když aplikace zavolá os_log, zpráva se zkopíruje do kruhového bufferu jádra o velikosti několika megabajtů. Buffer funguje na principu FIFO: pokud je plný, staré zprávy jsou přepsány novými. Démon logd periodicky kontroluje buffer a ukládá zprávy do souborů .tracev3 v chráněné oblasti souborového systému.
Podle údajů Apple Engineering, typické zpoždění od volání os_log do objevení se zprávy v Console.app je 1–5 sekund na zařízení a až 60 sekund při dávkovém zápisu na disk. Toto je vědomý kompromis: výkon aplikace netrpí kvůli logování, ale vývojář vidí zprávy s malým zpožděním.
// Deklarace os_log pomocí OSLog
import OSLog
let logger = Logger(
subsystem: "com.example.app",
category: "network"
)
Kruhový buffer os_log má pevný objem a nelze jej změnit z uživatelského prostoru. Velikost bufferu se pohybuje od 256 KB na Apple Watch do 4 MB na Macu. Když aplikace generuje více zpráv, než buffer pojme, staré zprávy se ztrácejí — to je očekávané chování pro logování s vysokým objemem.
Pro dlouhodobý sběr všech zpráv se používá příkaz log collect, který spustí sběrného démona na zařízení a exportuje .logarchive do počítače vývojáře. V tomto režimu se buffer nepřepisuje — zprávy se zapisují přímo do archivu.
os_log podporuje pět úrovní kritičnosti, z nichž každá odpovídá za samostatný typ zpráv a je systémem zpracovávána odlišně. Úroveň Default — základní pro zprávy, které vždy padnou do bufferu. Info a Debug jsou vypnuty v produkčních sestaveních bez profilu sběru. Error a Fault jsou vždy aktivní a označeny speciálním příznakem v databázi.
| Úroveň | Význam | Výchozí vstup do bufferu |
|---|---|---|
| Default | Běžné zprávy důležité pro diagnostiku | Ano |
| Info | Informační zprávy pro podrobnou analýzu | Ne (pouze s profilem) |
| Debug | Ladicí zprávy pro vývoj | Ne (pouze s profilem) |
| Error | Chyby vyžadující pozornost | Ano |
| Fault | Kritické selhání vedoucí k pádu | Ano |
Výběr správné úrovně kritičnosti je důležitý pro výkon: Info a Debug se nezapisují na disk v normálním režimu, takže je lze používat hojně bez rizika zpomalení aplikace. Error a Fault se vždy ukládají, ale jejich počet by měl být minimální — každá taková zpráva zvyšuje dobu zápisu kvůli dalším metadatům.
Subsystem — je identifikátor aplikace nebo modulu ve formátu reverse-DNS (com.example.app). Category — textový štítek v rámci subsystem, který seskupuje logy podle funkčních oblastí: network, ui, database, auth. Taková hierarchie umožňuje filtrovat logy bez čtení každé zprávy a sbírat statistiky pro každý modul zvlášť.
Apple doporučuje definovat jeden OSLog na modul a používat jej ve všech souborech tohoto modulu. Pro různé vrstvy aplikace — networking, UI, persistence — je třeba vytvořit samostatné kategorie. Pak lze v Console.app zapnout logy pouze pro network a vypnout pro ostatní, bez překompilování aplikace.
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 poskytuje vestavěný mechanismus kontroly soukromí: každá hodnota v řetězci formátu může být označena jako public, private nebo auto (výchozí chování). Ve výchozím nastavení os_log považuje všechny dynamické řetězce a objekty za potenciálně důvěrné a nahrazuje je maskou <private> v produkčních logách.
To je kriticky důležité pro dodržování požadavků GDPR a HIPAA: pokud aplikace loguje e-mail uživatele nebo číslo karty přes os_log v automatickém režimu, skutečná data se nikdy nedostanou na disk. Vývojář vidí úplnou zprávu pouze při připojení přes Xcode nebo při použití profilu sběru ze zařízení připojeného ke stejnému Macu.
let email = "user@example.com"
logger.log("User login: \(email, privacy: .public)")
// V produkčních logách: "User login: <private>"
// Při ladění přes Xcode: "User login: user@example.com"
logger.log("Payment token: \(token)")
Čísla (Int, Double, Float) jsou ve výchozím nastavení považována za public — lze je bezpečně logovat bez označení. Řetězce (String, NSString, StaticString) a objekty (NSObject, CFType) jsou ve výchozím nastavení private — maskují se v produkci. Statické řetězce (řetězcové literály v uvozovkách uvnitř formátovacího řetězce) jsou vždy viditelné — to je součást samotné zprávy, nikoli data.
Toto chování se liší od NSLog, kde byla všechna data logována v otevřené podobě. Přechod na os_log výrazně snižuje riziko úniku citlivých uživatelských dat prostřednictvím logů.
os_log je o 90–95% rychlejší než NSLog při vysokofrekvenčním logování. V testu s 10 000 voláními ve smyčce NSLog vytváří zpoždění asi 2,8 sekundy, zatímco os_log provádí stejná volání za 0,3 sekundy. Rozdíl je vysvětlen synchronním zápisem na disk u NSLog oproti asynchronnímu bufferování u os_log.
Podle údajů Apple Performance Lab (2016) ztrácí iOS aplikace s 20 voláními logování za sekundu přes NSLog 5–8 snímků animace za sekundu kvůli blokování hlavního vlákna. S os_log nedochází ke ztrátě snímků, protože bufferování probíhá v samostatném vlákně jádra.
| Parametr | NSLog | os_log |
|---|---|---|
| Mechanismus zápisu | Synchromní zápis na disk | Asynchronní bufferování v jádře |
| Čas na 10 000 volání | ~2,8 s | ~0,3 s |
| Dopad na FPS | Ztráta 5–8 snímků | 0 snímků |
| Úrovně kritičnosti | Žádné | 5 úrovní |
| Soukromí | Všechna data otevřená | Automatické maskování |
| Filtrování | Nepodporováno | Podle subsystem / category / level |
os_log má dvě API: klasické C os_log_create a moderní Swift obal Logger, představený v iOS 14. Swift Logger používá systém ResultBuilder pro formátování — argumenty jsou interpolovány pomocí řetězcových literálů s explicitním označením soukromí.
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 — nástroj příkazového řádku pro export sebraných logů ze zařízení. Spouští se z Terminálu po připojení zařízení k Macu přes USB.
// Sběr logů do .logarchive
// V terminálu: log collect --device --output ./app_logs.logarchive
// Zobrazení logů subsystem: log show --subsystem com.example.app
// Logování s dynamickými hodnotami
logger.log("User \(userId) opened screen \(screenName)")
Při použití Logger je důležité si pamatovat, že argumenty jsou interpolovány pomocí String Interpolation, nikoli pomocí formátovacích řetězců jako v C verzi os_log. To je bezpečnější, ale vyžaduje explicitní uvedení privacy pro každý argument, pokud výchozí chování vývojáři nevyhovuje.
Často kladené otázky
os_log asynchronně bufferuje zprávy v jádře a neblokuje hlavní vlákno, zatímco NSLog synchronně zapisuje na disk. os_log je 10krát rychlejší, poskytuje 5 úrovní kritičnosti a automaticky maskuje soukromá data — NSLog nemá žádnou z těchto vlastností.
Pro dočasné ladicí zprávy používejte .debug — jsou vypnuty v produkčním sestavení a neovlivňují výkon uživatelů. Pro důležité zprávy, které by měly být vždy zachovány, použijte .default nebo .info.
Přes Configure Profile v Xcode: Devices → vyberte zařízení → Open Console → Actions → Configure Profile. Nastavte úroveň sběru pro požadovaný subsystem na Include. Tím vytvoříte profil, který je aktivní do prvního restartu zařízení.
Ano, os_log funguje ve všech SwiftUI aplikacích bez dalšího nastavení. Vytvořte statický Logger v modelu nebo v rozšíření View a používejte jej v onChange, task a handlerech gest pro sledování životního cyklu obrazovek.
os_log ve výchozím nastavení maskuje řetězce a objekty jako private. Pro zobrazení hodnoty explicitně uveďte privacy: .public v interpolaci. Bez tohoto označení budou hodnoty v produkčních sestaveních nahrazeny maskou, ale při ladění přes Xcode se zobrazují normálně.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také