Normal view

There are new articles available, click to refresh the page.
Before yesterdayMain stream

Inside the Unified Log 5: Navigation

By: hoakley
14 October 2025 at 14:30

The greatest challenge in using the Unified log is how to navigate its many thousands of entries, to find those you want to read. Success depends on the combination of two aids: time and waypoints (or landmarks).

Time

No matter how you obtain log extracts, you need to know when to look for those entries. The more precisely you can work out the time of interest, the quicker and easier it will be to locate the entries you’re interested in. While the log command offers alternatives, LogUI works throughout using the local time applicable when you access the log, allowing for your current time zone and any seasonal adjustment to it, when accessing the live log in that Mac.

However, the underlying times given in log extracts are those recorded by the Mac or device whose log you’re accessing. If its system clock was five minutes slow when those entries were written to its log, then you need to allow for that. For example, when I first started my Mac yesterday its clock might have been 1 minute slow. An event that occurred at 10:56 yesterday by the room clock would therefore appear in the log entries for 10:55.

One important time you can discover is the boot time of the Mac. Mints offers a Boot button to retrieve boot times over the last 24 hours. If the logs were written by a different Mac or device, then you’ll need to search for the time of that last boot. Fortunately the first two log entries are easily recognised:
11:41:37.562774+0100 === system boot: D3CEA9B4-F045-434D-8D12-C6E794A02F14
11:41:42.758780+0100 kprintf initialized

The long gap between the first two entries is accounted for by the firmware phase of the boot process. If necessary you can search for a message containing === (three equals signs). Mints provides the time of the first of those for each boot, and its UUID.

There are two occasions when time can become confusing, when clock corrections are applied, and when clocks are moved forward or back to add or remove summer or seasonal time changes. Fortunately the latter only change twice each year, although when they do, you really don’t want to see what happened in the log, and those changes aren’t even applied at a predictable time.

Clock corrections, like kernel boot, are readily found by the === text in their message. They normally happen in pairs, with the first correction the larger, and the second often far smaller. Here’s an example seen in consecutive log entries:
08:26:16.140474+0100 /usr/libexec/sandboxd[80] ==> com.apple.sandboxd
08:26:10.043353+0100 === system wallclock time adjusted
08:26:10.044335+0100 Sandbox: distnoted(72) deny(1) file-read-metadata /private
08:26:10.044601+0100 2 duplicate reports for Sandbox: distnoted(72) deny(1) file-read-metadata /private
08:26:10.044606+0100 Sandbox: distnoted(72) deny(1) file-read-metadata /Library
08:26:10.089204+0100 === system wallclock time adjusted
08:26:10.091850+0100 started normally

The first adjustment dropped the clock back by 6.1 seconds, from 08:26:16.140474 to 08:26:10.043353. This means that you’ll see times of 08:26:12 both before the correction and afterwards. The second adjustment, from 08:26:10.044606 to 08:26:10.089204, was far smaller at 0.045 seconds, and at least went in the right direction.

The most substantial clock corrections are made shortly after booting. Although macOS does make them later, the size of those should be smaller.

Waypoints

Even working with times resolved to the second, those can still leave you browsing thousands of log entries. To locate more precisely you need details of one or more entries that will be sufficiently distinctive to focus in on a few dozen. These are waypoints for navigation.

LogUI provides three methods for locating these waypoints:

  • using a search predicate to determine which log entries are extracted from the log;
  • applying search text to filter out all entries that don’t contain a term;
  • searching a rich text export of the log extract.
Predicates

These are best used when the time period of your extract needs to be relatively long, so would return a large number of entries. For example, if you can only narrow the time down to several minutes, and are looking for the time that a specific app was launched, you can look for that app’s job description when it’s created and written to the log by RunningBoard.*

Over a period of two minutes, RunningBoard might write thousands of entries in the log, so looking for your app’s job description among them would be time-consuming. Set the start time and period to cover the whole of the time you want to search, then set a predicate for the subsystem com.apple.runningboard.

When LogUI fetches that log extract, there might still be over 2,000 entries, so now is the time to apply search text to filter those further.

Search filter

To filter those 2,000 entries and show only those containing job descriptions created by RunningBoard, enter the text constructed job in LogUI’s search box, with its menu set to Messages, and press Return. You’ll now see that list reduced to just a handful, and looking through them you can discover exactly when your waypoint occurred.

My example for this article starts with a period of just 2 minutes, in which there were more than 100,000 log entries.

Using the com.apple.runningboard predicate whittled those down to 13,443 entries.

Searching within those for constructed job left me with just 8 entries to look through.

Search rich text

Sometimes you can’t devise the right combination of predicate and search filter to discover what you’re looking for, which might be an error reported in a subsystem or a process that you can’t identify. One good way forward is to narrow your log extract as much as you can, then save the extract as Rich Text, open that in a suitable editor, and search through it for the word error. That will discover every log entry containing the word error anywhere, rather than confining it to the message text.

Using time and waypoints

Armed with your waypoint and the exact time of its entry in the log, you can now set that as the start time, set a period of a couple of seconds, and get a full log extract containing all the detail you might need. This should give you further clues to allow you to move through time using predicates and search filters to discover what happened. This is much quicker and less frustrating than trying to scan through thousands of log entries in search of vague clues.

Key points

  • Use time and waypoints to find log entries.
  • Mints’ Boot button gives times of each boot in the last 24 hours.
  • Reduce the number of log entries returned using a predicate.
  • Narrow those down using a search filter.
  • Search all text by exporting the log extract as Rich Text.

* Sadly, the days of being able to access freely RunningBoard’s informative job descriptions in the log are over. As of macOS Tahoe, all you’ll see is the dreaded <private> of censorship. If you want to examine these now, you’ll have to remove log privacy protection first. Thanks, Apple, for providing such useful tools then rendering them next to useless.

Inside the Unified Log 4: Log entries

By: hoakley
6 October 2025 at 14:30

For many years, I believed what the log command told me, that log entries could contain any of the 26 or more fields available, although they didn’t. It was only as I was developing utilities like LogUI that I discovered that log entries come in (at least) four types, each with its own format and set of fields. This article is a guide to those, and how they are displayed in LogUI.

Entry fields

Those available in the OSLog API are fewer in number than appear in log command output, and include some of limited interest. Those not exposed in LogUI include:

  • store category, which is invariably disk storage here.
  • format string, that used to format the contents of messages. This was introduced following serious security leaks in High Sierra.
  • components, linking in with the format string.

LogUI displays each entry using the same order of fields and colour-coding. Although they can’t all be seen together in any single log entry, the overall sequence is:

Standard levels are given as text, and chosen from one of the following: fault, error, notice, info, debug, undefined. These are only used with Regular log entries, not other types. LogUI always includes debug level entries when they’re available.

The four types of entry are:

  1. Regular
  2. Activity
  3. Boundary
  4. Signpost.

Regular entries

These are normally by far the most common, and are seen throughout the processes and subsystems writing log entries. They normally contain up to the following fields:
datestamp, [1], activity ID, category, level, sender, process, process ID, subsystem, thread ID, message

These examples include:
datestamp, 1, activity ID, category, level, sender, process, process ID, subsystem, thread ID, message
which is fairly normal.

Activities

These mark specific activities, and vary according to the process and subsystem. Some are valuable waypoints, and all are recognisably shorter than most regular entries, containing only the following fields:
datestamp, [2], activity ID, parent activity ID, sender, process, process ID, thread ID, message

These are examples of one of the most valuable activities reported, indicating a mouse-click or tap. They’re also complete, as they consist of:
datestamp, 2, activity ID, parent activity ID, sender, process, process ID, thread ID, message

Boundaries

These are rare but instantly recognisable because of their brevity. They consist of just three fields:
datestamp,[3], message

Two of the most important are the announcement of the start of kernel boot:

and adjustment of the internal clock:

These are also easy to search for, as their message contains the distinctive === string.

Signposts

By default, LogUI extracts omit Signposts, but if you tick the Show Signposts box before loading log entries, they will also be displayed. In parts, they can outnumber regular entries, and are unlikely to provide you with meaningful information unless you know internal details of their process. Each Signpost adds three type-specific fields, shown after the process ID:
datestamp, [4], activity ID, category, sender, process, process ID, signpost ID, signpost name, signpost type, subsystem, thread ID

These examples come from the most prolific user of Signposts, SkyLight for WindowServer, and contain
datestamp, 4, activity ID, category, sender, process, process ID, signpost ID, signpost name, signpost type, subsystem, thread ID
Note there’s no message field, as their meaning is determined by the signpost fields.

Datestamps and times

The OSLog API doesn’t currently give access to Mach times, only to opaque Date variables. From those, LogUI’s datestamps give:
year–month–day hour:minute:second.microsecond+timezone

All times and time zones given are those current when LogUI obtains that extract, not when that entry was written. This can become extremely confusing when clocks change to and from {summer time, daylight time, daylight saving time, daylight savings time, DST}, and when passing through time zones, and great care is required when reading logs containing such corrections. LogUI tries to make this easier, but care is still required.

If you have used the log command, you may be aware that can return Mach times as ticks, offering precision down to the nanosecond. This currently isn’t possible when using the OSLog API, but given the relatively slow clock of Apple silicon Macs, it’s no longer as useful as it used to be with Intel’s nanosecond Mach ticks.

Censorship

By default, all data in message fields is censored to protect the privacy of the user. This replaces chunks of text with the dreaded <private>, and can render many entries devoid of any useful information. In recent versions of macOS log privacy can be removed by installing a profile. However, that doesn’t apply retrospectively, only to log entries collected after the profile has been installed.

Removing privacy also increases the size in storage of log entries, and may reveal sensitive information. As soon as you have completed acquisition of the logs you need with privacy removed, you should therefore remove the profile, to allow normal censorship to resume.

A suitably signed profile is here: enablelogprivatedata

To install the profile, unzip the archive into a convenient folder and double-click it. You’ll be informed by a notification that you need to review the profile to install it. Open System Settings and select Device Management in its View menu, or search for it. Follow the instructions given there to install and activate it.

Sadly, this doesn’t cover all censorship. Some systems require additional configuration changes, for example CFNetwork diagnostic logging. Jeff Johnson explained how to enable that, but Apple has since blocked that, and I don’t know of a practical alternative. In some cases, additional detail can be obtained by adjusting log settings.

Summary

A short practical summary of LogUI’s log entries is provided in its Help book.

LogUI 1.0 build 74 adds a Diagnostics Tool for your logs

By: hoakley
1 October 2025 at 14:30

As promised earlier this week, I’m delighted to offer a new version of my log browser LogUI that provides a Diagnostics Tool to help you understand log folders and discover any problems with them.

Open its window using the Diagnostics Tool command in the Window menu, and you’re offered four tools at the top.

The first, Get Info, performs a simple analysis on the files in the selected diagnostics folder. By default, that’s your current live log, in the path /private/var/db/diagnostics, in your Data volume. After telling you how many log files there are in each of its three main folders, and the number of timesync files, it reports the date and time of the oldest Persist log file, marking the start of the continuous log record, in this case nearly 4 days ago.

You can use these tools on any diagnostics folder you can access through its dialog. This includes Time Machine backups, external boot disks, and other bootable systems. Don’t click on the Open button, though, until you’ve selected the diagnostics folder in the view above.

Locating the diagnostics folder in a Time Machine backup can be interesting, but once you’ve found it, LogUI will happily check it for you.

The Catalogue tool lists all the tracev3 log files in the folders inside diagnostics, starting with those in Persist. It gives each file’s creation and modification timestamps, indicating the range for log entries within them, their size in bytes, and an estimate of the period that file covers.

The Analyse tool extracts information from each of logd‘s statistics files, with the number of log entries broken down in frequency order. If you tick the CSV checkbox, they will be delivered in CSV format, ready to import into other software such as a spreadsheet.

The last of the tools, Save Text, saves the contents of the window to a text file for your records.

Further information about locations used for log files is in this article.

LogUI 1.0 build 74 is now available from here: logui174
from Downloads above, and from its Product Page.

Enjoy!

Inside the Unified Log 3: Log storage and attrition

By: hoakley
29 September 2025 at 14:30

By far the most common problem experienced with the Unified log isn’t its large number of entries, but a lack of entries. You go to check your Mac’s security using SilentKnight, or Skint runs its automatic daily check, and they can’t find any log entries recording XProtect Remediator scans. Since those are obtained by analysing the log for the previous 36 hours, when your Mac’s logs only go back 8-12 hours, entries for the last set of scans are likely to be missing. This article looks at why that happens, and how macOS maintains its log.

Traditional Unix-style logs are maintained according to their age. Every 24 hours, routine housekeeping procedures delete log files over a certain age, typically five days, in the process of rolling the log. Because the Unified log could over that five-day period have swallowed many GB of storage, its maintenance service logd deletes log records according to multiple criteria including the space they occupy, their age, and the type of entry. This is considerably more complex, and occurs in three phases, according to where the entry is stored.

Ephemeral entries

Log entries are initially written to memory, before logd writes most of them to permanent storage on disk.

mul102LogdFlow

The first substantial purging of entries thus occurs when logd decides which are ephemeral and won’t be written to disk. This can be seen by following the number of entries in a short period of high activity in the log, over time, and is shown in the chart below for a sample period of 3 seconds.

When fetched from the log within a minute of the entries being written, a total of 22,783 entries were recovered. Five minutes later there were only 82% (18,655) of those remaining. Attrition of entries then continued more slowly, leaving 80% (18,309) after 8 hours. Analysis suggests that over this period in which there were about 6,100 log entries per second written to disk, approximately 1,700 log entries per second were only kept in memory and never written to disk. That suggests about 22% were ephemeral, a proportion that’s likely to vary according to the origin and nature of log entries.

Persist entries

The great majority of log entries that survive to be written to permanent storage are kept in the tracev3 files in /private/var/db/diagnostics/Persist, where logd maintains them according to the total size of that folder, with a target of 520-530 MB. As each tracev3 file is up to 10.5 MB in size, that results in logd retaining about 50 files in that folder, although some are smaller than the maximum.

Two of my apps currently provide the datestamp of the start of the current collection of Persist log files as an indicator of the oldest log entry available from them: XProCheck, and Mints with its Logs button. This is a feature I intend adding shortly to LogUI.

However, not all log entries are stored in those Persist files. Looking back in time at total log entries available for a set ten minute period each day, you might see totals like:

  • 1 day old 33,827 in 10 minutes
  • 2 days old 98,534
  • 3 days old 59,296
  • 4 days old 10
  • 5 days old 1
  • 6 days old 40
  • 7 days old 0
  • 8 days old 358
  • 9 days old 1.

Those for the last 3 days are almost all Persist entries, but older entries are those retained in tracev3 files in the Special folder.

Special entries

Fault and Error log entries are normally written to the tracev3 files in the Special folder, and may contain additional message content kept in the warren of folders and files inside /private/var/db/uuidtext. logd purges entries from Special files separately, and apparently on the basis of their type and content rather than size. As a result, the Special folder can contain many tracev3 files of sizes ranging from over 2 MB to just a few KB, with their size tending to reduce with age. Any log entries recovered from dates before the oldest Persist file thus must have come from Special files.

Although files stored in the uuidtext folder are small, there can be a great many of them, and total size of that folder can exceed 1 GB. Those too are maintained by logd.

Other folders and files

tracev3 files stored in the Signpost folder contain only Signpost log entries used for performance assessment, and are purged at a slower rate than Persist files, but not retained as long as Special files. The High Volume folder appears seldom if ever used. The timesync folder contains time synchronisation data, small files that normally cover the whole period of Special files.

The /private/var/db/diagnostics folder contains several other files, including the logs of logd and logd_helper, and most significantly pairs of statistical summaries written during log maintenance, named logdata.statistics.[n] where n is a number starting from 0, in both .txt and .jsonl (JSON) format.

Each time logd performs maintenance on a tracev3 file, or on associated uuidtext files, it records data about the files in the current logdata.statistics files, both as plain text and in JSON format. The most useful of these records occurs when a Persist tracev3 file is rotated from being the file into which new log entries are written, to being retained without further additions. That entry records:

  • the name of the tracev3 file;
  • the time of rotation, thus the time immediately following the last log entry to be made to that file;
  • the total number of log entries in that file, for example 42,365,476;
  • for the top 20 processes that wrote entries in that period, the number of entries, the percentage of total entries, and the path to the process, listed in order of number of entries.

For example, in that tracev3 file containing a total of 42,365,476 entries, the kernel might have written 13,095,296 entries, 30.9%, and be top of the list, while runningboardd might only have written 1,223,157 or 2.9%. These statistics can be valuable in drawing attention to periods when there were problems, and for discovering which entries are limiting the coverage of Persist files, making it impossible for apps to recover entries for XProtect Remediator scans, for example.

Statistics entries for memory rollovers can also be useful, as are those for Special file rotation, although the latter contain fewer entries than Persist files.

Extending the period of log coverage

