os_log — то је unified logging API од Apple-а за iOS и macOS, који је заменио NSLog и os_trace. За разлику од старих механизама, os_log ради на нивоу језгра: поруке се баферишу у кружном баферу и уписују на диск тек када се достигне праг активности. Према подацима Apple WWDC 2016, os_log смањује оптерећење диска 10 пута у поређењу са NSLog-ом и даје контролу над нивоом детаљности кроз категорије и типове. То је основни дијагностички алат за iOS програмера: кроз Console.app се могу филтрирати поруке по процесу, категорији и нивоу критичности у реалном времену.
Главно
os_log — to je unified logging API који је Apple представио у iOS 10 и macOS Sierra. Објединио је разноврсне механизме логирања NSLog, os_trace и syslog у јединствени систем са баферисањем на нивоу XNU језгра.
За разлику од NSLog-а, који синхроно уписује сваку поруку на диск и блокира нит, os_log користи асинхрони кружни бафер у меморији. Поруке се исписују на диск само када активност премаши задати праг или по команди log collect. Ово радикално смањује утицај логирања на перформансе апликације.
os_log подржава шест нивоа критичности, разграничење по subsystem и category, као и уграђени механизам приватности: подаци означени као private се аутоматски маскирају у продакшен логовима и доступни су само програмеру при повезивању Xcode-а.
Пре iOS 10, програмери су користили NSLog за отклањање грешака и syslog за системске поруке. NSLog је писао у stderr и у конзолу, али је био крајње неефикасан: свака порука се синхроно уписивала на диск, изазивајући кашњења у UI при учесталом логирању. os_log је решио овај проблем премештањем баферисања на ниво BSD дела XNU језгра и чинећи упис на диск асинхроним.
os_log се користи у свим Apple апликацијама и Apple га препоручује као једини API логирања за iOS, macOS, tvOS и watchOS. Систем и апликације трећих страна пишу кроз њега поруке у јединствену базу података — која се чува у меморији и периодично исписује на диск. Анализирати ове логове може се кроз Console.app на Mac-у или кроз команду log у терминалу.
Архитектура os_log се састоји од три слоја: клијентски API у корисничком простору (libsystem_trace.dylib), кружни бафер у XNU језгру и демон logd, који асинхроно исписује бафер на диск.
Када апликација позове os_log, порука се копира у кружни бафер језгра величине неколико мегабајта. Бафер ради по принципу FIFO: ако се напуни, старе поруке се преписују новим. Демон logd периодично проверава бафер и чува поруке у .tracev3 фајлове у заштићеној области фајл система.
Према подацима Apple Engineering, типично кашњење од позива os_log до појављивања поруке у Console.app износи 1–5 секунди на уређају и до 60 секунди при испису на диск у пакетном режиму. Ово је свестан компромис: перформансе апликације не трпе због логирања, али програмер види поруке са малим кашњењем.
// Декларација os_log кроз OSLog
import OSLog
let logger = Logger(
subsystem: "com.example.app",
category: "network"
)
Кружни бафер os_log има фиксну запремину и не може се мењати из корисничког простора. Величина бафера варира од 256 KB на Apple Watch до 4 MB на Mac-у. Када апликација генерише више порука него што бафер може да прими, старе поруке се губе — ово је очекивано понашање за логирање високог обима.
За дугорочно прикупљање свих порука користи се команда log collect, која покреће демон прикупљања на уређају и извози .logarchive на рачунар програмера. У овом режиму бафер се не преписује — поруке се пишу директно у архиву.
os_log подржава пет нивоа критичности, од којих сваки одговара за посебан тип порука и различито се обрађује од стране система. Ниво Default — основни за поруке које увек доспевају у бафер. Info и Debug се искључују у продакшен верзијама без профила прикупљања. Error и Fault су увек активни и означени посебном заставицом у бази података.
| Ниво | Значење | Улазак у бафер подразумевано |
|---|---|---|
| Default | Обичне поруке важне за дијагностику | Да |
| Info | Информационе поруке за детаљну анализу | Не (само са профилом) |
| Debug | Поруке за отклањање грешака током развоја | Не (само са профилом) |
| Error | Грешке које захтевају пажњу | Да |
| Fault | Критични откази који воде до пада | Да |
Избор исправног нивоа критичности је важан за перформансе: Info и Debug се не уписују на диск у нормалном режиму, па се могу користити у обиљу без ризика од успоравања апликације. Error и Fault се увек чувају, али њихов број треба да буде минималан — свака таква порука повећава време уписа због додатних метаподатака.
Subsystem — идентификатор апликације или модула у reverse-DNS формату (com.example.app). Category — текстуална ознака унутар subsystem, која групише логове по функционалним областима: network, ui, database, auth. Ова хијерархија омогућава филтрирање логова без читања сваке поруке и прикупљање статистике за сваки модул посебно.
Apple препоручује дефинисање једног OSLog по модулу и коришћење у свим фајловима тог модула. За различите слојеве апликације — networking, UI, persistence — треба креирати посебне категорије. Тада се у Console.app могу укључити логови само за network и искључити за остале, без прекомпајлирања апликације.
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 пружа уграђени механизам контроле приватности: свака вредност у низу за форматирање може бити означена као public, private или auto (подразумевано понашање). Подразумевано, os_log сматра све динамичке низове и објекте потенцијално поверљивим и замењује их маском <private> у продакшен логовима.
Ово је критично важно за усаглашеност са захтевима GDPR и HIPAA: ако апликација логује имејл корисника или број картице кроз os_log у ауто-режиму, прави подаци никада не доспевају на диск. Програмер види пуну поруку само при повезивању кроз Xcode или при коришћењу профила прикупљања са уређаја повезаног на исти Mac.
let email = "user@example.com"
logger.log("User login: \(email, privacy: .public)")
// У продакшен логовима: "User login: <private>"
// У отклањању грешака кроз Xcode: "User login: user@example.com"
logger.log("Payment token: \(token)")
Бројеви (Int, Double, Float) се подразумевано сматрају public — могу се безбедно логовати без означавања. Низови (String, NSString, StaticString) и објекти (NSObject, CFType) су подразумевано private — маскирају се у продукцији. Статички низови (низовни литерали у наводницима унутар формата) су увек видљиви — то је део саме поруке, а не податак.
Ово понашање се разликује од NSLog-а, где су сви подаци логовани у отвореном облику. Прелазак на os_log значајно смањује ризик од цурења осетљивих корисничких података кроз логове.
os_log је 90–95% бржи од NSLog-а при високофреквентном логирању. У тесту са 10 000 позива у петљи, NSLog ствара кашњење од око 2.8 секунди, док os_log извршава исте позиве за 0.3 секунде. Разлика се објашњава синхроним уписом на диск код NSLog-а у односу на асинхроно баферисање код os_log-а.
Према подацима Apple Performance Lab (2016), iOS апликација са 20 позива логирања у секунди кроз NSLog губи 5–8 кадрова анимације у секунди због блокирања главне нити. Са os_log не долази до губитка кадрова, јер се баферисање одвија у посебној нити језгра.
| Параметар | NSLog | os_log |
|---|---|---|
| Механизам уписа | Синхрони упис на диск | Асинхроно баферисање у језгру |
| Време за 10 000 позива | ~2.8 с | ~0.3 с |
| Утицај на FPS | Губитак 5–8 кадрова | 0 кадрова |
| Нивои критичности | Нема | 5 нивоа |
| Приватност | Сви подаци отворени | Ауто-маскирање |
| Филтрирање | Није подржано | По subsystem / category / level |
os_log има два API-ја: класични C os_log_create и савремени Swift омотач Logger, представљен у iOS 14. Swift Logger користи ResultBuilder систем за форматирање — аргументи се интерполирају кроз низовне литерале са експлицитним означавањем приватности.
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 — командна алатка за извоз прикупљених логова са уређаја. Покреће се из терминала након повезивања уређаја са Mac-ом преко USB-а.
// Прикупљање логова у .logarchive
// У терминалу: log collect --device --output ./app_logs.logarchive
// Преглед логова subsystem: log show --subsystem com.example.app
// Логирање са динамичким вредностима
logger.log("User \(userId) opened screen \(screenName)")
При коришћењу Logger важно је запамтити да се аргументи интерполирају кроз String Interpolation, а не кроз формате, као у C верзији os_log-а. Ово је безбедније, али захтева експлицитно навођење privacy за сваки аргумент ако подразумевано понашање не одговара програмеру.
Често постављана питања
os_log асинхроно баферише поруке у језгру и не блокира главну нит, а NSLog синхроно уписује на диск. os_log је 10 пута бржи, даје 5 нивоа критичности и аутоматски маскира приватне податке — NSLog нема ниједно од ових својстава.
За привремене поруке за отклањање грешака користите .debug — оне се искључују у продакшен верзији и не утичу на перформансе корисника. За важне поруке које треба увек чувати, користите .default или .info.
Кроз Configure Profile у Xcode: Devices → изаберите уређај → Open Console → Actions → Configure Profile. Подесите ниво прикупљања за потребни subsystem на Include. Ово креира профил који је активан до првог поновног покретања уређаја.
Да, os_log ради у свим SwiftUI апликацијама без додатних подешавања. Креирајте статички Logger у моделу или у проширењу View и користите га у onChange, task и хендлерима гестова за праћење животног циклуса екрана.
os_log подразумевано маскира низове и објекте као private. Да бисте видели вредност, експлицитно наведите privacy: .public у интерполацији. Без овог означавања вредности ће бити замењене маском у продакшен верзијама, а при отклањању грешака кроз Xcode се приказују нормално.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође