6464
6565try :
6666 # pylint: disable=deprecated-class
67- from typing import Optional , Hashable
67+ from typing import Optional , Hashable , Dict
6868 from typing_extensions import Protocol
6969
7070 class WriteableStream (Protocol ):
@@ -148,12 +148,85 @@ def _logRecordFactory(name, level, msg, args):
148148 return LogRecord (name , level , _level_for (level ), msg , time .monotonic (), args )
149149
150150
151+ class Formatter :
152+ """
153+ Responsible for converting a LogRecord to an output string to be
154+ interpreted by a human or external system.
155+
156+ Only implements a sub-set of CPython logging.Formatter behavior,
157+ but retains all the same arguments in order to match the API.
158+
159+ The only init arguments currently supported are: fmt, defaults and
160+ style. All others are currently ignored
161+
162+ The only two styles currently supported are '%' and '{'. The default
163+ style is '{'
164+ """
165+
166+ def __init__ ( # pylint: disable=too-many-arguments
167+ self ,
168+ fmt : Optional [str ] = None ,
169+ datefmt : Optional [str ] = None ,
170+ style : str = "%" ,
171+ validate : bool = True ,
172+ defaults : Dict = None ,
173+ ):
174+ self .fmt = fmt
175+ self .datefmt = datefmt
176+ self .style = style
177+ if self .style not in ("{" , "%" ):
178+ raise ValueError (
179+ "Only '%' and '{' formatting style are supported at this time."
180+ )
181+
182+ self .validate = validate
183+ self .defaults = defaults
184+
185+ def format (self , record : LogRecord ) -> str :
186+ """
187+ Format the given LogRecord into an output string
188+ """
189+ if self .fmt is None :
190+ return record .msg
191+
192+ vals = {
193+ "name" : record .name ,
194+ "levelno" : record .levelno ,
195+ "levelname" : record .levelname ,
196+ "message" : record .msg ,
197+ "created" : record .created ,
198+ "args" : record .args ,
199+ }
200+ if "{asctime}" in self .fmt or "%(asctime)s" in self .fmt :
201+ now = time .localtime ()
202+ # pylint: disable=line-too-long
203+ vals [
204+ "asctime"
205+ ] = f"{ now .tm_year } -{ now .tm_mon :02d} -{ now .tm_mday :02d} { now .tm_hour :02d} :{ now .tm_min :02d} :{ now .tm_sec :02d} "
206+
207+ if self .defaults :
208+ for key , val in self .defaults .items ():
209+ if key not in vals :
210+ vals [key ] = val
211+
212+ if self .style not in ("{" , "%" ):
213+ raise ValueError (
214+ "Only '%' and '{' formatting style are supported at this time."
215+ )
216+
217+ if self .style == "%" :
218+ return self .fmt % vals
219+
220+ return self .fmt .format (** vals )
221+
222+
151223class Handler :
152224 """Base logging message handler."""
153225
154226 def __init__ (self , level : int = NOTSET ) -> None :
155227 """Create Handler instance"""
156228 self .level = level
229+ self .formatter = None
157230
158231 def setLevel (self , level : int ) -> None :
159232 """
@@ -167,7 +240,8 @@ def format(self, record: LogRecord) -> str:
167240
168241 :param record: The record (message object) to be logged
169242 """
170-
243+ if self .formatter :
244+ return self .formatter .format (record )
171245 return f"{ record .created :<0.3f} : { record .levelname } - { record .msg } "
172246
173247 def emit (self , record : LogRecord ) -> None :
@@ -182,6 +256,12 @@ def emit(self, record: LogRecord) -> None:
182256 def flush (self ) -> None :
183257 """Placeholder for flush function in subclasses."""
184258
259+ def setFormatter (self , formatter : Formatter ) -> None :
260+ """
261+ Set the Formatter to be used by this Handler.
262+ """
263+ self .formatter = formatter
264+
185265
186266# pylint: disable=too-few-public-methods
187267class StreamHandler (Handler ):
0 commit comments