В этом уроке разберем, как устроено unified logging изнутри, как доставать нужное через log show и log stream с предикатами, что делать с приватными данными в логах, где лежат crash report mac (отчеты о сбоях) и зачем нужен sysdiagnose. Цель - чтобы после урока ты диагностировал проблему на чужом Mac за минуты, а не часами тыкал console mac наугад.
Почему текстовых /var/log больше нет: механика unified logging
Старый syslog был прост и туп: процесс форматировал строку, отдавал ее демону, тот дописывал в текстовый файл. Дешево по коду, дорого по ресурсам - на каждое сообщение строка форматируется (даже если ее никто не прочитает), пишется на диск, занимает место. На ноутбуке с SSD и батареей это расточительно.
Unified logging построен наоборот. Ключевая идея - отложенная сериализация. Когда код вызывает os_log, сообщение не превращается сразу в строку. Сохраняется компактная бинарная запись: указатель на формат-строку (которая лежит в самом бинарнике), сырые аргументы, метаданные (subsystem, category, тип, время, PID, activity ID). Форматирование в человекочитаемый текст происходит только в момент, когда ты реально запрашиваешь лог через log show. Это резко дешевле: писать почти нечего, а если запись никто не прочитает - работы по форматированию ноль.
Где это живет. Сначала записи попадают в кольцевые буферы в памяти (in-memory ring buffer) - самое свежее, быстрое, эфемерное. Записи уровня default и важнее переливаются на диск в /var/db/diagnostics (файлы .tracev3 - бинарный формат), плюс служебные данные в /var/db/uuidtext (там карта UUID бинарников и format-строк, без нее .tracev3 не расшифровать). Демон, который всем этим дирижирует, - logd. Никакого rsyslog, никакого systemd-journald - своя система.
Теперь критичный момент про уровни. В unified logging их несколько, и от уровня зависит, переживет ли запись перезагрузку:
- default - обычное сообщение, пишется на диск, хранится долго (дни-недели, зависит от объема).
- info - дополнительная информация. В память пишется, но на диск по умолчанию НЕ персистится надолго - живет, пока ее не вытеснили.
- debug - отладка. Самый эфемерный уровень, по умолчанию вообще не собирается, пока ты явно его не включишь.
- error и fault - ошибки уровня процесса и системные сбои, persistent.

