How we use os_log

How we use os_log

A nice feature in iOS, that already exists since iOS 10, is os_log . It is a somewhat obscure feature that little people use or know about. That's why I want to give a small introduction on how we use this logging mechanism in our apps.

In short,  os_log makes it possible for you to add logs to your application in a more structured way. It allows you to filter your logs in the Console.app application. This way, you get a better view on why a certain error occurred. You can see it as an advanced version of the Xcode Console.

Structure your logs

There are two important properties that you can pass to the os_log function through OSLog: subsystem and category.

The subsystem is the identifier that represents the part of your application that performs the logging, for example a Today extension. You can use this identifier to filter your logs in the Console application. The category allows you to group your logs within a subsystem. This way you can get a clear overview of the logs that are triggered and for what reason.

How to use them is simple. Just create an OSLog object and pass this to os_log function.

import os
 
let app = OSLog(subsytem: "com.icapps.app", category: "Player")
os_log("▶️ Start playing", log: app, type: .info)
os_log("▶️ Pause playing", log: app, type: .info)
 
let memory = OSLog(subsytem: "com.icapps.app", category: "Memory")
os_log("💥 Deallocating object", log: memory, type: .info)

In the Console application you can then filter by subsystem and category which can come in very handy. You can even save the current filter for later use.

Extending OSLog

I tend to create an extension on OSLog to make it a bit easier to use the subsystem and category implementation. Instead of having to create an OSLog instance every time, you can just pass the static variable.

Here is an example of the code above, but while using the OSLog extension.

os_log("▶️ Start playing", log: OSLog.app, type: .info)
os_log("⏸ Pause playing", log: OSLog.app, type: .info)
os_log("💥 Deallocating object", log: OSLog.memory, type: .info)

Here is what the extension looks like:

import os
 
extension OSLog {
    static let app = OSLog(category: "Application")
    static let memory = OSLog(category: "Memory")
 
    private convenience init(category: String, bundle: Bundle = Bundle.main) {
        let identifier = bundle.infoDictionary?["CFBundleIdentifier"] as? String
        self.init(subsystem: (identifier ?? "").appending(".logs"), category: category)
    }
}

sysdiagnosE

Most crashes that are hard to reproduce occur when not connected to a debugger. But wouldn't it be nice if you could see what your application was doing prior to the crash?

Meet sysdiagnose

Sysdiagnose provides a lot of helpful information when something unexpected is happening in your application. It contains the logs from different parts of the OS and specific logs from your application. There is only one condition required, add os_log's to your application. The more logs you have, the better you can find out what is happening!

Now how do you generate such a sysdiagnose file? This can be tricky, but Apple has provided us with some instructions on the Profiles and Logs page. This is what you should do:

  1. Press both volume buttons and the lock button simultaneously.
    You will feel a short vibration when sysdiagnose is correctly triggered.
  2. Wait for 10 minutes until the sysdiagnose process finished. 
  3. Go to Settings.app -> Privacy > Analytics > Analytics Data
  4. Here you can find a file that starts with sysdiagnose
  5. AirDrop this (zipped) file to your Mac.

When you open the unzipped file on your Mac you will find useful information inside. Some files can just be opened with the Console application and you can apply the filter you want in order to find the details you need.

The screenshot above shows the content of the sysdiagnose file. In most cases you will only need the system_logs.logarchive file. Just double click the file to open it in the Console application and you see the logs the same way as if your device was connected.

Awesome isn't it? 🤘

Recap

This blogpost was inspired by a talk I did recently during one of our iOS tech gatherings at icapps. We discussed the usage of the os framework provided by Apple. If you're curious about this presentation, check it below.

Unified Logging

Since I got familiar with os_log, it became the one and only way for me to add logging in my app. When something unexpected happens it's a lot easier to get a better understanding of what happened prior to the issue. And of course, NSLog can still be used, but you won't have the power of subsystem and category in your logs.