The unified system log on Apple platforms gets a lot of stick for being ‘too verbose’. I understand that perspective: If you’re used to a traditional Unix-y system log, you might expect to learn something about an issue by manually looking through the log, and the unified system log is way too chatty for that. However, that’s a small price to pay for all its other benefits.
This post is my attempt to explain those benefits, broken up into a series of short bullets. Hopefully, by the end, you’ll understand why I’m best friends with the system log, and why you should be too!
If you have questions or comments about this, start a new thread and 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"
Your Friend the System Log
Apple’s unified system log is very powerful. If you’re writing code for any Apple platform, and especially if you’re working on low-level code, it pays to become friends with the system log!
The Benefits of Having a Such Good Friend
The public API for logging is fast and full-featured.
And it’s particularly nice in Swift.
Logging is fast enough to leave log points [1] enabled in your release build, which makes it easier to debug issues that only show up in the field.
The system log is used extensively by the OS itself, allowing you to correlate your log entries with the internal state of the system.
Log entries persist for a long time, allowing you to investigate an issue that originated well before you noticed it.
Log entries are classified by subsystem, category, and type. Each type has a default disposition, which determines whether that log entry is enable and, if it is, whether it persists in the log store. You can customise this, based on the subsystem, category, and type, in four different ways:
Install a configuration profile created by Apple (all platforms) [2].
Add an OSLogPreferences property to your app’s Info.plist (all platforms).
Run the log tool with the config command (macOS only)
Create and install a custom configuration profile with the com.apple.system.logging payload (macOS only).
When you log a value, you may tag it as private. These values are omitted from the log by default but you can configure the system to include them. For information on how to do that, see Recording Private Data in the System Log.
The Console app displays the system log. On the left, select either your local Mac or an attached iOS device. Console can open and work with log snapshots (.logarchive). It also supports surprisingly sophisticated searching. For instructions on how to set up your search, choose Help > Console Help.
Console’s search field supports copy and paste. For example, to set up a search for the subsystem com.foo.bar, paste subsystem:com.foo.bar into the field.
Console supports saved searches. Again, Console Help has the details.
Console supports viewing log entries in a specific timeframe. By default it shows the last 5 minutes. To change this, select an item in the Showing popup menu in the pane divider. If you have a specific time range of interest, select Custom, enter that range, and click Apply.
Instruments has os_log and os_signpost instruments that record log entries in your trace. Use this to correlate the output of other instruments with log points in your code.
Instruments can also import a log snapshot. Drop a .logarchive file on to Instruments and it’ll import the log into a trace document, then analyse the log with Instruments’ many cool features.
The log command-line tool lets you do all of this and more from Terminal.
The log stream subcommand supports multiple output formats. The default format includes column headers that describe the standard fields. The last column holds the log message prefixed by various fields. For example:
cloudd: (Network) [com.apple.network:connection] nw_flow_disconnected …
In this context:
cloudd is the source process.
(Network) is the source library. If this isn’t present, the log came from the main executable.
[com.apple.network:connection] is the subsystem and category. Not all log entries have these.
nw_flow_disconnected … is the actual message.
There’s a public API to read back existing log entries, albeit one with significant limitations on iOS (more on that below).
Every sysdiagnose log includes a snapshot of the system log, which is ideal for debugging hard-to-reproduce problems. For more details on that, see Using a Sysdiagnose Log to Debug a Hard-to-Reproduce Problem. For general information about sysdiagnose logs, see Bug Reporting > Profiles and Logs.
But you don’t have to use sysdiagnose logs. To create a quick snapshot of the system log, run the log tool with the collect subcommand. If you’re investigating recent events, use the --last argument to limit its scope. For example, the following creates a snapshot of log entries from the last 5 minutes:
% sudo log collect --last 5m
For more information, see:
os > Logging
OSLog
log man page
os_log man page (in section 3)
os_log man page (in section 5)
WWDC 2016 Session 721 Unified Logging and Activity Tracing
[1] Well, most log points. If you’re logging thousands of entries per second, the very small overhead for these disabled log points add up.
[2] These debug profiles can also help you focus on the right subsystems and categories. Imagine you’re investigating a CryptoTokenKit problem. If you download and dump the CryptoTokenKit debug profile, you’ll see this:
% security cms -D -i "CTK_iOS_Logging.mobileconfig" | plutil -p -
{
…
"PayloadContent" => [
0 => {
…
"Subsystems" => {
"com.apple.CryptoTokenKit" => {…}
"com.apple.CryptoTokenKit.APDU" => {…}
}
}
]
…
}
That’s a hint that log entries relevant to CryptoTokenKit have a subsystem of either com.apple.CryptoTokenKit and com.apple.CryptoTokenKit.APDU, so it’d make sense to focus on those.
Foster Your Friendship
Good friendships take some work on your part, and your friendship with the system log is no exception. Follow these suggestions for getting the most out of the system log.
The system log has many friends, and it tries to love them all equally. Don’t abuse that by logging too much. One key benefit of the system log is that log entries persist for a long time, allowing you to debug issues with their roots in the distant past. But there’s a trade off here: The more you log, the shorter the log window, and the harder it is to debug such problems.
Put some thought into your subsystem and category choices. One trick here is to use the same category across multiple subsystems, allowing you to track issues as they cross between subsystems in your product. Or use one subsystem with multiple categories, so you can search on the subsystem to see all your logging and then focus on specific categories when you need to.
Don’t use too many unique subsystem and context pairs. As a rough guide: One is fine, ten is OK, 100 is too much.
Choose your log types wisely. The documentation for each OSLogType value describes the default behaviour of that value; use that information to guide your choices.
Remember that disabled log points have a very low cost. It’s fine to leave chatty logging in your product if it’s disabled by default.
Some app extension types have access to extremely sensitive user data and thus run in a restricted sandbox, one that prevents them from exporting any data. For example, an iOS Network Extension content filter data provider runs in such a sandbox. While I’ve never investigated this for other app extension types, an iOS NE content filter data provider cannot record system log entries.
This restriction only applies if the provider is distribution signed. A development-signed provider can record system log entries.
Apple platforms have accumulated many different logging APIs over the years. All of these are effectively deprecated [1] in favour of the system log API discussed in this post. That includes:
NSLog (documented here)
CFShow (documented here)
Apple System Log (see the asl man page)
syslog (see the syslog man page)
Most of these continue to work [2], simply calling through to the underlying system log. However, there are good reasons to move on to the system log API directly:
It lets you control the subsystem and category, making it much easier to track down your log entries.
It lets you control whether data is considered private or public.
In Swift, the Logger API is type safe, avoiding the classic bug of mixing up your arguments and your format specifiers.
[1] Some formally and some informally.
[2] Although you might bump into new restrictions. For example, the macOS Tahoe 26 Release Notes describe such a change for NSLog.
No Friend Is Perfect
The system log API is hard to wrap. The system log is so efficient because it’s deeply integrated with the compiler. If you wrap the system log API, you undermine that efficiency. For example, a wrapper like this is very inefficient:
-*-*-*-*-*- DO NOT DO THIS -*-*-*-*-*-
void myLog(const char * format, ...) {
va_list ap;
va_start(ap, format);
char * str = NULL;
vasprintf(&str, format, ap);
os_log_debug(sLog, "%s", str);
free(str);
va_end(ap);
}
-*-*-*-*-*- DO NOT DO THIS -*-*-*-*-*-
This is mostly an issue with the C API, because the modern Swift API is nice enough that you rarely need to wrap it.
If you do wrap the C API, use a macro and have that pass the arguments through to the underlying os_log_xyz macro.
Note If you’re curious about why adding a wrapper is bad, see my explanation on this thread.
iOS has very limited facilities for reading the system log. Currently, an iOS app can only read entries created by that specific process, using .currentProcessIdentifier scope. This is annoying if, say, the app crashed and you want to know what it was doing before the crash. What you need is a way to get all log entries written by your app (r. 57880434).
There are two known bugs with the .currentProcessIdentifier scope. The first is that the .reverse option doesn’t work (r. 87622922). You always get log entries in forward order. The second is that the getEntries(with:at:matching:) method doesn’t honour its position argument (r. 87416514). You always get all available log entries.
Xcode 15 has a shiny new console interface. For the details, watch WWDC 2023 Session 10226 Debug with structured logging. For some other notes about this change, search the Xcode 15 Release Notes for 109380695.
In older versions of Xcode the console pane was not a system log client (r. 32863680). Rather, it just collected and displayed stdout and stderr from your process. This approach had a number of consequences:
The system log does not, by default, log to stderr. Xcode enabled this by setting an environment variable, OS_ACTIVITY_DT_MODE. The existence and behaviour of this environment variable is an implementation detail and not something that you should rely on.
Xcode sets this environment variable when you run your program from Xcode (Product > Run). It can’t set it when you attach to a running process (Debug > Attach to Process).
Xcode’s Console pane does not support the sophisticated filtering you’d expect in a system log client.
When I can’t use Xcode 15, I work around the last two by ignoring the console pane and instead running Console and viewing my log entries there.
If you don’t see the expected log entries in Console, make sure that you have Action > Include Info Messages and Action > Include Debug Messages enabled.
The system log interface is available within the kernel but it has some serious limitations. Here’s the ones that I’m aware of:
Prior to macOS 14.4, there was no subsystem or category support (r. 28948441).
There is no support for annotations like {public} and {private}.
Adding such annotations causes the log entry to be dropped (r. 40636781).
The system log interface is also available to DriverKit drivers. For more advice on that front, see this thread.
Metal shaders can log using the interface described in section 6.19 of the Metal Shading Language Specification.
Revision History
2025-09-18 Added a link to the macOS Tahoe 26 Release Notes discussion of NSLog. Remove the beta epithet when referring to Xcode 15. It’s been released for a while now (-:
2025-08-19 Added information about effectively deprecated logging APIs, like NSLog.
2025-08-11 Added information about the restricted sandbox applied to iOS Network Extension content filter data providers.
2025-07-21 Added a link to a thread that explains why wrapping the system log API is bad.
2025-05-30 Fixed a grammo.
2025-04-09 Added a note explaining how to use a debug profile to find relevant log subsystems and categories.
2025-02-20 Added some info about DriverKit.
2024-10-22 Added some notes on interpreting the output from log stream.
2024-09-17 The kernel now includes subsystem and category support.
2024-09-16 Added a link to the the Metal logging interface.
2023-10-20 Added some Instruments tidbits.
2023-10-13 Described a second known bug with the .currentProcessIdentifier scope. Added a link to Using a Sysdiagnose Log to Debug a Hard-to-Reproduce Problem.
2023-08-28 Described a known bug with the .reverse option in .currentProcessIdentifier scope.
2023-06-12 Added a call-out to the Xcode 15 Beta Release Notes.
2023-06-06 Updated to reference WWDC 2023 Session 10226. Added some notes about the kernel’s system log support.
2023-03-22 Made some minor editorial changes.
2023-03-13 Reworked the Xcode discussion to mention OS_ACTIVITY_DT_MODE.
2022-10-26 Called out the Showing popup in Console and the --last argument to log collect.
2022-10-06 Added a link WWDC 2016 Session 721 Unified Logging and Activity Tracing.
2022-08-19 Add a link to Recording Private Data in the System Log.
2022-08-11 Added a bunch of hints and tips.
2022-06-23 Added the Foster Your Friendship section. Made other editorial changes.
2022-05-12 First posted.
OSLog
RSS for tagOSLog is a unified logging system for the reading of historical data.
Posts under OSLog tag
38 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
On iOS when I run my Autofill extension target from Xcode and attach to Safari, Swift print() statements appear in the Xcode console log. If I run the same extension on MacOS Sequoia, no log messages appear. The header strip in the debugger area remains blank. Anyone know how to see these log messages?
In both cases, the scheme is set to Debug build, and "Debug Executable" is not selected. In fact, for iOS the "Debug Executable" is grayed out. When I set Debug Executable on the MacOS run, I get failure to attach with a warning about permission to debug Safari.
I'm developing a macOS console application that uses ODBC to connect to PostgreSQL. The application works fine when run normally, but fails to load the ODBC driver when debugging with LLDB(under root works fine as well).
Error Details
When running the application through LLDB, I get this sandbox denial in the system log (via log stream):
Error 0x0 0 0 kernel: (Sandbox) Sandbox: logd_helper(587) deny(1) file-read-data /opt/homebrew/lib/psqlodbcw.so
The application cannot access the PostgreSQL ODBC driver located at /opt/homebrew/lib/psqlodbcw.so(also tried copy to /usr/local/lib/...).
Environment
macOS Version: Latest Sequoia
LLDB: Using LLDB from Xcode 16.3 (/Applications/Xcode16.3.app/Contents/Developer/usr/bin/lldb)
ODBC Driver: PostgreSQL ODBC driver installed via Homebrew
Code Signing: Application is signed with Apple Development certificate
What is the recommended approach for debugging applications that need to load dynamic libraries?
Are there specific entitlements or configurations that would allow LLDB to access ODBC drivers during debugging sessions?
Any guidance would be greatly appreciated.
Thank you for any assistance!
When attempting to open sysdiagnose logs collected from an iPhone running iOS 26 beta 3 on macOS Console, the application consistently crashes.
Crash report has been added for reference.
Apple Feedback- FB18834450
Is there a way to ensure a kernel extension in the Auxiliary Kernel Collection loads (and runs its start routines) before launchd?
I'm emitting logs via os_log_t created with an os_log_create (custom subsystem/category) in both my KMOD's start function and the IOService::start() function. Those messages-- which both say "I've been run"-- inconsistently show up in log show --predicate 'subsystem == "com.bluefalconhd.pandora"' --last boot, which makes me think they are running very early. However, I also record timestamps (using mach_absolute_time, etc.) and expose them to user space through an IOExternalMethod. The results (for the most recent boot):
hayes@fortis Pandora/tests main % build/pdtest
Pandora Metadata:
kmod_start_time:
Time: 2025-07-22 14:11:32.233
Mach time: 245612546
Nanos since boot: 10233856083 (10.23 seconds)
io_service_start_time:
Time: 2025-07-22 14:11:32.233
Mach time: 245613641
Nanos since boot: 10233901708 (10.23 seconds)
user_client_init_time:
Time: 2025-07-22 14:21:42.561
Mach time: 14893478355
Nanos since boot: 620561598125 (620.56 seconds)
hayes@fortis Pandora/tests main % ps -p 1 -o lstart=
Tue Jul 22 14:11:27 2025
Everything in the kernel extension appears to be loading after launchd (PID 1) starts. Also, the kext isn't doing anything crazy which could cause that kind of delay.
For reference, here's the Info.plist:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>Pandora</string>
<key>CFBundleIdentifier</key>
<string>com.bluefalconhd.Pandora</string>
<key>CFBundleName</key>
<string>Pandora</string>
<key>CFBundlePackageType</key>
<string>KEXT</string>
<key>CFBundleVersion</key>
<string>1.0.7</string>
<key>IOKitPersonalities</key>
<dict>
<key>Pandora</key>
<dict>
<key>CFBundleIdentifier</key>
<string>com.bluefalconhd.Pandora</string>
<key>IOClass</key>
<string>Pandora</string>
<key>IOMatchCategory</key>
<string>Pandora</string>
<key>IOProviderClass</key>
<string>IOResources</string>
<key>IOResourceMatch</key>
<string>IOKit</string>
<key>IOUserClientClass</key>
<string>PandoraUserClient</string>
</dict>
</dict>
<key>OSBundleLibraries</key>
<dict>
<key>com.apple.kpi.dsep</key>
<string>24.2.0</string>
<key>com.apple.kpi.iokit</key>
<string>24.2.0</string>
<key>com.apple.kpi.libkern</key>
<string>24.2.0</string>
<key>com.apple.kpi.mach</key>
<string>24.2.0</string>
</dict>
</dict>
</plist>
My questions are:
A. Why don't the early logs (from KMOD's start function and IOService::start) consistently appear in the unified log, while logs later in IOExternalMethods do?
B. How can I force this kext to load earlier-- ideally before launchd?
Thanks in advance for any guidance!
I am in the process of writing a macOS app using NSFileProviderExtension so that I can map my customer's data in Finder. I am in the process of building it out, I had initially started in Obj-C and then after the advice of folks here (https://developer.apple.com/forums//thread/793272?answerId=849339022&page=1#849752022) I have switched to Swift as the NSReplicatedFileProvider is not available in Obj-C.
I have done the main app and also started plugging away with the FileProviderExtension. To verify that I have properly setup, I create a static list of files in the FileProviderExtension enumerate so I can see it work in Finder. I build the app and it works as expected and I see it mounted in Finder and I see the static list of files.
But what I don't see is any debug statements in the app, none of those message show up in the logs. I am baffled by this. I check the log stream to see what the process prints out and I know it is logging everything else eg:
2025-07-18 11:32:05.772364-0700 0x19ea3f9 Activity 0x1172100 63622 0 DriveFileProviderExtension: (libsystem_secinit.dylib) AppSandbox
2025-07-18 11:32:05.794609-0700 0x19ea3f9 Activity 0x1172101 63622 0 DriveFileProviderExtension: (libsystem_info.dylib) Retrieve User by ID
2025-07-18 11:32:05.800179-0700 0x19ea3f9 Default 0x0 63622 0 DriveFileProviderExtension: (ExtensionFoundation) [com.apple.extensionkit:default] Extension `/Users/radwar/Library/Developer/Xcode/DerivedData/Drive-fxfhbjutfvumoabnnfmxxstpifha/Build/Products/Debug/Drive.app/Contents/PlugIns/DriveFileProviderExtension.appex/Contents/MacOS/DriveFileProviderExtension` of type: `1` launched.
2025-07-18 11:32:05.801453-0700 0x19ea3f9 Default 0x0 63622 0 DriveFileProviderExtension: (RunningBoardServices) [com.apple.runningboard:connection] Initializing connection
2025-07-18 11:32:05.803083-0700 0x19ea3f9 Default 0x0 63622 0 DriveFileProviderExtension: (RunningBoardServices) [com.apple.runningboard:process] Removing all cached process handles
2025-07-18 11:32:05.803231-0700 0x19ea613 Default 0x0 63622 0 DriveFileProviderExtension: (RunningBoardServices) [com.apple.runningboard:connection] Sending handshake request attempt #1 to server
2025-07-18 11:32:05.803414-0700 0x19ea613 Default 0x0 63622 0 DriveFileProviderExtension: (RunningBoardServices) [com.apple.runningboard:connection] Creating connection to com.apple.runningboard
2025-07-18 11:32:05.803459-0700 0x19ea613 Default 0x0 63622 0 DriveFileProviderExtension: (libxpc.dylib) [com.apple.xpc:connection] [0x12f106e10] activating connection: mach=true listener=false peer=false name=com.apple.runningboard
2025-07-18 11:32:05.805893-0700 0x19ea613 Default 0x0 63622 0 DriveFileProviderExtension: (RunningBoardServices) [com.apple.runningboard:connection] Handshake succeeded
2025-07-18 11:32:05.805955-0700 0x19ea613 Default 0x0 63622 0 DriveFileProviderExtension: (RunningBoardServices) [com.apple.runningboard:connection] Identity resolved as xpcservice<clio.Drive.DriveFileProviderExtension([osservice<com.apple.FileProvider(501)>:990])(501)>{vt hash: 247410607}[uuid:454E32DB-3FB4-4DC6-9C05-F4B2F97333E0]{persona:9EF54117-4998-4D72-83C4-F12587C95FBA}
but none of my print lines are being printed. I have code like this sprinkled through the methods:
print("🔍 FileProviderExtension INIT - Logger configured")
print("🔍 Domain: \(domain.displayName)")
None of these show up anywhere. I also do a pure log stream with all system output, and there, too, I don't see anything. What am I missing? With my Obj-C version, all NSLogs used to show up as expected. With Swift, am I missing something?
We've been using our own logging system for quite a long time but we are interested in the benefits offered by Logger/OSLog and plan to migrate to it.
Before modifying thousands of logging calls, we want to understand a bit more how, when and where (ie. from which thread/queue) OSLog strings interpolation is performed.
More specifically, we are concerned by simultaneous access to properties from different threads. Our app usually handles that using DispatchQueues (single or concurrent) and calls to our logging system is safe as the log string is built synchronously.
On the other hand, when using Logger/OSLog, the provided string is in fact an OSLogMessage which keeps a reference to values and properties in order to build the final String later (asynchronously). If it is correct, the "later" part concerns us.
Example
Let's consider the following class property profile (instance of Profile class which implements CustomStringConvertible):
private var profile: Profile?
With our own logging system, we used to log the profile property at the time the logging method is called (and when the access to profile is safe):
Log.debug(logModule, "Current profile: \(profile)")
Now moving to Logger/OSLog, the following error appears:
logger.debug("Current profile: \(profile)")
// Reference to property 'profile' in closure requires explicit use of 'self' to make capture semantics explicit
Our understanding is that the property profile is not accessed synchronously but later, possibly after or even worse while the property is being mutated from another thread (-> crash). In which case fixing the error using "Current profile: \(self.profile)" instead would be a very bad idea...
The same goes with class instance properties used in the implementation of CustomStringConvertible.description property. If the description property is built asynchronously, the class instance properties may have been mutated or may be being mutated from another thread.
TL;DR
We have searched for good practices when using Logger/OSLog but could not find any dealing with the thread-safeness of logged objects.
Is it a good idea to capture self in Logger calls?
Is it safe to log non value-type objects such as class instances?
Thanks for clarifications.
I came across several "errors" being reported when I run my app, however my app seems to function correctly.
I believe they fall in the category listed on this ( now locked ) thread https://developer.apple.com/forums/thread/115461
However, I wanted to post the ones I found to clarify ( close to submission) just in case any of these end up being more than just log noise later. PLEASE let me know if you've come across these before and whether they impacted anything or if you can confirm they are just log noise. Thanks in advance!
-[RTIInputSystemClient remoteTextInputSessionWithID:performInputOperation:] perform input operation requires a valid sessionID. inputModality = Keyboard, inputOperation = , customInfoType = UIEmojiSearchOperations
AVAudioSession_iOS.mm:2,223 Server returned an error from destroySession:. Error Domain=NSCocoaErrorDomain Code=4099 “The connection to service with pid 102 named com.apple.audio.AudioSession was invalidated from this process.” UserInfo={NSDebugDescription=The connection to service with pid 102 named com.apple.audio.AudioSession was invalidated from this process.
CAReportingClient.mm:532 Attempted to remove a reporter not created by this client { careporter_id=408,331,130,765,320 }
load_eligibility_plist: Failed to open //private/var/db/os_eligibility/eligibility.plist: Operation not permitted(1) - I verified and this file is indeed in non read mode on my Mac for the user. However it affects nothing.
The following 2 pop up - although I have no explicit code or 3P code ( nor Ads) that require impressions - each time I click on the Game Center Access Point post authentication.
Cannot add impressions because no tracker is specified by the metrics fields context. Did you forget to set one from your view controller or data source?
Cannot add page fields because none are specified by the metrics fields context. Did you forget to add an PageMetricsPresenter to the object graph used for actions?
Hi all,
I'm working on a non-interactive macOS application (a service or daemon), and I'm trying to understand the best practices around logging and error reporting, particularly in failure scenarios.
If a daemon or service fails in macOS, where is it expected to log errors, and how can users or developers discover what went wrong?
Specifically, I have a few questions:
What is the recommended location or system for logging errors from a non-interactive macOS application?
Should we use os_log, standard error output, or write directly to files somewhere?
How can a user or developer access these logs to diagnose issues—should logs be visible via the Console app?
Is there a standard approach to making failure information easily accessible for debugging and support, especially for daemons running under launchd?
Any guidance or best practices would be appreciated.
Hi,
macOS (latest macOS, latest HW, but doesn't matter) seems to prevent CoreMIDI driver logging with standard logging procedures (syslog, unified logging).
The only chance to log something is writing to a file at one of the rare write-accessible locations for CoreMIDI.
How is this supposed to work? Any hint is highly appreciated. Thanks!
I folks,
I have an application that is already available in production and I would like to store some logs to the app, so that on real device I will see outputs for better tracing.
iOS13 would be fine, but I can migrate it into e.g. iOS 14.
The next question is where to find logs on real devices, so that users can send me for tracing.
Thanks
Petr
I've got an iOS app with lots of extensions, some of them complex and doing a lot of stuff.
After a bug I'd like to be able to use OSLogStore to get a holistic picture of logging for the app and its extensions and send that to a debugging server to retrospectively view logs for the app and its extensions.
The constructor is OSLogStore.init(scope: OSLogStore.Scope), however scope only has one value .currentProcessIdentifier.
Implying if that is called from within the app it can only get access to logging for its process only. I tried it out to confirm this is the case - if I log something in an extension (using Logger), then run the app with code like this:
let logStore = try! OSLogStore(scope: .currentProcessIdentifier)
let oneHourAgo = logStore.position(date: Date().addingTimeInterval(-3600))
let allEntries = try! logStore.getEntries(at: oneHourAgo)
for entry in allEntries {
look at the content of the entry
Then none of the entries are from the extension.
Is there anyway from within the app I can access logging made within an extension?
I have installed the following configuration profile:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>PayloadDisplayName</key>
<string>Enable Private Data Logging for Unified Logging</string>
<key>PayloadEnabled</key>
<true/>
<key>PayloadIdentifier</key>
<string>com.apple.system.logging.2BFB8109-8829-4020-AEB7-BA21761AE50C</string>
<key>PayloadType</key>
<string>com.apple.system.logging</string>
<key>PayloadUUID</key>
<string>2BFB8109-8829-4020-AEB7-BA21761AE50C</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>System</key>
<dict>
<key>Enable-Private-Data</key>
<true/>
</dict>
</dict>
</array>
<key>PayloadDisplayName</key>
<string>Enable Private Logging Data</string>
<key>PayloadIdentifier</key>
<string>Kentzo-Macbook.D000DF5D-AE7A-4D22-B1DC-8F9CD71A2DD2</string>
<key>PayloadRemovalDisallowed</key>
<false/>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadUUID</key>
<string>1CF75441-D3C2-4E5B-B36A-394C397E8529</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>ConsentText</key>
<dict>
<key>default</key>
<string>Warning: Installing this profile will enable private data logging for all of unified logging.</string>
</dict>
</dict>
</plist>
But both Console.app and log show values like <mask.hash: 'Z9xIxlLTn0KlWPUjmpOSkg=='> for the com.apple.mDNSResponder subsystem.
What do I need to do to reveal this information?
The app stops on the breakpoint when "All Runtime Issues" is added. If I disable the breakpoint, the app runs normally. Is there a new project setting to avoid this breakpoint from being set. It does not appear to be a valid error. I am running Xcode 16.2. I edited the "Type" field in the breakpoint box. This occurs with the "System Frameworks" option only.
Hi there!
Sorry in advance, this is going to be a long post of Apple developer pains which I want to share with you, and, hopefully, find the answer and help Apple become better.
I'm at the very beginning of my new and exciting personal project which (I hope) may one day feed me and be my daily source of inspiration. I'm not a newbie in Apple development nor am I a senior-level developer — just a fellow developa'.
Here's the problem I bring to you — why Apple promotes Unified Logging System and recommends using it as the primary way to implement logging in 3rd-party apps? No doubt, OSLog is a great, secure, efficient, and centralized way to gather diagnostics information, and I, starting my new project, am itching to choose exactly this 1st-party logging infrastructure. This decision in theory has a number of benefits:
I don't have to depend on 3rd-party logging frameworks which may eventually be discontinued;
I have extensive documentation, great WWDC sessions explaining how to use the framework, and stackoverflow answers from the whole Apple dev community in case I experience any troubles;
I have this cool Console.app and upcoming Xcode 15 tools with great visualization and filtering of my logs;
It's quite a robust and stable infrastructure which I may restfully rely on.
But... the thing is there's this big elephant in the room — this API is non-customizable, inconvenient, and hard to use in terms of the app architecture. I can't write my own protocol wrapper around it to abstract my domain logic from implementation details or just simplify the usage at the call site. I can't configure my own format for log messages (this is debatable, since Console.app doesn't provide "***** strings" as Xcode 14 and earlier, but still). And what's most important — I can't conveniently retrieve the logs!
I can't implement the functionality where my user just taps the button, and the logs are sent on the background queue to my support email (eskimo's answer). They would have to go through this monstrous procedure of holding volume buttons on the iPhone, connecting their device to the Mac, gathering sysdiagnose, entering some weird Terminal commands (jeez, these nerdy developers...), etc. If it ever succeeds, of course, and something doesn't go wrong, leaving my user angry and dissatisfied with my app.
Regarding the protocol wrapper, I can't do something like this:
protocol Logging {
var logger: Logger { get }
func info(_ message: OSLogMessage)
}
extension Logging {
var logger: Logger {
return Logger(
subsystem: "com.my.bundle.id",
category: String(describing: Self.self)
)
}
func info(_ message: OSLogMessage) {
logger.info(message)
}
}
class MyClass: Logging {
func someImportantMethod() {
// ...
self.info("Some useful debug info: \(someVar, privacy: .public)")
}
}
I've been investigating this topic for 2 days, and it's the farthest I want to go in beating my head over how to do two simple things:
How to isolate logging framework implementation decision from my main code and write convenience wrappers?
How to easily transfer the log files from the user to the developer?
And I'm not the only one struggling. Here's just one example among hundreds of other questions that are being asked on dev forums: https://www.hackingwithswift.com/forums/ios/unified-logging-system-retrieve-logs-on-device/838. I've read almost all Apple docs which describe the modern Unified Logging System, I've read through eskimo's thread on Apple Developer Forum about the API, but I still haven't found the answer.
Maybe, I've misperceived this framework and it's not the tool I'm searching for? Maybe, it focuses on different aspects of logging, e.g. signposting, rather than logging the current state of the app? What am I missing?
import SwiftUI
import OsLog
let logger = Logger(subsystem: "Test", category: "Test")
struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundStyle(.tint)
Text("Hello, world!")
}
.padding()
.task {
logger.info("Hallo")
}
}
}
#Preview {
ContentView()
}
27 | .padding()
28 | .task {
29 | logger.info(__designTimeString("#6734_2", fallback: "Hallo"))
| `- error: argument must be a string interpolation
30 | }
31 | }
Should OsLog be compatible with __designTimeString?
Background
I have a SwiftUI app that uses OSLog and the new Logger framework. In my SwiftUI views, I would like to use something like Self._logChanges() to help debug issues.
After some trial and error, I can see the messages appear in the System console log for the app I am debugging using the com.apple.SwiftUI subsystem.
Problem
I'd like to see those same messages directly in Xcode's console window so I can filter them as needed. How do I do that?
Thanks! -Patrick
Hi,
Our team is facing an issue where the os log message is not fully printed in the Xcode console. Even when using Debug → Attach to Process by PID or Name, the logs appear truncated (Both simulator and physical device).
We noticed that the Xcode 15.0 release notes we saw that there is some truncation made for debug lines in console. We also tried checking the logs using Console.app, but the truncation remains.
If limitation is there how much maximum lines it can print ?
Could anyone please provide guidance on how we can view the complete logs? Any suggestions or workarounds would be highly appreciated.
I'd like to give control to the app developer that uses my library to select which level of logs they'd like to see from my lib (e.g. do they want to see all debug messages or just errors).
I know there are filtering controls that Xcode gives us, but I'm wondering if there is a way to pull this off with code. Ideally the user callsite would look like MyLib(logLevel: .info).
And then I would pass that info level somehow to OSLog. Today, I create my logger like this:
let myLogger = Logger(
subsystem: Bundle.main.bundleIdentifier ?? "UnknownApp",
category: "MyLibrary"
)
As far as I can tell, there is nothing I can then pass to my myLogger instance to configure the threshold level. I'm imagining an interface like:
myLogger.logLevel(.warning)
// Later...
myLogger.debug("You won't see this")
myLogger.error("But you will see this")
Does OSLog and friends give us any ability to do this out of the box, or are we building little wrappers around OSLog to accomplish this?
Thank you,
Lou
I was using os_log in my code and in header of oslog, it has been mentioned that there is physical cap of 1024 bytes per log line for dynamic content. So I was looking for a work around but before that I am not able see the truncation when I tried creating this issue.
let baseString = String(repeating: "a", count: 1020)
let criticalMarker = "LAST_5_BYTES"
let testString = baseString + criticalMarker // 1020 + 12 = 1032 bytes
os_log("LONG_STRING: %@", testString)
I used this as a sample code to check the truncation but in Xcode debugger it logs all the 1020 bytes and the last 12 bytes as well. I even checked the console and there also it was logging all the bytes.
Can anyone help me with this as to what I am missing here?