Currently, there doesn’t appear to be any way to set logd‘s size allowance for Persist files. When the Unified log was first introduced, it wasn’t unusual for that setting to preserve full log records covering a period of up to 20 days. As more processes now write copious entries in the log, a Mac that’s left awake and running at all times may only retain the last 20 hours of Persist entries, or even less.

Macs that don’t need to be awake and running at all times can extend their log coverage by sleeping or being shut down, when of course no entries can be written. If that isn’t possible, you can write your own XML property lists to /Library/Preferences/Logging to limit the retention of specific categories of entry. However, experience shows that only achieves small extensions to time coverage. It’s also worth bearing in mind that disabling privacy protection in the log will increase the size taken by most log entries, so shortening time covered by the retained logs.

Perhaps the wisest and most effective way to extend the time coverage of logs is to discover the causes of excess entries and address those, although that’s inevitably the most difficult solution.

One little-used technique is to turn log records in backups into logarchives, enabling old log entries to be accessed days or even years after they have been written. As an example of what can be achieved, the log entries below were written eight years ago, on 26 September 2017, saved in a logarchive, and browsed using LogUI on 26 September 2025.

I will explain how to do this in a future article in this series.

Summary

  • Log maintenance is a sophisticated managed process that discards log messages for several reasons, most generally to keep total log file size within limits, rather than removing entries purely on the basis of age.
  • A fifth of log entries are likely to be ephemeral, and lost from the log within the first minutes after they’re written. If you want the fullest entries possible, obtain the log excerpt as soon as possible after its entries have been written.
  • Most retained log entries are written in Persist logs, where tracev3 files are removed by age to keep their total size to just over 500 MB. Those should preserve log entries for hours or days after they’re written.
  • Entries for Faults and Errors are stored in Special logs, where they’re kept for longer, sometimes for weeks.
  • logdata.statistics files provide detailed statistics for log files as they’re rotated, and can tell you which processes wrote most entries.
  • As logs aren’t written during sleep, or when shut down, allowing sleep and shutting down will extend the duration of log records.
  • Time Machine and other backups can be used to recover old logs as logarchives.

Inside the Unified Log 2: Why browse the log?

By: hoakley
25 September 2025 at 14:30

Following the introduction of the Unified Log, a surprising number of folk you would expect to use it have stopped. Some experienced developers and those providing advice in Apple Support Communities seemingly take pride in their lack of log literacy, claiming that the log is now impossible to use, and only accessible to Apple’s engineers. While it does present obstacles, pretending that the log isn’t a vital tool in diagnosis and understanding is burying your head in the sand. This article shows why.

Why the log?

The log provides support for several purposes. It’s widely used to investigate events such as bugs and unexpected behaviours, to establish their cause before working out how to fix them, in diagnosis and troubleshooting. It’s also used to discover how subsystems within macOS work, and what they do. Examples of those include LaunchServices, DAS-CTS scheduling and dispatching, and RunningBoard, all of which are nearly invisible to other methods, but write copious and explicit entries in the log. With its precise time recording and special Signpost log entries, it’s also invaluable for measuring performance, hence in optimising code.

Concentrating on the log’s use in diagnosis, log entries can be used to answer most of the key questions:

  • What happened?
  • When did it happen?
  • Who made it happen?
  • How did it happen?
  • Why did it happen?
  • What can I do about it?

Diagnose a mystery update

To illustrate those in practice, I’ll use an example that happens to be fresh in my mind, the silent updating of XProtect data last week. The only two clues available outside the log were the fact that XProtect had been updated, and that had occurred at 06:46:43 GMT on the morning of 17 September. There was no record of this event anywhere else that might have given any better information on how it had occurred.

Browsing the log from one second earlier confirmed what and when that had happened. I quickly discovered who made it happen when I found the log entry
2025-09-17 06:46:42.615072 com.apple.duetactivityscheduler REQUESTING START: 0:com.apple.security.syspolicy.xprotect-update:7874AD
revealing that update had been accomplished by a background check scheduled and dispatched by DAS-CTS, and performed by an update service com.apple.security.syspolicy.xprotect-update.

That in turn fired up XProtectUpdateService, which recorded that it promptly completed and activated the update:
2025-09-17 06:46:42.695517 com.apple.xprotect Connecting to XProtectUpdateService
2025-09-17 06:46:42.744182 com.apple.security.XProtectFramework.XProtectUpdateService XProtectUpdateService booting
2025-09-17 06:46:43.157255 com.apple.security.XProtectFramework.XProtectUpdateService Attempting to apply update: [private]
2025-09-17 06:46:43.191178 com.apple.security.XProtectFramework.XProtectUpdateService Update completed. Activated update [private]

XProtectUpdateService initiated a connection to the iCloud service now used to update XProtect in Sequoia and later, but log entries didn’t show the update being downloaded from that source. Instead, there was an error reported in the entry
2025-09-17 06:46:43.193159 com.apple.syspolicy.activities Finished Xprotect update in 496.4100122451782 ms: Error Domain=XProtectUpdateError Code=2 "Activated update LocalUpdate[5315]" UserInfo={NSLocalizedDescription=Activated update LocalUpdate[5315]}

Although the messages in many of these log entries are opaque, that entry makes it clear that XProtectUpdateService hadn’t downloaded the new version from iCloud, but had activated a local update, which we know from experience means the copy already downloaded to the traditional XProtect location had been used to install that update in its new location. That used to be available to the user through the xprotect update command, but is no longer. Thus, the only way that update could have been installed was the result of com.apple.security.syspolicy.xprotect-update being run routinely.

If we can’t intervene and force the update manually, the final piece of information we need is how often com.apple.security.syspolicy.xprotect-update is run, and that’s revealed into a later log entry:
2025-09-17 06:46:43.202474 com.apple.duetactivityscheduler Submitted: 0:com.apple.security.syspolicy.xprotect-update:58B6CE at priority 5 with interval 86400 (Wed Sep 17 22:58:51 2025 - Thu Sep 18 18:58:51 2025)
i.e. every 86,400 seconds, or 24 hours.

Procedure

How difficult was that to discover? Even with minimal prior knowledge, consummately easy, and it only took a couple of minutes.

My starting point was the timestamp reported for the update by xprotect version, of 06:46:43 GMT. I therefore set LogUI to display 5 seconds of log starting from one second before that. In the screenshots below, times are shown in BST, one hour in advance of GMT.

This returned a total of 7,174 log entries, far too many to browse. Knowing that I was looking for entries concerning XProtect, I then typed that into the search box and pressed Return, to display only those entries whose message contained the text xprotect.

That narrowed down the number of entries to just 65, starting with the scoring and dispatch of com.apple.security.syspolicy.xprotect-update by DAS-CTS at 42.608967 seconds, and containing all the entries quoted above. Just to be certain that I hadn’t missed anything relevant, I then cleared the search box and pressed Return to display all log entries again, scrolled down to 42.608967 seconds, and checked through those for the period to 43.19315 seconds, when the update had completed.

Of course, not all log investigations are as simple or successful, but many are just as straightforward. The main limitation isn’t the excessive number of log entries, but those from subsystems that make very few entries, as occurs in Spotlight indexing and search. There’s always a way to filter out unwanted entries, but you can’t magic in entries that were never made in the first place.

Conclusions

Browsing the log might appear daunting, even overwhelming or terrifying at first, but as you become more familiar with it you appreciate the rich information it can provide. Log literacy is one of the basic skills required for anyone who wants or needs to dig deeper into their Mac or Apple device. Without it diagnosis, research and performance measurement are like trying to paint a landscape while wearing a blindfold.

Inside the Unified Log 1: Goals and architecture

By: hoakley
23 September 2025 at 14:30

The introduction of Mac OS X in 2000-01 brought conventional text-based logging to the Mac, with standard logs including system.log and console.log.

console2005

With the release of macOS 10.12 Sierra in 2016, those were almost completely replaced by the Unified Log, which has continued evolving since. Intended to provide a single efficient logging mechanism for user and kernel modes, its design goals are:

  • to maximise information collection with minimum observer effect;
  • as much logging on as much of the time as possible;
  • compression of log data for efficient use of space;
  • a managed log message lifecycle;
  • designed-in privacy protection;
  • to be common across all Apple’s operating systems;
  • all legacy APIs (NSLog, asl_log_message, syslog, etc.) to be redirected into the single log;
  • to support debugging, but not system administration or audit.

To achieve this, log entries are written using the OSLog API (or Logger from Swift), handled by the logd daemon and compressed into a buffer. From there entries are either retained in memory if intended to be ephemeral, or written out to a file. Log entries are only made from the start of kernel boot; earlier phases of the boot process instead save breadcrumbs that are largely inaccessible and unintelligible.

mul102LogdFlow

Performance of writing log entries is excellent, with a single process able to write sequential entries every 400 ns from a code loop, a rate of 2.5 million entries per second, although that rate is unlikely to be sustainable for long.

Log storage

There are two main groups of files that store log entries: those kept in /var/db/diagnostics/Persist/ in the form of tracev3 files containing regular log entries, and further tracev3 files in /var/db/diagnostics/Special/ containing additional shorter-life entries. Additional and lengthier log data can instead be stored in files named by UUID in /var/db/uuidtext/, and there’s also support for special Signpost entries intended for performance measurement, and scope for high-volume collection.

tracev3 files use a proprietary compressed binary format that remains undocumented to this day, but has been largely reversed. Apple’s APIs don’t give direct access to their contents, only through closed-source utilities such as the log command tool and programmatically through the OSLog API. Where users want a more portable format, Apple recommends conversion to a logarchive package, although that’s also undocumented and only directly accessible using log, the Console app and some third-party utilities. Logarchives still store all log entries in tracev3 files.

Collection and retention of entries from different subsystems is configured in logging profiles, XML property lists stored in /System/Library/Preferences/Logging (read-only, in the System volume) and /Library/Preferences/Logging, which the user controls. You can create your own custom profiles, or modify them on the fly using the log command.

The logd service maintains tracev3 files and their ancillaries, weeding them to remove time-expired entries. It does so to constrain the total size of log files in the system, rather than rolling them to retain all entries for a specified period. Most ephemeral entries are weeded within 5 minutes of being written to the log, leaving persistent entries to remain for the following days, depending on the volume of new entries being written. For example, in a 3 second period of collection, when all entries were obtained within a minute of being written, 22,783 entries were found. Five minutes after they had been written, only 82% of those remained, and over 8 hours later that had fallen to 80%, or 18,309.

Log content

In contrast to traditional text-based logs, entries in the macOS log contain structured data in set fields, such as the datestamp, subsystem and log message. The number of fields available from the log command has risen from 16 to over 25, but entries are constrained in which fields are available, according to their type. Currently there are four types, Regular, Activity, Boundary and Signpost.

Some log entries, notably those written to /var/db/uuidtext/, can have very long message fields, extending to dozens of lines. Among the most verbose is the com.apple.runningboard subsystem, which not infrequently writes the whole of its records for an app into the message field of a single log entry.

Privacy

Privacy protection censors content of log entries when they’re written, replacing sections of the message field with <private> in accordance with the formatting string for that message. This protection can be disabled, but as entries are censored when they’re written, that only affects those written after censorship has been removed, and can’t be applied retrospectively.

One significant change in macOS 26 Tahoe is that the message contents of all log entries written using the old NSLog interface are replaced with <private>, rendering them essentially useless. Since macOS 10.12.4, all access to log entries has required admin privileges.

