Користувач повідомляє: «Додаток падає при надсиланні форми». Ви перевіряєте — на симуляторі все працює. Без діагностичних логів така ситуація — типовий головний біль. У 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 тижня. Отримайте консультацію щодо впровадження діагностики у ваш додаток — зв’яжіться з нами.







