Can I have launchctl output stdout/stderr from my application to the system-wide Unified Logging mechanism?
You could use blowhole
.
blowhole
is a command-line tool that takes a string as an argument and sends it to the unified logging system. It is supported from Sierra (10.12) up to Catalina (10.15).
How to use it
(Tested on macOS Catalina 10.15.5)
Modify the ProgramArguments
array in your .plist
file like this:
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>COMMAND OPTIONS 2> >(while read; do /path/to/blowhole -e "$REPLY"; done) | while read; do /path/to/blowhole -d "$REPLY"; done</string>
where COMMAND OPTIONS
is the command you want to execute, followed by any desired options.
Here, I make use of bash
's support for redirection (2>
), process substitution (>()
) and pipelines (|
) to:
- sort out the command's standard error and standard output
-
process them separately inside two
while
loops. The firstwhile
loop runsblowhole -e
to log standard error with an "Error" level:<timestamp> Error <info> blowhole: [co.eclecticlight.blowhole:general] Blowhole: STANDARD ERROR MESSAGES
and the second one runs
blowhole -d
to log standard output with a "Default" level:<timestamp> Default <info> blowhole: [co.eclecticlight.blowhole:general] Blowhole: STANDARD OUTPUT MESSAGES
(Since
blowhole
can't read from standard input, we needwhile
loops to feed it a line of input at a time.)
The blowhole: [co.eclecticlight.blowhole:general] Blowhole:
string is not configurable, but you can add a prefix of your choice to the logged messages. For example, since you mention offlineimap
in your question:
/path/to/blowhole -d "offlineimap stdout: $REPLY";
and:
/path/to/blowhole -e "offlineimap stderr: $REPLY";
You can read log entries with sudo log show | grep blowhole:general
or sudo log show | grep offlineimap
, if you added the customized prefix. To read log entries as they are generated, in a manner similar to tail -f
, use show stream
instead.
Alternatively, you can wrap the command you want to execute in a shell script so that blowhole
logs the command's standard output and error in a way similar to above. This is convenient if you want to run some code prior to executing the actual command:
#!/bin/bash
# Add the code you want to execute prior to the actual command here
COMMAND ARGUMENTS \
> >(
while read; do
/path/to/blowhole -d "$REPLY";
done) \
2> >(
while read; do
/path/to/blowhole -e "$REPLY";
done)
You can then configure the ProgramArguments
array of your .plist
file to run the script instead of your command:
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>/path/to/script.sh</string>
</array>
Where to get it from
You can download blowhole
from its project page or directly from here. The program is provided as a signed, hardened and notarized executable (as required by Catalina) and as an Installer package.