Swift: print() vs println() vs NSLog()
Asked Answered
C

6

566

What's the difference between print, NSLog and println and when should I use each?

For example, in Python if I wanted to print a dictionary, I'd just print myDict, but now I have 2 other options. How and when should I use each?

Chinoiserie answered 20/9, 2014 at 16:59 Comment(5)
possible duplicate of Difference between println and print in SwiftRicardaricardama
what about NSLog and printing a NSDictionary doesn't give me anything useful?Chinoiserie
From iOS 10.0 forward it's recommended that one uses os_log. Please see my answer below.Reuben
In addition to seeing the Swift documentation on os_log: try seeing the full documentation of the objective-C page. It's much more complete.Wormy
... and effective iOS 14 (and macOS 11, etc.), use Logger in lieu of os_log.Unlikely
U
948

A few differences:

  1. print vs println:

    The print function prints messages in the Xcode console when debugging apps.

    The println is a variation of this that was removed in Swift 2 and is not used any more. If you see old code that is using println, you can now safely replace it with print.

    Back in Swift 1.x, print did not add newline characters at the end of the printed string, whereas println did. But nowadays, print always adds the newline character at the end of the string, and if you don't want it to do that, supply a terminator parameter of "".

  2. NSLog:

    • NSLog adds a timestamp and identifier to the output, whereas print will not.

    • NSLog statements appear in both the device’s console and debugger’s console whereas print only appears in the debugger console.

    • NSLog in iOS 10-13/macOS 10.12-10.x uses printf-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

    • NSLog from iOS 14/macOS 11 can use string interpolation. (Then, again, in iOS 14 and macOS 11, we would generally favor Logger over NSLog. See next point.)

    Nowadays, while NSLog still works, we would generally use “unified logging” (see below) rather than NSLog.

  3. Effective iOS 14/macOS 11, we have Logger interface to the “unified logging” system. For an introduction to Logger, see WWDC 2020 Explore logging in Swift.

    • To use Logger, you must import os:

      import os
      
    • Like NSLog, unified logging will output messages to both the Xcode debugging console and the device console, too.

    • Create a Logger and log a 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 subsystem and category. 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:

      logger.log("url = \(url, privacy: .public)")
      
    • Note, that in Xcode 15 and later, you can now filter the log by type, subsystem, category, or whatever. Personally, in large projects, I find it useful to use a separate Logger for each source file, then I can filter a voluminous log to something more specific (e.g., just log messages of type “error” for a particular “category”, etc.). For more information, see WWDC 2023 video Debug with structured logging.

    • Also, unlike print and NSLog, when you use Logger with Xcode 15 or later, you can control-click (or right-click, if you have enabled the right mouse button) on a log message in the Xcode console, and choose “Jump to source” to jump to the relevant line of code.

  4. Prior to iOS 14/macOS 11, iOS 10/macOS 10.12 introduced os_log for “unified logging”.

    • Import os.log:

      import os.log
      
    • You should define the subsystem and category:

      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 with print and Logger you do:

      logger.log("url = \(url)")
      

      But with os_log, you would have to do:

      os_log("url = %@", url.absoluteString)
      
    • The os_log enforces 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://mcmap.net/q/74433/-how-to-identify-key-events-in-xcode-instruments.

    • Like Logger, for OSLog you can control-click ( or right-click) on the message and jump to the relevant line of code in Xcode 15 and later.

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.

Unlikely answered 20/9, 2014 at 17:40 Comment(16)
Nice summary! To add a few more: you can pass in an NSString to println, but not NSLog; you can add args for NSLog, but not println; Swift style string interpolation sometimes crashes for NSLog, but not println.Bit
an interesting note about Swift compiler optimisation and the use of print() medium.com/ios-os-x-development/…Lotetgaronne
@Unlikely if i use print then does it appear in the debugger console or not?or should we use debugPrint?Hitch
If you use print, it shows up in the debug area of Xcode, just like debugPrint. The only difference is that print ends up calling description method of the object, and debugPrint calls debugDescription, which may be more verbose than description.Unlikely
@Rob, your answer seems to address issues with logging in Swift. I am trying to relate this to a problem I am having with NSLog in Objective C since I upgraded to Xcode 9 https://mcmap.net/q/74434/-how-do-i-display-this-log-message-in-the-debug-area-using-xcode9/2348597Hartzel
I think this answer could benefit from a disclaimer or something about how difficult it is for the average user to trigger, retrieve, send a sysdiagnose log...I don't think it used to be that difficult before with NSLog. FWIW I've addressed that hereWormy
Great writeup, another difference between NSLog and print I've seen is that if you're redirecting the output to a file (with giving stderr/stdout to freopen) then print doesn't get logged into the file in a timely manner like NSLog does, so try not to mix them if you're redirecting to a file!Arica
Starting from iOS 14/macOS 11 , i also noticed another logging framework from Apple: github.com/apple/swift-log. What's the difference with import os?Sonority
@Unlikely The truth is if I use that open source via "import Loggin", I can not see any log output in Console.app. This is the puzzle part i haveSonority
@Sonority - It depends upon what level of logging you are doing with this open source rendition of Logger. By default, you’ll see info and error, but not, for example, debug (unless you change logLevel, e.g., logger.logLevel = .debug).Unlikely
"NSLog statements appear in both the device’s console and debugger’s console whereas print only appears in the debugger console". Does this mean neither NSLog nor print will appear in Sysdiagnose? because they both only send messages to debugger's console and the debugger isn't attached when the logs are happening on the phone?Wormy
The point of that quoted sentence is to point out that print and NSLog behave differently. In answer to your question, I suspect that print would not appear in sysdiagose, but NSLog would.Unlikely
In an app unattached to XCode, how does print function? Is is still called and then ignored (viz. is it safe/performant to leave print functions lying around or should they be removed, or disabled with a macro?) If it's not called, presumably string interpolation is still run on args to it (or is it removed completely)?Ayers
1. Benchmark it and you will see performance impact with print statements (though not really observably so unless you are doing millions of them). 2. If you use Logger, instead, not only do you have the option to see logging statements in the macOS console, but it’s more performant than print. 3. With Logger or os_log, you can define your log to be .disabled and you’ll see performance benefit over print in release builds (and you'll be confident that you won't leak anything to the end-user). I use the latter.Unlikely
Great article; small typo: 'os_log("url = (url, privacy: .public)")' should be 'logger.log("url = (url, privacy: .public)")'Edwyna
That typo has been fixed. Thanks.Unlikely
W
85

