Add more context to Sentry reports early during startup (#5682)

* Initialize machine information before Sentry

* Set user and machine for all reports

Now that we initialize machine earlier we can report user and machine
for all events, even before Supervisor is completely initialized.

Also use the new tag format which is a dictionary.

Note that it seems that with the current Sentry SDK version the
AioHttpIntegration no longer sets the URL as a tag. So sanitation is
no longer reuqired.

* Update pytests
This commit is contained in:
Stefan Agner 2025-02-27 15:45:11 +01:00 committed by GitHub
parent 39f5b91f12
commit 0ad559adcd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 41 additions and 32 deletions

View File

@ -18,7 +18,6 @@ from .const import (
ENV_SUPERVISOR_MACHINE,
ENV_SUPERVISOR_NAME,
ENV_SUPERVISOR_SHARE,
MACHINE_ID,
SOCKET_DOCKER,
LogLevel,
UpdateChannel,
@ -81,6 +80,9 @@ async def initialize_coresys() -> CoreSys:
coresys.bus = Bus(coresys)
coresys.mounts = await MountManager(coresys).load_config()
# Set Machine/Host ID
await coresys.init_machine()
# diagnostics
if coresys.config.diagnostics:
init_sentry(coresys)
@ -88,10 +90,6 @@ async def initialize_coresys() -> CoreSys:
# bootstrap config
initialize_system(coresys)
# Set Machine/Host ID
if MACHINE_ID.exists():
coresys.machine_id = MACHINE_ID.read_text(encoding="utf-8").strip()
# Check if ENV is in development mode
if coresys.dev:
_LOGGER.warning("Environment variable 'SUPERVISOR_DEV' is set")
@ -105,16 +103,6 @@ async def initialize_coresys() -> CoreSys:
# Convert datetime
logging.Formatter.converter = lambda *args: coresys.now().timetuple()
# Set machine type
if os.environ.get(ENV_SUPERVISOR_MACHINE):
coresys.machine = os.environ[ENV_SUPERVISOR_MACHINE]
elif os.environ.get(ENV_HOMEASSISTANT_REPOSITORY):
coresys.machine = os.environ[ENV_HOMEASSISTANT_REPOSITORY][14:-14]
_LOGGER.warning(
"Missing SUPERVISOR_MACHINE environment variable. Fallback to deprecated extraction!"
)
_LOGGER.info("Setting up coresys for machine: %s", coresys.machine)
return coresys

View File

@ -15,7 +15,13 @@ from typing import TYPE_CHECKING, Any, Self, TypeVar
import aiohttp
from .config import CoreConfig
from .const import ENV_SUPERVISOR_DEV, SERVER_SOFTWARE
from .const import (
ENV_HOMEASSISTANT_REPOSITORY,
ENV_SUPERVISOR_DEV,
ENV_SUPERVISOR_MACHINE,
MACHINE_ID,
SERVER_SOFTWARE,
)
from .utils.dt import UTC, get_time_zone
if TYPE_CHECKING:
@ -107,6 +113,26 @@ class CoreSys:
await self.config.read_data()
return self
async def init_machine(self):
"""Initialize machine information."""
def _load_machine_id() -> str | None:
if MACHINE_ID.exists():
return MACHINE_ID.read_text(encoding="utf-8").strip()
return None
self.machine_id = await self.run_in_executor(_load_machine_id)
# Set machine type
if os.environ.get(ENV_SUPERVISOR_MACHINE):
self.machine = os.environ[ENV_SUPERVISOR_MACHINE]
elif os.environ.get(ENV_HOMEASSISTANT_REPOSITORY):
self.machine = os.environ[ENV_HOMEASSISTANT_REPOSITORY][14:-14]
_LOGGER.warning(
"Missing SUPERVISOR_MACHINE environment variable. Fallback to deprecated extraction!"
)
_LOGGER.info("Setting up coresys for machine: %s", self.machine)
@property
def dev(self) -> bool:
"""Return True if we run dev mode."""

View File

@ -35,6 +35,12 @@ def filter_data(coresys: CoreSys, event: dict, hint: dict) -> dict:
return None
event.setdefault("extra", {}).update({"os.environ": dict(os.environ)})
event.setdefault("user", {}).update({"id": coresys.machine_id})
event.setdefault("tags", {}).update(
{
"machine": coresys.machine,
}
)
# Not full startup - missing information
if coresys.core.state in (CoreState.INITIALIZE, CoreState.SETUP):
@ -47,7 +53,6 @@ def filter_data(coresys: CoreSys, event: dict, hint: dict) -> dict:
]
# Update information
event.setdefault("user", {}).update({"id": coresys.machine_id})
event.setdefault("contexts", {}).update(
{
"supervisor": {
@ -92,19 +97,12 @@ def filter_data(coresys: CoreSys, event: dict, hint: dict) -> dict:
{plugin.slug: plugin.version for plugin in coresys.plugins.all_plugins}
)
event.setdefault("tags", []).extend(
[
["installation_type", "os" if coresys.os.available else "supervised"],
["machine", coresys.machine],
],
event["tags"].update(
{
"installation_type": "os" if coresys.os.available else "supervised",
}
)
# Sanitize event
for i, tag in enumerate(event.get("tags", [])):
key, value = tag
if key == "url":
event["tags"][i] = [key, sanitize_url(value)]
if event.get("request"):
if event["request"].get("url"):
event["request"]["url"] = sanitize_url(event["request"]["url"])

View File

@ -72,7 +72,7 @@ def test_defaults(coresys):
with patch("shutil.disk_usage", return_value=(42, 42, 2 * (1024.0**3))):
filtered = filter_data(coresys, SAMPLE_EVENT, {})
assert ["installation_type", "supervised"] in filtered["tags"]
assert filtered["tags"]["installation_type"] == "supervised"
assert filtered["contexts"]["host"]["arch"] == "amd64"
assert filtered["contexts"]["host"]["machine"] == "qemux86-64"
assert filtered["contexts"]["versions"]["supervisor"] == AwesomeVersion(
@ -84,7 +84,6 @@ def test_defaults(coresys):
def test_sanitize(coresys):
"""Test event sanitation."""
event = {
"tags": [["url", "https://mydomain.com"]],
"request": {
"url": "https://mydomain.com",
"headers": [
@ -101,8 +100,6 @@ def test_sanitize(coresys):
with patch("shutil.disk_usage", return_value=(42, 42, 2 * (1024.0**3))):
filtered = filter_data(coresys, event, {})
assert ["url", "https://example.com"] in filtered["tags"]
assert filtered["request"]["url"] == "https://example.com"
assert ["Host", "example.com"] in filtered["request"]["headers"]