Skip to content

Conversation

@sairon
Copy link
Member

@sairon sairon commented Jul 9, 2025

Proposed change

Use JournaldLogHandler from logging-journald [1] to log events from Python logging to Systemd journal directly. This makes it possible to better filter the log messages based on priority and persist more useful metadata (like filename and line number) for the log events in the Journal. The Journal log priority is also distingushed based on different log priorities of logging events.

Another advantage is that stack traces are saved as a single log event rather than multiple separated lines printed to container stdout/stderr. The coloring isn't done using ANSI escape sequences from the ColoredFormatter anymore, resulting in plaintext MESSAGE fields (instead of byte arrays). Colors are added based on the priority metadata when the events are read from the Systemd Journal Gatewayd to maintain backwards compatibility.

To correctly match non-Python error logs, log entries must be written to the Journal with the same SYSLOG_IDENTIFIER field as the logs coming form the container directly (e.g. from s6, bashio, etc.). To obtain the Python logs through the docker logs command, container ID (obtained though a hacky method currently) is added to the log records as well - this is useful to keep the falllback method for getting the logs working.

[1] https://github.com/mosquito/logging-journald/
[2] https://systemd.io/JOURNAL_NATIVE_PROTOCOL/

Type of change

  • Dependency upgrade
  • Bugfix (non-breaking change which fixes an issue)
  • New feature (which adds functionality to the supervisor)
  • Breaking change (fix/feature causing existing functionality to break)
  • Code quality improvements to existing code or addition of tests

Additional information

Checklist

  • The code change is tested and works locally.
  • Local tests pass. Your PR cannot be merged unless tests pass
  • There is no commented out code in this PR.
  • I have followed the development checklist
  • The code has been formatted using Ruff (ruff format supervisor tests)
  • Tests have been added to verify that the new code works.

If API endpoints or add-on configuration are added/changed:

Use JournaldLogHandler from logging-journald [1] to log events from
Python logging to Systemd journal directly. This makes it possible to
better filter the log messages based on priority and persist more useful
metadata (like filename and line number) for the log events in the
Journal. The Journal log priority is also distingushed based on
different log priorities of logging events.

Another advantage is that stack traces are saved as a single log event
rather than multiple separated lines printed to container stdout/stderr.
The coloring isn't done using ANSI escape sequences from the
ColoredFormatter anymore, resulting in plaintext MESSAGE fields (instead
of byte arrays). Colors are added based on the priority metadata when
the events are read from the Systemd Journal Gatewayd to maintain
backwards compatibility.

To correctly match non-Python error logs, log entries must be written to
the Journal with the same SYSLOG_IDENTIFIER field as the logs coming
form the container directly (e.g. from s6, bashio, etc.). To obtain the
Python logs through the docker logs command, container ID (obtained
though a hacky method currently) is added to the log records as well -
this is useful to keep the falllback method for getting the logs
working.

[1] https://github.com/mosquito/logging-journald/
[2] https://systemd.io/JOURNAL_NATIVE_PROTOCOL/
@sairon sairon requested a review from agners July 9, 2025 13:09

def _entry_is_haos_log(entry: dict[str, str]) -> bool:
"""Check if the entry is a Home Assistant Operating System log entry."""
return entry.get("SYSLOG_FACILITY") == str(HAOSLogHandler.SYSLOG_FACILITY)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears that nothing else is using this facility in HAOS currently so it's fairly reliable indicator it's "our" logs.. Facility.LOCAL7 == 23 is the default in JournaldLogHandler as well but I defined it on the HAOSLogHandler class to be explicit and stable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I see this is syslog backward compatibility, and the numbers are from RFC5424, interesting.

It seems in my logs that quite some entries omit any SYSLOG_FACILITY. I wonder if we really want to use that as the means to identify our logs? 🤔

I see the library currently maps the standard logging priorities to the systemd-journald priority levels, maybe it would be better to store the Python logging level (which also can have custom levels) as a separate field (e.,g. PYTHON_LEVEL), and use that as a means to detect "our" logs? 🤔 I guess it would involve some changes to the library, but could be a good way to test how active that upstream project is.

self.listener = None


class HAOSLogHandler(JournaldLogHandler):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the Core should log through Systemd as well, I think it would be good to extract this to a shared Python package.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah make sense, it is fine by me to have this in Supervisor for the time being.

for line in f:
if b"/docker/containers/" in line:
container_id = line.split(b"/docker/containers/")[-1]
return str(container_id.split(b"/")[0])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've discussed it elsewhere, but this seems a bit brittle.

Using the Docker --cidfile and bind mount that into the container seems a better approach. I think we need to store it somewhere persistent, as we don't recreate containers on restart. E.g. /mnt/data/supervisor/cidfiles/${containername}. For now, I'd do it just for Supervisor and Core, eventually we could add it to all the containers if there is a need. Probably best is to create this as a separate PR.


def _entry_is_haos_log(entry: dict[str, str]) -> bool:
"""Check if the entry is a Home Assistant Operating System log entry."""
return entry.get("SYSLOG_FACILITY") == str(HAOSLogHandler.SYSLOG_FACILITY)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, I see this is syslog backward compatibility, and the numbers are from RFC5424, interesting.

It seems in my logs that quite some entries omit any SYSLOG_FACILITY. I wonder if we really want to use that as the means to identify our logs? 🤔

I see the library currently maps the standard logging priorities to the systemd-journald priority levels, maybe it would be better to store the Python logging level (which also can have custom levels) as a separate field (e.,g. PYTHON_LEVEL), and use that as a means to detect "our" logs? 🤔 I guess it would involve some changes to the library, but could be a good way to test how active that upstream project is.

self.listener = None


class HAOSLogHandler(JournaldLogHandler):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah make sense, it is fine by me to have this in Supervisor for the time being.

@github-actions
Copy link
Contributor

There hasn't been any activity on this pull request recently. This pull request has been automatically marked as stale because of that and will be closed if no further activity occurs within 7 days.
Thank you for your contributions.

@github-actions github-actions bot added the stale label Aug 15, 2025
@agners agners added no-stale Avoids issue going stale and removed stale labels Aug 15, 2025
sairon added a commit to home-assistant/operating-system that referenced this pull request Sep 2, 2025
Use the --cidfile Docker CLI argument when starting the container and
bind-mount the generated file containing full ID of the container to the
container itself.

Using --mount instead of --volume is needed, as --volume is racy and creates
empty directory volume at the destination path instead.

This is prerequisite for home-assistant/supervisor#6006 but can come handy for
other cases too.
sairon added a commit that referenced this pull request Sep 3, 2025
…un/cid

There is no standard way to get the container ID in the container
itself, which can be needed for instance for #6006. The usual pattern is
to use the --cidfile argument of Docker CLI and mount the generated file
to the container. However, this is feature of Docker CLI and we can't
use it when creating the containers via API. To get container ID to
implement native logging in e.g. Core as well, we need the help of the
Supervisor.

This change implements similar feature fully in Supervisor's DockerAPI
class that orchestrates lifetime of all containers managed by
Supervisor. The files are created in the SUPERVISOR_DATA directory, as
it needs to be persisted between reboots, just as the instances of
Docker containers are.

Supervisor's cidfile must be created when starting the Supervisor
itself, for that see home-assistant/operating-system#4276.
)
for profile in card.profile_list
if profile.available
if profile.is_available
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, this is probably side-effect of the IDE refactoring tool, shouldn't be here.

sairon added a commit that referenced this pull request Sep 5, 2025
…un/cid

There is no standard way to get the container ID in the container
itself, which can be needed for instance for #6006. The usual pattern is
to use the --cidfile argument of Docker CLI and mount the generated file
to the container. However, this is feature of Docker CLI and we can't
use it when creating the containers via API. To get container ID to
implement native logging in e.g. Core as well, we need the help of the
Supervisor.

This change implements similar feature fully in Supervisor's DockerAPI
class that orchestrates lifetime of all containers managed by
Supervisor. The files are created in the SUPERVISOR_DATA directory, as
it needs to be persisted between reboots, just as the instances of
Docker containers are.

Supervisor's cidfile must be created when starting the Supervisor
itself, for that see home-assistant/operating-system#4276.
agners pushed a commit that referenced this pull request Sep 9, 2025
…un/cid (#6154)

* Write cidfiles of Docker containers and mount them individually to /run/cid

There is no standard way to get the container ID in the container
itself, which can be needed for instance for #6006. The usual pattern is
to use the --cidfile argument of Docker CLI and mount the generated file
to the container. However, this is feature of Docker CLI and we can't
use it when creating the containers via API. To get container ID to
implement native logging in e.g. Core as well, we need the help of the
Supervisor.

This change implements similar feature fully in Supervisor's DockerAPI
class that orchestrates lifetime of all containers managed by
Supervisor. The files are created in the SUPERVISOR_DATA directory, as
it needs to be persisted between reboots, just as the instances of
Docker containers are.

Supervisor's cidfile must be created when starting the Supervisor
itself, for that see home-assistant/operating-system#4276.

* Address review comments, fix mounting of the cidfile
agners pushed a commit to home-assistant/operating-system that referenced this pull request Sep 9, 2025
Use the --cidfile Docker CLI argument when starting the container and
bind-mount the generated file containing full ID of the container to the
container itself.

Using --mount instead of --volume is needed, as --volume is racy and creates
empty directory volume at the destination path instead.

This is prerequisite for home-assistant/supervisor#6006 but can come handy for
other cases too.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking-change cla-signed new-feature A new feature no-stale Avoids issue going stale

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants