Skip to content

Commit c29fc3d

Browse files
committed
Add logging framework and Tracer
1 parent e944113 commit c29fc3d

File tree

14 files changed

+397
-23
lines changed

14 files changed

+397
-23
lines changed

.vscode/launch.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,13 @@
3939
"request": "launch",
4040
"justMyCode": false,
4141
"module": "fastcs.demo",
42-
"args": ["run", "${workspaceFolder:FastCS}/src/fastcs/demo/controller.yaml"],
42+
"args": [
43+
"run",
44+
"${workspaceFolder:FastCS}/src/fastcs/demo/controller.yaml",
45+
"--log-level",
46+
"TRACE",
47+
// "--graylog"
48+
],
4349
"console": "integratedTerminal",
4450
}
4551
]

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ requires-python = ">=3.11"
3232
[project.optional-dependencies]
3333
dev = [
3434
"copier",
35+
"loguru~=0.7",
3536
"myst-parser",
3637
"pipdeptree",
3738
"pre-commit",

src/fastcs/attributes.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from typing import Any, Generic
77

88
import fastcs
9+
from fastcs.tracer import Tracer
910

1011
from .datatypes import ATTRIBUTE_TYPES, AttrCallback, DataType, T
1112

@@ -21,7 +22,7 @@ class AttrMode(Enum):
2122
READ_WRITE = 3
2223

2324

24-
class _BaseAttrHandler:
25+
class _BaseAttrHandler(Tracer):
2526
async def initialise(self, controller: fastcs.controller.BaseController) -> None:
2627
pass
2728

@@ -62,7 +63,7 @@ async def update(self, attr: AttrR) -> None:
6263
raise RuntimeError("SimpleHandler cannot update")
6364

6465

65-
class Attribute(Generic[T]):
66+
class Attribute(Generic[T], Tracer):
6667
"""Base FastCS attribute.
6768
6869
Instances of this class added to a ``Controller`` will be used by the backend.
@@ -76,6 +77,8 @@ def __init__(
7677
handler: Any = None,
7778
description: str | None = None,
7879
) -> None:
80+
super().__init__()
81+
7982
assert issubclass(datatype.dtype, ATTRIBUTE_TYPES), (
8083
f"Attr type must be one of {ATTRIBUTE_TYPES}, "
8184
"received type {datatype.dtype}"
@@ -155,6 +158,8 @@ def get(self) -> T:
155158
return self._value
156159

157160
async def set(self, value: T) -> None:
161+
self.log_event("Attribute set", attribute=self, value=value)
162+
158163
self._value = self._datatype.validate(value)
159164

160165
if self._update_callbacks is not None:

src/fastcs/backend.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@
44

55
from fastcs.cs_methods import Command, Put, Scan
66
from fastcs.datatypes import T
7+
from fastcs.logging import logger as _fastcs_logger
8+
from fastcs.tracer import Tracer
79

810
from .attributes import ONCE, AttrHandlerR, AttrHandlerW, AttrR, AttrW
911
from .controller import BaseController, Controller
1012
from .controller_api import ControllerAPI
1113
from .exceptions import FastCSError
1214
from .util import validate_hinted_attributes
1315

16+
tracer = Tracer(name=__name__)
17+
logger = _fastcs_logger.bind(name=__name__)
18+
1419

1520
class Backend:
1621
"""For keeping track of tasks during FastCS serving."""
@@ -153,9 +158,14 @@ def _create_updater_callback(attribute: AttrR[T]):
153158

154159
async def callback():
155160
try:
161+
tracer.log_event("Call attribute updater", topic=attribute)
156162
await updater.update(attribute)
157-
except Exception as e:
158-
print(f"Update loop in {updater} stopped:\n{e.__class__.__name__}: {e}")
163+
except Exception:
164+
logger.opt(exception=True).error(
165+
"Update loop failed",
166+
updater=updater,
167+
attribute=attribute,
168+
)
159169
raise
160170

161171
return callback

src/fastcs/connections/ip_connection.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import asyncio
22
from dataclasses import dataclass
33

4+
from fastcs.tracer import Tracer
5+
46

57
class DisconnectedError(Exception):
68
"""Raised if the ip connection is disconnected."""
@@ -44,10 +46,11 @@ async def close(self):
4446
await self.writer.wait_closed()
4547

4648

47-
class IPConnection:
49+
class IPConnection(Tracer):
4850
"""For connecting to an ip using a `StreamConnection`."""
4951

5052
def __init__(self):
53+
super().__init__()
5154
self.__connection = None
5255

5356
@property
@@ -61,14 +64,20 @@ async def connect(self, settings: IPConnectionSettings):
6164
reader, writer = await asyncio.open_connection(settings.ip, settings.port)
6265
self.__connection = StreamConnection(reader, writer)
6366

64-
async def send_command(self, message) -> None:
67+
async def send_command(self, message: str) -> None:
6568
async with self._connection as connection:
6669
await connection.send_message(message)
6770

68-
async def send_query(self, message) -> str:
71+
async def send_query(self, message: str) -> str:
6972
async with self._connection as connection:
7073
await connection.send_message(message)
71-
return await connection.receive_response()
74+
response = await connection.receive_response()
75+
self.log_event(
76+
"Received query response",
77+
query=message.strip(),
78+
response=response.strip(),
79+
)
80+
return response
7281

7382
async def close(self):
7483
async with self._connection as connection:

src/fastcs/controller.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
from typing import get_type_hints
66

77
from fastcs.attributes import Attribute
8+
from fastcs.tracer import Tracer
89

910

10-
class BaseController:
11+
class BaseController(Tracer):
1112
"""Base class for controller."""
1213

1314
#: Attributes passed from the device at runtime.
@@ -18,6 +19,8 @@ class BaseController:
1819
def __init__(
1920
self, path: list[str] | None = None, description: str | None = None
2021
) -> None:
22+
super().__init__()
23+
2124
if (
2225
description is not None
2326
): # Use the argument over the one class defined description.

src/fastcs/cs_methods.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import Any, Generic, TypeVar
66

77
from fastcs.controller import BaseController
8+
from fastcs.tracer import Tracer
89

910
from .exceptions import FastCSError
1011

@@ -31,10 +32,12 @@
3132
)
3233

3334

34-
class Method(Generic[Controller_T]):
35+
class Method(Generic[Controller_T], Tracer):
3536
"""Generic base class for all FastCS Controller methods."""
3637

3738
def __init__(self, fn: MethodCallback, *, group: str | None = None) -> None:
39+
super().__init__()
40+
3841
self._docstring = getdoc(fn)
3942

4043
sig = signature(fn, eval_str=True)

src/fastcs/demo/controller.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ transport:
1010
port: 8083
1111
log_level: info
1212
- ca_ioc:
13-
pv_prefix: DEMO
13+
pv_prefix: GARYDEMO
1414
gui:
1515
title: Temperature Controller Demo
1616
output_path: ./demo.bob

src/fastcs/demo/controllers.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ class TemperatureControllerHandler(AttrHandlerRW):
3030
update_period: float | None = 0.2
3131
_controller: TemperatureController | TemperatureRampController | None = None
3232

33+
def __post_init__(self):
34+
super().__init__()
35+
3336
async def initialise(self, controller: BaseController):
3437
assert isinstance(controller, TemperatureController | TemperatureRampController)
3538
self._controller = controller
@@ -42,15 +45,21 @@ def controller(self) -> TemperatureController | TemperatureRampController:
4245
return self._controller
4346

4447
async def put(self, attr: AttrW, value: Any) -> None:
45-
await self.controller.connection.send_command(
46-
f"{self.name}{self.controller.suffix}={attr.dtype(value)}\r\n"
47-
)
48+
command = f"{self.name}{self.controller.suffix}={attr.dtype(value)}"
49+
50+
await self.controller.connection.send_command(f"{command}\r\n")
51+
self.log_event("Put request for attribute", topic=attr, command=command)
4852

4953
async def update(self, attr: AttrR) -> None:
50-
response = await self.controller.connection.send_query(
51-
f"{self.name}{self.controller.suffix}?\r\n"
52-
)
54+
query = f"{self.name}{self.controller.suffix}?"
55+
response = await self.controller.connection.send_query(f"{query}\r\n")
5356
response = response.strip("\r\n")
57+
self.log_event(
58+
"Query for attribute",
59+
topic=attr,
60+
query=query,
61+
response=response,
62+
)
5463

5564
await attr.set(attr.dtype(response))
5665

@@ -87,10 +96,17 @@ async def close(self) -> None:
8796

8897
@scan(0.1)
8998
async def update_voltages(self):
99+
query = "V?"
90100
voltages = json.loads(
91-
(await self.connection.send_query("V?\r\n")).strip("\r\n")
101+
(await self.connection.send_query(f"{query}\r\n")).strip("\r\n")
92102
)
93103
for index, controller in enumerate(self._ramp_controllers):
104+
self.log_event(
105+
"Update voltages",
106+
topic=controller.voltage,
107+
query=query,
108+
response=voltages,
109+
)
94110
await controller.voltage.set(float(voltages[index]))
95111

96112

src/fastcs/launch.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from ruamel.yaml import YAML
1414

1515
from fastcs import __version__
16+
from fastcs.logging import LogLevel, configure_logging, logger
1617
from fastcs.transport.epics.ca.transport import EpicsCATransport
1718
from fastcs.transport.epics.pva.transport import EpicsPVATransport
1819
from fastcs.transport.graphql.transport import GraphQLTransport
@@ -86,6 +87,12 @@ async def serve(self) -> None:
8687

8788
coros.append(self._interactive_shell(context))
8889

90+
logger.info(
91+
"Starting FastCS",
92+
controller=self._controller,
93+
transports=f"[{', '.join(str(t) for t in self._transports)}]",
94+
)
95+
8996
try:
9097
await asyncio.gather(*coros)
9198
except asyncio.CancelledError:
@@ -199,10 +206,23 @@ def run(
199206
help=f"A yaml file matching the {controller_class.__name__} schema"
200207
),
201208
],
209+
log_level: Annotated[
210+
Optional[LogLevel], # noqa: UP045
211+
typer.Option(),
212+
] = None,
213+
graylog: Annotated[
214+
bool,
215+
typer.Option(
216+
"--graylog",
217+
help="Enable graylog logging",
218+
),
219+
] = False,
202220
):
203221
"""
204222
Start the controller
205223
"""
224+
configure_logging(log_level, graylog)
225+
206226
controller_class = ctx.obj.controller_class
207227
fastcs_options = ctx.obj.fastcs_options
208228

0 commit comments

Comments
 (0)