Buffered Remote logging in Swift

I would like to implement remote logging in my Swift app for iOS. Log lines will need to be sent to a server so that I can analyse problems when the app is actually being used 'in the field'. This is the easy part. For user privacy, this might be limited to TestFlight versions of the app.

The app is sometimes used in an area of poor or non-existent connectivity. So sendng the logs to our servers may fail. Therefore, I would like to see that the logs are buffered when connecting to our log server failed.

The easiest way to buffer is in Memory (Array of 'LogEntry'), but when a bug we are trying to track involves a crash ... all memory is lost, so I would like the logs to be buffered on disk.

Is there a way to do that?

Perhaps using OSLog, or perhaps some other way? I understand that on iOS, the app does not (yet) have access to its logs. That would solve my problem, cos then I can read back the logs that I made and send them to the server when needed.

I have thought about creating my own Log file, and (when there is a server connection) I read lines from the start of the file, and send that to the server. However, I have found no way to 'read and delete the first line of a file' without reading the whole file, and writing 'everything except the first line'. Seems like CoreData would not be such a bad idea, albeit a bit heavy for such a small task.

Any suggestions? Any existing components that I can include in my project? (using cocoapods, SPM?)

Thanks! Wouter

Replies

I understand that on iOS, the app does not (yet) have access to its logs.

That changed in iOS 15 with the advent of the .currentProcessIdentifier scope. The main drawback to this is covered by this thread.

Beyond that, this is a hard problem to solve. There’s a fundamental trade-off between log fidelity and both performance and flash wear. For maximum fidelity you want to write every log entry to a file as it’s generated. However, doing that is slow and generates a lot of writes to flash.

The system log solves both of these problems by spooling log entries to a daemon using a share memory buffer. Once the log entries are in the buffer, they persist even if the app crashes. On the back end, the daemon is responsible for gathering up log entries, compressing them, and writing them to disk in a flash-friendly fashion.

Unfortunately this sort of architecture is not possible for third-party code on iOS.

IMO the best way to approach this problem is to lean harder into the system log. Rather than try to automate this within your app, ask the user to trigger a sysdiagnose log when they encounter a problem and then send it your way. That’ll include your log entries and a world of system log entries that might be useful in debugging the problem. For more info about this whole topic, see Your Friend the System Log.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"