Access

macOS provides users with two means of accessing entries in the log: the log command tool, and Console app. In common use, log generates text output, so losing the original data fields, one of the major advantages of the Unified Log. Console offers two methods of access: a live stream of entries, or browsing past entries in a saved logarchive.

I have four main tools that provide extensive log access:

  • LogUI, using the OSLog API and displaying entries as formatted SwiftUI Lists;
  • Mints, offering special-purpose browsers for Time Machine, APFS, and other domains;
  • Ulbow, using the log tool and displaying entries as formatted Rich Text using AppKit;
  • Consolation, using the log tool and displaying entries as formatted Rich Text using AppKit.

These are available from their product page, and here for Mints.

How to check if your Apple silicon Mac is booting securely

By: hoakley
21 August 2025 at 14:30

There are so many controls in macOS that sometimes you can’t see the wood for the trees. This can leave uncertainty over essentials, such as whether your Apple silicon Mac really is properly secure, or maybe there’s something sinister going on with it? This is a question I’m asked not infrequently, usually when someone has been spreading disinformation or FUD (fear, uncertainty, doubt). So how can you check that your Mac is properly locked down and boots securely?

Quick checks

There are two quick checks that cover the essentials. First, open System Information and select the Controller section in Hardware.

This provides a brief summary of your Mac’s boot security, which should read as shown above. If you still need to use a kernel extension or similar, your Mac might show Reduced Security with Allow All Kernel Extensions enabled, but you should do everything you can to avoid that.

Secure Boot is controlled using Startup Security Utility in Recovery mode, and if you care to start up in that mode, you can confirm or correct its settings there.

bootsec2

Back in normal user mode, open Privacy & Security settings and ensure you have FileVault enabled there.

filevault3

SilentKnight also checks that XProtect/Gatekeeper checks are enabled, and that security data are up to date, giving you complete confidence.

Details

Although those should be sufficient for most, some want to go further and verify that their Mac’s boot process and security systems are also working correctly. To do that, shut your Mac down, wait ten seconds or so, and start up normally with the startup chime sounding at a known time. Enter your password, wait a few seconds for the Finder to get set up and running, and open LogUI. Set its time to that of the startup chime, and get the first 10 seconds or 10,000 log entries. You may need to adjust the seconds to capture the full boot sequence. When you have, look through the log and identify the following waypoints.

In each of these log entries, I have emboldened a word or two that you can copy from here and paste into LogUI’s Search box, then press Return. That will display the log entry, and sometimes others you might find relevant. Times are given here in seconds, with the startup chime occurring at about 37 seconds. Version numbers shown are those for macOS 15.6.

The start of boot is recorded as
37.562774 === system boot: [UUID]
and a little while after that, the kernel declares its version details
42.759300 Darwin Kernel Version 24.6.0: Mon Jul 14 11:30:40 PDT 2025; root:xnu-11417.140.69~1/RELEASE_ARM64_T6041
for macOS 15.6.

Further down you’ll come across more information about key security components, including the Trusted Execution Monitor
43.060422 [Log]: Code Signing Monitor Image4 Module Version 7.0.0: Fri Jul 11 16:51:29 PDT 2025; root:AppleImage4_txm-320.100.22~1090
43.060447 [Log]: build variant: txm.macosx.release.TrustedExecutionMonitor_Guarded-135.100.37

Then the iBoot firmware version
43.061758 iBoot version: iBoot-11881.140.96
43.061760 iBoot Stage 2 version: iBoot-11881.140.96

CoreCrypto support is vital, and another Image4 extension
43.137635 FIPSPOST_KEXT [133796636] fipspost_post:154: [FIPSPOST][Module-ID] Apple corecrypto Module v18.3 [Apple silicon, Kernel, Software, SL1]
43.242334 Darwin Image4 Extension Version 7.0.0: Mon Jul 14 11:23:46 PDT 2025; root:AppleImage4-320.100.22~2585/AppleImage4/RELEASE_ARM64E

You should see entries reporting the loading of security policy components
43.242343 Security policy loaded: AppleImage4 hooks (AppleImage4)
43.242961 Security policy loaded: Apple Mobile File Integrity (AMFI)
43.243092 Security policy loaded: Seatbelt sandbox policy (Sandbox)

The Secure Enclave Processor or SEP is another key component that has to be started up
43.264594 "AppleSEPKeyStore":326:0: starting (BUILT: Jul 14 2025 23:34:10) ("normal" variant 🌽 , 1827.120.2)
43.264639 "AppleSEPKeyStore":471:0: _sep_enabled = 1

Apple System Policy should follow a bit later
43.760156 Security policy loaded: Apple System Policy (ASP)
43.760188 AppleSystemPolicy has been successfully started

The root of the file system is then identified in two entries whose origins go right back to the start of Mac OS X
43.940643 BSD root: disk3s1
43.940644 , major 1, minor 13

And APFS mounts the root file system, using the SSV snapshot
43.941048 apfs_vfsop_mountroot:2984: apfs: mountroot called!
44.034685 apfs_vfsop_mount:2763: disk3s1 Rooting from snapshot with xid 1724240.

One of the most important entries comes shortly after that, where successful validation of the SSV’s root hash is reported
44.038830 authenticate_root_hash:642: disk3s1 successfully validated on-disk root hash

It’s now time to start user space processes, and for that launchd must be loaded so it can launch everything else
44.103761 load_init_program: attempting to load /sbin/launchd

How Secure Boot works

Apple silicon Macs have a small ROM to support DFU mode in case a full Restore is required, and to check and load the first stage of the ‘firmware’, the Low-Level Bootloader or LLB. Only if that matches its signature will the ROM firmware hand over to it and proceed with the boot process. The LLB in turn performs the same checks on the second stage ‘firmware’, iBoot proper. That goes on to check the kernel, before loading that and handing over for kernel boot to take over.

