NSLog unreliable in Xcode 15 Console

I'm finding that in Xcode 15, NSLog statements (from Objective-C, in this case) will sometimes appear in Xcode's Console and sometimes will not.

This is making it difficult to debug using our existing logging, which for our REST API requests and responses has been working well for about ten years and is a tough thing to lose.

Is there any way I can restore reliable handling of NSLog in Xcode 15's Console without having to rewrite all (over 200) of our uses of NSLog to use OSLog?

Note: I am aware that Xcode 15's Console sometimes takes more than 30 seconds to start tracking app output, but it appears that in some cases the content of individual NSLog statements never will appear. (If this 30 second delay is news to anyone, it may be related to a spam of identical warnings that are okay which occur on launch of our app. I have suspected that Xcode 15 just gets a bit flustered with identical messages and gives up for a bit.)

Thank you!

Replies

NSLog does two things [1]:

  • It creates a log entry in the system log.

  • It prints a serialised form of the log entry to stderr.

Xcode 14 ignores the former and collects the latter. Xcode 15 is the reverse. That means that you start bumping into system log limitations. For example:

  • The system log has a log entry size limit and, by default, only captures the log entry up to that size [2].

  • Under extremely heavy load the system log can drop log entries. [I’ll come back to this point below.]

  • The system log can ‘quarantine’ programs that are logging excessively.

The best path forward kinda depends on your goals:

  • Do you need to be able to capture this log from devices running release builds in the field? Or are you only concerned about logging during development?

  • What platform are you targeting?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] … that are relevant to this issue. The exact behaviour of NSLog is really complex.

[2] Although it supported oversized log entries, via the Enable-Oversize-Messages option.

  • I also noticed today that NSLog is not logging to the debugger console, but the messages do appear in the log facility. I am debugging a MacOS app (intel+applesilicon) running on M1, max OS 16.6.3. This would not seem to have anything to do with the log entry size, since the message are properly recorded in the system log. They just no longer appear in the debugger.

Add a Comment

I too am encountering this issue just since updating to XCode 15.0.1. I am using NSLog to report data from an actual Apple Watch device running in debug mode, coded in Objective-C. The watch is now connected directly as a standalone device (which is great and has solved the usual connection issues). But now no NSLogs appear ever, not from any process. Just a blank console. I only need NSLog to report variable values, track when functions are called while debugging my WatchOS app. After decades of "just using it", NSLog appears to have just switched-off! Any advice to see debug logging greatly appreciated. Right now I have to put in breakpoints and inspect which is very slow. Not sure if this is an XCode 15 issue, or the new way of connecting Apple Watch devices via network (which I like a LOT) Thanks

Thanks to the helpful explanation in this article https://lapcatsoftware.com/articles/2023/10/5.html

I understood the cryptic references in the XCode 15 release Notes and answered this myself. To return to XCode 14 and earlier world in which NSLog works you have to do this:

  1. Select the relevant product in the the Scheme dropdown (e.g. "[AppName] Watchkit App", or "[AppName]" for iOS app
  2. Select "Edit Scheme..."
  3. Select "Run" in list on the left
  4. Select "Arguments" tab
  5. Click ">" next to "Environment Variables" to show a (probably empty) list.
  6. Click "+"
  7. In "Name" column, double click the space and enter: IDELogRedirectionPolicy
  8. In "Value" column double click the space and enter: oslogToStdio
  9. Ensure the checkbox is selected
  10. Click Close button.

You have to repeat these steps for each iOS, WatchOS product as each has its own Run Scheme.

Now NSLogs will print to the Debug Console again. I have no idea about other side effects as I don't need more sophisticated logging. Works fine when connected by Network too.

  • This worked! Thank you - I have spent days trying to get WatchOS debugging on a real device working again with Xcode 15.0.1.

Add a Comment

djmlewis wrote:

I am using NSLog to report data from an actual Apple Watch

watchOS is a very different story [1], one that I’m not super familiar with. If you want to explore the watchOS side of things beyond your IDELogRedirectionPolicy approach, please start a new thread focused on that platform (tag it with OSLog so that I see it).

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] For performance reasons the system log setup on Apple Watch is much more restrictive than it is on other platforms.

  • Thanks Quinn. The IDELogRedirectionPolicy setting has fixed the NSLog problem. All working well now. Cheers.

Add a Comment

Above I wrote:

Under extremely heavy load the system log can drop log entries.

which is misleading. The system log should not drop log entries. Your app and the system log’s daemon share a memory buffer. If the daemon is unable to clear the buffer quickly enough, the system log API you’ve called will block wait for space.

However, this story isn’t that simple:

  • There’s the quarantine concept I touched on above.

  • There are internal logging routines that will drop rather than block.

  • AFAIK NSLog is not layered on one of those, but that’s very much an implementation detail of NSLog. I generally recommend that you move off NSLog and on to a proper system log API.

  • Some log entries are volatile and only exist in memory.

  • Log entries that do persist are subject to a TTL.

  • The above says nothing about the system log display tool (Xcode in this case). Those have their own set of limitations.

None of this is really relevant to the original issue on this thread. I’m just following up to head off any potential confusion for folks who stumble across this thread in The Future™.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Wow. Thanks for these really detailed responses. They help a lot and I understand the changes better now.

In my case, I am just interested in the output of active debugging during development. No need for any logging in the field. I am targeting iOS (and iPadOS). The ideal for my case is just to see the stdout/stderr without any length limits.

Thank you!

Mark