From d59bb24a99a646cb6c624434ca3e8bcd32a8fa82 Mon Sep 17 00:00:00 2001 From: James O'Hea Date: Mon, 16 Feb 2026 16:15:59 +0000 Subject: [PATCH 1/9] Use file path and line number in format record string --- src/fastcs/logging/_logging.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fastcs/logging/_logging.py b/src/fastcs/logging/_logging.py index a4358426..6586c983 100644 --- a/src/fastcs/logging/_logging.py +++ b/src/fastcs/logging/_logging.py @@ -108,7 +108,8 @@ def format_record(record) -> str: return f"""\ [{time} {record["level"].name[0]}] \ -{record["message"]:<80} \ +{record["message"]} \ +{f"[{record['file'].path}:{record['line']}]":<80} \ [{name}] \ {extras} {{exception}}\ From be8477231d8264b94e974430dc65c4ffc1fb701a Mon Sep 17 00:00:00 2001 From: James O'Hea Date: Tue, 17 Feb 2026 10:08:39 +0000 Subject: [PATCH 2/9] Remove redundant bind_logger function --- src/fastcs/logging/__init__.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/fastcs/logging/__init__.py b/src/fastcs/logging/__init__.py index ba11b087..567de1d8 100644 --- a/src/fastcs/logging/__init__.py +++ b/src/fastcs/logging/__init__.py @@ -7,7 +7,7 @@ from ._graylog import GraylogStaticFields as GraylogStaticFields from ._graylog import parse_graylog_env_fields as parse_graylog_env_fields from ._graylog import parse_graylog_static_fields as parse_graylog_static_fields -from ._logging import Logger, LogLevel, _configure_logger +from ._logging import LogLevel, _configure_logger logger = _logger.bind(logger_name="fastcs") """FastCS logger @@ -57,18 +57,6 @@ """ -def bind_logger(logger_name: str) -> Logger: - """Create a wrapper of the singleton fastcs logger with the given name bound - - The name will be displayed in all log messages from the returned wrapper. - - See the docstring for ``fastcs.logging.logger`` for more information. - - """ - - return logger.bind(logger_name=logger_name) - - def configure_logging( level: LogLevel = LogLevel.INFO, graylog_endpoint: GraylogEndpoint | None = None, From 05d7203f612f23dc8047020be0269010ba8397c3 Mon Sep 17 00:00:00 2001 From: James O'Hea Date: Tue, 17 Feb 2026 10:12:53 +0000 Subject: [PATCH 3/9] Remove all calls to bind_logger. Replace import with logger where still used --- docs/snippets/static14.py | 8 ++------ docs/snippets/static15.py | 8 ++------ src/fastcs/attributes/attr_r.py | 5 +---- src/fastcs/attributes/attr_rw.py | 3 --- src/fastcs/attributes/attr_w.py | 5 +---- src/fastcs/attributes/attribute.py | 3 --- src/fastcs/control_system.py | 3 +-- src/fastcs/controllers/base_controller.py | 4 +--- src/fastcs/methods/command.py | 3 +-- src/fastcs/methods/scan.py | 3 +-- src/fastcs/transports/controller_api.py | 2 -- src/fastcs/transports/epics/ca/ioc.py | 3 +-- src/fastcs/transports/epics/ca/transport.py | 4 +--- src/fastcs/transports/epics/gui.py | 4 +--- src/fastcs/transports/epics/pva/transport.py | 4 +--- 15 files changed, 14 insertions(+), 48 deletions(-) diff --git a/docs/snippets/static14.py b/docs/snippets/static14.py index 2e1824ea..fb794b19 100644 --- a/docs/snippets/static14.py +++ b/docs/snippets/static14.py @@ -10,13 +10,11 @@ from fastcs.controllers import Controller from fastcs.datatypes import Enum, Float, Int, String from fastcs.launch import FastCS -from fastcs.logging import bind_logger, configure_logging +from fastcs.logging import configure_logging, logger from fastcs.methods import command, scan from fastcs.transports.epics import EpicsGUIOptions, EpicsIOCOptions from fastcs.transports.epics.ca import EpicsCATransport -logger = bind_logger(__name__) - NumberT = TypeVar("NumberT", int, float) @@ -33,8 +31,6 @@ class TemperatureControllerAttributeIO( def __init__(self, connection: IPConnection, suffix: str = ""): super().__init__() - self.logger = bind_logger(__class__.__name__) - self._connection = connection self._suffix = suffix @@ -49,7 +45,7 @@ async def send( ) -> None: command = f"{attr.io_ref.name}{self._suffix}={attr.dtype(value)}" - self.logger.info("Sending attribute value", command=command, attribute=attr) + logger.info("Sending attribute value", command=command, attribute=attr) await self._connection.send_command(f"{command}\r\n") diff --git a/docs/snippets/static15.py b/docs/snippets/static15.py index 4df6934c..0246d2cb 100644 --- a/docs/snippets/static15.py +++ b/docs/snippets/static15.py @@ -10,13 +10,11 @@ from fastcs.controllers import Controller from fastcs.datatypes import Enum, Float, Int, String from fastcs.launch import FastCS -from fastcs.logging import LogLevel, bind_logger, configure_logging +from fastcs.logging import LogLevel, configure_logging, logger from fastcs.methods import command, scan from fastcs.transports.epics import EpicsGUIOptions, EpicsIOCOptions from fastcs.transports.epics.ca import EpicsCATransport -logger = bind_logger(__name__) - NumberT = TypeVar("NumberT", int, float) @@ -33,8 +31,6 @@ class TemperatureControllerAttributeIO( def __init__(self, connection: IPConnection, suffix: str = ""): super().__init__() - self.logger = bind_logger(__class__.__name__) - self._connection = connection self._suffix = suffix @@ -52,7 +48,7 @@ async def send( ) -> None: command = f"{attr.io_ref.name}{self._suffix}={attr.dtype(value)}" - self.logger.info("Sending attribute value", command=command, attribute=attr) + logger.info("Sending attribute value", command=command, attribute=attr) await self._connection.send_command(f"{command}\r\n") diff --git a/src/fastcs/attributes/attr_r.py b/src/fastcs/attributes/attr_r.py index 59a2f227..95a9fa38 100644 --- a/src/fastcs/attributes/attr_r.py +++ b/src/fastcs/attributes/attr_r.py @@ -8,10 +8,7 @@ from fastcs.attributes.attribute_io_ref import AttributeIORefT from fastcs.attributes.util import AttrValuePredicate, PredicateEvent from fastcs.datatypes import DataType, DType_T -from fastcs.logging import bind_logger - -logger = bind_logger(logger_name=__name__) - +from fastcs.logging import logger AttrIOUpdateCallback = Callable[["AttrR[DType_T, Any]"], Awaitable[None]] """An AttributeIO callback that takes an AttrR and updates its value""" diff --git a/src/fastcs/attributes/attr_rw.py b/src/fastcs/attributes/attr_rw.py index deff0278..5f0c2edb 100644 --- a/src/fastcs/attributes/attr_rw.py +++ b/src/fastcs/attributes/attr_rw.py @@ -3,9 +3,6 @@ from fastcs.attributes.attribute import AttributeAccessMode from fastcs.attributes.attribute_io_ref import AttributeIORefT from fastcs.datatypes import DataType, DType_T -from fastcs.logging import bind_logger - -logger = bind_logger(logger_name=__name__) class AttrRW(AttrR[DType_T, AttributeIORefT], AttrW[DType_T, AttributeIORefT]): diff --git a/src/fastcs/attributes/attr_w.py b/src/fastcs/attributes/attr_w.py index 4532364a..fa2b3d89 100644 --- a/src/fastcs/attributes/attr_w.py +++ b/src/fastcs/attributes/attr_w.py @@ -5,10 +5,7 @@ from fastcs.attributes.attribute import Attribute, AttributeAccessMode from fastcs.attributes.attribute_io_ref import AttributeIORefT from fastcs.datatypes import DataType, DType_T -from fastcs.logging import bind_logger - -logger = bind_logger(logger_name=__name__) - +from fastcs.logging import logger AttrOnPutCallback = Callable[["AttrW[DType_T, Any]", DType_T], Awaitable[None]] """Callbacks to be called when the setpoint of an attribute is changed""" diff --git a/src/fastcs/attributes/attribute.py b/src/fastcs/attributes/attribute.py index ae6e3852..ca4955b5 100644 --- a/src/fastcs/attributes/attribute.py +++ b/src/fastcs/attributes/attribute.py @@ -4,11 +4,8 @@ from fastcs.attributes.attribute_io_ref import AttributeIORefT from fastcs.datatypes import DataType, DType, DType_T -from fastcs.logging import bind_logger from fastcs.tracer import Tracer -logger = bind_logger(logger_name=__name__) - AttributeAccessMode = Literal["r", "w", "rw"] diff --git a/src/fastcs/control_system.py b/src/fastcs/control_system.py index cae16b77..e992521c 100644 --- a/src/fastcs/control_system.py +++ b/src/fastcs/control_system.py @@ -8,13 +8,12 @@ from IPython.terminal.embed import InteractiveShellEmbed from fastcs.controllers import BaseController, Controller -from fastcs.logging import bind_logger +from fastcs.logging import logger from fastcs.methods import ScanCallback from fastcs.tracer import Tracer from fastcs.transports import ControllerAPI, Transport tracer = Tracer(name=__name__) -logger = bind_logger(logger_name=__name__) class FastCS: diff --git a/src/fastcs/controllers/base_controller.py b/src/fastcs/controllers/base_controller.py index 0e5fc1de..47257352 100755 --- a/src/fastcs/controllers/base_controller.py +++ b/src/fastcs/controllers/base_controller.py @@ -6,12 +6,10 @@ from typing import _GenericAlias, get_args, get_origin, get_type_hints # type: ignore from fastcs.attributes import AnyAttributeIO, Attribute, AttrR, AttrW, HintedAttribute -from fastcs.logging import bind_logger +from fastcs.logging import logger from fastcs.methods import Command, Scan, UnboundCommand, UnboundScan from fastcs.tracer import Tracer -logger = bind_logger(logger_name=__name__) - class BaseController(Tracer): """Base class for controllers diff --git a/src/fastcs/methods/command.py b/src/fastcs/methods/command.py index 87d0e0b1..5259e4b6 100644 --- a/src/fastcs/methods/command.py +++ b/src/fastcs/methods/command.py @@ -2,13 +2,12 @@ from types import MethodType from typing import TYPE_CHECKING -from fastcs.logging import bind_logger +from fastcs.logging import logger from fastcs.methods.method import Controller_T, Method if TYPE_CHECKING: from fastcs.controllers import BaseController # noqa: F401 -logger = bind_logger(logger_name=__name__) UnboundCommandCallback = Callable[[Controller_T], Coroutine[None, None, None]] """A Command callback that is unbound and must be called with a `Controller` instance""" diff --git a/src/fastcs/methods/scan.py b/src/fastcs/methods/scan.py index a20b7e94..5701e79d 100644 --- a/src/fastcs/methods/scan.py +++ b/src/fastcs/methods/scan.py @@ -2,13 +2,12 @@ from types import MethodType from typing import TYPE_CHECKING -from fastcs.logging import bind_logger +from fastcs.logging import logger from fastcs.methods.method import Controller_T, Method if TYPE_CHECKING: from fastcs.controllers import BaseController # noqa: F401 -logger = bind_logger(logger_name=__name__) UnboundScanCallback = Callable[[Controller_T], Coroutine[None, None, None]] """A Scan callback that is unbound and must be called with a `Controller` instance""" diff --git a/src/fastcs/transports/controller_api.py b/src/fastcs/transports/controller_api.py index 3a13fae0..b4242ae2 100644 --- a/src/fastcs/transports/controller_api.py +++ b/src/fastcs/transports/controller_api.py @@ -4,13 +4,11 @@ from dataclasses import dataclass, field from fastcs.attributes import Attribute, AttributeIORef, AttrR -from fastcs.logging import bind_logger from fastcs.methods import Command, Scan, ScanCallback from fastcs.tracer import Tracer from fastcs.util import ONCE tracer = Tracer(name=__name__) -logger = bind_logger(logger_name=__name__) @dataclass diff --git a/src/fastcs/transports/epics/ca/ioc.py b/src/fastcs/transports/epics/ca/ioc.py index fbaaa993..fd8bf227 100644 --- a/src/fastcs/transports/epics/ca/ioc.py +++ b/src/fastcs/transports/epics/ca/ioc.py @@ -9,7 +9,7 @@ from fastcs.attributes import AttrR, AttrRW, AttrW from fastcs.datatypes import DataType, DType_T from fastcs.datatypes.waveform import Waveform -from fastcs.logging import bind_logger +from fastcs.logging import logger from fastcs.methods import Command from fastcs.tracer import Tracer from fastcs.transports.controller_api import ControllerAPI @@ -28,7 +28,6 @@ tracer = Tracer(name=__name__) -logger = bind_logger(logger_name=__name__) class EpicsCAIOC: diff --git a/src/fastcs/transports/epics/ca/transport.py b/src/fastcs/transports/epics/ca/transport.py index 13f07231..71ed3ada 100644 --- a/src/fastcs/transports/epics/ca/transport.py +++ b/src/fastcs/transports/epics/ca/transport.py @@ -4,7 +4,7 @@ from softioc import softioc -from fastcs.logging import bind_logger +from fastcs.logging import logger from fastcs.transports.controller_api import ControllerAPI from fastcs.transports.epics import ( EpicsDocsOptions, @@ -16,8 +16,6 @@ from fastcs.transports.epics.gui import EpicsGUI from fastcs.transports.transport import Transport -logger = bind_logger(logger_name=__name__) - @dataclass class EpicsCATransport(Transport): diff --git a/src/fastcs/transports/epics/gui.py b/src/fastcs/transports/epics/gui.py index 9bc47f7b..c4e69b09 100644 --- a/src/fastcs/transports/epics/gui.py +++ b/src/fastcs/transports/epics/gui.py @@ -33,14 +33,12 @@ String, Waveform, ) -from fastcs.logging import bind_logger +from fastcs.logging import logger from fastcs.methods import Command from fastcs.transports.controller_api import ControllerAPI from fastcs.transports.epics.options import EpicsGUIFormat, EpicsGUIOptions from fastcs.util import snake_to_pascal -logger = bind_logger(logger_name=__name__) - class EpicsGUI: """For creating gui in the EPICS transports.""" diff --git a/src/fastcs/transports/epics/pva/transport.py b/src/fastcs/transports/epics/pva/transport.py index 1ccc8042..14854dea 100644 --- a/src/fastcs/transports/epics/pva/transport.py +++ b/src/fastcs/transports/epics/pva/transport.py @@ -1,7 +1,7 @@ import asyncio from dataclasses import dataclass, field -from fastcs.logging import bind_logger +from fastcs.logging import logger from fastcs.transports.controller_api import ControllerAPI from fastcs.transports.epics import ( EpicsDocsOptions, @@ -14,8 +14,6 @@ from .ioc import P4PIOC -logger = bind_logger(logger_name=__name__) - @dataclass class EpicsPVATransport(Transport): From 54126571c102b9678ef14c064b42b15c524bba68 Mon Sep 17 00:00:00 2001 From: James O'Hea Date: Tue, 17 Feb 2026 10:14:08 +0000 Subject: [PATCH 4/9] Update logging docs to remove mention of bind_logger --- docs/tutorials/static-drivers.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/tutorials/static-drivers.md b/docs/tutorials/static-drivers.md index 09c0a975..2397c553 100644 --- a/docs/tutorials/static-drivers.md +++ b/docs/tutorials/static-drivers.md @@ -409,10 +409,7 @@ DEMO:R1:Enabled_RBV Off FastCS has convenient logging support to provide status and metrics from the application. To enable logging from the core framework call `configure_logging` with no -arguments (the default logging level is INFO). To log messages from a driver, either -import the singleton `logger` directly, or to provide more context to the message, call -`bind_logger` with a name (usually either the name of the module or the name of the -class). +arguments (the default logging level is INFO). To log messages from a driver, import the singleton `logger` directly. Create a module-level logger to log status of the application start up. Create a class logger for `TemperatureControllerAttributeIO` to log the commands it sends. From ac93aa0146eb7de249390b6d3dc2ca660d8e7f9c Mon Sep 17 00:00:00 2001 From: James O'Hea Date: Tue, 17 Feb 2026 10:46:20 +0000 Subject: [PATCH 5/9] Update emphasize-lines line numbers after edits CI was failing the sphinx build as an emphasized line didn't exist anymore after the changes to logging --- docs/tutorials/static-drivers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/static-drivers.md b/docs/tutorials/static-drivers.md index 2397c553..f881444e 100644 --- a/docs/tutorials/static-drivers.md +++ b/docs/tutorials/static-drivers.md @@ -418,7 +418,7 @@ logger for `TemperatureControllerAttributeIO` to log the commands it sends. :class: dropdown, hint :::{literalinclude} /snippets/static14.py -:emphasize-lines: 15,20-21,53-55,116,123 +:emphasize-lines: 13,51,113,120 ::: :::: From 860aba035c111a472a5d5c04a228281e1b633199 Mon Sep 17 00:00:00 2001 From: James O'Hea Date: Tue, 24 Feb 2026 09:33:41 +0000 Subject: [PATCH 6/9] Place padding on message, not file/path --- src/fastcs/logging/_logging.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fastcs/logging/_logging.py b/src/fastcs/logging/_logging.py index 6586c983..059bd3e3 100644 --- a/src/fastcs/logging/_logging.py +++ b/src/fastcs/logging/_logging.py @@ -108,8 +108,8 @@ def format_record(record) -> str: return f"""\ [{time} {record["level"].name[0]}] \ -{record["message"]} \ -{f"[{record['file'].path}:{record['line']}]":<80} \ +{record["message"]:<80} \ +[{record["file"].path}:{record["line"]}] \ [{name}] \ {extras} {{exception}}\ From 2297a76559c102ec8868f633dcab28980212eeb5 Mon Sep 17 00:00:00 2001 From: James O'Hea Date: Tue, 24 Feb 2026 09:51:27 +0000 Subject: [PATCH 7/9] Update emphasize-lines line numbers after edits --- docs/tutorials/static-drivers.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorials/static-drivers.md b/docs/tutorials/static-drivers.md index f881444e..7efeff7b 100644 --- a/docs/tutorials/static-drivers.md +++ b/docs/tutorials/static-drivers.md @@ -418,7 +418,7 @@ logger for `TemperatureControllerAttributeIO` to log the commands it sends. :class: dropdown, hint :::{literalinclude} /snippets/static14.py -:emphasize-lines: 13,51,113,120 +:emphasize-lines: 13,48,110,117 ::: :::: @@ -443,7 +443,7 @@ visible. :class: dropdown, hint :::{literalinclude} /snippets/static15.py -:emphasize-lines: 15,47-49,119 +:emphasize-lines: 13,49-51,120 ::: :::: From 27718091e973dfc53a5fbad7b548119586f990ff Mon Sep 17 00:00:00 2001 From: Gary Yendell Date: Tue, 27 Jan 2026 13:37:25 +0000 Subject: [PATCH 8/9] Don't disable logging by default In child libraries this breaks because logging is partially enabled --- src/fastcs/logging/__init__.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/fastcs/logging/__init__.py b/src/fastcs/logging/__init__.py index 567de1d8..86fa2d62 100644 --- a/src/fastcs/logging/__init__.py +++ b/src/fastcs/logging/__init__.py @@ -82,10 +82,6 @@ def configure_logging( ) -# Configure logger with defaults - INFO level and disabled -_configure_logger(logger) - - class _StdLoggingInterceptHandler(logging.Handler): """A std logging handler to forward messages to loguru with nice formatting.""" From 35adfbb4301f4eafd00b0d492a938d1ea61c6a1d Mon Sep 17 00:00:00 2001 From: Gary Yendell Date: Mon, 2 Mar 2026 16:38:05 +0000 Subject: [PATCH 9/9] Tweaks Remove bind_logger now that we report file path and line Update Tracer for new logging --- docs/tutorials/static-drivers.md | 12 +++++---- src/fastcs/control_system.py | 2 +- src/fastcs/logging/__init__.py | 23 +++++++--------- src/fastcs/logging/_logging.py | 6 ++--- src/fastcs/tracer.py | 26 +++++++++---------- src/fastcs/transports/controller_api.py | 3 --- src/fastcs/transports/epics/ca/ioc.py | 2 +- .../transports/epics/pva/_pv_handlers.py | 2 +- 8 files changed, 36 insertions(+), 40 deletions(-) diff --git a/docs/tutorials/static-drivers.md b/docs/tutorials/static-drivers.md index 7efeff7b..31c34592 100644 --- a/docs/tutorials/static-drivers.md +++ b/docs/tutorials/static-drivers.md @@ -409,7 +409,8 @@ DEMO:R1:Enabled_RBV Off FastCS has convenient logging support to provide status and metrics from the application. To enable logging from the core framework call `configure_logging` with no -arguments (the default logging level is INFO). To log messages from a driver, import the singleton `logger` directly. +arguments (the default logging level is INFO). To log messages from a driver, import the +singleton `logger` directly. Create a module-level logger to log status of the application start up. Create a class logger for `TemperatureControllerAttributeIO` to log the commands it sends. @@ -431,7 +432,7 @@ Try setting a PV and check the console for the log message it prints. A similar log message could be added for the update method of the IO, but this would be very verbose. For this use case FastCS provides the `Tracer` class, which is inherited -by `AttributeIO`, among other core FastCS classes. This adds a enables logging `TRACE` +by `AttributeIO`, among other core FastCS classes. This enables the logging of `TRACE` level log messages that are disabled by default, but can be enabled at runtime. Update the `send` method of the IO to log a message showing the query that was sent and @@ -449,7 +450,8 @@ visible. :::: Enable tracing on the `power` attribute by calling `enable_tracing` and then enable a -ramp so that the value updates. Check the console to see the messages. Call `disable_tracing` to disable the log messages for `power. +ramp so that the value updates. Check the console to see the messages. Call +`disable_tracing` to disable the log messages for `power`. ``` In [1]: controller.power.enable_tracing() @@ -469,7 +471,7 @@ In [1]: controller.power.enable_tracing() In [2]: controller.power.disable_tracing() ``` -These log messages includes other trace loggers that log messages with `power` as the +These log messages include other trace loggers that log messages with `power` as the `topic`, so they also appear automatically, so the log messages show changes to the attribute throughout the stack: the query to the device and its response, the value the attribute is set to, and the value that the PV in the EPICS CA transport is set to. @@ -481,7 +483,7 @@ The `Tracer` can also be used as a module-level instance for use in free functio ```python from fastcs.tracer import Tracer -tracer = Tracer(__name__) +tracer = Tracer() def handle_attribute(attr): tracer.log_event("Handling attribute", topic=attr) diff --git a/src/fastcs/control_system.py b/src/fastcs/control_system.py index e992521c..b1a6ea6c 100644 --- a/src/fastcs/control_system.py +++ b/src/fastcs/control_system.py @@ -13,7 +13,7 @@ from fastcs.tracer import Tracer from fastcs.transports import ControllerAPI, Transport -tracer = Tracer(name=__name__) +tracer = Tracer() class FastCS: diff --git a/src/fastcs/logging/__init__.py b/src/fastcs/logging/__init__.py index 86fa2d62..d47a268f 100644 --- a/src/fastcs/logging/__init__.py +++ b/src/fastcs/logging/__init__.py @@ -21,8 +21,7 @@ and metrics in graylog. It is best to keep the message short and use extra fields for additional information for -messages to be formatted nicely in the console. To add kwargs to format the message -without them appearing as extra fields, prepend the key with ``_``. +messages to be formatted nicely in the console. .. code-block:: python @@ -30,24 +29,22 @@ logger.info("PV put: {pv} = {value}", pv=pv, value=value) -By default messages will be logged with the name ``fastcs``. Within different modules -and classes it can be useful to override this name. This can be done with the ``bind`` -method. To create a module logger with its name +To add kwargs to format the message without them appearing as extra fields, prepend the +key with ``_``. + +By default messages will be logged with the name ``fastcs``. Within a driver it may be +useful to set a distinct logger name. This can be done with the ``bind`` method. To +create a new logger with the the name of the driver, use the following in a logging.py +module and use it throughout the package instead of the fastcs logger: .. code-block:: python from fastcs.logging import logger as _logger - logger = _logger.bind(logger_name=__name__) - -or to create a class logger with its name - -.. code-block:: python - - self.logger = _logger.bind(logger_name=__class__.__name__) + logger = _logger.bind(logger_name="fastcs-driver") As standard ``loguru`` supports ``trace`` level monitoring, but it should not be used in -fastcs. Instead there is a ``Tracer`` class for verbose logging with fine-grained +fastcs. Instead there is a `Tracer` class for verbose logging with fine-grained controls that can be enabled by the user at runtime. Use ``configure_logging`` to re-configure the logger at runtime. For more advanced diff --git a/src/fastcs/logging/_logging.py b/src/fastcs/logging/_logging.py index 059bd3e3..c62d7a60 100644 --- a/src/fastcs/logging/_logging.py +++ b/src/fastcs/logging/_logging.py @@ -90,6 +90,7 @@ def format_record(record) -> str: time = f"{_time}{_timezone}" name = record["extra"].pop("logger_name", None) or record["name"] + message_pad = max(0, 100 - len(name)) sep = ", " if "extra" in record: @@ -108,9 +109,8 @@ def format_record(record) -> str: return f"""\ [{time} {record["level"].name[0]}] \ -{record["message"]:<80} \ -[{record["file"].path}:{record["line"]}] \ -[{name}] \ +{record["message"]:<{message_pad}} \ +{name} [{record["file"].path}:{record["line"]}] \ {extras} {{exception}}\ """ diff --git a/src/fastcs/tracer.py b/src/fastcs/tracer.py index 6a5a8cca..a62f93c8 100644 --- a/src/fastcs/tracer.py +++ b/src/fastcs/tracer.py @@ -1,7 +1,10 @@ from collections import defaultdict from typing import Any -from fastcs.logging import logger +from fastcs.logging import logger as _logger + +# Pass depth so that reported line number in log messages is `log_event` call site +logger = _logger.opt(depth=1) class Tracer: @@ -16,9 +19,9 @@ class Tracer: object, or from another ``Tracer`` that uses the object as the ``topic``, will be logged. - Note: The global logger level must be set to ``TRACE`` for the messages to be logged + Note: The logger level must be set to ``TRACE`` for the messages to be logged - Example usage: + Example usage in interactive shell: .. code-block:: python controller.ramp_rate.enable_tracing() @@ -27,23 +30,20 @@ class Tracer: controller.connection.add_tracing_filter("query", "V?") controller.connection.remove_tracing_filter("query", "V?") controller.connection.disable_tracing() - - :param name: The name of the logger. Attached to log messages as ``logger_name``. - """ - def __init__(self, name: str | None = None): + def __init__(self): self.__tracing_enabled: bool = False self.__tracing_filters: dict[str, list[Any]] = defaultdict(list) - self.__logger_name = name if name is not None else self.__class__.__name__ def log_event(self, event: str, topic: "Tracer | None" = None, *args, **kwargs): """Log an event only if tracing is enabled and the filter matches - :param event: A message describing the event - :param topic: Another `Tracer` related to this event to enable it to be logged - :param args: Positional arguments for underlying logger - :param kwargs: Keyword arguments for underlying logger + Args: + event: A message describing the event + topic: Another `Tracer` related to this event to enable it to be logged + args: Positional arguments for underlying logger + kwargs: Keyword arguments for underlying logger """ if self.__tracing_enabled or (topic is not None and topic.__tracing_enabled): # noqa: SLF001 @@ -54,7 +54,7 @@ def log_event(self, event: str, topic: "Tracer | None" = None, *args, **kwargs): else: return - logger.trace(event, *args, logger_name=self.__logger_name, **kwargs) + logger.trace(event, *args, **kwargs) def enable_tracing(self): """Enable trace logging for this object""" diff --git a/src/fastcs/transports/controller_api.py b/src/fastcs/transports/controller_api.py index b4242ae2..b41d8ad3 100644 --- a/src/fastcs/transports/controller_api.py +++ b/src/fastcs/transports/controller_api.py @@ -5,11 +5,8 @@ from fastcs.attributes import Attribute, AttributeIORef, AttrR from fastcs.methods import Command, Scan, ScanCallback -from fastcs.tracer import Tracer from fastcs.util import ONCE -tracer = Tracer(name=__name__) - @dataclass class ControllerAPI: diff --git a/src/fastcs/transports/epics/ca/ioc.py b/src/fastcs/transports/epics/ca/ioc.py index fd8bf227..04a72bba 100644 --- a/src/fastcs/transports/epics/ca/ioc.py +++ b/src/fastcs/transports/epics/ca/ioc.py @@ -27,7 +27,7 @@ EPICS_MAX_NAME_LENGTH = 60 -tracer = Tracer(name=__name__) +tracer = Tracer() class EpicsCAIOC: diff --git a/src/fastcs/transports/epics/pva/_pv_handlers.py b/src/fastcs/transports/epics/pva/_pv_handlers.py index 099a9c33..5ba819f9 100644 --- a/src/fastcs/transports/epics/pva/_pv_handlers.py +++ b/src/fastcs/transports/epics/pva/_pv_handlers.py @@ -21,7 +21,7 @@ p4p_timestamp_now, ) -tracer = Tracer(name=__name__) +tracer = Tracer() class WritePvHandler: