Normal view

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

LogUI build 65 introduces a Logarchive Tool

By: hoakley
11 June 2025 at 15:00

Just before the start of WWDC, I released an update to my log browser LogUI adding support for accessing logarchives. I promised that there was more support for logarchives on its way. LogUI 1.0 build 65 dedicates a whole window to them, in its Logarchive Tool.

There are many situations where you can’t access the active log, and you can’t create a logarchive using the log command tool or a sysdiagnose. These include:

  • When you only have access to the contents of the Mac or device’s storage, particularly in forensics, or following hardware failure.
  • When you want access to the logs in a backup. Time Machine backups normally include full log files, for example.
  • When you don’t have ssh or similar access to a remote Mac.
  • When the log records may be incomplete or damaged.

Provided that you can copy two folders from the hidden /var/db folder on that Mac or device, LogUI can turn those into a browsable logarchive.

Create a logarchive from folders

On your Mac, create a folder somewhere convenient such as ~/Documents. As this method doesn’t use the log command, this can be on an external disk if you wish.

From the source Data volume copy the folders at /var/db/diagnostics and /var/db/uuidtext to your folder, so it looks like this.

Open LogUI, and from its Window menu open its Logarchive Tool. This offers you four tools and two checkboxes. Click on the Create Logarchive tool and first select the folder you created, containing the log folders. Then give the new logarchive a suitable name and save it somewhere convenient.

LogUI should then inform you in its window that creation has completed. As this is performed using undocumented code for an undocumented format, it may not always work correctly. If there are any problems, repeat the same with the Debug checkbox ticked, and it will give you a detailed commentary of what it does, which should help you understand what went wrong.

Getting info about a logarchive

The trickiest part of accessing logarchives is knowing what they contain, more specifically the time periods for which they have log records. LogUI’s Logarchive window provides two aids to provide you with that information, in its Catalogue and Analyse tools.

Catalogue simply lists all the tracev3 files in the logarchive, giving the datestamps each was created and last modified, together with the period between those, and the file size.

Leave that open as you browse that logarchive, to guide your way through its entries.

Analyse goes further, in telling you about the entries in each of the persist tracev3 files in the logarchive. It tells you the most common processes that wrote the entries in each of those files, allowing you to hone in on which are of most interest. If you want to extract that information for analysis in a spreadsheet, tick the CSV checkbox and it will be shown ready to import into your favourite spreadsheet.

Finally, to save the contents of the current window as a text file, click on the Save Text tool at the right.

I have now checked LogUI’s compatibility with the first developer beta of Tahoe, and found and fixed one obscure bug in the Logarchive Tool before this new build. LogUI should now be fully compatible with macOS 14.6 and later, including Tahoe. It’s available now from here: logui165
and from its Product Page.

Enjoy!

LogUI build 60 reads iOS, iPadOS, macOS and other logarchives

By: hoakley
9 June 2025 at 14:30

Until now, LogUI has only been able to access the active log of your Mac, by reading it directly. There are occasions when you can’t do that, or want to preserve the log for future reference. You also can’t browse the log directly on any of Apple’s devices. In these cases, and others, the best solution is to make a logarchive, and browse that instead. I’m delighted to provide an update to LogUI that can browse logarchives, including those created in iOS, iPadOS, and on Apple’s other devices.

What is a logarchive?

A logarchive is an undocumented package containing copies of all the files from the active log at the moment the logarchive was created. They can be opened and browsed by Console, Consolation 3, Ulbow, the log command tool, and now by LogUI. Because they contain all the files that make up the log, they can be large, and typically range in size from about 300 MB to over 1 GB. All the files containing log entries are stored in their original binary tracev3 format, proprietary to Apple, and again undocumented, although that format has been reversed in the past.

Create a logarchive

The easiest way to create a logarchive is to run a sysdiagnose, and that’s the standard way for saving a logarchive on one of Apple’s devices. Methods vary by device, and include:

  • On a Mac, use the System Diagnostics… option in Activity Monitor’s Action tool, or press the Shift, Command, Control, Option and . keys at the same time, or run sudo sysdiagnose -f ~/Documents to save it to your Documents folder.
  • On an iPhone or iPad, press and hold both volume buttons and the side or top button at the same time, for about 2 seconds. This combination may trigger other features, though. The sysdiagnose file will be made available in Settings > Privacy & Security > Analytics & Improvements > Analytics Data, from where you can transfer it to your Mac.

Unpack the .tar.gz archive resulting from that, and you’ll find a system_logs.logarchive inside it.

On a Mac, you can instead use the log collect command to create a logarchive directly. For example,
log collect --output ~/Documents/my.logarchive --last 5m
collects the last 5 minutes of log in the specified logarchive package. macOS security will block you from trying to save that logarchive on an external volume, though.

My free log browser Ulbow uses another method for assembling logarchives, and the next build of LogUI will incorporate that and other tools for working with logarchives.

Browse a logarchive in LogUI

This new build of LogUI has a seventh tool, to Use Logarchive. Click on that and you’ll be prompted to select the logarchive to open and browse.

Because the dates and times used in the logarchive will be different from current clock time, the LogUI window displays red warning text just to the left of the Start time. Set the date and time to a period within the scope of that logarchive, and use the Get Log tool as normal.

The log excerpt shown in the screenshot above is taken from the kernel boot sequence of my iPhone 15 Pro, to demonstrate how this all works.

If you want to return that window to browsing the active log, click on the Use Logarchive tool again, but this time cancel the selection. Other windows will of course continue to browse the active log unless you set them to use a logarchive as well.

Coming soon

Although browsing saved log entries in a logarchive is exactly the same as those of the active log, dates and times can be a pain. If you want to check when log files in a logarchive were written, use the Finder’s contextual menu to show their contents, scroll to the foot of the folders inside, select the Persist folder and check the file creation dates there.

This is made even easier in the forthcoming new build of LogUI, which features a Logarchive Tool to help you navigate logarchives, and learn which date and time ranges are appropriate.

LogUI 1.0 build 60 is now available from here: logui160
and from its Product Page.

I’ll be along with a new build in a few days, once I have tested and documented its Logarchive Tool. In the meantime, I hope you’ll find LogUI useful for studying the first beta-releases of Apple’s new operating systems.

LogUI build 58 reads and writes JSON, and more

By: hoakley
5 June 2025 at 14:30

Until now, LogUI has largely been an interactive log browser, and only able to export log excerpts in Rich Text format. This new build adds three tools to greatly increase its usefulness, as it can now write log excerpts in JSON format, open saved files, and filter out unwanted log entries. To give a simple example, this screenshot shows a log extract that has been reduced to entries made only by LaunchServices, converted from JSON to CSV, and imported into Numbers.

There are three new tools in LogUI’s toolbar.

From the left:

  • Get Log fetches and displays log entries specified by the current log settings, as before.
  • Read JSON opens a dialog for you select a LogUI JSON file, and loads its log entries into the window, replacing its current contents.
  • Gloss opens the Gloss window with message text from selected log entries, enabling you to use Writing Tools and other aids, as before.
  • Reduce applies the current search to current log entries. This removes all entries that are excluded by that search, so reducing the number of log entries. If you want to keep a copy of the unreduced log, save it as a JSON file before reduction.
  • Save JSON opens a dialog for you to save the current log records to a new LogUI JSON file. This has the type co.eclecticlight.loguilog, and the extension .logui, and can be used as a regular JSON export with other apps capable of importing JSON format.
  • Save RTF saves the current log excerpt as a Rich Text Format file, using the same colour conventions, as before.

You can now filter log entries in several ways, one at a time or all together:

  • You can apply a predicate to determine which log entries are fetched in the first place, perhaps limiting them to one or two subsystems.
  • You can search for subsystems, sender or process names, or contents of messages in the search box.
  • When you have filtered the displayed log entries to those that you want, you can reduce the excerpt to include just those, and save that excerpt.
  • You can then open the saved, reduced excerpt and use further searches to navigate and analyse those entries.

As we’ve recently been discussing different types of Spotlight search, I should perhaps explain that LogUI’s search isn’t word-based, simply finds matching sequences of characters, and doesn’t support regex or wild card search. If you search for the characters arb, then it will find them in the words nearby and arbitrary, but not in arabesque.

There are two current limitations with these new JSON documents. If you double-click one, LogUI should launch automatically and will then open a new window, but won’t load that document into the window, as that part of its internal wiring isn’t yet complete. The same also happens if you try dropping a document onto the app. I did hope to have drag and drop working in this release, but the resulting code proved too complex for the Swift compiler in Xcode to negotiate, so that will also wait for a future build.

LogUI 1.0 build 58 is now available from here: logui158
and from its Product Page.

This is likely to be the last update for a couple of weeks, as I expect I’ll be immersed in WWDC and its fallout for much of the next fortnight. My outstanding tasks are:

  • complete the predicate editor in Settings;
  • add support for opening documents from the Finder;
  • add drag and drop support.

I’m also thinking about additional search/filter support, and whether a more compact binary document format might be a worthwhile alternative for large log excerpts.

As ever, I value your comments and suggestions.

Last Week on My Mac: Successful search strategies

By: hoakley
1 June 2025 at 15:00

How everything grows over time. Twenty years ago a hard disk of 100 GB was often ample, now twenty times that can be insufficient, and some have even larger media libraries. Finding files from among tens of thousands used to be straightforward, but now we’re working with millions we’re often struggling. Last week’s discussions of Spotlight search and its alternatives highlighted how important search strategies have become.

Strategy

Perhaps the most common strategy we use to search quickly and effectively is to apply a series of properties or attributes narrowing from the general to the specific: a dog, a small dog, a small grey-and-white dog, a small grey-and-white Havanese dog. In just a few adjectives we have narrowed the field to a description applying to a small number of domestic pets.

This strategy has two essential requirements: the target of your search must be included in the list of items being searched, and each of the attributes or criteria you apply in succession must include the search target. The first is obvious and critical to Spotlight’s success, and the second is the basis of how attributes are chosen. If the dog’s colour had been specified as red, then that search would have failed.

One of many skills in successful searching is judging how exclusive each criterion should be, and being more inclusive to ensure none of the criteria might inadvertently exclude the target.

Although you can combine attributes in this way when searching using the general Spotlight search window accessed through the menu bar, that’s a global search including websites and everything searchable from Wikipedia to Photos albums and Messages. When looking for a file, searching in the Finder immediately narrows the scope, and saves you wading through many irrelevant results. You can then add a search bar for each criterion, perhaps specifying that you’re looking for an image in your ~/Documents folder, each time reducing the number of hits until your choice becomes sufficiently limited.

Incremental search

Spotlight offers another technique that has become popular in search engines as their performance has improved, in what’s known as live or incremental search. As you type letters into one of its search boxes, it shows results as it gets them. This isn’t much use when entering common combinations of letters, but as they become more specific this can save time and accommodate any uncertainty you might have over spelling or the rest of the word. I use this frequently in MarsEdit when looking for old articles I have written: for example, typing wrestl will find wrestler, wrestlers, wrestling, wrestled, etc.

This works well with most languages including English, where roots and meanings are concentrated in the first parts of words, and declension and conjugation are usually found in their endings. Not all languages work like that, though, and this may not perform as well in Georgian or even German due to their morphology.

Predicates

For those who prefer to use the command line, mdfind can use predicates to express combinations of attributes, but those aren’t readily used in the same incremental way to narrow results down interactively. Another situation where predicates often come into play is when searching log entries and using the log show command, and that brings me on to LogUI, my other concern last week.

Searching the log

Let’s say you want to discover all the information RunningBoard gathers about an app, something you know is written in a log entry by the com.apple.runningboard subsystem shortly after that app starts its launch sequence. While you could search for all entries for that subsystem in the minute or so around the time you launched the app, there are likely to be thousands of hits.

To narrow down that search you have several options, including:

  • launch the app at a known time, and set that as the Start time, with a Period of just a couple of seconds;
  • set a one-off predicate to subsystem == "com.apple.launchservices" OR subsystem == "com.apple.runningboard";
  • search subsystems for com.apple.launchservices to identify the time that LaunchServices announces the app will be launched through RunningBoard;
  • search messages for constructed job description, RunningBoard’s log entry giving the details you’re looking for.

Those are ordered in increasing specificity, reducing numbers of hits, and increasing requirement for prior knowledge. That’s a general association, in that the more prior knowledge you have, the more specific you can make your criteria, and the fewer irrelevant hits you will see. As with Spotlight search, the more of these criteria you apply, the greater your chance of success, provided they all match the entry you’re looking for.

LogUI

To make LogUI more amenable to incremental search strategies, two additional features are needed. Instead of only exporting whole log extracts to Rich Text, the app needs to save and read formatted extracts. It also needs the ability to eliminate entries that don’t meet search criteria. Together those will enable use of a predicate to save an extract of reduced size, then application of search criteria, maybe saving an even smaller extract.

One way to combine multiple searches is to use multiple search bars, in a similar way to the Finder’s Find window. However, that tends to become overcomplicated, and I suspect is relatively little-used. If you do need a series of search criteria, then you also need different ways of combining them, including OR as well as AND, and that becomes a GUI predicate editor. I have yet to see any successful GUI predicate editor.

Next week, in the days prior to WWDC, I’m going to be focussing on search strategies for Spotlight, before turning to LogUI to implement these changes. This is an ideal time to let me know what you’d like to see, and how LogUI can support more successful search.

Can you trust times shown in the log?

By: hoakley
30 May 2025 at 14:30

Next to the order of entries in the log, their date and time stamps are one of the most important pieces of information they contain. In some cases, such as when you’re using the log to estimate performance, their accuracy is vital. This article reports the results from tests to validate the times given in log records provided by the log show command, and in my free log browsers LogUI and Ulbow.

Clock date and time

Dates and times given in log extracts invariably match those of the Mac’s system clock, the only catch here being adjustments for time zone and DST. The latter can become confusing if you look at the log when DST is changed, or from a different time zone. To cope with that you can use the --timezone local option in log show to express all times with uniform adjustment. Ulbow doesn’t use that, but LogUI does now synchronise all time and date stamps to the current time zone and DST.

Time differences

The log is an excellent tool for measuring time and performance, either using regular entries or Signposts that are intended for the purpose. Writing an entry into the log incurs minimal overhead, and is simple to perform from any code or script. If your favourite scripting language doesn’t give direct access to writing entries, then you can use my free command tool blowhole to do so. If you want to assess processes in macOS, then it’s usually straightforward to identify appropriate milestones that mark events and use those to calculate the period. These all depend on the times reported in log entries being sufficiently accurate.

Gold standard

Since Mac OS X, every Mac has had a high-precision internal clock within it (prior to that Time Manager could resolve times down to the microsecond but no further). This increments monotonically in ‘ticks’, an unsigned 64-bit integer, starting from an arbitrary value, and is referred to as Mach Absolute Time (MAT).

Intel Macs increment their ‘tick’ count once every nanosecond, so the difference between two readings of the clock represents the time interval in nanoseconds. Life isn’t as simple with Apple silicon Macs, as they tick three times every 125 nanoseconds, or once every 41.67 nanoseconds. Apple’s latest documentation on MAT, its units and use, comes in a Technical Q&A dated 2005.

Once correctly converted into nanoseconds, MAT is the closest available measurement of time to a gold standard.

I suspect that log entries are originally given a raw MAT as their time, and that can be made available using the log show command, or in Ulbow, as that uses log show to obtain log entries. LogUI reads the log directly, through the OSLog API in macOS, which currently doesn’t provide MAT values, instead giving a lower resolution Date value.

This validation therefore compares time intervals given by Ulbow from log entry timestamps, and those given in LogUI, against MAT intervals obtained in Ulbow. To increase the challenge, log entries used are from blowhole writing 25 log entries as fast as it can, a worst case scenario as that writes 2-3 entries each microsecond.

Comparisons

Log extracts obtained using log show in Ulbow and those estimated by LogUI were compared, and their timestamps were found to be identical.

On an Apple silicon Mac (M4 Pro), entries written by blowhole had raw MAT values that recorded intervals of 9 or 10 ticks between them, after the first three were made 73 and 15 ticks apart. From the third of the series of 25 log entries there was a strong linear relationship between recorded MAT in nanoseconds elapsed and loop number, as shown in the chart below.

The gradient of the regressed line shows that blowhole‘s log entries occurred at intervals of just under 405 nanoseconds.

Because LogUI (and regular timestamps in log show and Ulbow) only resolve to microseconds, the matching plot for LogUI’s times against loop number is stepped.

The gradient of this regression line is 0.4, indicating that the intervals occurred at 400 nanoseconds, almost identical to that found for the MAT.

Plotting times measured by LogUI (which also represents those for Ulbow and log show, as they’re identical) against that of MAT shows a good linear relationship with a gradient of just under 1.009, indicating that timestamps in log show, Ulbow and LogUI are accurate and reliable estimates of MAT.

Differences between pairs of time estimates obtained from MAT and LogUI ranged from -83 to +792 nanoseconds, with a median of +370 and quartiles of +83 to +583 nanoseconds.

Conclusion

  • Times given for log entries in LogUI, Ulbow and log show are reliable estimates of MAT to within +0.8 microseconds.
  • When nanosecond resolution is needed, the machTimestamp field from log show or Ulbow should be used, and converted into nanoseconds.

LogUI build 52 handles time zones and microseconds better

By: hoakley
27 May 2025 at 14:30

Last week’s deep dive into the precision of times in the log concluded that, with the data currently available, the highest they can aspire to are microseconds, 10^-6 seconds, and not the nanoseconds given in the last build of LogUI. The aims of this new build are to deliver correctly rounded microsecond time resolution, and to adopt consistency in time zones and DST corrections.

I described in detail yesterday how I arrived at conversion of log entry dates to yield the time in microseconds. Here I’ll consider how to treat time zones correctly.

When you enter a start time in LogUI, this is assumed to be given in the local time set on your Mac, including both its time zone and any DST correction. LogUI already takes those into account, and performs any necessary adjustments.

Now that I have completely overhauled how LogUI converts the date stored for each log entry, I have changed behaviour in comparison with the default option of the log show command, and Ulbow that accepts its timestamps without question. The best way to see that is to observe log entries when the Mac’s time zone is changed.

Ulbow, and by default the log show command, uses date and time stamps adjusted for the time zone and DST setting at the time each entry is written to the log. You should therefore be prepared to see abrupt discontinuities in its timestamps. This condensed sequence of log entries from Ulbow shows what happens when you change time zone from New York to the UK during DST:
10:50:00.729672-0400 com.apple.Settings /AppleInternal/Library/BuildRoots/ … obfuscated() obfuscate output: …
10:50:00.729687-0400 com.apple.xpc.alarm Setting timer for "com.apple. …
15:50:00.730675+0100 === system wallclock time adjusted
15:50:00.730733+0100 com.apple.mobiletimer.logging … rescheduling 0 alarms
15:50:00.730754+0100 com.apple.xpc.alarm Setting timer for "com.apple.mdmclient.timer.ManualCertRenewalCheck" in 86080 seconds.

Instead, LogUI adjusts all log entries to the current time zone for consistency in reading:
15:50:00.729672+0100 com.apple.Settings /AppleInternal/Library/BuildRoots/ … obfuscated() obfuscate output: …
15:50:00.729687+0100 com.apple.xpc.alarm Setting timer for "com.apple. …
15:50:00.730675+0100 Boundary === system wallclock time adjusted
15:50:00.730733+0100 com.apple.mobiletimer.logging … rescheduling 0 alarms
15:50:00.730754+0100 com.apple.xpc.alarm Setting timer for "com.apple.mdmclient.timer.ManualCertRenewalCheck" in 86080 seconds.

If you’re using log show, you can add the --timezone local option to adopt the same behaviour.

This also solves the dilemma when you’re browsing the log through a time zone change, for example when the clock has gone forward to DST, or the reverse. In LogUI the rule is simple: enter the current time, and all log entries will be adjusted to and shown in that single time zone.

I’ve previously explained some of the confusion that can arise with DST changes, in the autumn/fall, and again in the Spring. Hopefully LogUI won’t lead to any confusion now.

LogUI 1.0 build 52 is now available from here: logui152
and from its Product Page.

Enjoy!

Never work with children, animals or time: nailing the nanosecond

By: hoakley
26 May 2025 at 14:30

Quite unintentionally, last week became a saga about time, and a good demonstration of how you should do your utmost to avoid working with it. This all started with an irksome problem in my new log browser LogUI.

LogUI departs from my previous log browsers in accessing log entries through the macOS API added in Catalina. When it first arrived, I found it opaque, and its documentation too incomplete to support coding a competitive browser at that time. Since then I have revisited this on several occasions, and each time retreated to using the log show command to obtain log extracts. As far as time is concerned, that presents me with two alternatives: a formatted string containing a date and timestamp down to the microsecond (10^-6 second), and a Mach timestamp giving the ‘ticks’ from an arbitrary start time.

Although the latter can only give relative time, the timestamp provided suffices for most purposes, and comes already formatted as
2025-05-25 07:51:05.200099+0100
for example.

Milliseconds

When you use the macOS API, date and time don’t come formatted, but are supplied as a Date, an opaque structure that can be formatted using a DateFormatter
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSSZ"
let dateString = dateFormatter.string(from: date)

to provide
2025-05-24 13:46:12.683+0100

That only gives time down to the millisecond (10^-3 second), which is inadequate for many purposes. But changing the formatting string to "yyyy-MM-dd HH:mm:ss.SSSSSSZ"
just returns
2025-05-24 13:46:12.683000+0100
with zeroed microseconds. As I can’t find any documentation that states the expected time resolution of Dates, it’s unclear whether this is a bug or feature, but either way a different approach is needed to resolve time beyond milliseconds.

Nanoseconds

The only method I can see to recover higher precision in the macOS API is using DateComponents to provide nanoseconds (10^-9 second).
Calendar.current.component(.nanosecond, from: date)
returns an integer ready to format into a string using
nanosecStr = String(format: "%09d", nanoSeconds)
to give all nine digits.

Inserting that into a formatted date is a quick and simple way to construct what we want,
2025-05-24 13:46:12.683746842+0100
down to the nanosecond.

It’s only when you come to examine more closely whether the numbers returned as nanoseconds match changes seen in Mach timestamp, that you realise they’re a fiction, and what’s given as nanoseconds is in fact microseconds with numerical decoration.

Microseconds

The answer then is to round what’s given as nanoseconds to the nearest microsecond, which then matches what’s shown in Mach timestamps
let nanoSeconds = Int((Double(Calendar.current.component(.nanosecond, from: date))/1000.0).rounded())
and that can be converted into a string using
nanosecStr = String(format: "%06d", nanoSeconds)

Rather than manually format the rest of the date and timestamp, you can splice microseconds into the @ position of the format string
"yyyy-MM-dd HH:mm:ss.@Z"
turning
2025-05-24 13:46:12.@+0100
into
2025-05-24 13:46:12.683747+0100

Unfortunately, that’s too simple. If you test that method using times, you’ll discover disconcerting anomalies arising from the fact that seconds and microseconds are rounded differently. This is reflected in a sequence such as
2025-05-24 13:46:12.994142+0100
2025-05-24 13:46:13.999865+0100
2025-05-24 13:46:13.000183+0100

where the second rounds up to the next second even though the microseconds component hasn’t yet rounded up. The only way to address that is to format all the individual components in the string using DateComponents. And that leaves a further problem: how to get the time zone in standard format like +0100?

Time zones

Current time zone is available as an opaque TimeZone structure, obtained as
TimeZone.current
Note that this doesn’t need to be obtained individually for each Date, as its components are obtained using current Calendar settings, not those at the time the Date was set in that log entry. This should have the beneficial side-effect of unifying times to the same time zone and DST setting.

But that doesn’t offer it in a format like +0100, so that has to be calculated and formatted as
let timeZone = (TimeZone.current.secondsFromGMT())/36
let tzStr = String(format: "%+05d", timeZone)

Solution

The complete solution is thus:
let timeZone = (TimeZone.current.secondsFromGMT())/36
let tzStr = String(format: "%+05d", timeZone)
let dateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second, .nanosecond, from: date)
let yearStr = String(format: "%04d", dateComponents.year ?? 0)
let monthStr = String(format: "%02d", dateComponents.month ?? 0)
let dayStr = String(format: "%02d", dateComponents.day ?? 0)
let hourStr = String(format: "%02d", dateComponents.hour ?? 0)
let minuteStr = String(format: "%02d", dateComponents.minute ?? 0)
let secondStr = String(format: "%02d", dateComponents.second ?? 0)
let nanoSeconds = Int((Double(dateComponents.nanosecond ?? 0)/1000.0).rounded())
let nanosecStr = String(format: "%06d", nanoSeconds)

then concatenate those together with punctuation marks as separators to deliver the string
2025-05-24 13:46:12.683746+0100

If you’re coding in Swift, you might instead consider using Date.FormatStyle, although its documentation only refers to handling milliseconds, so I suspect that might turn out to be another wild goose chase.

If you know of a better way of handling this explicitly, don’t hesitate to let my code therapist know.

A Dance to the Music of Time in Intel and Apple silicon logs

By: hoakley
20 May 2025 at 14:30

Now we’ve got LogUI to give us the times of log entries down to the nearest nanosecond, it’s time to see whether that’s any improvement over other tools. Given that Mach Absolute Time resolves to nanoseconds (10^-9 seconds) in Intel Macs, and just under 42 nanoseconds in Apple silicon Macs, can we now resolve times of events better than when using microseconds (10^-6 seconds)?

Methods

To test this out, I used my command tool blowhole that can run a tight loop writing entries in the log as fast as possible. To do this, it uses the code
for index in 1...number {
os_log("%d", log: Blowhole.gen_log, type: type, index)
}

I then wrote loops of 20 to the log of an iMac Pro (Intel Xeon W CPU), M3 Pro and M4 Pro, and extracted the resulting log entries using LogUI with its new nanosecond times.

Results

Plotting these times against loop number resulted in unexpected patterns.

In this graph, results from the iMac Pro are shown in black, those from the M3 Pro in blue, and the M4 Pro in red. Although less well-ordered in the first to fourth loops, from the fifth loop onwards there were linear relationships between time of log entries and loop number. Linear regression equations are shown in the legend, and demonstrate:

  • on the iMac Pro each loop takes 1.0 x 10^-6 seconds, i.e. 1 μs;
  • on the M3 Pro each loop takes 5.6 x 10^-7 seconds, i.e. 0.6 μs;
  • on the M4 Pro each loop takes 4.6 x 10^-7 seconds, i.e. 0.5 μs.

which perhaps isn’t surprising.

However, the patterns of individual points are quite different. Apart from loops 4 and 5, subsequent loops on the iMac Pro are evenly spaced in time. Those on the Apple silicon chips are grouped in pairs or, for loops 14-16 on the M4 Pro, in a triplet, where two or three loops are assigned the same time in the log.

Looking at time differences, a clear pattern emerges, that log times are incremented in steps of 954 ns. For the iMac Pro, each loop occurs one step later, while for the M3 Pro and M4 Pro steps between pairs and triplets are also 954 ns. In a few cases, the step difference is slightly greater at around 1192 ns instead of 954 ns. Apple silicon chips are faster on average because each step includes two or more loops, while the iMac Pro only manages one loop per step.

Explanation

If log entry times had been given in microseconds rather than nanoseconds, the same patterns would have been seen. But without the additional precision in their times, it wouldn’t have been clear that multiple log entries were being written with identical times, down to the nanosecond.

One likely explanation is that macOS only writes log entries approximately every microsecond, and the entry time recorded for each is that of that writing. Writing log entries must occur asynchronously for the M3 Pro and M4 Pro to be able to send pairs or triplets to be written, rather than having to wait for each writing process to complete.

Thus, the time resolution of log entries is approximately 1 μs, or 954 or 1192 ns to be more precise, and that’s the same regardless of whether macOS is running on a recent Intel Mac or the latest Apple silicon chips. Although a time resolution of 1 μs is sufficient for general purposes, if you want to dig deeper, as I have done here, access to the finer resolution provided in nanosecond times is essential.

Conclusions

  • Times written in log entries are incremented every 954 or 1192 ns on both Intel and Apple silicon.
  • Faster Apple silicon chips can write more than one entry in the same time increment.
  • Although expressing the time of log entries in microseconds is sufficient for general purposes, using nanoseconds can confirm which have occurred simultaneously.

The Music of Time

So what is a Dance to the Music of Time? It’s one of Nicolas Poussin’s most brilliant paintings, that inspired a series of twelve novels written by Anthony Powell. While I wouldn’t attempt to summarise those, here’s the painting to enjoy.

poussindancemusictime
Nicolas Poussin (1594–1665), A Dance to the Music of Time (c 1634-6), oil on canvas, 82.5 × 104 cm, The Wallace Collection, London. Wikimedia Commons.

Poussin’s Dance to the Music of Time (c 1634-6) shows four young people dancing, who are currently believed to be Poverty (male at the back, facing away), Labour (closest to Time and looking at him), Wealth (in golden skirt and sandals, also looking at Time), and Pleasure (blue and red clothes) who fixes the viewer with a very knowing smile. Opposite Pleasure is a small herm of Janus, whose two faces look to the past and the future. Father Time at the right is playing his lyre to provide the music, and an infant seated by him holds a sandglass, to measure time periods. Above them in the heavens, Aurora (goddess of the dawn) precedes Apollo’s sun chariot, on which the large ring represents the Zodiac, thus the passage of the months. Behind the chariot are the Horai, the four seasons of the year.

Resolve time better in LogUI build 48

By: hoakley
19 May 2025 at 14:30

Macs keep time exceedingly precisely, although there are important differences here between Intel and Apple silicon models. Their most precise record of time is given in Mach Absolute Time (MAT), the number of ‘ticks’ since an arbitrary start. In Intel Macs, each tick occurs every nanosecond (10^-9 second), providing high resolution, but ticks are less frequent in Apple silicon chips, occurring once every 41.666… nanoseconds instead.

You can test this on an Apple silicon Mac using my free utility Mints, which has a convenient button to display Mach timebase information. Run Mints in native mode, the default as it’s a Universal App, and it reports the Arm values; force it to launch as an app translated by Rosetta, and it will give the same timebase as an Intel Mac.

time01

time02

Although entries in the log are recorded in MAT’s high resolution, most methods of accessing log entries lose much of that precision. The datestamps provided by the log show command, for example, round time to the nearest microsecond (10^-6 second), although it’s capable of delivering a field in MAT’s nanoseconds. The bundled Console app is similar, but lacks any option to display MAT. My Ulbow app can display both datestamps with microseconds and MAT, but until now LogUI has been limited to displaying only time in milliseconds (10^-3 second).

To put these into context, in each microsecond the P core in the CPU of an M4 chip can complete execution of over 1,000 instructions, and in a millisecond that becomes more than a million instructions.

LogUI has been constrained by the formatted output available using Date and DateFormatter in the macOS API, which appear to be incapable of generating time any more precisely than milliseconds. After several earlier unsuccessful attempts, I’m now able to obtain time in nanoseconds, using Calendar and DateComponents in the API. For example, to obtain a formatted string containing the nanosecond component of a Date variable named date:
let nanoSeconds = Calendar.current.component(.nanosecond, from: date)
let nsTime = String(format: "%09d", nanoSeconds)

The string nsTime can then be spliced into a formatted date string.

I have incorporated this into a new build of LogUI, which also has extended information about the log display in its Help book. LogUI 1.0 build 48 is now available from here: logui148
and from its Product Page.

Tomorrow I’ll consider whether these more precise times are of use, and what we can learn from them.

Use Writing Tools to understand the log in LogUI build 46

By: hoakley
12 May 2025 at 14:30

I’ll be the first to admit that I’m sceptical of AI, and seldom use ChatGPT or Writing Tools. One of the conditions imposed by the magazines that I write for is that none of my contributions use AI in any way, either to research topics or to manipulate the text. Besides, I’d far rather all the mistakes I make are mine, and not introduced by AI. But I am impressed by Writing Tools, and think you might find them useful when trying to extract information from and understand the log, when using LogUI.

I’m therefore delighted to announce a new build of LogUI that gives access to summarisation features in Writing Tools to analyse and clarify log entries.

Most obviously, this build reworks the interface by promoting the button commands to its toolbar. From the left, these are:

  • Get log extract (circular arrows).
  • Gloss (magnifying glass on document page).
  • Save as Rich Text (downward arrow and box).

Following those is the popup menu to select the field to be used for search, and at the right end the Search box itself.

A more subtle change is aimed at making it less confusing when using multiple windows and search criteria on log extracts from the same time. The title shown in each window now appends any search term in use. That’s shown in the window, and in the Window menu, but isn’t included in the default file name if you save the extract in Rich Text.

Currently, LogUI supports two ways of moving log extracts to other apps: you can save the whole extract as a formatted Rich Text file, or copy the most important fields in selected entries as text. Build 46 adds a third as the bridge to Writing Tools, in what I term a gloss. This is generally used for words added to a text, either in between its lines or in the margin, that explain words in the main body of that text, and can be gathered together in a glossary. In LogUI this functions as a special pasteboard and plain text editor, the Gloss window.

The message field of some log entries is very large, containing a lot of information some of which may be of importance. The example I use here gives the registration details of an app being launched through RunningBoard. To copy those into a Gloss, select that log entry and click on the Gloss button in the toolbar. The contents of the selected message field(s) are then displayed in the Gloss window, a basic text editor, where you can manually format them if you wish, copy and paste the contents into a text document in another app. This works fully on all Macs running macOS 14.6 or later.

As this window contains a SwiftUI TextEditor view, you can also use Writing Tools on any selection you make within its text (Apple silicon Macs only). Select all using Command-A, hover the pointer over the selected text and you’ll see the Writing Tools blue button appear to the left of the window. Click on that, or use the contextual menu on the selected text, and you can now apply any of those Writing Tools to the contents.

If you try to use a Writing Tool that can’t cope with the text contents, you’ll see the error Writing Tools Unavailable.

In that case, open the contextual menu and try a different tool that can cope. Some log messages don’t contain much English, and in those cases Writing Tools might try another language such as German before reporting that the language isn’t recognised or supported.

Text in that Gloss window persists, and is saved into LogUI’s preferences. Only one Gloss window is available, and it contains the text from those entries selected the last time you clicked on the Gloss button. When you do that, the new gloss replaces the old. If you want to save the previous gloss, copy and paste it before clicking on the Gloss button.

Writing Tools can’t interpret log entries or diagnose problems from the log, but its summaries can make complex lists more comprehensible, and sometimes provide additional information that can be helpful. Always check its output against the original, as it can make mistakes.

To illustrate how this might help, here are examples taken from an original RunningBoard registration for the Pages app, starting
'app<application.com.apple.iWork.Pages.6871230.6871236(501)>' Constructed job description:
<dictionary: 0x910d117a0> { count = 23, transaction: 0, voucher = 0x0, contents =
"Platform" => <int64: 0x94ab1b6b9872cd77>: 1
"ProcessType" => <string: 0x910c2dc20> { length = 3, contents = "App" }
"EnableTransactions" => <bool: 0x1fb02aba0>: false

and running on for many more lines after those.

Writing Tools’ Summary reads:
The job description for the Pages application is provided. It includes details about the platform, process type, environment variables, and more. The description also specifies the program arguments and the path to the Pages executable.

Key Points start:

  • Application Name: com.apple.iWork.Pages
  • Application Bundle Identifier: com.apple.iWork.Pages
  • Application Platform: 1
  • Session Types: “Aqua”, “LoginWindow”
  • Initial Task Role: 2
  • Environment Variables: Includes variables like `__CF_USER_TEXT_ENCODING`, `TMPDIR`, `SHELL`, `HOME`, `SSH_AUTH_SOCK`, `LOGNAME`.
  • User Information: USER: hoakley, HOME: /Users/hoakley

List starts:

  • The job description is for a constructed application.
  • The application is identified as ‘app<application.com.apple.iWork.Pages.6871230.6871236(501)>’.
  • The application is running on a platform with an ID of 1.
  • The process type is “App”.
  • Transactions are not enabled for the application.
  • The application is managed by “com.apple.runningboard”.
  • The bundle identifier for the application is “com.apple.iWork.Pages”.
  • The application is part of a resource coalition with an ID of “app<application.com.apple.iWork.Pages.6871230.6871236(501)>”.
  • Pointer authentication is disabled for the application.

I think the first item there is a minor misreading, as I would understand that the text reports that a job description has been constructed for the application, not that the application is somehow constructed. However, other items listed appear to be faithful to the contents of the original message.

In some cases I have seen Writing Tools spell out an abbreviation, and in most it lays out the contents of long lists and dictionaries more accessibly. Try it out and see what you think.

LogUI 1.0 build 46 is now available from here: logui146
and from its Product Page.

Enjoy!

Reducing noise by searching LogUI’s views

By: hoakley
8 May 2025 at 14:30

App architectures can enable or constrain. My previous log browsers, Consolation and Ulbow, have been document-based, so each window represents a different log extract. As there seemed little reason to open two document windows on a single extract from the log, I’ve normally tweaked and fiddled with each window to display the entries I want to browse, an inevitable compromise that usually leaves me scrolling through thousands of entries.

LogUI is lighter in weight and not based on documents, merely windows and views. Although they’re backed by the data of a log extract, there’s no reason that you shouldn’t open several windows on the same data if that makes analysis easier. And it does, particularly when used with Search. Here’s an example.

Over the last couple of weeks, I’ve been looking at many log extracts covering app launch, one of the busiest events in macOS. In the first few seconds following a double-click to run an app, the log can accumulate well over 25,000 entries. No matter how experienced you are in dealing with them, or how efficiently you can look for milestones marking progress, it can take hours to read the launch process thoroughly in the log. This is partly because of the sheer volume of entries, but most of all because so many subsystems in macOS are involved. Any given period of tens of milliseconds can have long volleys in TCC, interspersed with a series of RunningBoard assertions, and occasional progress reports from LaunchServices. They’ll suddenly be interrupted by DAS dispatching an unrelated background activity, Wi-Fi updates, and other everyday tasks.

Try this instead.

Watch your Mac’s clock until it’s about to change minute. Just as the seconds count changes to 00, double-click the app and take your hands away from mouse/trackpad and keyboard. Once the clock reaches 10 seconds, assuming the app launch is complete, quit that app and start up LogUI. Its new window should show the time when you launched the app, so ensure its time period is correct, say anything between 5 and 30 seconds, and allow it to collect up to the 5-10 seconds required. When that’s ready, click on its Get Log button and check that it contains all the entries you require. Open second, third, fourth, and fifth windows likewise, with the same start times and settings. Once they’re all open, Get Log in each of them, and check that the number of entries is identical, indicating that each has the same log entries.

In my case, launching Pages brought over 40,000 log entries in the first 5 seconds, ending with the app running and prompting me to open a document. Even if you speed-read those at one entry every second, that would take you nearly 12 hours to work through. Let’s reduce that to a few minutes to see each of its important phases.

In a second window, type sendaction: into the Search box at the top right and press Return. That will find all entries marking the clicks/taps you triggered the launch with.

There are two paired entries 0.237 seconds apart, representing my double-tap. So we now know that we can safely ignore everything in the log that happened before the time 11:55:00.767. Leave that window open with just those entries showing, as a reminder.

Normally the first subsystem on the scene to handle those actions is LaunchServices. In the third window, change the search field 🔍 popup menu to Subsystems, type launchservices in the Search box, and press Return.

Sure enough, from a time of 00.779 seconds, only 0.012 seconds after the second tap, LaunchServices reports that it’s getting ready to launch Pages through RunningBoard, and constructs its EnvironmentDictionary for the purpose. This is the cue to move onto the fourth window, where we set the popup menu to Subsystems again, enter runningboard into the Search box, and press Return.

This details the launch request being handled by RunningBoard, which a few moments later constructs and reports an extensive job description for Pages. Another early arrival during the launch process is TCC to check the new app’s privacy access. In the fifth window, set Subsystems again, and search on tcc.

So far we’ve ignored another group of subsystems and processes that are more interesting than the drudgery of TCC, security. For a first look at how this is checked, switch the popup to Processes and search for amfid, the service for Apple Mobile File Integrity (AMFI) checking.

This shows its check entering first the path to the main executable at 00.805 seconds, two immediate security checks being called, then entering the path for one of the frameworks inside the Pages app bundle 0.05 seconds later. We can readily trace each framework check through the rest of the launch process in this view.

A more general summary of security checking milestones is provided by com.apple.syspolicy.exec, so switch the popup to Subsystems, and search on syspolicy.

This reveals how syspolicyd called a Gatekeeper process assessment on Pages, showed that the code had already been evaluated (despite it not having been run previously in this session), and allowed the launch to proceed without any further checks like an XProtect scan being made.

Finally, for a permanent record of this launch, in any of the windows click on the Save button to write all 41,712 log entries to a Rich Text file. That turned out to be 9.3 MB in size, so I’m glad I’m not still trawling through that trying to make sense of its contents.

To do this, LogUI will require a little more memory: in this case, working with five windows, each with over 40,000 log entries, LogUI took 520 MB. I think the convenience is worth that small cost, but I am going to return to Xcode to see if that can’t be managed better and reduced in size.

At the moment, LogUI doesn’t use any tricks to share a single log extract across different windows, and I’m wondering why and how that could be improved. One approach might be to obtain a single log extract as a Project, from which you can open individual windows for different searches. Although that shouldn’t be difficult to code, it would make LogUI more complex to use, and limit the flexibility of each window. Please try this out yourselves and let me know what you think. For now, I’m surprised at how useful search can be across several open windows.

Search your log extracts with LogUI build 42

By: hoakley
6 May 2025 at 14:30

The biggest shortcoming of the first versions of LogUI has been their lack of search or find. If you needed to look for anything, the best approach was to save an extract as Rich Text and search through that. I’m delighted to offer a solution in LogUI 1.0 build 42.

Ulbow already supports Find for its log extracts, but that just searches the text contents regardless of which field the text appears in. Sometimes that works well, but if you want to look for specific field entries it’s highly inefficient. When working with its Lists, SwiftUI is different, and its regular Search doesn’t just find text, but only displays those items in the list that contain the text in a given field.

What I’ve attempted to do here is follow the SwiftUI model, so search returns only those log entries that contain the search text, but provide support for searching four different fields:

  • the contents of the message,
  • the process name responsible,
  • the name of the sender,
  • the subsystem.

All searches are performed as case-insensitive for simplicity and speed.

The best way to see how this works is to try it.

Here I’ve simply obtained a 20 second period of recent log, with Max entries set to accommodate them, a total of over 13,000 entries, far too many to search through manually.

I left the search field, in the popup menu next to the magnifying glass symbol 🔍, set to Messages, then typed cfprefsd into the search box at the top right, and pressed the Return key to initiate the search. This returns all the entries in that log excerpt that contain the text cfprefsd in their message field.

SwiftUI can perform live incremental search, so that as you type characters into the search box, the search is performed. While that can be impressive on shorter lists, and minimises the number of characters you have to type in for a useful result, it’s costly when performed on large lists, and with log extracts can be quite distracting. If you want to see live search in action, I’ve used that in AppexIndexer, where it only has to deal with a few hundred rows at a time.

I then select one of those.

Having found all those entries containing cfprefsd in the messages, I switch the popup menu to Processes and press the Return key again. If the search box has lost the focus, the Mac will ‘beep’, so all you do is click on the search box and press Return to perform the search.

Now the window contains all those log entries containing cfprefsd in their process name. Note how the previously selected entry is still highlighted: selections in log entries should be preserved throughout searches.

For the final search I set the popup menu to Senders, and press the Return key.

This time there are no log entries that meet the search criterion, and the window informs you of that.

To return to the full log extract at any time, empty the search box, perhaps using its 🅧 tool, and press Return.

When you save a log extract in the midst of searching, the file should still contain the whole of the extract, not just what’s currently visible in the search. You should be able to copy only selected entries from a search, though.

As it stands, LogUI now has a toolbar containing the search box and two rows of settings and controls below that. I’m open to suggestions as to how that could be changed or reordered, ideally to reduce it to two rows without increasing minimum window width.

LogUI 1.0 build 42 is now available from here: logui142
and from its Product Page.

I hope you find it useful and more productive.

A primer on predicates for LogUI

By: hoakley
3 April 2025 at 14:30

All good log browsers provide tools to narrow down the log entries they display. Without those, it would be easy to waste all day wandering through tens of thousands of entries. One common tool provided by macOS, directly and in the log command tool, is filtering using predicates. Although LogUI provides easy access to simple predicates, to get the best from them, it’s worth digging a little deeper, as I do here.

Instant predicates

LogUI’s instant predicates filter log entries according to any of four basic predicate types:

  • subsystem, such as com.apple.sharing, the field shown in yellow in log extracts;
  • eventMessage, the text message listed in white/black at the end of each entry;
  • processImagePath, such as mediaanalysisd, shown in blue, the name of the process making that entry;
  • senderImagePath, such as libxpc.dylib, shown in red, the name of the process sending that entry.

These are quick to enter in the text box to the right of the popup menu in the window’s toolbar, but in many circumstances can prove too broad, and need narrowing down further. In other situations, you want to browse entries from two subsystems, or using a combination of criteria. The best way to do that is to write a short predicate. For single use, you can do that in the one-off predicate editor using the Set button.

When you want to reuse that, you can add it to the predicate popup menu using Settings Predicate (currently a bit kludgy).

Predicates

macOS can use predicates in other situations, most commonly for Spotlight search. If you’re interested in those, see Apple’s Predicate Programming Guide. Here I’ll describe predicates as they’re more commonly used to filter log entries, as they’re usually much simpler.

Each simple predicate consist of three parts:

  • the name of one of the fields in a log entry, such as subsystem or eventMessage. This sets where the filter looks in each entry;
  • an operator, which might be == for ‘equals’ exactly, or for text is commonly CONTAINS[c] for case-insensitive contains;
  • text or a numeric value to look for, such as “error” or 513. Only those entries equalling or containing (or whatever the operator means) this in the specified field will then be returned from the log and displayed.

Here are some basic examples.

eventMessage CONTAINS[c] "error"
entries will only be those with the text error in their message field.

subsystem == "com.apple.duetactivityscheduler"
entries will all have that text, ignoring case, but only that text, as the name of their subsystem.

subsystem CONTAINS[c] "com.apple.xpc"
entries will have any subsystem containing that text, which also includes com.apple.xpc.activity.

Fields

Although you can use any of the fields shown in LogUI (and some that aren’t), the most commonly used are, in order as they are shown in LogUI’s window:

  • eventType (red) – matches the type of event, such as logEvent (1024), traceEvent (768), activityCreateEvent (513), or activityTransitionEvent (514). Can be given as characters (case-sensitive) without quotation marks, or using the digits given in parentheses. Use these only with the operators == or !=, as they are treated as numbers rather than text.
  • category (green) – this matches the category, and varies according to subsystem. This is given as text in quotation marks, and is normally lower-case.
  • messageType (white/black) – matches the type of message for logEvent and traceEvent, and includes default (0), release (0), info (1), debug (2), error (16), and fault (17). Can be given as characters (case-sensitive) without quotation marks, or digits as shown in parentheses. Use these only with the operators == or !=, as they are treated as numbers rather than text.
  • senderImagePath (red) – this matches the text pattern in the name of the sender, which might be the name of a library, extension, or executable.
  • processImagePath (blue) – this matches the text pattern in the name of the process that originated the event.
  • subsystem (yellow) – this matches the subsystem specifier, e.g. com.apple.TimeMachine, given as text in quotation marks. You may find it best to use CONTAINS[c] rather than ==, to allow for differences in case and extended subsystem specifiers.
  • eventMessage (white/black) – for this, you specify a text pattern, or text, within the message, given as text in quotation marks.

Operators

The following comparisons and other operators are available:

  • == (two equals signs) for equality
  • != or <> for inequality
  • >= or => for greater than or equal to
  • <= or =< for less than or equal to
  • > for greater than
  • < for less than
  • AND or && for logical and
  • OR or || for logical or
  • NOT or ! for logical not
  • BEGINSWITH, CONTAINS, ENDSWITH, LIKE, MATCHES for string comparisons, using regex expressions when desired; strings can be compared with case insensitivity and diacritic insensitivity by appending [cd] to the operator, e.g. CONTAINS[c] means case-insensitive comparison
  • FALSE, TRUE, NULL have their expected literal meanings.

There are others as well, but you’ll seldom use them to filter log entries.

Building complex predicates

To see the scheduling and dispatch of background activities by DAS-CTS, you need to look at log extracts showing both their entries. Use the predicate
subsystem == "com.apple.duetactivityscheduler" OR subsystem CONTAINS "com.apple.xpc"
to do that. The first part of it includes those entries from DAS, and the second includes those for XPC and its relatives that run CTS. Using an OR between the two parts combines both sets of entries in the one extract.

To see the reports posted by XProtect Remediator, you need to look at those entries made by its subsystem that have the right category, using the predicate
subsystem == "com.apple.XProtectFramework.PluginAPI" AND category == "XPEvent.structured"
Using the AND operator ensures that the only entries shown come from that one subsystem, and they are given just that category.

Time Machine involves a combination of different subsystems and messages. To get a good overview of relevant entries, you can use
subsystem == "com.apple.TimeMachine" OR
(subsystem == "com.apple.duetactivityscheduler" AND eventMessage CONTAINS[c] "Rescoring all") OR
(subsystem == "com.apple.xpc.activity" AND eventMessage CONTAINS[c] "com.apple.backupd-auto") OR
eventMessage CONTAINS[c] "backup" OR
eventMessage CONTAINS[c] "Time Machine" OR eventMessage CONTAINS[c] "TimeMachine"

I’ve broken this down into separate lines, but you shouldn’t do that in the predicate. Taking it line by line it becomes simpler to understand. Use parentheses () to group each part of the predicate carefully as shown.

You can see other examples in the Help book for my free utility Mints: the Further Information pages towards the end give each of the predicates that Mints uses for its log extracts.

Quick summary

  • [field name] [operator] [text or numeric value]
  • common field names: senderImagePath, processImagePath, subsystem, eventMessage
  • common operators: ==, CONTAINS[c]
  • filter info: “text”
  • combine filters using AND, OR.

LogUI build 37 now has more power for browsing the log

By: hoakley
1 April 2025 at 14:30

By anyone’s standards, the macOS log contains a great many entries, and being able to filter out the noise is essential. This is accomplished by applying predicates to determine which entries are extracted and shown in a log browser like LogUI. However, using predicates requires knowledge about the log and its entries, and forms the greatest barrier for most users. This new version of LogUI improves features to help you use predicates to make the log more accessible.

This all happens in the toolbar of its browser window.

The section at the left of the lower row of tools now provides two methods to apply your own predicates: a one-off predicate editor, and an editor for custom entries in its popup menu.

One-off predicates

Click on the Set button to open the one-off predicate editor.

Here you can compose and paste in your own custom predicates that will extract only the log entries that you’re interested in. In this example, only entries whose subsystem is com.apple.duetactivityscheduler, or contains com.apple.xpc, will be gathered and displayed. Those tell you what’s going on with DAS and CTS scheduling and dispatch of background activities.

LogUI keeps that one-off predicate, even after a restart, as it’s automatically written to its preference file.

Once you’ve clicked Save, selecting the [ … ] item in the predicate menu will apply that predicate to each log extract you obtain.

There’s also an additional standard predicate using the senderImagePath.

Custom menu predicates

Predicates listed in that menu below blowhole are custom predicates saved to LogUI’s preferences using its new Predicate tab in its Settings. This editor is very basic at the moment, and its use a little awkward. This is because SwiftUI much prefers menu contents to be static, so adding items to the predicate menu doesn’t go down too well. This editor allows you to add one predicate at a time, in plain text format.

Click on the Append button here and there’ll be a new predicate named XProtect Remediator with the predicate shown. You can only add one new predicate, then need to quit the app before adding another. I’m sorry that’s so laborious, but once you have set up your custom predicates you can return to using LogUI fully.

The Settings General pane now contains a button to Reset Predicates back to their defaults.

Predicates

A basic predicate is composed of a log field name, like subsystem, followed by an operator such as == (equals) or CONTAINS[c] (case-insensitive contains), and a filter term, usually a string like "com.apple.xpc". So the predicate
subsystem CONTAINS[c] "com.apple.xpc"
will return all log entries with their subsystem containing the text com.apple.xpc. You can combine those basic elements into a more selective predicate using combinators such as AND and OR, so
subsystem == "com.apple.duetactivityscheduler" OR subsystem CONTAINS|c] "com.apple.xpc"
returns entries with a subsystem of precisely com.apple.duetactivityscheduler together with those whose subsystem contains the text com.apple.xpc.

