from pathlib import Path
from typing import Any
from ..models import CollectorError, InstanceDescriptor, JobDescriptor
from ..normalizer import observation_from_signals
from .base import CollectorContext, result
class ProcessCollector:
name = "process"
version = "0.1"
def collect(self, context: CollectorContext): # type: ignore[no-untyped-def]
started = context.now
observations = []
errors: list[CollectorError] = []
for definition in context.config.definitions:
process_name = str(definition["process_name"])
job = JobDescriptor(str(definition["job_key"]), str(definition.get("display_name", process_name)), "process",
str(definition.get("job_type", "worker")), stale_after_seconds=definition.get("stale_after_seconds"),
timeout_seconds=context.config.timeout_seconds)
instance = InstanceDescriptor(job, context.host, "process", process_name, process_name=process_name)
signals: dict[str, Any] = {"source": "procfs", "process_name": process_name, "process_exists": False}
try:
for proc in Path("/proc").glob("[0-9]*"):
try:
cmdline = (proc / "cmdline").read_bytes().replace(b"\x00", b" ").decode(errors="replace").strip()
comm = (proc / "comm").read_text(encoding="utf-8").strip()
if comm == process_name or Path(cmdline.split(" ", 1)[0]).name == process_name:
signals.update({"process_exists": True, "process_id": int(proc.name), "process_status": (proc / "status").read_text(encoding="utf-8")[:4096]})
break
except (FileNotFoundError, PermissionError, ValueError):
continue
except PermissionError as exc:
errors.append(CollectorError("permission_denied", str(exc), instance.instance_key))
signals["permission_denied"] = True
observations.append(observation_from_signals(job, instance, None, signals, started))
return result(self.name, self.version, started, observations, errors, {"read_only": True, "source": "/proc"})