log show: достаем историю с предикатами
log show - это машина времени по логу. Читает .tracev3 с диска (или из указанного архива) и разворачивает в текст. Главные рычаги - временное окно и предикат-фильтр.
Временное окно. Либо --last с относительным интервалом, либо --start/--end с абсолютной меткой:
Код: Выделить всё
log show --last 30m
log show --start "2026-06-15 09:00:00" --end "2026-06-15 09:05:00"
Уровни. По умолчанию show отдает только default и выше. Чтобы увидеть info и debug:
Код: Выделить всё
log show --last 10m --info --debug
Реальный сценарий: разбираемся, почему Mac не уходит в сон. Смотрим подсистему управления питанием:
Код: Выделить всё
log show --last 1h --predicate 'subsystem == "com.apple.powerd"' --info
Код: Выделить всё
log show --last 1d --predicate 'process == "sshd"'
Код: Выделить всё
Timestamp Thread Type Activity PID TTL
2026-06-15 09:42:11.083214+0300 0x1f3a Default 0x0 481 0 sshd: (libpam) Authentication failure
2026-06-15 09:42:11.084902+0300 0x1f3a Error 0x0 481 0 sshd: error: PAM: authentication error for admin from 203.0.113.7
Полезный флаг вывода - --style. compact дает однострочный плотный формат, json - машиночитаемый для скриптов и jq, syslog - привычный для тех, кто пришел с Linux:
Код: Выделить всё
log show --last 5m --predicate 'process == "sshd"' --style syslog
Если log show - это про прошлое, то log stream - про здесь и сейчас. Это аналог tail -f, но по unified logging. Подключаешься к logd и видишь события по мере их появления. Незаменимо, когда воспроизводишь баг руками: запустил stream, ткнул в приложение, смотришь, что полетело.
Предикаты те же, что в show. Ловим живьем все, что пишет конкретное приложение:
Код: Выделить всё
log stream --predicate 'process == "Safari"' --level debug
Грабля стрима: по умолчанию он показывает default-уровень и НЕ показывает activity-tracing-сообщения и backtraces, плюс глушит часть приватных полей. Если охотишься за деталями - ставь --level debug и при необходимости --type. И помни: stream это живой поток, прошлое он не покажет в принципе - для прошлого только show.
Связка на каждый день - живой мониторинг сетевого демона при отладке VPN или Wi-Fi:
Код: Выделить всё
log stream --predicate 'subsystem == "com.apple.network" OR process CONTAINS "wifi"' --info
Рано или поздно ты упрешься в это и потеряешь полчаса, если не знать заранее. Запускаешь log show, ждешь увидеть имя файла, IP, имя пользователя - а вместо значения стоит <private>:
Код: Выделить всё
2026-06-15 10:03:55.124+0300 Default mDNSResponder Query for <private> (PTR)
Критично понять: редакция происходит при записи, а не при чтении. Когда в .tracev3 уже легло <private> - реального значения там нет, оно не сохранялось вообще. Никаким sudo и никаким флагом log show ты его задним числом не достанешь. Если хочешь видеть приватные данные - надо ВКЛЮЧИТЬ их раскрытие ДО того, как событие произошло.
Делается это конфигурационным профилем (.mobileconfig) с payload SystemLogging и свойством Enable-Private-Data для нужного subsystem, либо - для локальной отладки - кладешь plist в /Library/Preferences/Logging/Subsystems/ с именем подсистемы. Например, чтобы раскрыть приватные данные mDNSResponder:
Код: Выделить всё
sudo defaults write /Library/Preferences/Logging/Subsystems/com.apple.mDNSResponder Enable-Private-Data -bool YES
Код: Выделить всё
sudo rm /Library/Preferences/Logging/Subsystems/com.apple.mDNSResponder.plist
Console.app, отчеты о сбоях и архивы для пересылки
Графика для тех, кому терминал не нужен или нужен второй ракурс - Console (console mac, Программы -> Утилиты -> Консоль). Слева список устройств (включая подключенные iPhone/iPad), сверху живой поток с поиском, и - самое ценное - раздел отчетов. Чтобы Console показывала info и debug, в меню Действие надо явно включить "Включить сообщения информации" и "Включить отладочные сообщения" - по умолчанию, как и в CLI, они скрыты.
Crash report mac - отчеты о сбоях. Когда приложение падает, система пишет детальный отчет: стек вызовов всех потоков, состояние регистров, загруженные образы, причина (signal, exception). Лежат эти отчеты в двух местах:
- ~/Library/Logs/DiagnosticReports - сбои пользовательских процессов (от твоего имени).
- /Library/Logs/DiagnosticReports - системные и корневые процессы.
Код: Выделить всё
ls -lt ~/Library/Logs/DiagnosticReports | head
log show --last 1d --predicate 'eventMessage CONTAINS "crash" OR messageType == 17'
log collect - архив логов для пересылки. Нужно отдать коллеге или в support срез лога? Не копируй /var/db/diagnostics руками (без uuidtext он бесполезен). Есть штатная команда:
Код: Выделить всё
sudo log collect --last 2h --output ~/Desktop/incident.logarchive
sysdiagnose: тяжелая артиллерия диагностики
Когда проблема плавающая, охватывает всю систему и одним предикатом ее не локализуешь, нужен sysdiagnose. Это не лог, а полный снимок состояния системы одной командой: весь unified log за период, лог ядра, spindump, несколько секунд fs_usage и top, список загруженных kext, отчет System Profiler, ВСЕ crash/spin-отчеты, состояние сети, использование диска, IOKit-реестр, лог управления питанием и так далее. Именно sysdiagnose просит инженер Apple, когда заводишь Feedback по серьезному багу.
Код: Выделить всё
sudo sysdiagnose
Что все-таки осталось в /var/log
Чтобы не было иллюзии, что текстовых логов нет совсем. Часть подсистем и сторонних демонов по историческим причинам пишет по-старому. Самый известный житель - /var/log/install.log: подробный текстовый лог установки системы и обновлений, читается обычным tail/grep и очень помогает при разборе провалившегося апдейта macOS. Там же могут лежать wifi.log (если включено сетевое логирование), логи отдельных пакетов из Homebrew или сторонних установщиков. Но это исключения. Системные события - в unified logging, и точка. Привычку "сразу в /var/log" на Mac надо переучить в "сразу в log show / log stream".
Типичные грабли
- Ищешь логи в /var/log и делаешь вывод, что система молчит. Она не молчит - смотри через log, а не через файлы.
- log show без временного окна. Вывалит весь лог от загрузки, утонешь. Всегда --last или --start/--end.
- Не видишь info/debug. В show нужны --info --debug, в stream нужен --level debug. По умолчанию они отфильтрованы.
- Путаешь флаги уровней. show использует --info/--debug, stream использует --level. Не взаимозаменяемы.
- Ждешь, что <private> раскроется задним числом. Нет. Редакция при записи. Раскрывать надо профилем/plist ДО события и потом вернуть обратно.
- Копируешь сырые .tracev3 для пересылки. Без uuidtext они не читаются. Используй log collect и .logarchive.
- Предикат как grep. Это NSPredicate, а не регексп. CONTAINS, не =~. Регексп только через matches и ICU-синтаксис.
- Запускаешь sysdiagnose на нагруженном проде в пик. Команда тяжелая, бьет по производительности на минуты.
- 1. Открой Терминал. Выполни log show --last 5m --predicate 'process == "loginwindow"'. Найди в выводе колонки Type и PID, определи, есть ли там Error.
- 2. Запусти живой поток своего браузера: log stream --predicate 'process CONTAINS "Safari"' --level info. В другом окне открой вкладку и посмотри, что полетело. Останови по Control-C.
- 3. Найди приватные данные: log show --last 10m --predicate 'process == "mDNSResponder"' и убедись, что часть значений - <private>.
- 4. Сними архив за полчаса: sudo log collect --last 30m --output ~/Desktop/lab.logarchive. Открой его обратно: log show --archive ~/Desktop/lab.logarchive --last 5m.
- 5. Загляни в отчеты о сбоях: ls -lt ~/Library/Logs/DiagnosticReports. Если есть .ips, открой его и найди строку Exception Type.
- 6. Бонус: открой Console (console mac), включи в меню Действие отображение info и debug, поищи "wifi" в живом потоке.
- 1. Почему unified logging дешевле старого syslog по ресурсам - в чем суть отложенной сериализации?
- 2. Команда log show --last 1h ничего не показывает на уровне debug, хотя ты уверен, что debug-события были. Что добавить и почему?
- 3. В выводе стоит <private>. Можно ли достать реальное значение из уже записанного лога? Что нужно было сделать заранее и где это переключается?
- 4. Чем отличаются log collect и sysdiagnose, и в какой ситуации ты выберешь каждый?
Логи на Mac - это не файлы, это запросы. Сердце системы - unified logging: бинарные структурированные записи в памяти и в /var/db/diagnostics, которыми рулит logd, а форматируются они только при чтении. Твои основные инструменты: log show для истории с предикатами и временным окном, log stream для живого потока, log collect для самодостаточного .logarchive на пересылку, Console для графики и отчетов о сбоях, и sysdiagnose для полного снимка системы. Помни про уровни (default/info/debug) и явное включение info/debug, про редакцию <private> при записи, и про то, что crash report mac лежат в DiagnosticReports в формате .ips. Освой предикаты NSPredicate - и любой Mac станет для тебя прозрачным.