Ваше приложение работает идеально? Давайте проверим
Вы когда-нибудь запускали Swift-приложение в продакшн и молились, чтобы оно не упало? Я — да. И каждый раз, когда пользователь писал «оно зависло», я чувствовал себя слепым котёнком. Логирование и мониторинг — это те самые глаза и уши, которые превращают хаос в порядок. Без них вы гадаете, что пошло не так. С ними — видите каждый шаг.
В этой статье мы разберём, как настроить мониторинг Swift приложений так, чтобы вы спали спокойно. Никакой сухой теории — только живой код, реальные кейсы и лайфхаки, которые я собирал годами. Погнали!
Почему логирование в Swift — это не просто print()
Первый порыв любого разработчика — воткнуть print("здесь всё сломалось") в каждом закруглении. И это работает… пока вы не выпустили приложение. В продакшене print исчезает в пустоте, а пользователь остаётся один на один с багом. Нам нужно нечто большее.
Современное логирование Swift приложений — это структурированные данные, уровни важности и возможность удалённого сбора. Apple даёт нам мощный инструмент — OSLog. Он лёгкий, быстрый и встроен прямо в систему. Давайте посмотрим, как его приручить.
OSLog: базовый уровень
Подключаем фреймворк и создаём логгер:
import OSLog
let logger = Logger(
subsystem: "com.pythonlib.monitoring",
category: "network"
)
func fetchUserData() {
logger.info("Начинаем загрузку данных пользователя")
// ... код запроса ...
logger.debug("Получен ответ: \(response.statusCode)")
}
Обратите внимание на subsystem и category. Это ваши теги для фильтрации. Когда у вас 50 модулей, вы сможете отсеять только логи сети или только логи базы данных — без лишнего шума.
Уровни логирования: когда кричать, а когда шептать
OSLog поддерживает шесть уровней. Вот как их использовать:
debug— для разработки, отключается в релизе (если не включить explicitly).info— полезная информация, которая не засоряет консоль.notice— важные события, например, успешный запуск.error— ошибки, которые можно обработать.fault— критические сбои, после которых приложение может упасть.
Пример с реальной ошибкой:
func handleApiError(_ error: Error) {
logger.error("Ошибка API: \(error.localizedDescription, privacy: .public)")
// privacy: .public — чтобы видеть текст в логах, иначе будет скрыто
}
Мониторинг Swift приложений: метрики и производительность
Логи — это прошлое. Мониторинг Swift приложений — это настоящее. Вы хотите знать, что происходит прямо сейчас: сколько памяти жрёт ваш код, не тормозит ли анимация, не падает ли частота кадров.
Apple предоставляет MetricKit — фреймворк для сбора диагностики на устройствах пользователей. Он собирает краш-репорты, данные о батарее и метрики запуска. Но для полноценного мониторинга часто подключают сторонние сервисы.
Подключаем Crashlytics (Firebase) — быстро и больно
Crashlytics остаётся стандартом индустрии. Вот минимальная настройка:
import FirebaseCrashlytics
// Где-то при старте приложения
Crashlytics.crashlytics().setUserID("user_123")
Crashlytics.crashlytics().setCustomValue(true, forKey: "is_premium")
// Логируем нефатальные ошибки
do {
try riskyOperation()
} catch {
Crashlytics.crashlytics().record(error: error)
logger.error("Нефатальная ошибка: \(error)")
}
Важный момент: не логируйте персональные данные пользователя без согласия. Используйте setUserID только если у вас есть анонимизированный идентификатор.
Отправляем метрики вручную
Иногда хочется знать, сколько раз пользователь нажал на кнопку «Купить», но не завершил покупку. Для этого заводим кастомные события:
Analytics.logEvent("purchase_attempt", parameters: [
"product_id": productId,
"price": price
])
Это может быть Firebase Analytics, Amplitude или ваш собственный сервер. Главное — единый формат и консистентность.
Расширенное логирование Swift приложений: пишем свой менеджер
Стандартный OSLog хорош, но когда проектов становится много, хочется иметь единую точку входа. Давайте напишем простой LogManager, который объединит OSLog и удалённую отправку.
import OSLog
import Foundation
enum LogLevel: String {
case debug, info, notice, error, fault
}
final class LogManager {
static let shared = LogManager()
private let osLogger: Logger
private init() {
osLogger = Logger(subsystem: "com.pythonlib.app", category: "general")
}
func log(
_ message: String,
level: LogLevel = .info,
file: String = #file,
function: String = #function,
line: Int = #line
) {
let fileName = (file as NSString).lastPathComponent
let formatted = "[\(fileName):\(line)] \(function) -> \(message)"
switch level {
case .debug: osLogger.debug("\(formatted)")
case .info: osLogger.info("\(formatted)")
case .notice: osLogger.notice("\(formatted)")
case .error: osLogger.error("\(formatted)")
case .fault: osLogger.fault("\(formatted)")
}
// Отправляем на сервер только ошибки и фатальные сбои
if level == .error || level == .fault {
sendToRemote(formatted)
}
}
private func sendToRemote(_ message: String) {
// Здесь ваш код отправки на сервер или в Crashlytics
// Например, URLSession POST запрос
}
}
Теперь в любом месте приложения пишем:
LogManager.shared.log("Пользователь вышел из аккаунта", level: .info)
И всё — и в консоль, и на сервер (если уровень серьёзный). Удобно, единообразно, масштабируемо.
Логирование Swift приложений в фоне: не потеряйте ни строчки
Одна из частых проблем — логи теряются, когда приложение уходит в фон. iOS может прибить процесс в любой момент. Решение — использовать os_log с типом .persistent или писать в файл.
Вот пример записи в файл с обработкой фонового режима:
func writeLogToFile(_ message: String) {
guard let documentsDir = FileManager.default.urls(
for: .documentDirectory, in: .userDomainMask
).first else { return }
let logFile = documentsDir.appendingPath