33"""
44
55import pathlib
6- import typing
6+ from typing import Optional , Callable
77
88from pkg_resources import iter_entry_points
99import can .typechecking
1010
11+ from ..message import Message
1112from ..listener import Listener
12- from .generic import BaseIOHandler
13+ from .generic import BaseIOHandler , MessageWriter
1314from .asc import ASCWriter
1415from .blf import BLFWriter
1516from .canutils import CanutilsLogWriter
@@ -51,7 +52,7 @@ class Logger(BaseIOHandler, Listener): # pylint: disable=abstract-method
5152
5253 @staticmethod
5354 def __new__ (
54- cls , filename : typing . Optional [can .typechecking .StringPathLike ], * args , ** kwargs
55+ cls , filename : Optional [can .typechecking .StringPathLike ], * args , ** kwargs
5556 ):
5657 """
5758 :param filename: the filename/path of the file to write to,
@@ -78,3 +79,97 @@ def __new__(
7879 raise ValueError (
7980 f'No write support for this unknown log format "{ suffix } "'
8081 ) from None
82+
83+
84+ class RotatingFileLogger (Listener ):
85+ """Log CAN messages to a sequence of files with a given maximum size.
86+
87+ The log file name and path must be returned by the function`filename_func`
88+ as a path-like object (e.g. a string). The log file format is defined by
89+ the suffix of the path-like object.
90+
91+ The RotatingFileLogger currently supports
92+ * .asc: :class:`can.ASCWriter`
93+ * .blf :class:`can.BLFWriter`
94+ * .csv: :class:`can.CSVWriter`
95+ * .log :class:`can.CanutilsLogWriter`
96+ * .txt :class:`can.Printer`
97+
98+ The log files may be incomplete until `stop()` is called due to buffering.
99+
100+ Example::
101+
102+ from can import Notifier
103+ from can.interfaces.vector import VectorBus
104+ from can import RotatingFileLogger
105+
106+ bus = VectorBus(channel=[0], app_name="CANape", fd=True)
107+ logger = RotatingFileLogger(
108+ filename_func=lambda idx: f"CAN_Log_{idx:03}.txt", # filename with three digit counter
109+ max_bytes=5 * 1024 ** 2, # =5MB
110+ initial_file_number=23, # start with number 23
111+ )
112+ notifier = Notifier(bus=bus, listeners=[logger])
113+
114+ """
115+
116+ supported_writers = {
117+ ".asc" : ASCWriter ,
118+ ".blf" : BLFWriter ,
119+ ".csv" : CSVWriter ,
120+ ".log" : CanutilsLogWriter ,
121+ ".txt" : Printer ,
122+ }
123+
124+ def __init__ (
125+ self ,
126+ filename_func : Callable [[int ], can .typechecking .StringPathLike ],
127+ max_bytes : int ,
128+ initial_file_number : int = 0 ,
129+ * args ,
130+ ** kwargs ,
131+ ):
132+ """
133+ :param filename_func:
134+ A function or lambda expression that returns the path of the new
135+ log file e.g. `lambda file_number: f"C:\\ my_can_logfile_{file_number:03}.asc"`.
136+ :param max_bytes:
137+ The file size threshold in bytes at which a new file is created.
138+ :param initial_file_number:
139+ The first log file will start with number `initial_file_number`.
140+ :raises ValueError:
141+ The filename's suffix is not supported.
142+ """
143+ self .filename_func = filename_func
144+ self .max_bytes = max_bytes
145+ self .file_number = initial_file_number
146+
147+ self .writer_args = args
148+ self .writer_kwargs = kwargs
149+
150+ self .writer : MessageWriter = self ._get_new_writer ()
151+
152+ def on_message_received (self , msg : Message ):
153+ if self .writer .file is not None and self .writer .file .tell () >= self .max_bytes :
154+ self .writer .stop ()
155+ self .file_number += 1
156+ self .writer = self ._get_new_writer ()
157+
158+ self .writer .on_message_received (msg )
159+
160+ def on_error (self , exc : Exception ):
161+ self .writer .on_error (exc )
162+
163+ def stop (self ):
164+ self .writer .stop ()
165+
166+ def _get_new_writer (self ) -> MessageWriter :
167+ filename = self .filename_func (self .file_number )
168+ suffix = pathlib .Path (filename ).suffix .lower ()
169+ try :
170+ writer_class = self .supported_writers [suffix ]
171+ except KeyError :
172+ raise ValueError (
173+ f'Log format "{ suffix } is not supported by RotatingFileLogger."'
174+ )
175+ return writer_class (filename , * self .writer_args , ** self .writer_kwargs )
0 commit comments