Some years ago I wrote a primer here, and you’ll find some useful predicates in the Further Information section in the Help book for Mints. I’ll be writing more here to help you get the best out of LogUI.

There are a couple of oddities with predicates. SwiftUI tends to like using typographic double-quotation marks, but the macOS predicate builder doesn’t accept them as a substitute for straight marks. So LogUI changes all styled marks to straight ones automatically for you, to ensure those shouldn’t cause a problem. However, when it encounters errors it can behave erratically; while I’m trying to make this more robust, I apologise in advance if using a broken predicate upsets LogUI. It’s worth being careful to check your predicates before trying to use them.

LogUI version 1.0 build 37 is now available from here: logui137

My next task is to improve editing and saving predicates to its preferences, to make them accessible as menu customisations.

预言在应验:五年前所讨论的未来人机交互的新范式_6.ylog

By: Steven
16 June 2024 at 22:58

从 2024 年的今天,回望 2019 年的 Apple 和 Ive 团队,我们会发现有些变化和趋势似乎是早已注定的。在过往的观察和分析中,我们所预言的事情正在成为现实和主流。常言道以史为镜可以知兴替,今天再看当时的 Apple 和 Ive 团队,关于产品的演进思路和设计策略的变化都早有端倪,也能预见在 AI 席卷的浪潮下,Apple 将会如何应对。

