OSLog as a replacement of print
and NSLog
is the recommended way of logging by Apple. It’s a bit harder to write, but it comes with some nice advantages compared to it’s better-known friends.
Setting up OSLog
OSLog makes it possible to log per category, which can be used to filter logs using the Console app. By defining a small extension you can easily adopt multiple categories
import os.log
extension OSLog {
private static var subsystem = Bundle.main.bundleIdentifier!
/// Logs the view cycles like viewDidLoad.
static let viewCycle = OSLog(subsystem: subsystem, category: "viewcycle")
}
This extension uses the bundle identifier of the app and creates a static instance for each category. In this case, we have a view cycle category, which we can use to log in our app:
override func viewDidLoad() {
super.viewDidLoad()
os_log("View did load!", log: OSLog.viewCycle, type: .info)
}
Log levels
The OSLog API requires to pass in an OSLogType
which can be used to automatically send messages at the appropriate level. A log type controls the conditions under which a message should be logged and is another way of filtering in the Console app.
- default: The default log level, which is not really telling anything about the logging. It’s better to be specific by using the other log levels.
- info: Call this function to capture information that may be helpful, but isn’t essential, for troubleshooting.
- debug: Debug-level messages are intended for use in a development environment while actively debugging.
- error: Error-level messages are intended for reporting critical errors and failures.
- fault: Fault-level messages are intended for capturing system-level or multi-process errors only.
You can pass in a log level as a type parameter:
/// We're logging an .error type here as data failed to load.
os_log("Failed loading the data", log: OSLog.data, type: .error)
Logging parameters
Parameters can be logged in two ways depending on the privacy level of the log. Private data can be logged using %{PRIVATE}@
and public data with %{PUBLIC}@
.
In the following example, we’re logging the username in both public and private to show the differences.
override func viewDidLoad() {
super.viewDidLoad()
os_log("User %{PUBLIC}@ logged in", log: OSLog.userFlow, type: .info, username)
os_log("User %{PRIVATE}@ logged in", log: OSLog.userFlow, type: .info, username)
}
The Xcode console and the Console.app will show the data as normal when a debugger is attached.
LogExample[7784:105423] [viewcycle] User Antoine logged in
LogExample[7784:105423] [viewcycle] User Antoine logged in
However, opening the app while no debugger is attached will show the following output in the Console.app.
debug 18:58:40.532132 +0100 LogExample User Antoine logged in
debug 18:58:40.532201 +0100 LogExample User <private> logged in
The username is logged as <private>
instead which prevents your data from being readable by anyone inside the logs.
Reading logs with the Console.app
Using the Console.app in combination with OSLog
is recommended to get the most out of this way of logging.
Start by selecting your device on the left in the devices menu. Simulators and connected devices will show up in this list.

Devices menu in the Console.app
After selecting your device you can start entering a keyword in the search field, after which an option appears as any inside a drop-down menu.
This is the place in which you can filter on your category:

Category filtering
We could go even further if this isn’t enough filtering by passing in the subsystem:

Adding a subsystem as a filter
Make sure to include info and debug messages by enabling them from the action menu, so all your messages show up:

Including info and debug messages to make them show up
This should be enough to get you started with reading logs inside the Console.app.

An example of the logs inside the Console.app
Saving search patterns
To make your workflow faster, you can save your most common search patterns. They will end up in the subheader to quickly filter out logs and start debugging efficiently.

Saved search patterns in the sub-header to quickly filter your OSLog implementation
Further reading
WWDC this year included a dedicated session to logging, including performance logging APIs. You can watch the session here: Measuring Performance Using Logging.
For more in-depth documentation, check out the Apple docs on logging