Normal view

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

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 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.

❌
❌