Diagnostic Logs Collection Implementation for Mobile App

NOVASOLUTIONS.TECHNOLOGY is engaged in the development, support and maintenance of iOS, Android, PWA mobile applications. We have extensive experience and expertise in publishing mobile applications in popular markets like Google Play, App Store, Amazon, AppGallery and others.
Development and support of all types of mobile applications:
Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1 servicesAll 1735 services
Diagnostic Logs Collection Implementation for Mobile App
Medium
~2-3 business days
FAQ
Our competencies:
Development stages
Latest works
  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    756
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    624
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1052
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    947
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    862
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    445

Implementing Diagnostic Logs Collection in Mobile Apps

A user reports a problem—but reproducing it on another device is impossible. Without diagnostic logs, you're left guessing. A properly configured logging system allows you to obtain complete context directly from the user's device: sequence of actions, network requests, memory state.

Logging Architecture

In-Memory Ring Buffer

Writing to a file for every log call is expensive. Instead, maintain a ring buffer in memory and flush to disk only when collecting diagnostics or on crash:

// Android: ring buffer for logs
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 entries is enough to recover the last few minutes of app activity. More is usually excessive and consumes significant memory.

Timber Tree for Android

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)
        // In debug builds, additionally write to Android Logcat
        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

// Custom Logger with 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 for Instruments and Console.app
        osLog.log(level: level.osLogType, "\(message)")
    }
}

#file and #line provide automatic attachment to the call location. In a diagnostic report, you see not just "what happened" but "where in the code."

Saving to File

When collecting diagnostics, serialize the buffer to a file:

suspend fun exportDiagnosticReport(): File = withContext(Dispatchers.IO) {
    val file = File(context.cacheDir, "diagnostic_${System.currentTimeMillis()}.txt")
    file.bufferedWriter().use { writer ->
        writer.appendLine("=== App: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE}) ===")
        writer.appendLine("=== Device: ${Build.MANUFACTURER} ${Build.MODEL}, Android ${Build.VERSION.RELEASE} ===")
        writer.appendLine("=== Report generated: ${Date()} ===")
        writer.appendLine()
        logBuffer.getLast(500).forEach { entry ->
            writer.appendLine("[${entry.levelTag}] ${entry.formattedTime} ${entry.tag}: ${entry.message}")
        }
    }
    file
}

Sanitizing Sensitive Data

Logs should not contain tokens, passwords, or card data. Filter at the logger-tree level:

private val sensitivePatterns = listOf(
    Regex("""Bearer\s+[\w\-._~+/]+=*"""),          // Authorization header
    Regex("""\b\d{13,19}\b"""),                       // Card numbers
    Regex(""""password"\s*:\s*"[^"]*"""")             // JSON password field
)

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)
}

User-Initiated Report Submission

The diagnostic file is attached to a feedback form or sent upon support request:

// iOS: Share sheet to send the file
let diagnosticURL = try await DiagnosticLogger.shared.exportReport()
let activityVC = UIActivityViewController(
    activityItems: [diagnosticURL],
    applicationActivities: nil
)
present(activityVC, animated: true)

Additionally—the ability to send directly to a support ticket via Zendesk/Freshdesk API as an attachment.

Timeline Estimates

In-memory buffer implementation, Timber tree / OSLog wrapper, and file export—3–5 days. With sensitive data sanitization, helpdesk integration, and user UI—up to 1 week.