A few differences:
-
printvsprintln:The
printfunction prints messages in the Xcode console when debugging apps.The
printlnis a variation of this that was removed in Swift 2 and is not used any more. If you see old code that is usingprintln, you can now safely replace it withprint.Back in Swift 1.x,
printdid not add newline characters at the end of the printed string, whereasprintlndid. But nowadays,printalways adds the newline character at the end of the string, and if you don’t want it to do that, supply aterminatorparameter of"". -
NSLog:-
NSLogadds a timestamp and identifier to the output, whereasprintwill not; -
NSLogstatements appear in both the device’s console and debugger’s console whereasprintonly appears in the debugger console. -
NSLogin iOS 10-13/macOS 10.12-10.x usesprintf-style format strings, e.g.NSLog("%0.4f", CGFloat.pi)that will produce:
2017-06-09 11:57:55.642328-0700 MyApp[28937:1751492] 3.1416
-
NSLogfrom iOS 14/macOS 11 can use string interpolation. (Then, again, in iOS 14 and macOS 11, we would generally favorLoggeroverNSLog. See next point.)
Nowadays, while
NSLogstill works, we would generally use “unified logging” (see below) rather thanNSLog. -
-
Effective iOS 14/macOS 11, we have
Loggerinterface to the “unified logging” system. For an introduction toLogger, see WWDC 2020 Explore logging in Swift.-
To use
Logger, you must importos:import os -
Like
NSLog, unified logging will output messages to both the Xcode debugging console and the device console, too -
Create a
Loggerandloga message to it:let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "network") logger.log("url = \(url)")When you observe the app via the external Console app, you can filter on the basis of the
subsystemandcategory. It is very useful to differentiate your debugging messages from (a) those generated by other subsystems on behalf of your app, or (b) messages from other categories or types. -
You can specify different types of logging messages, either
.info,.debug,.error,.fault,.critical,.notice,.trace, etc.:logger.error("web service did not respond \(error.localizedDescription)")So, if using the external Console app, you can choose to only see messages of certain categories (e.g. only show debugging messages if you choose “Include Debug Messages” on the Console “Action” menu). These settings also dictate many subtle issues details about whether things are logged to disk or not. See WWDC video for more details.
-
By default, non-numeric data is redacted in the logs. In the example where you logged the URL, if the app were invoked from the device itself and you were watching from your macOS Console app, you would see the following in the macOS Console:
url = <private>
If you are confident that this message will not include user confidential data and you wanted to see the strings in your macOS console, you would have to do:
os_log("url = \(url, privacy: .public)")
-
-
Prior to iOS 14/macOS 11, iOS 10/macOS 10.12 introduced
os_logfor “unified logging”. For an introduction to unified logging in general, see WWDC 2016 video Unified Logging and Activity Tracing.-
Import
os.log:import os.log -
You should define the
subsystemandcategory:let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "network")When using
os_log, you would use a printf-style pattern rather than string interpolation:os_log("url = %@", log: log, url.absoluteString) -
You can specify different types of logging messages, either
.info,.debug,.error,.fault(or.default):os_log("web service did not respond", type: .error) -
You cannot use string interpolation when using
os_log. For example withprintandLoggeryou do:logger.log("url = \(url)")But with
os_log, you would have to do:os_log("url = %@", url.absoluteString) -
The
os_logenforces the same data privacy, but you specify the public visibility in the printf formatter (e.g.%{public}@rather than%@). E.g., if you wanted to see it from an external device, you’d have to do:os_log("url = %{public}@", url.absoluteString) -
You can also use the “Points of Interest” log if you want to watch ranges of activities from Instruments:
let pointsOfInterest = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: .pointsOfInterest)And start a range with:
os_signpost(.begin, log: pointsOfInterest, name: "Network request")And end it with:
os_signpost(.end, log: pointsOfInterest, name: "Network request")For more information, see https://stackoverflow.com/a/39416673/1271826.
-
Bottom line, print is sufficient for simple logging with Xcode, but unified logging (whether Logger or os_log) achieves the same thing but offers far greater capabilities.
The power of unified logging comes into stark relief when debugging iOS apps that have to be tested outside of Xcode. For example, when testing background iOS app processes like background fetch, being connected to the Xcode debugger changes the app lifecycle. So, you frequently will want to test on a physical device, running the app from the device itself, not starting the app from Xcode’s debugger. Unified logging lets you still watch your iOS device log statements from the macOS Console app.