Logs Collector
The Logs Collector module writes the SDK's own logs to disk and lets you send them to support. You open an upload session, which creates a ticket, share the ticket's code with a support agent, and attach the log files to it. It can also export the logs to a local ZIP file, with no ticket and no network.
This module is separate from the LogCallbackProtocol described in Setting up the SDK. That callback streams log events to your app. File logging keeps its own copy on disk, and the callback keeps working whether file logging is on or off.
Enabling File Logging
File logging is off by default. Two settings enable it, and both must be set before the SdkManager is created. Later changes have no effect.
| Setting | Description |
|---|---|
setBasePath(path) | A writable directory. Logs go to <path>/logs, the cache to <path>/cache_root unless setCacheFilePath already set one. Can be set only once; a second call throws |
setLoggingConfig(config) or logging: YAML | Turns file logging on and sets its limits |
- Swift
- Kotlin
- YAML
try configuration.setBasePath(path: appSupportDirectory)
configuration.setLoggingConfig(config: LoggingConfig())
let manager = try SdkManager(configuration: configuration)
configuration.setBasePath(appFilesDirectory)
configuration.setLoggingConfig(LoggingConfig())
val manager = SdkManager(configuration = configuration)
logging:
logfile_max_mb: 50
LoggingConfig
Every field is optional and falls back to its default.
| Name | Type | Default | Description |
|---|---|---|---|
| prelogMaxMb | UInt64 | 4 | Size cap of the in-memory buffer kept around startup, in MB |
| prelogMaxMinutes | UInt64 | 60 | How long the in-memory buffer is kept, in minutes |
| logfileMaxMb | UInt64 | 100 | Size cap of the log files on disk, in MB |
| logRetentionHours | UInt64 | 168 | How long log files are kept on disk, in hours |
Log files are encrypted on disk. The methods below decrypt them, so what reaches support or the ZIP file is plain JSON Lines.
setDangerousYouAreResponsibleForUnencryptedPlaintextLogs(true) writes the logs unencrypted, into one file with no size cap and no retention. Logs contain user identifiers, tokens and network names. Use it for local debugging only, never in a production build. It has no YAML equivalent.
Getting the Module
- Swift
- Kotlin
let logs = try manager.logsCollector()
val logs = manager.logsCollector()
All methods throw LoggingException. The async methods also have a blocking variant without the Async suffix.
create_session
Creates a ticket and returns a LogUploadSession bound to it.
suspend fun createSessionAsync(): LogUploadSession
The ticket records the OS, OS version, app version, SDK version and device model. They come from the ClientDetails you already set, so you pass nothing extra.
No login is needed, so a user can upload logs after signing in has failed.
create_session_with_attributes
Same as create_session, plus key/value pairs recorded on the ticket and shown to support staff. Use them for what the SDK cannot know, such as the network type or an app-side correlation id.
suspend fun createSessionWithAttributesAsync(
attributes: Map<String, String>
): LogUploadSession
The collector rejects the whole request if any pair breaks these rules:
| Rule | Limit |
|---|---|
| Count | At most 20 pairs per ticket |
| Key | 1–64 bytes of [a-z0-9_.-], starting with a lower-case letter |
| Value | 1–128 bytes, no control characters |
| Reserved keys | os_name, os_version, app_version, sdk_version, device_model, and any key starting with kp_ or kp. |
Support staff can read these values, and they are kept as long as the ticket. Do not put personal data in them.
export_logs_to_zip
Writes the current log files into a ZIP file, decrypted. Needs no ticket and no network.
suspend fun exportLogsToZipAsync(zipPath: String): UInt
zip_path must be an absolute path with no .. component. The archive is created if missing and appended to otherwise. A file already in the archive is skipped, so exporting again adds only the files written since. Returns the number of files added.
flush_logs
Blocks until buffered log records are written to disk.
fun flushLogs(): Boolean
Call it from suspend and exit handlers, chiefly in an iOS Network Extension, so the last records survive the process being frozen or killed. Returns false when nothing was written to disk: file logging is off, or only the in-memory buffer is active.
LogUploadSession
A session is bound to one ticket.
| Method | Returns | Description |
|---|---|---|
code() | String | The ticket code. The only value the user reads out to support |
uploadId() | String | Secret upload token. Required for every attach call; do not show or share it |
setAttributes(attributes) | Map<String, String> | Adds pairs to the ticket and returns all of its pairs |
attachFile(filename, contentType, body) | AttachFileResponse | Attaches one file of your own to the ticket |
uploadSdkLogs(progress) | Array of AttachFileResponse | Attaches every SDK log file, newest first |
set_attributes
Records a value that exists only after the ticket does, such as the helpdesk ticket id an agent creates once the user has read out the code. It keeps working for as long as the ticket exists.
Each key is write-once:
- A new key is added.
- A key sent again with the same value succeeds and changes nothing, so retrying after a lost response is safe.
- A key sent again with a different value fails the whole call with HTTP 409, and none of the pairs are applied.
The rules and the warning of create_session_with_attributes apply. An empty map writes nothing and returns the current pairs.
attach_file
Each call creates a new file on the ticket. Retrying after a failure uploads the file again rather than replacing it.
upload_sdk_logs
Attaches each SDK log file, decrypted, in its own request. Returns one AttachFileResponse per uploaded file. A file that cannot be read or decrypted is skipped. A file cut short by a crash is sent up to the last readable record. Files already uploaded stay on the ticket if the upload stops early.
LogUploadProgressCallback
interface LogUploadProgressCallback {
fun onProgress(current: UInt, total: UInt): Boolean
}
Called with current = 0 before the first upload, then once per file: (0, N), (1, N), …, (N, N). Skipped files are counted too, so current always reaches total. Return false, or throw, to stop after the current file.
AttachFileResponse
| Name | Type | Description |
|---|---|---|
| fileId | String | ID of the file on the ticket |
| filename | String | Name of the file |
| contentType | String | MIME type. application/x-ndjson for SDK logs |
| size | UInt64 | Size in bytes |
| status | String | Upload status reported by the collector |
| uploadStartedAt | Int64 | Upload start, as a Unix timestamp |
| uploadFinishedAt | Int64? | Upload end, as a Unix timestamp, if finished |
Example
- Swift
- Kotlin
let session = try await manager.logsCollector()
.createSessionWithAttributesAsync(attributes: ["network_type": "wifi"])
showToUser(session.code())
try await session.uploadSdkLogsAsync(progress: ProgressBar())
val session = manager.logsCollector()
.createSessionWithAttributesAsync(attributes = mapOf("network_type" to "wifi"))
showToUser(session.code())
session.uploadSdkLogsAsync(ProgressBar())