iBoot ‘firmware’ doesn’t write anything in the log, but once the kernel takes over its log entries provide a detailed account of its progress. The great majority of its log entries are unintelligible to anyone outside Apple, but the waypoints I have given above identify some of the most important steps it takes. When it’s ready, the kernel validates the root hash for the SSV snapshot, as noted above, enabling the boot process to proceed to load and run other parts of macOS. The remaining hash checking of the SSV, to confirm that it’s exactly as Apple intends, proceeds in a ‘lazy’ fashion, as access is needed to its contents.

This chain of validation before loading the next stage ensures that nothing in the boot process can be tampered with or changed, and the boot is secure throughout. Apple provides further details in its Platform Security Guide.

What does RunningBoard do? 5 Log insights

By: hoakley
7 August 2025 at 14:30

If RunningBoard’s copious log entries record the life cycle of apps, can we use them to our advantage? To test this out, I’ve analysed a section of log using waypoints and information provided in RunningBoard’s entries.

Waypoints, or landmarks if you prefer, are essential for navigating the log. In an ideal world we would always know when any significant event occurred, timed to the millisecond. In practice we may only be able to estimate to the nearest few seconds or minutes. Without some means of discovering exactly when to obtain log extracts, you can waste many hours trying to pin down when something happened, and in many cases will be forced to abandon the hunt.

Methods

My aim was to separate RunningBoard log entries into those handling its assertions, which should represent life-cycle events, and job descriptions giving details of apps launched. To try this yourself, open LogUI and set the Start date and time to the start of the period of interest. Set the Period to at least 100 seconds, and Max entries to 2,000 or more. Then set the Predicate popup menu to read subsystem, and type in com.apple.runningboard so that LogUI only gets entries from that subsystem. Click on the Get Log tool in the toolbar, and check you get at least 1,000 entries.

Click on the Save JSON tool to save the whole of this excerpt. To view all the RunningBoard assertions acquired over that excerpt, type acquiring assertion in the search box at the top right, and press Enter.

The window name then changes to append the text from the search box.

To save this limited selection of entries, first click on the Reduce tool to discard all the others, then click on the Save JSON tool again and save the entries using a different filename. Repeat that using each of three search terms:

  • acquiring assertion to see RunningBoard’s outline of life cycle events,
  • constructed job to see job descriptions for newly launched apps,
  • extension overlay to see similar information for appexes and similar extensions.

In my case, I analysed a period of 4 minutes of user activity on a Mac mini M4 Pro running macOS 15.6, during which there were 6,580 RunningBoard entries, suggesting that the whole log would have had well over 50,000 entries in that time, far too many to attempt any more detailed analysis. My search terms found 305 assertions acquired, 3 job descriptions constructed, and 20 extension overlays submitted.

Job descriptions

The three captured represented the three apps I had launched over those four minutes, Cormorant, DelightEd and Consolation 3. Each of them has a platform ID of 1, as notarized macOS apps. I also noticed that one was run in app translocation, although this was long after its first run. Recent versions of macOS, Sequoia in particular, appear prone to this, when an app that should have cleared quarantine gets stuck in perpetual app translocation.

Extension overlays

While I had expected to find those three app launches, I didn’t expect to see any appexes being run over this period. Those encountered consisted of three widgets, none of which had ever been opened, and five QuickLook thumbnail extensions. Most of these were run multiple times, with ScappleThumbnail appearing on six occasions. All were platform 1, except for the widgets, all three of which are bundled in macOS, for the FindMy widget and NewsToday2. They were platform 6, and subject to RunningBoard’s resource controls.

RunningBoard’s log entries here give potentially valuable insights that might otherwise be missed.

Assertions

It’s straightforward to construct a narrative of the key events that took place over that period of 4 minutes using a selection of assertions acquired. Rather than reproduce the whole of each of those waypoints, here they are laid out in more readable form.

11:40:50.424950 Acquiring assertion targeting app application.co.eclecticlight.Cormorant.10809046.10809052(501)
description "LS launch co.eclecticlight.Cormorant" ID:424-620-716
target: application.co.eclecticlight.Cormorant.10809046.10809052(501)
attributes: domain:"com.apple.launchservicesd" name:"LaunchRoleUserInteractive" sourceEnvironment:"(null)"

This marks the start of the launch process for Cormorant, handled in the normal way through LaunchServices and RunningBoard.

11:41:18.304367 Acquiring assertion targeting app application.co.eclecticlight.DelightEd.10206240.10206246(501)
description "LS launch co.eclecticlight.DelightEd" ID:424-620-756
target: application.co.eclecticlight.DelightEd.10206240.10206246(501)
attributes: domain:"com.apple.launchservicesd" name:"LaunchRoleUserInteractive" sourceEnvironment:"(null)"

This is the same for DelightEd.

11:41:20.050166 Acquiring assertion targeting [xpcservice com.apple.appkit.xpc.openAndSavePanelService([app application.co.eclecticlight.DelightEd.10206240.10206246(501) :1553])(501) 1576:1576]
description "viewBridge:1576" ID:424-391-769
target: 1576
attributes: domain:"com.apple.launchservicesd" name:"RoleViewBridge" sourceEnvironment:"(null)"

DelightEd is here being used to open a document using the openAndSavePanelService, a separate process.

11:41:44.171636 Acquiring assertion targeting app application.co.eclecticlight.Consolation3.430650.430692(501)
description "LS launch co.eclecticlight.Consolation3" ID:424-620-788
target: application.co.eclecticlight.Consolation3.430650.430692(501)
attributes: domain:"com.apple.launchservicesd" name:"LaunchRoleUserInteractive" sourceEnvironment:"(null)"