在这一期,你会听到:

—- 二十年前的专利文件:通体透光的 iPhone

—- 国产厂商和 Apple 在设计上的差异

—- 成功的设计:AirPods 只是剪掉线的 EarPods

—- 塑料手机的设计巅峰:iPhone 5c

—- 刘海与机器视觉:早早布局的 AI 伏笔

—- 未来十年的人机交互:人和人之间怎么交互?

—- 设计策略上的「S型曲线」体现在哪里?

—- 产品路径上迷路的 iPad

—- 光洁的划痕:是矫情还是哲学?

—- 史上最佳手机壳:iPhone 5c 的多彩硅胶壳

—- 拟物化的残党,现在理解扁平化的先进性了吗?

|相关图片|

首款 Unibody 设计于 2008 年 10 月发布
截图来自:Designed by Apple in California

查看更多图片和设计讨论:Mac Pro 2019

|拓展阅读|

如何评价 iPhone X 的工业设计?

交互的王,时代的狂!万字详解灵动岛的今生来世!

十年轮回?经典进化!工业设计师深入解读 iPhone12!

从技术寿命 S 曲线,看阳极氧化铝的设计

抽象的产品,用户「界面」的设计

如何看待 Evans Hankey 从 Apple 设计团队离职?

注定会离职的 Jonathan Ive 和科技产品的设计趋势

|登场人物|

苏志斌:工业设计师,车联网智能硬件产品经理 / 联创,《设以观复》作者

王汉洋:AI 行业从业者,多档播客主播,《拯救东北1910》《山有虎》作者

|相关链接|

若你所使用的播客客户端未能完整显示插图,或遇网络问题未能正常播放,请访问:

荒野楼阁 WildloG 的地址:https://suithink.me/zlink/podcast/

阅读设计相关的各类文章:https://suithink.me/zlink/idea/

|其他社交网络媒体|

苏志斌 @ 知乎|SUiTHiNK @ 即刻 / 微博

苏志斌SUiTHiNK @ Bilibili / YouTube / 小红书

|联络邮箱|

suithink.su@gmail.com

欢迎在 小宇宙、Spotify、YouTube、Apple Podcast 收听本节目,期待你的留言。

❌
❌