From c7b3d88169362fbe5326bcf5154bca68fdd56bc2 Mon Sep 17 00:00:00 2001 From: Ethan Date: Wed, 30 Oct 2024 12:27:42 -0400 Subject: [PATCH] RSDK-5985 - per-resource logging (#769) --- src/viam/components/board/client.py | 3 --- src/viam/components/component_base.py | 5 ++++- src/viam/logging.py | 11 ++++++++++- src/viam/module/module.py | 2 ++ src/viam/resource/base.py | 4 ++++ src/viam/services/service_base.py | 5 ++++- 6 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/viam/components/board/client.py b/src/viam/components/board/client.py index e730bf361..7f715c629 100644 --- a/src/viam/components/board/client.py +++ b/src/viam/components/board/client.py @@ -5,7 +5,6 @@ from grpclib.client import Channel from grpclib.client import Stream as ClientStream -from viam.logging import getLogger from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry from viam.proto.component.board import ( BoardServiceStub, @@ -33,8 +32,6 @@ from .board import Board, TickStream -LOGGER = getLogger(__name__) - class AnalogClient(Board.Analog): def __init__(self, name: str, board: "BoardClient"): diff --git a/src/viam/components/component_base.py b/src/viam/components/component_base.py index 42b545bf5..5503bf9e3 100644 --- a/src/viam/components/component_base.py +++ b/src/viam/components/component_base.py @@ -1,9 +1,11 @@ import abc +from logging import Logger from typing import TYPE_CHECKING, Any, ClassVar, Dict, List, Mapping, Optional, SupportsBytes, SupportsFloat, Union, cast from typing_extensions import Self from viam.errors import MethodNotImplementedError +from viam.logging import getLogger from viam.proto.common import Geometry from viam.resource.base import ResourceBase @@ -23,8 +25,9 @@ class ComponentBase(abc.ABC, ResourceBase): SUBTYPE: ClassVar["Subtype"] - def __init__(self, name: str): + def __init__(self, name: str, *, logger: Optional[Logger] = None): self.name = name + self.logger = logger if logger is not None else getLogger(f'{self.SUBTYPE}.{name}') @classmethod def from_robot(cls, robot: "RobotClient", name: str) -> Self: diff --git a/src/viam/logging.py b/src/viam/logging.py index bbddd368c..28c0f7347 100644 --- a/src/viam/logging.py +++ b/src/viam/logging.py @@ -83,7 +83,8 @@ async def handle_task_result(self, task: asyncio.Task): def emit(self, record: logging.LogRecord): assert isinstance(record, logging.LogRecord) - name = record.name.split(".")[-1] + # Fully qualified name of form "{subtype triplet}/{name}", e.g. "rdk:component:arm/myarm" + name = record.name.replace('.', '/') message = f"{record.filename}:{record.lineno}\t{record.getMessage()}" stack = f"exc_info: {record.exc_info}, exc_text: {record.exc_text}, stack_info: {record.stack_info}" time = datetime.fromtimestamp(record.created) @@ -151,6 +152,14 @@ def addHandlers(logger: logging.Logger, use_default_handlers=False): _addHandlers([logger], use_default_handlers) +def update_log_level(logger: logging.Logger, level: Union[int, str]): + if level == '': + level = LOG_LEVEL + logger.setLevel(level) + for handler in logger.handlers: + handler.setLevel(level) + + def _addHandlers(loggers: Iterable[logging.Logger], use_default_handlers=False): format = _ColorFormatter("%(asctime)s\t\t" + "%(levelname)s\t" + "%(name)s (%(filename)s:%(lineno)d)\t" + "%(message)s\t") diff --git a/src/viam/module/module.py b/src/viam/module/module.py index 7bc0b83c2..5748534ae 100644 --- a/src/viam/module/module.py +++ b/src/viam/module/module.py @@ -11,6 +11,7 @@ from viam import logging from viam.errors import ResourceNotFoundError, ValidationError +from viam.logging import update_log_level from viam.proto.app.robot import ComponentConfig from viam.proto.module import ( AddResourceRequest, @@ -183,6 +184,7 @@ async def add_resource(self, request: AddResourceRequest): model = Model.from_string(config.model, ignore_errors=True) creator = Registry.lookup_resource_creator(subtype, model) resource = creator(config, dependencies) + update_log_level(resource.logger, config.log_configuration.level.upper()) self.server.register(resource) async def reconfigure_resource(self, request: ReconfigureResourceRequest): diff --git a/src/viam/resource/base.py b/src/viam/resource/base.py index 56a08e0c7..0a5f37a09 100644 --- a/src/viam/resource/base.py +++ b/src/viam/resource/base.py @@ -1,5 +1,6 @@ from abc import abstractmethod from typing import TYPE_CHECKING, Any, ClassVar, Mapping, Optional, Protocol, runtime_checkable +from logging import Logger from typing_extensions import Self @@ -25,6 +26,9 @@ class ResourceBase(Protocol): name: str """The name of the Resource""" + logger: Logger + """A logger allowing for setting log levels on a per-resource basis""" + @classmethod def get_resource_name(cls, name: str) -> ResourceName: """ diff --git a/src/viam/services/service_base.py b/src/viam/services/service_base.py index 99daba28c..33ee8bd9e 100644 --- a/src/viam/services/service_base.py +++ b/src/viam/services/service_base.py @@ -1,8 +1,10 @@ import abc +from logging import Logger from typing import TYPE_CHECKING, ClassVar, Mapping, Optional, cast from typing_extensions import Self +from viam.logging import getLogger from viam.resource.base import ResourceBase from viam.utils import ValueTypes @@ -18,8 +20,9 @@ class ServiceBase(abc.ABC, ResourceBase): SUBTYPE: ClassVar["Subtype"] - def __init__(self, name: str) -> None: + def __init__(self, name: str, *, logger: Optional[Logger] = None) -> None: self.name = name + self.logger = logger if logger is not None else getLogger(f'{self.SUBTYPE}.{name}') @classmethod def from_robot(cls, robot: "RobotClient", name: str) -> Self: