os_log — це уніфікований API логування від Apple для iOS та macOS, який замінив NSLog та os_trace. На відміну від старих механізмів, os_log працює на рівні ядра: повідомлення буферизуються в кільцевому буфері та записуються на диск лише після досягнення порогу активності. За даними Apple WWDC 2016, os_log знижує навантаження на диск у 10 разів порівняно з NSLog і дає контроль над рівнем деталізації через категорії та типи. Це основний інструмент діагностики для iOS-розробника: через Console.app можна фільтрувати повідомлення за процесом, категорією та рівнем критичності в реальному часі.
Головне
os_log — це уніфікований 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 КБ на Apple Watch до 4 МБ на 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: якщо додаток логує email користувача або номер картки через os_log з авто-режимом, реальні дані ніколи не потраплять на диск. Розробник бачить повне повідомлення лише при підключенні через Xcode або при використанні профілю збору з пристрою, підключеного до того ж Mac.
let email = "user@example.com"
logger.log("User login: \(email, privacy: .public)")
// У продакшен-логах: "User login: "
// У налагодженні через 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 кадрів анімації на секунду через блокування main thread. З 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 асинхронно буферизує повідомлення в ядрі та не блокує main thread, а 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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також