If you're using Swift 2, now you can only use print() to write something to the output.

Apple has combined both println() and print() functions into one.

Updated to iOS 9

By default, the function terminates the line it prints by adding a line break.

print("Hello Swift")

Terminator

To print a value without a line break after it, pass an empty string as the terminator

print("Hello Swift", terminator: "")

Separator

You now can use separator to concatenate multiple items

print("Hello", "Swift", 2, separator:" ")

Both

Or you could combine using in this way

print("Hello", "Swift", 2, separator:" ", terminator:".")
Wivinia answered 16/6, 2015 at 18:32 Comment(3)
appendNewline has a default value of trueTog
In iOS (9.0) you need to use terminator : "", e.g. print("...", terminator: "")Checklist
The statement in your first sentence is incorrect. NSLog() still works, even in latest Swift 2.xGrossman
L
64

Moreover, Swift 2 has debugPrint() (and CustomDebugStringConvertible protocol).

Don't forget about debugPrint() which works like print() but most suitable for debugging.

Examples:

  • Strings
    • print("Hello World!") becomes Hello World
    • debugPrint("Hello World!") becomes "Hello World" (Quotes!)
  • Ranges
    • print(1..<6) becomes 1..<6
    • debugPrint(1..<6) becomes Range(1..<6)

Any class can customize their debug string representation via CustomDebugStringConvertible protocol.

Littleton answered 4/12, 2015 at 2:57 Comment(3)
DebugPrintable protocol has been renamed to CustomDebugStringConvertible protocol.Natale
So Swift's description is to debugDescription as Python's str is to repr?Junitajunius
Yes, I think so.Littleton
R
44

To add to Rob's answer, since iOS 10.0, Apple has introduced an entirely new "Unified Logging" system that supersedes existing logging systems (including ASL and Syslog, NSLog), and also surpasses existing logging approaches in performance, thanks to its new techniques including log data compression and deferred data collection.

From Apple:

The unified logging system provides a single, efficient, performant API for capturing messaging across all levels of the system. This unified system centralizes the storage of log data in memory and in a data store on disk.

Apple highly recommends using os_log going forward to log all kinds of messages, including info, debug, error messages because of its much improved performance compared to previous logging systems, and its centralized data collection allowing convenient log and activity inspection for developers. In fact, the new system is likely so low-footprint that it won't cause the "observer effect" where your bug disappears if you insert a logging command, interfering the timing of the bug to happen.

Performance of Activity Tracing, now part of the new Unified Logging system

You can learn more about this in details here.

To sum it up: use print() for your personal debugging for convenience (but the message won't be logged when deployed on user devices). Then, use Unified Logging (os_log) as much as possible for everything else.

Reuben answered 18/2, 2017 at 10:24 Comment(0)
V
15

iOS logger

[Console Log]

  1. NSLog - add meta info (like timestamp and identifier) and allows you to output 1023 symbols. Also print message into Console. The slowest method. Is not safe because other applications has an access to log file

    //@import Foundation //Objective-C
    @import Foundation
    NSLog("SomeString")
    
  2. print - prints all string to Xcode. Has better performance than previous

    //@import Foundation //Objective-C
    import Foundation
    print("SomeString")
    
  3. println (only available Swift v1) and add \n at the end of string

  4. os_log (from iOS v10) - prints 32768 symbols also prints to console. Has better performance than previous

    //@import os.log //Objective-C
    @import os.log
    os_log("SomeIntro: %@", log: .default, type: .info, "someString")
    
  5. Logger (from iOS v14) - prints 32768 symbols also prints to console. Has better performance than previous

    //@import os //Objective-C
    import os
    let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "someCategory")
    
    logger.log("some message \(Date)")
    //some message <private>
    //it is about privacy level
    
    logger.log("some message \(Date, privacy: .public)")
    //some message 2023-11-11 16:09:03 +0000
    
Vander answered 11/11, 2020 at 12:18 Comment(0)
C
6

There's another method called dump() which can also be used for logging:

func dump<T>(T, name: String?, indent: Int, maxDepth: Int, maxItems: Int)

Dumps an object’s contents using its mirror to standard output.

From Swift Standard Library Functions

Casady answered 20/9, 2014 at 16:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.