This is the launch of Consolation 3, a user error on this occasion!

Two long runs of assertions were discovered, both involving syncing the address book for Contacts.

11:41:57.462118 Acquiring assertion targeting [osservice com.apple.AddressBook.SourceSync(501):1610]
description "App is holding power assertion" ID:424-362-811
target: 1610
attributes: domain:"com.apple.appnap" name:"PowerAssertion" sourceEnvironment:"(null)", RBSAcquisitionCompletionAttribute policy:AfterApplication

The first of these was to sync the Address Book with its source in iCloud, and lasted 12.181 seconds.

11:42:11.840349 Acquiring assertion targeting [osservice com.apple.AddressBook.abd(501):1625]
description "Contacts shared filesystem lock" ID:424-1625-857
target: 1625
attributes: domain:"com.apple.common" name:"FinishTaskUninterruptable" sourceEnvironment:"(null)", RBSAcquisitionCompletionAttribute policy:AfterApplication

Shortly after that completed, synced data was updated by a service abd (Address Book Daemon?) that I had been unaware of. This only lasted 2.238 seconds.

RunningBoard’s assertions seldom appear for single-shot shell commands, and most commonly handle executable bundles rather than bare Mach-O binaries. They also don’t appear to provide clear information recording when apps are quit normally.

Conclusions

  • RunningBoard entries can readily provide accurate waypoints for many events in the log.
  • Establishing waypoints on the strength of RunningBoard log entries is straightforward in LogUI.
  • Job descriptions are readily found, and provide useful information on apps launched.
  • Extension overlays provide details of appexes that might otherwise be overlooked.
  • Acquisition of assertions identify various events in the app life cycle.
  • Once RunningBoard entries have provided precise times of events, attention can be focussed on obtaining more detail using short log extracts.

Last Week on My Mac: Search and you’ll find

By: hoakley
3 August 2025 at 15:00

One thing we humans are good at is searching. It’s a task we engage in from a few moments after birth until the time we slip away in death, we search everything around us. Locating and identifying that bird of prey wheeling high, finding the house keys, and that book we mislaid some time last week, meeting the perfect partner, discovering the right job, choosing the best education, looking through a Where’s Wally? or Where’s Waldo? book, and so on. Searching has transformed some into explorers like Christopher Columbus, and was the purpose of the chivalric quest. It’s what researchers in every field do, and thanks to Douglas Adams can be answered by the number 42.

Last week my searching took two new turns.

Spotlight

The first was more of a meta-search, in trying to discover more about the internals of Spotlight. Following the example of Maynard Handley, who has used them so successfully in understanding how M-series CPUs work, I looked through patents that have been awarded to Apple for the work of its search engineers. Yesterday’s slightly fuller history of Spotlight search is one result, and there are more to come in the future as I digest those patents concerned with performing search.

There’s a tinge of irony here, as many of my searches have been conducted using Google Patents, alongside Google Scholar one of the remaining search engines that doesn’t yet use AI and attempt to provide its own answers.

Logs

The other marks a new phase in my quest to get more information from the Unified log. Looking back to my first comment here, I realise how wildly over-optimistic I was when I wrote that it “should make my life a lot easier”, and that “a new version of Console will provide improved features to help us wade through logs.” Nine years later, I look wistfully at what remains of Console and realise how wrong I was on both counts.

When RunningBoard arrived in macOS Catalina, I soon noticed how “its log entries are profuse, detailed, and largely uncensored for privacy.” Since then it has proved garrulous to the point where its apparently ceaseless log chatter is a distraction, and can overwhelm attempts to read other log entries. I suspect it has contributed significantly to those advanced Mac users who now refuse to even try to make sense of the log.

One answer might be to tweak log preferences to shut out this noise, but given the purpose of RunningBoard in monitoring the life cycle of apps, why not try to use the information it provides? To do that, it’s first necessary to understand RunningBoard’s idiosyncratic language of assertions, and its protocols under which they’re acquired. The only way to do that without documentation is by observation: catalogue over 30 of those assertions for an interesting example like Apple’s Developer app, and see what they reveal.

By far the most informative entries from RunningBoard are those announcing that it’s acquiring an assertion, such as
Acquiring assertion targeting [app<application.developer.apple.wwdc-Release.9312198.9312203(501)>:2946] from originator [osservice<com.apple.uikitsystemapp(501)>:748] with description <RBSAssertionDescriptor| "com.apple.frontboard.after-life.subordinate" ID:424-748-2228 target:2946 attributes:[
<RBSDomainAttribute| domain:"com.apple.frontboard" name:"AfterLife-Subordinate" sourceEnvironment:"(null)">
]>

In a log often censored to the point of being unintelligible, this contains frank and explicit detail. The app is identified clearly, with the user ID of 501 and process ID of 2946. The originator is similarly identified as com.apple.uikitsystemapp with its PID of 748, which is confirmed in the middle digits in the Assertion ID. This is explicitly related to FrontBoard and an attribute named AfterLife-Subordinate. There’s not a single <private> to blight this entry, although further knowledge is needed to decode it fully.

Normally to get such information from a running process would require its source code to be instrumented with calls to write log entries, many of which would be lost to <private>, yet RunningBoard seems happy, for the moment, to provide that information freely. You can see what I mean by applying the predicate
subsystem == "com.apple.runningboard" AND message CONTAINS "Acquiring assertion t"
in LogUI, to obtain a running commentary on active apps and processes. Once you’ve identified a relevant assertion, you can focus attention on other log entries immediately prior to that. I will be following this up in the coming week, with fuller instructions and some demonstrations.

Although neither patents nor assertions have the significance of the number 42, in their own ways they show how the art and science of search aren’t dead yet, nor have they succumbed to AI.

❌
❌