Skip to content

macOS: Route service logs to Unified Logging instead of unmanaged files #8564

Description

@danieloleary

Prerequisites

  • I checked the Wiki and Discussions and found no answer
  • I searched existing issues and found no duplicate
  • This is an enhancement proposal, not a support question

Thanks for building AdGuard Home. I use it on my Mac and noticed these service
logs growing outside the normal rotation settings.

Plain English

The Mac service currently writes two log files that AdGuard Home does not
manage. They can keep getting bigger even when AdGuard's own log limits are
configured.

The proposed fix stops creating those files and sends the same messages to the
Mac's built-in logging system instead. A small helper carries the messages. If
that helper crashes, AdGuard Home stays running and starts another one.

The open question is whether AdGuard wants to rely on that helper or require a
deeper native macOS integration.

Problem

The generated macOS LaunchDaemon sends stdout and stderr to:

  • /var/log/AdGuardHome.stdout.log
  • /var/log/AdGuardHome.stderr.log

launchd appends to them, but AdGuard Home does not rotate them. They can grow
independently of log.max_size, log.max_backups, and log.max_age.

Proposed patch

I have a clean local patch that:

  1. Removes both file destinations from the generated plist.
  2. Routes default macOS service output to Unified Logging through one
    supervised /usr/bin/logger process.
  3. Preserves explicit YAML and --logfile destinations.
  4. Restarts the helper with bounded exponential backoff if it exits.
  5. Removes the two old files after successful service install or uninstall.

Existing installations would need to reinstall the service. A binary-only
update does not rewrite the installed plist.

I have not pushed the branch or opened a PR because the contribution guide asks
for design approval first.

Proof

On macOS ARM64, the real bridge delivered application logs, startup failures,
stdout/stderr, and a complete panic stack to Unified Logging.

I killed the real helper four times. Replacements appeared after 1.030, 2.067,
4.086, and 8.077 seconds. A new marker reached Unified Logging after every
restart. The test process had 11 open file descriptors before and after.

Also passed:

  • make go-check
  • make go-os-check
  • 100 race-enabled repetitions of the supervision and service cleanup tests
  • Darwin AMD64 and native cgo-disabled compilation
  • explicit file logging regression coverage

I did not modify the installed AdGuard Home service, DNS listener, or live
configuration.

Maintainer decision

Is the /usr/bin/logger bridge acceptable with these known tradeoffs?

  • Records appear as process /usr/bin/logger, without a native AdGuard Home
    subsystem or category.
  • Messages enter the system-wide Unified Logging store and use OS-managed
    retention.
  • The bridge cannot apply native os_log privacy annotations to individual
    fields.
  • Startup output and panic text can exist before YAML logging settings are
    available.
  • AdGuard Home ignores SIGPIPE after starting the bridge so helper death does
    not terminate the service.
  • A helper that stays alive but stops reading can eventually block the
    synchronous pipe.

If native identity or per-field privacy is required, the tested service and
migration work can remain while the sink is replaced with a native macOS
adapter.

After design approval, I can run the complete install, update, reinstall, and
uninstall matrix on a disposable Mac or VM before opening the PR.

Apple references:

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions