11import atexit
2+ import dataclasses
23import logging
34import threading
45import time
1011from pythonlogs .core .settings import get_log_settings
1112from pythonlogs .size_rotating import SizeRotatingLog as _SizeRotatingLogImpl
1213from pythonlogs .timed_rotating import TimedRotatingLog as _TimedRotatingLogImpl
13- from typing import assert_never
1414
1515
1616@dataclass
@@ -217,6 +217,48 @@ def get_memory_limits(cls) -> dict[str, int]:
217217 with cls ._registry_lock :
218218 return {"max_loggers" : cls ._max_loggers , "ttl_seconds" : cls ._logger_ttl }
219219
220+ # Mapping of logger types to their implementation classes and accepted fields
221+ _LOGGER_IMPL = {
222+ LoggerType .BASIC : (
223+ _BasicLogImpl ,
224+ {"level" , "name" , "encoding" , "datefmt" , "timezone" , "showlocation" },
225+ ),
226+ LoggerType .SIZE_ROTATING : (
227+ _SizeRotatingLogImpl ,
228+ {
229+ "level" ,
230+ "name" ,
231+ "directory" ,
232+ "filenames" ,
233+ "maxmbytes" ,
234+ "daystokeep" ,
235+ "encoding" ,
236+ "datefmt" ,
237+ "timezone" ,
238+ "streamhandler" ,
239+ "showlocation" ,
240+ },
241+ ),
242+ LoggerType .TIMED_ROTATING : (
243+ _TimedRotatingLogImpl ,
244+ {
245+ "level" ,
246+ "name" ,
247+ "directory" ,
248+ "filenames" ,
249+ "when" ,
250+ "sufix" ,
251+ "daystokeep" ,
252+ "encoding" ,
253+ "datefmt" ,
254+ "timezone" ,
255+ "streamhandler" ,
256+ "showlocation" ,
257+ "rotateatutc" ,
258+ },
259+ ),
260+ }
261+
220262 @staticmethod
221263 def create_logger (logger_type : LoggerType | str , config : LoggerConfig | None = None , ** kwargs ) -> logging .Logger :
222264 """
@@ -225,7 +267,7 @@ def create_logger(logger_type: LoggerType | str, config: LoggerConfig | None = N
225267 Args:
226268 logger_type: Type of logger to create (LoggerType enum or string)
227269 config: LoggerConfig object with logger parameters
228- **kwargs: Individual logger parameters (for backward compatibility )
270+ **kwargs: Individual logger parameters (kwargs take precedence over config )
229271
230272 Returns:
231273 Configured logger instance
@@ -242,159 +284,21 @@ def create_logger(logger_type: LoggerType | str, config: LoggerConfig | None = N
242284 f"Invalid logger type: { logger_type } . Valid types: { [t .value for t in LoggerType ]} "
243285 ) from err
244286
245- # Merge config and kwargs (kwargs take precedence for backward compatibility )
287+ # Merge config and kwargs (kwargs take precedence)
246288 if config is None :
247289 config = LoggerConfig ()
248-
249- # Create a new config with kwargs overriding config values
250- final_config = LoggerConfig (
251- level = kwargs .get ("level" , config .level ),
252- name = kwargs .get ("name" , config .name ),
253- directory = kwargs .get ("directory" , config .directory ),
254- filenames = kwargs .get ("filenames" , config .filenames ),
255- encoding = kwargs .get ("encoding" , config .encoding ),
256- datefmt = kwargs .get ("datefmt" , config .datefmt ),
257- timezone = kwargs .get ("timezone" , config .timezone ),
258- streamhandler = kwargs .get ("streamhandler" , config .streamhandler ),
259- showlocation = kwargs .get ("showlocation" , config .showlocation ),
260- maxmbytes = kwargs .get ("maxmbytes" , config .maxmbytes ),
261- when = kwargs .get ("when" , config .when ),
262- sufix = kwargs .get ("sufix" , config .sufix ),
263- rotateatutc = kwargs .get ("rotateatutc" , config .rotateatutc ),
264- daystokeep = kwargs .get ("daystokeep" , config .daystokeep ),
265- )
290+ merged = {f .name : kwargs .get (f .name , getattr (config , f .name )) for f in dataclasses .fields (LoggerConfig )}
266291
267292 # Convert enum values to strings for logger classes
268- level_str = final_config .level .value if isinstance (final_config .level , LogLevel ) else final_config .level
269- when_str = final_config .when .value if isinstance (final_config .when , RotateWhen ) else final_config .when
270-
271- # Create logger based on type
272- match logger_type :
273- case LoggerType .BASIC :
274- return _BasicLogImpl (
275- level = level_str ,
276- name = final_config .name ,
277- encoding = final_config .encoding ,
278- datefmt = final_config .datefmt ,
279- timezone = final_config .timezone ,
280- showlocation = final_config .showlocation ,
281- ).init ()
282- case LoggerType .SIZE_ROTATING :
283- return _SizeRotatingLogImpl (
284- level = level_str ,
285- name = final_config .name ,
286- directory = final_config .directory ,
287- filenames = final_config .filenames ,
288- maxmbytes = final_config .maxmbytes ,
289- daystokeep = final_config .daystokeep ,
290- encoding = final_config .encoding ,
291- datefmt = final_config .datefmt ,
292- timezone = final_config .timezone ,
293- streamhandler = final_config .streamhandler ,
294- showlocation = final_config .showlocation ,
295- ).init ()
296- case LoggerType .TIMED_ROTATING :
297- return _TimedRotatingLogImpl (
298- level = level_str ,
299- name = final_config .name ,
300- directory = final_config .directory ,
301- filenames = final_config .filenames ,
302- when = when_str ,
303- sufix = final_config .sufix ,
304- daystokeep = final_config .daystokeep ,
305- encoding = final_config .encoding ,
306- datefmt = final_config .datefmt ,
307- timezone = final_config .timezone ,
308- streamhandler = final_config .streamhandler ,
309- showlocation = final_config .showlocation ,
310- rotateatutc = final_config .rotateatutc ,
311- ).init ()
312- case _ as unreachable : # pragma: no cover
313- assert_never (unreachable )
314-
315- @staticmethod
316- def create_basic_logger (
317- level : LogLevel | str | None = None ,
318- name : str | None = None ,
319- encoding : str | None = None ,
320- datefmt : str | None = None ,
321- timezone : str | None = None ,
322- showlocation : bool | None = None ,
323- ) -> logging .Logger :
324- """Convenience method for creating a basic logger"""
325- return LoggerFactory .create_logger (
326- LoggerType .BASIC ,
327- level = level ,
328- name = name ,
329- encoding = encoding ,
330- datefmt = datefmt ,
331- timezone = timezone ,
332- showlocation = showlocation ,
333- )
334-
335- @staticmethod
336- def create_size_rotating_logger (
337- level : LogLevel | str | None = None ,
338- name : str | None = None ,
339- directory : str | None = None ,
340- filenames : list | tuple | None = None ,
341- maxmbytes : int | None = None ,
342- daystokeep : int | None = None ,
343- encoding : str | None = None ,
344- datefmt : str | None = None ,
345- timezone : str | None = None ,
346- streamhandler : bool | None = None ,
347- showlocation : bool | None = None ,
348- ) -> logging .Logger :
349- """Convenience method for creating a size rotating logger"""
350- return LoggerFactory .create_logger (
351- LoggerType .SIZE_ROTATING ,
352- level = level ,
353- name = name ,
354- directory = directory ,
355- filenames = filenames ,
356- maxmbytes = maxmbytes ,
357- daystokeep = daystokeep ,
358- encoding = encoding ,
359- datefmt = datefmt ,
360- timezone = timezone ,
361- streamhandler = streamhandler ,
362- showlocation = showlocation ,
363- )
293+ if isinstance (merged .get ("level" ), LogLevel ):
294+ merged ["level" ] = merged ["level" ].value
295+ if isinstance (merged .get ("when" ), RotateWhen ):
296+ merged ["when" ] = merged ["when" ].value
364297
365- @staticmethod
366- def create_timed_rotating_logger (
367- level : LogLevel | str | None = None ,
368- name : str | None = None ,
369- directory : str | None = None ,
370- filenames : list | tuple | None = None ,
371- when : RotateWhen | str | None = None ,
372- sufix : str | None = None ,
373- daystokeep : int | None = None ,
374- encoding : str | None = None ,
375- datefmt : str | None = None ,
376- timezone : str | None = None ,
377- streamhandler : bool | None = None ,
378- showlocation : bool | None = None ,
379- rotateatutc : bool | None = None ,
380- ) -> logging .Logger :
381- """Convenience method for creating a timed rotating logger"""
382- return LoggerFactory .create_logger (
383- LoggerType .TIMED_ROTATING ,
384- level = level ,
385- name = name ,
386- directory = directory ,
387- filenames = filenames ,
388- when = when ,
389- sufix = sufix ,
390- daystokeep = daystokeep ,
391- encoding = encoding ,
392- datefmt = datefmt ,
393- timezone = timezone ,
394- streamhandler = streamhandler ,
395- showlocation = showlocation ,
396- rotateatutc = rotateatutc ,
397- )
298+ # Create logger using table-driven dispatch
299+ impl_class , valid_fields = LoggerFactory ._LOGGER_IMPL [logger_type ]
300+ logger_kwargs = {k : v for k , v in merged .items () if k in valid_fields }
301+ return impl_class (** logger_kwargs ).init ()
398302
399303
400304# Public API wrapper classes - act like logging.Logger with context manager support
@@ -439,7 +343,8 @@ def __init__(
439343 timezone : str | None = None ,
440344 showlocation : bool | None = None ,
441345 ):
442- self ._logger = LoggerFactory .create_basic_logger (
346+ self ._logger = LoggerFactory .create_logger (
347+ LoggerType .BASIC ,
443348 level = level ,
444349 name = name ,
445350 encoding = encoding ,
@@ -477,7 +382,8 @@ def __init__(
477382 streamhandler : bool | None = None ,
478383 showlocation : bool | None = None ,
479384 ):
480- self ._logger = LoggerFactory .create_size_rotating_logger (
385+ self ._logger = LoggerFactory .create_logger (
386+ LoggerType .SIZE_ROTATING ,
481387 level = level ,
482388 name = name ,
483389 directory = directory ,
@@ -522,7 +428,8 @@ def __init__(
522428 showlocation : bool | None = None ,
523429 rotateatutc : bool | None = None ,
524430 ):
525- self ._logger = LoggerFactory .create_timed_rotating_logger (
431+ self ._logger = LoggerFactory .create_logger (
432+ LoggerType .TIMED_ROTATING ,
526433 level = level ,
527434 name = name ,
528435 directory = directory ,
0 commit comments