OSLog and Unified logging as recommended by Apple – onlinecode
In this post we will give you information about OSLog and Unified logging as recommended by Apple – onlinecode. Hear we will give you detail about OSLog and Unified logging as recommended by Apple – onlinecodeAnd how to use it also give you demo for it if it is necessary.
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.
By writing a small extension you make it fairly easy to replace your print statements. Using the Console app in combination with your logs can help you debug issues in a more efficient way. OSLog has a low-performance overhead and is archived on the device for later retrieval. These are two of the advantages of using OSLog instead of print statements.
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.
Note: if you’re using iOS 14 and up, there are new APIs available explained further down this post.
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 (notice): 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.
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:
We could go even further if this isn’t enough filtering by passing in the subsystem:
Make sure to include info and debug messages by enabling them from the action menu, so all your messages show up:
This should be enough to get you started with reading 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.
Improved APIs in iOS 14 and up
WWDC 2020 introduced improved APIs that make it even easier to work with OSLog. The APIs look much more similar to popular frameworks like CocoaLumberjack and are better aligned with other Swift APIs.
All previous covered explanations in this blogpost still apply and the code examples are still working on iOS 14. However, if you’re supporting iOS 14 and up you might want to go with the improved APIs as they look nicer and come with a few new features.
Using a Logger instance
One of the differences is using the newly introduced Logger instance. The initialiser matches the one from OSLog:
extension Logger {
private static var subsystem = Bundle.main.bundleIdentifier!
/// Logs the view cycles like viewDidLoad.
static let viewCycle = Logger(subsystem: subsystem, category: "viewcycle")
}
The differences are visible when trying to log messages as you now have to use methods like info(_:)
and debug(_:)
:
Logger.viewCycle.info("View did load!")
Removed restriction of static strings
A big improvements is the support for string interpolation and string literals. With the old API, it isn’t possible to use string interpolation which makes it harder to log values. With the new APIs you can log as you’re used to with print(_:)
statements:
Logger.viewCycle.debug("User (username) logged in")
Setting the right privacy level
When I used the old API it often happened that I forgot about the %{public}@
syntax. In fact, I’ve been often writing it wrong with uppercase PUBLIC
, for example.
With the new API we can make use of a better discoverable enum to set the right privacy level:
Logger.viewCycle.debug("User (username, privacy: .private) logged in")
The string interpolation support is very useful here as we can decide the privacy level per logged value.
New alignment APIs
There can be cases in which you would like to adjust the alignment of your logs a bit to improve readability. Especially when you’re logging multiple values in a line it can be useful to have certain table formatting applied.
For example, the following log statement would not be nicely aligned if printed out without any formatting:
func log(_ person: Person) {
Logger.statistics.debug("(person.index) (person.name) (person.identifier) (person.age)")
}
/// [statistics] 14 Antoine 8DA690DD-5D97-4B53-897A-C2D98BA0440D 17.442274
/// [statistics] 54 Jaap 31C442DC-BA95-49D3-BB38-E1DD4483E124 99.916344
/// [statistics] 35 Lady 879378DB-FF29-460A-8CA4-B927233A3AA9 93.896309
/// [statistics] 97 Maaike E0A5396E-2B82-4487-86D5-597A108AE36A 9.242964
/// [statistics] 96 Jacobien BC19603E-B078-4DFB-AE36-FD7592FB2E49 59.958466
You can see that the identifier is aligned directly after the name which results in jumping alignments if the names aren’t having the same length.
We can fix this with the new alignment APIs in Swift:
func log(_ person: Person) {
Logger.statistics.debug("(person.index) (person.name, align: .left(columns: Person.maxNameLength)) (person.identifier)")
}
/// [statistics] 42 Antoine 71C6B472-6D90-45D2-A7B4-AA3B5A0FE10F 17.442274
/// [statistics] 55 Jaap 6991D0A2-D755-4527-9512-EDE0D431F460 99.916344
/// [statistics] 35 Lady 66129DE6-E874-4854-B2E0-00BBDB2A5FBB 93.896309
/// [statistics] 62 Maaike D1984459-B67A-44BE-AC83-A43E6460C1E1 9.242964
/// [statistics] 83 Jacobien 24CD3087-91C2-4229-A337-B190D69461BA 59.958466
This improves readability and can help you to more easily digest lots of logs.
Lastly, we can format the age to only show two decimals by using the new formatting string interpolation method:
func log(_ person: Person) {
Logger.statistics.debug("(person.index) (person.name, align: .left(columns: Person.maxNameLength)) (person.identifier) (person.age, format: .fixed(precision: 2))")
}
/// [statistics] 95 Antoine F205DD9C-C92A-4B48-B27A-CF19C6081EB3 85.33
/// [statistics] 84 Jaap C55C3F42-5C02-43E0-B416-2E0B7356A964 88.70
/// [statistics] 58 Lady FD25FB54-51CA-4D6D-805E-547D29D5AE34 38.30
/// [statistics] 69 Maaike 4FDE8D73-ECBF-4015-AE5F-2AED7295D6B2 9.72
/// [statistics] 86 Jacobien E200351B-920F-4351-9752-212912B42ECB 69.23
Further reading
WWDC often includes dedicated sessions to logging, including performance logging APIs. You can watch the sessions here:
- Measuring Performance Using Logging (WWDC 2018)
- Explore logging in Swift (WWDC 2020)
For more in-depth documentation, check out the Apple docs on logging.
Conclusion
OSLog is the future of logging in Swift. It’s a great replacement for commonly used print statements and comes with several advantages like reading out logs from the Console app and low-performance overheads.
If you like to learn more tips on debugging, check out the debugging category page. Feel free to contact me or tweet to me on Twitter if you have any additional tips or feedback.
Thanks!
Hope this code and post will helped you for implement OSLog and Unified logging as recommended by Apple – onlinecode. if you need any help or any feedback give it in comment section or you have good idea about this post you can give it comment section. Your comment will help us for help you more and improve us. we will give you this type of more interesting post in featured also so, For more interesting post and code Keep reading our blogs