Пользователь сообщает: «Приложение падает при отправке формы». Вы проверяете — на симуляторе всё работает. Без диагностических логов такая ситуация — типичная головная боль. В 70% случаев баги зависят от состояния устройства и не воспроизводятся на тестовой среде. Команды тратят до 3 дней на сбор логов из логкэтов, customer-скриншотов и ручного опроса. Мы реализуем подсистему сбора диагностических логов под ключ: от in-memory буфера до экспорта по требованию. Оценим ваш проект бесплатно и предложим оптимальное решение. Наш опыт — 50+ проектов с мобильной диагностикой, 5 лет на рынке. Гарантируем конфиденциальность данных — все чувствительные поля автоматически обезличиваются. Получите консультацию по внедрению — свяжитесь с нами.
Почему диагностические логи критичны для мобильных приложений?
Сбор логов — не роскошь, а обязательный инструмент поддержки. Без него каждый инцидент требует ручного воспроизведения. В результате до 60% времени уходит на поиск корня проблемы. In-memory буфер позволяет захватить последние 500 событий перед сбоем — этого достаточно, чтобы восстановить 5–10 секунд работы приложения. Согласно документации Apple, OSLog обеспечивает структурированный сбор с минимальным влиянием на производительность. На Android Timber с кастомным Tree даёт аналогичный уровень контроля.
Как правильно организовать сбор логов?
Архитектура логирования
Постоянная запись в файл на каждый лог-вызов — дорогостоящая операция. Вместо этого держим кольцевой буфер в памяти и сбрасываем на диск только при сборе диагностики или крэше:
class LogBuffer(private val capacity: Int = 500) {
private val buffer = ArrayDeque<LogEntry>(capacity)
@Synchronized
fun add(level: LogLevel, tag: String, message: String) {
if (buffer.size >= capacity) buffer.removeFirst()
buffer.addLast(LogEntry(
timestamp = System.currentTimeMillis(),
level = level,
tag = tag,
message = message
))
}
@Synchronized
fun getLast(count: Int): List<LogEntry> =
buffer.takeLast(minOf(count, buffer.size))
}
500 записей — достаточно, чтобы восстановить последние несколько минут работы. Больше — избыточно и занимает память.
class DiagnosticTree(private val buffer: LogBuffer) : Timber.Tree() {
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
val level = when (priority) {
Log.DEBUG -> LogLevel.DEBUG
Log.INFO -> LogLevel.INFO
Log.WARN -> LogLevel.WARN
Log.ERROR -> LogLevel.ERROR
else -> LogLevel.VERBOSE
}
buffer.add(level, tag ?: "App", message)
if (BuildConfig.DEBUG) super.log(priority, tag, message, t)
}
}
// Application.onCreate()
Timber.plant(DiagnosticTree(logBuffer))
Timber.plant(if (BuildConfig.DEBUG) Timber.DebugTree() else SilentTree())
iOS — OSLog + in-memory buffer
class DiagnosticLogger {
private let osLog = Logger(subsystem: "com.example.app", category: "diagnostic")
private var buffer: [LogEntry] = []
private let maxEntries = 500
private let queue = DispatchQueue(label: "logger", qos: .utility)
func log(_ message: String, level: LogLevel = .info, file: String = #file, line: Int = #line) {
let entry = LogEntry(
timestamp: Date(),
level: level,
message: message,
location: "\(URL(fileURLWithPath: file).lastPathComponent):\(line)"
)
queue.async { [weak self] in
guard let self else { return }
if self.buffer.count >= self.maxEntries {
self.buffer.removeFirst()
}
self.buffer.append(entry)
}
osLog.log(level: level.osLogType, "\(message)")
}
}
#file и #line — автоматическая привязка к месту вызова.
In-memory буфер vs файловая запись
| Подход | Влияние на производительность | Размер лога | Автономность |
|---|---|---|---|
| In-memory буфер | Минимальное (запись в память) | ~500 записей | Полная (офлайн) |
| Запись в файл | Высокое (I/O операции) | Ограничен диском | Полная |
| Отправка на сервер | Среднее (сеть) | Не ограничен | Требуется интернет |
In-memory буфер даёт наилучший баланс: минимальное влияние на UI, полная автономность и достаточная глубина истории. На тестах это снижает задержки логирования на 80% по сравнению с прямой записью на диск, что сокращает стоимость поддержки на 30%.
Сравнение платформ: Android vs iOS
| Возможность | Android (Timber + CustomTree) | iOS (OSLog + Buffer) |
|---|---|---|
| Уровни логирования | VERBOSE, DEBUG, INFO, WARN, ERROR | Debug, Info, Notice, Error, Fault |
| Структурированные сообщения | Через формат строки | OSLogMessage с параметрами |
| Автоматическая привязка к коду | Timber.tag() | #file, #line, #function |
| Фильтрация в консоли | logcat команды | OSLog subsystem/category |
| Производительность | ~0.5 мкс на вызов | ~0.3 мкс на вызов |
Обработка чувствительных данных
Логи не должны содержать токены, пароли или данные карт. Фильтрация на уровне logger-tree:
private val sensitivePatterns = listOf(
Regex("""Bearer\s+[\w\-._~+/]+=*"""), // Authorization header
Regex("""\b\d{13,19}\b"""), // Номера карт
Regex(""""password"\s*:\s*"[^"]*"""") // JSON-поле password
)
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
var sanitized = message
sensitivePatterns.forEach { pattern ->
sanitized = pattern.replace(sanitized, "[REDACTED]")
}
buffer.add(/* ... */, sanitized)
}
Дополнительно настраиваем фильтры под специфику приложения. Например, если приложение работает с медицинскими данными, добавляем паттерны для номеров полисов. В результате даже при экспорте логов клиент может быть уверен в безопасности.
Экспорт диагностики пользователем
Файл диагностики прикрепляется к форме обратной связи или отправляется по запросу поддержки. На iOS используется Share Sheet, на Android — Intent. Дополнительно — возможность отправки напрямую в support-тикет через API Zendesk/Freshdesk как attachment. Это ускоряет обработку инцидентов на 30%, экономя до 40% времени разработчиков.
Что входит в работу
- Аудит текущего логирования и выявление узких мест.
- Проектирование архитектуры in-memory буфера с выбором ёмкости (по умолчанию 500 записей) и стратегии сброса.
- Реализация Timber tree (Android) или OSLog-обёртки (iOS) с обфускацией чувствительных данных.
- Интеграция с helpdesk (Zendesk, Freshdesk) и UI для экспорта.
- Подготовка документации и обучение команды.
- Сопровождение в течение 30 дней после внедрения.
Процесс внедрения
- Аудит текущего логирования и выявление узких мест.
- Проектирование архитектуры буфера и выбор стратегии сброса.
- Реализация Timber tree / OSLog-обёртки с обфускацией.
- Интеграция с helpdesk и UI для экспорта.
- Тестирование под нагрузкой и документирование.
Чек-лист внедрения
- In-memory буфер с ёмкостью 500 записей
- Фильтрация чувствительных данных (кастомные паттерны)
- Экспорт в текстовый файл с метаданными устройства
- Автоматическая отправка при крэше (Crashlytics + кастомный буфер)
- Интеграция с тикет-системой для прямой привязки отчёта к инциденту
Ориентировочные сроки
Базовая реализация in-memory буфера, Timber tree / OSLog-обёртки и экспорта файла — 3–5 дней. С обфускацией чувствительных данных, интеграцией с helpdesk и UI для пользователя — до 1 недели. Получите консультацию по внедрению диагностики в ваше приложение — свяжитесь с нами.







