pythonLogs 5.0.1__cp312-cp312-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,32 @@
1
+ # pythonLogs Environment Configuration
2
+ # Copy this file to .env and modify values as needed
3
+
4
+ # Basic Logger Settings
5
+ LOG_LEVEL=DEBUG
6
+ LOG_TIMEZONE=UTC
7
+ LOG_ENCODING=UTF-8
8
+ LOG_APPNAME=app
9
+ LOG_FILENAME=app.log
10
+ LOG_DIRECTORY=/app/logs
11
+ LOG_DAYS_TO_KEEP=30
12
+ LOG_DATE_FORMAT=%Y-%m-%dT%H:%M:%S
13
+ LOG_STREAM_HANDLER=True
14
+ LOG_SHOW_LOCATION=False
15
+
16
+ # Memory Management Settings
17
+ LOG_MAX_LOGGERS=50
18
+ LOG_LOGGER_TTL_SECONDS=1800
19
+
20
+ # SizeRotatingLog Settings
21
+ LOG_MAX_FILE_SIZE_MB=10
22
+
23
+ # TimedRotatingLog Settings
24
+ LOG_ROTATE_WHEN=midnight
25
+ LOG_ROTATE_AT_UTC=True
26
+ LOG_ROTATE_FILE_SUFIX=%Y%m%d
27
+
28
+ # Available LOG_LEVEL values: DEBUG, INFO, WARNING, ERROR, CRITICAL
29
+ # Available LOG_TIMEZONE values: UTC, localtime, or any valid timezone (e.g., America/New_York)
30
+ # Available LOG_ROTATE_WHEN values: midnight, S, M, H, D, W0-W6, daily, hourly, weekly
31
+ # LOG_STREAM_HANDLER: Set to True to enable console output, False to disable
32
+ # LOG_SHOW_LOCATION: Set to True to include filename:function:line in log messages
pythonLogs/__init__.py ADDED
@@ -0,0 +1,91 @@
1
+ import logging
2
+ from importlib.metadata import version
3
+ from typing import Literal, NamedTuple
4
+ from pythonLogs.basic_log import BasicLog
5
+ from pythonLogs.constants import LogLevel, RotateWhen
6
+ from pythonLogs.factory import (
7
+ basic_logger,
8
+ clear_logger_registry,
9
+ create_logger,
10
+ get_or_create_logger,
11
+ get_registered_loggers,
12
+ LoggerFactory,
13
+ LoggerType,
14
+ shutdown_logger,
15
+ size_rotating_logger,
16
+ timed_rotating_logger,
17
+ )
18
+ from pythonLogs.memory_utils import (
19
+ clear_directory_cache,
20
+ clear_formatter_cache,
21
+ force_garbage_collection,
22
+ get_memory_stats,
23
+ optimize_lru_cache_sizes,
24
+ set_directory_cache_limit,
25
+ )
26
+ from pythonLogs.size_rotating import SizeRotatingLog
27
+ from pythonLogs.timed_rotating import TimedRotatingLog
28
+
29
+
30
+ __all__ = (
31
+ "BasicLog",
32
+ "TimedRotatingLog",
33
+ "SizeRotatingLog",
34
+ "LoggerFactory",
35
+ "LoggerType",
36
+ "LogLevel",
37
+ "RotateWhen",
38
+ "create_logger",
39
+ "get_or_create_logger",
40
+ "basic_logger",
41
+ "size_rotating_logger",
42
+ "timed_rotating_logger",
43
+ "clear_logger_registry",
44
+ "get_registered_loggers",
45
+ "shutdown_logger",
46
+ # Memory management utilities
47
+ "get_memory_stats",
48
+ "clear_formatter_cache",
49
+ "clear_directory_cache",
50
+ "force_garbage_collection",
51
+ "optimize_lru_cache_sizes",
52
+ "set_directory_cache_limit",
53
+ )
54
+
55
+ __title__ = "pythonLogs"
56
+ __author__ = "Daniel Costa"
57
+ __email__ = "danieldcsta@gmail.com>"
58
+ __license__ = "MIT"
59
+ __copyright__ = "Copyright 2024-present ddc"
60
+ _req_python_version = (3, 12, 0)
61
+
62
+
63
+ try:
64
+ _version = tuple(int(x) for x in version(__title__).split("."))
65
+ except ModuleNotFoundError:
66
+ _version = (0, 0, 0)
67
+
68
+
69
+ class VersionInfo(NamedTuple):
70
+ major: int
71
+ minor: int
72
+ micro: int
73
+ releaselevel: Literal["alpha", "beta", "candidate", "final"]
74
+ serial: int
75
+
76
+
77
+ __version__ = _version
78
+ __version_info__: VersionInfo = VersionInfo(
79
+ major=__version__[0], minor=__version__[1], micro=__version__[2], releaselevel="final", serial=0
80
+ )
81
+ __req_python_version__: VersionInfo = VersionInfo(
82
+ major=_req_python_version[0],
83
+ minor=_req_python_version[1],
84
+ micro=_req_python_version[2],
85
+ releaselevel="final",
86
+ serial=0,
87
+ )
88
+
89
+ logging.getLogger(__name__).addHandler(logging.NullHandler())
90
+
91
+ del logging, NamedTuple, Literal, VersionInfo, version, _version, _req_python_version
@@ -0,0 +1,68 @@
1
+ import logging
2
+ from typing import Optional
3
+ from pythonLogs.log_utils import get_format, get_level, get_timezone_function
4
+ from pythonLogs.log_utils import cleanup_logger_handlers
5
+ from pythonLogs.memory_utils import register_logger_weakref
6
+ from pythonLogs.settings import get_log_settings
7
+ from pythonLogs.thread_safety import auto_thread_safe
8
+
9
+
10
+ @auto_thread_safe(['init', '_cleanup_logger'])
11
+ class BasicLog:
12
+ """Basic logger with context manager support for automatic resource cleanup."""
13
+
14
+ def __init__(
15
+ self,
16
+ level: Optional[str] = None,
17
+ name: Optional[str] = None,
18
+ encoding: Optional[str] = None,
19
+ datefmt: Optional[str] = None,
20
+ timezone: Optional[str] = None,
21
+ showlocation: Optional[bool] = None,
22
+ ):
23
+ _settings = get_log_settings()
24
+ self.level = get_level(level or _settings.level)
25
+ self.appname = name or _settings.appname
26
+ self.encoding = encoding or _settings.encoding
27
+ self.datefmt = datefmt or _settings.date_format
28
+ self.timezone = timezone or _settings.timezone
29
+ self.showlocation = showlocation or _settings.show_location
30
+ self.logger = None
31
+
32
+ def init(self):
33
+ logger = logging.getLogger(self.appname)
34
+ logger.setLevel(self.level)
35
+ logging.Formatter.converter = get_timezone_function(self.timezone)
36
+ _format = get_format(self.showlocation, self.appname, self.timezone)
37
+
38
+ # Only add handler if logger doesn't have any handlers
39
+ if not logger.handlers:
40
+ handler = logging.StreamHandler()
41
+ formatter = logging.Formatter(_format, datefmt=self.datefmt)
42
+ handler.setFormatter(formatter)
43
+ logger.addHandler(handler)
44
+
45
+ self.logger = logger
46
+ # Register weak reference for memory tracking
47
+ register_logger_weakref(logger)
48
+ return logger
49
+
50
+ def __enter__(self):
51
+ """Context manager entry."""
52
+ if not hasattr(self, 'logger') or self.logger is None:
53
+ self.init()
54
+ return self.logger
55
+
56
+ def __exit__(self, exc_type, exc_val, exc_tb):
57
+ """Context manager exit with automatic cleanup."""
58
+ if hasattr(self, 'logger'):
59
+ self._cleanup_logger(self.logger)
60
+
61
+ def _cleanup_logger(self, logger: logging.Logger) -> None:
62
+ """Clean up logger resources by closing all handlers with thread safety."""
63
+ cleanup_logger_handlers(logger)
64
+
65
+ @staticmethod
66
+ def cleanup_logger(logger: logging.Logger) -> None:
67
+ """Static method for cleaning up logger resources (backward compatibility)."""
68
+ cleanup_logger_handlers(logger)
@@ -0,0 +1,56 @@
1
+ import logging
2
+ from enum import Enum
3
+
4
+ # File and Directory Constants
5
+ MB_TO_BYTES = 1024 * 1024
6
+ DEFAULT_FILE_MODE = 0o755
7
+ DEFAULT_BACKUP_COUNT = 30
8
+
9
+ # Date Format Constants
10
+ DEFAULT_DATE_FORMAT = "%Y-%m-%dT%H:%M:%S"
11
+ DEFAULT_ROTATE_SUFFIX = "%Y%m%d"
12
+
13
+ # Encoding Constants
14
+ DEFAULT_ENCODING = "UTF-8"
15
+
16
+ # Timezone Constants
17
+ DEFAULT_TIMEZONE = "UTC"
18
+
19
+
20
+ class LogLevel(str, Enum):
21
+ """Log levels"""
22
+
23
+ CRITICAL = "CRITICAL"
24
+ CRIT = "CRIT"
25
+ ERROR = "ERROR"
26
+ WARNING = "WARNING"
27
+ WARN = "WARN"
28
+ INFO = "INFO"
29
+ DEBUG = "DEBUG"
30
+
31
+
32
+ class RotateWhen(str, Enum):
33
+ """Rotation timing options for TimedRotatingLog"""
34
+
35
+ MIDNIGHT = "midnight"
36
+ MONDAY = "W0"
37
+ TUESDAY = "W1"
38
+ WEDNESDAY = "W2"
39
+ THURSDAY = "W3"
40
+ FRIDAY = "W4"
41
+ SATURDAY = "W5"
42
+ SUNDAY = "W6"
43
+ HOURLY = "H"
44
+ DAILY = "D"
45
+
46
+
47
+ # Level mapping for performance optimization
48
+ LEVEL_MAP = {
49
+ LogLevel.DEBUG.value.lower(): logging.DEBUG,
50
+ LogLevel.WARNING.value.lower(): logging.WARNING,
51
+ LogLevel.WARN.value.lower(): logging.WARNING,
52
+ LogLevel.ERROR.value.lower(): logging.ERROR,
53
+ LogLevel.CRITICAL.value.lower(): logging.CRITICAL,
54
+ LogLevel.CRIT.value.lower(): logging.CRITICAL,
55
+ LogLevel.INFO.value.lower(): logging.INFO,
56
+ }
pythonLogs/factory.py ADDED
@@ -0,0 +1,446 @@
1
+ import atexit
2
+ import logging
3
+ import threading
4
+ import time
5
+ from dataclasses import dataclass
6
+ from enum import Enum
7
+ from typing import Dict, Optional, Tuple, Union
8
+ from pythonLogs.basic_log import BasicLog
9
+ from pythonLogs.constants import LogLevel, RotateWhen
10
+ from pythonLogs.log_utils import cleanup_logger_handlers
11
+ from pythonLogs.settings import get_log_settings
12
+ from pythonLogs.size_rotating import SizeRotatingLog
13
+ from pythonLogs.timed_rotating import TimedRotatingLog
14
+
15
+
16
+ @dataclass
17
+ class LoggerConfig:
18
+ """Configuration class to group logger parameters"""
19
+
20
+ level: Optional[Union[LogLevel, str]] = None
21
+ name: Optional[str] = None
22
+ directory: Optional[str] = None
23
+ filenames: Optional[list | tuple] = None
24
+ encoding: Optional[str] = None
25
+ datefmt: Optional[str] = None
26
+ timezone: Optional[str] = None
27
+ streamhandler: Optional[bool] = None
28
+ showlocation: Optional[bool] = None
29
+ maxmbytes: Optional[int] = None
30
+ when: Optional[Union[RotateWhen, str]] = None
31
+ sufix: Optional[str] = None
32
+ rotateatutc: Optional[bool] = None
33
+ daystokeep: Optional[int] = None
34
+
35
+
36
+ class LoggerType(str, Enum):
37
+ """Available logger types"""
38
+
39
+ BASIC = "basic"
40
+ SIZE_ROTATING = "size_rotating"
41
+ TIMED_ROTATING = "timed_rotating"
42
+
43
+
44
+ class LoggerFactory:
45
+ """Factory for creating different types of loggers with optimized instantiation and memory management"""
46
+
47
+ # Logger registry for reusing loggers by name with timestamp tracking
48
+ _logger_registry: Dict[str, Tuple[logging.Logger, float]] = {}
49
+ # Thread lock for registry access
50
+ _registry_lock = threading.RLock()
51
+ # Memory optimization settings
52
+ _max_loggers = 100 # Maximum number of cached loggers
53
+ _logger_ttl = 3600 # Logger TTL in seconds (1 hour)
54
+ _initialized = False # Flag to track if memory limits have been initialized
55
+ _atexit_registered = False # Flag to track if atexit cleanup is registered
56
+
57
+ @classmethod
58
+ def _ensure_initialized(cls) -> None:
59
+ """Ensure memory limits are initialized from settings on first use."""
60
+ if not cls._initialized:
61
+ settings = get_log_settings()
62
+ cls._max_loggers = settings.max_loggers
63
+ cls._logger_ttl = settings.logger_ttl_seconds
64
+ cls._initialized = True
65
+
66
+ # Register atexit cleanup on first use
67
+ if not cls._atexit_registered:
68
+ atexit.register(cls._atexit_cleanup)
69
+ cls._atexit_registered = True
70
+
71
+ @classmethod
72
+ def get_or_create_logger(
73
+ cls,
74
+ logger_type: Union[LoggerType, str],
75
+ name: Optional[str] = None,
76
+ **kwargs,
77
+ ) -> logging.Logger:
78
+ """
79
+ Get an existing logger from registry or create a new one.
80
+ Loggers are cached by name for performance.
81
+
82
+ Args:
83
+ logger_type: Type of logger to create
84
+ name: Logger name (used as cache key)
85
+ **kwargs: Additional logger configuration
86
+
87
+ Returns:
88
+ Cached or newly created logger instance
89
+ """
90
+ # Use the default name if none provided
91
+ if name is None:
92
+ name = get_log_settings().appname
93
+
94
+ # Thread-safe check-and-create operation
95
+ with cls._registry_lock:
96
+ # Initialize memory limits from settings on first use
97
+ cls._ensure_initialized()
98
+
99
+ # Clean up expired loggers first
100
+ cls._cleanup_expired_loggers()
101
+
102
+ # Check if logger already exists in the registry
103
+ if name in cls._logger_registry:
104
+ logger, _ = cls._logger_registry[name]
105
+ # Update timestamp for LRU tracking
106
+ cls._logger_registry[name] = (logger, time.time())
107
+ return logger
108
+
109
+ # Ensure registry size limit
110
+ cls._enforce_size_limit()
111
+
112
+ # Create a new logger and cache it with timestamp
113
+ logger = cls.create_logger(logger_type, name=name, **kwargs)
114
+ cls._logger_registry[name] = (logger, time.time())
115
+ return logger
116
+
117
+ @classmethod
118
+ def clear_registry(cls) -> None:
119
+ """Clear the logger registry with proper resource cleanup."""
120
+ with cls._registry_lock:
121
+ for logger, _ in cls._logger_registry.values():
122
+ cls._cleanup_logger(logger)
123
+ cls._logger_registry.clear()
124
+
125
+ @classmethod
126
+ def _cleanup_expired_loggers(cls) -> None:
127
+ """Remove expired loggers from registry based on TTL."""
128
+ current_time = time.time()
129
+ expired_keys = []
130
+
131
+ for name, (logger, timestamp) in cls._logger_registry.items():
132
+ if current_time - timestamp > cls._logger_ttl:
133
+ expired_keys.append(name)
134
+ cls._cleanup_logger(logger)
135
+
136
+ for key in expired_keys:
137
+ cls._logger_registry.pop(key, None)
138
+
139
+ @classmethod
140
+ def _enforce_size_limit(cls) -> None:
141
+ """Enforce maximum registry size by removing the oldest entries (LRU eviction)."""
142
+ if cls._max_loggers <= 0:
143
+ # Special case: if max_loggers is 0 or negative, clear all
144
+ cls.clear_registry()
145
+ return
146
+
147
+ if len(cls._logger_registry) >= cls._max_loggers:
148
+ # Sort by timestamp (oldest first) and remove the oldest entries
149
+ sorted_entries = sorted(cls._logger_registry.items(), key=lambda x: x[1][1])
150
+ entries_to_remove = len(sorted_entries) - cls._max_loggers + 1
151
+
152
+ for i in range(min(entries_to_remove, len(sorted_entries))):
153
+ name, (logger, _) = sorted_entries[i]
154
+ cls._cleanup_logger(logger)
155
+ cls._logger_registry.pop(name, None)
156
+
157
+ @classmethod
158
+ def set_memory_limits(cls, max_loggers: int = 100, ttl_seconds: int = 3600) -> None:
159
+ """Configure memory management limits for the logger registry at runtime.
160
+
161
+ Args:
162
+ max_loggers: Maximum number of cached loggers
163
+ ttl_seconds: Time-to-live for cached loggers in seconds
164
+ """
165
+ with cls._registry_lock:
166
+ cls._max_loggers = max_loggers
167
+ cls._logger_ttl = ttl_seconds
168
+ cls._initialized = True # Mark as manually configured
169
+ # Clean up immediately with new settings
170
+ cls._cleanup_expired_loggers()
171
+ cls._enforce_size_limit()
172
+
173
+ @classmethod
174
+ def _atexit_cleanup(cls) -> None:
175
+ """Cleanup function registered with atexit to ensure proper resource cleanup."""
176
+ try:
177
+ cls.clear_registry()
178
+ except Exception:
179
+ # Silently ignore exceptions during shutdown cleanup
180
+ pass
181
+
182
+ @staticmethod
183
+ def _cleanup_logger(logger: logging.Logger) -> None:
184
+ """Clean up logger resources by closing all handlers."""
185
+ cleanup_logger_handlers(logger)
186
+
187
+ @classmethod
188
+ def shutdown_logger(cls, name: str) -> bool:
189
+ """Shutdown and remove a specific logger from registry.
190
+
191
+ Args:
192
+ name: Logger name to shut down
193
+
194
+ Returns:
195
+ True if logger was found and shutdown, False otherwise
196
+ """
197
+ with cls._registry_lock:
198
+ if name in cls._logger_registry:
199
+ logger, _ = cls._logger_registry.pop(name)
200
+ cls._cleanup_logger(logger)
201
+ return True
202
+ return False
203
+
204
+ @classmethod
205
+ def get_registered_loggers(cls) -> dict[str, logging.Logger]:
206
+ """Get all registered loggers. Returns a copy of the registry."""
207
+ with cls._registry_lock:
208
+ return {name: logger for name, (logger, _) in cls._logger_registry.items()}
209
+
210
+ @classmethod
211
+ def get_memory_limits(cls) -> dict[str, int]:
212
+ """Get current memory management limits.
213
+
214
+ Returns:
215
+ Dictionary with current max_loggers and ttl_seconds settings
216
+ """
217
+ with cls._registry_lock:
218
+ return {
219
+ 'max_loggers': cls._max_loggers,
220
+ 'ttl_seconds': cls._logger_ttl
221
+ }
222
+
223
+ @staticmethod
224
+ def create_logger(
225
+ logger_type: Union[LoggerType, str], config: Optional[LoggerConfig] = None, **kwargs
226
+ ) -> logging.Logger:
227
+ """
228
+ Factory method to create loggers based on type.
229
+
230
+ Args:
231
+ logger_type: Type of logger to create (LoggerType enum or string)
232
+ config: LoggerConfig object with logger parameters
233
+ **kwargs: Individual logger parameters (for backward compatibility)
234
+
235
+ Returns:
236
+ Configured logger instance
237
+
238
+ Raises:
239
+ ValueError: If invalid logger_type is provided
240
+ """
241
+ # Convert string to enum if needed
242
+ if isinstance(logger_type, str):
243
+ try:
244
+ logger_type = LoggerType(logger_type.lower())
245
+ except ValueError:
246
+ raise ValueError(f"Invalid logger type: {logger_type}. Valid types: {[t.value for t in LoggerType]}")
247
+
248
+ # Merge config and kwargs (kwargs take precedence for backward compatibility)
249
+ if config is None:
250
+ config = LoggerConfig()
251
+
252
+ # Create a new config with kwargs overriding config values
253
+ final_config = LoggerConfig(
254
+ level=kwargs.get('level', config.level),
255
+ name=kwargs.get('name', config.name),
256
+ directory=kwargs.get('directory', config.directory),
257
+ filenames=kwargs.get('filenames', config.filenames),
258
+ encoding=kwargs.get('encoding', config.encoding),
259
+ datefmt=kwargs.get('datefmt', config.datefmt),
260
+ timezone=kwargs.get('timezone', config.timezone),
261
+ streamhandler=kwargs.get('streamhandler', config.streamhandler),
262
+ showlocation=kwargs.get('showlocation', config.showlocation),
263
+ maxmbytes=kwargs.get('maxmbytes', config.maxmbytes),
264
+ when=kwargs.get('when', config.when),
265
+ sufix=kwargs.get('sufix', config.sufix),
266
+ rotateatutc=kwargs.get('rotateatutc', config.rotateatutc),
267
+ daystokeep=kwargs.get('daystokeep', config.daystokeep),
268
+ )
269
+
270
+ # Convert enum values to strings for logger classes
271
+ level_str = final_config.level.value if isinstance(final_config.level, LogLevel) else final_config.level
272
+ when_str = final_config.when.value if isinstance(final_config.when, RotateWhen) else final_config.when
273
+
274
+ # Create logger based on type
275
+ match logger_type:
276
+ case LoggerType.BASIC:
277
+ logger_instance = BasicLog(
278
+ level=level_str,
279
+ name=final_config.name,
280
+ encoding=final_config.encoding,
281
+ datefmt=final_config.datefmt,
282
+ timezone=final_config.timezone,
283
+ showlocation=final_config.showlocation,
284
+ )
285
+
286
+ case LoggerType.SIZE_ROTATING:
287
+ logger_instance = SizeRotatingLog(
288
+ level=level_str,
289
+ name=final_config.name,
290
+ directory=final_config.directory,
291
+ filenames=final_config.filenames,
292
+ maxmbytes=final_config.maxmbytes,
293
+ daystokeep=final_config.daystokeep,
294
+ encoding=final_config.encoding,
295
+ datefmt=final_config.datefmt,
296
+ timezone=final_config.timezone,
297
+ streamhandler=final_config.streamhandler,
298
+ showlocation=final_config.showlocation,
299
+ )
300
+
301
+ case LoggerType.TIMED_ROTATING:
302
+ logger_instance = TimedRotatingLog(
303
+ level=level_str,
304
+ name=final_config.name,
305
+ directory=final_config.directory,
306
+ filenames=final_config.filenames,
307
+ when=when_str,
308
+ sufix=final_config.sufix,
309
+ daystokeep=final_config.daystokeep,
310
+ encoding=final_config.encoding,
311
+ datefmt=final_config.datefmt,
312
+ timezone=final_config.timezone,
313
+ streamhandler=final_config.streamhandler,
314
+ showlocation=final_config.showlocation,
315
+ rotateatutc=final_config.rotateatutc,
316
+ )
317
+
318
+ case _:
319
+ raise ValueError(f"Unsupported logger type: {logger_type}")
320
+
321
+ return logger_instance.init()
322
+
323
+ @staticmethod
324
+ def create_basic_logger(
325
+ level: Optional[Union[LogLevel, str]] = None,
326
+ name: Optional[str] = None,
327
+ encoding: Optional[str] = None,
328
+ datefmt: Optional[str] = None,
329
+ timezone: Optional[str] = None,
330
+ showlocation: Optional[bool] = None,
331
+ ) -> logging.Logger:
332
+ """Convenience method for creating a basic logger"""
333
+ return LoggerFactory.create_logger(
334
+ LoggerType.BASIC,
335
+ level=level,
336
+ name=name,
337
+ encoding=encoding,
338
+ datefmt=datefmt,
339
+ timezone=timezone,
340
+ showlocation=showlocation,
341
+ )
342
+
343
+ @staticmethod
344
+ def create_size_rotating_logger(
345
+ level: Optional[Union[LogLevel, str]] = None,
346
+ name: Optional[str] = None,
347
+ directory: Optional[str] = None,
348
+ filenames: Optional[list | tuple] = None,
349
+ maxmbytes: Optional[int] = None,
350
+ daystokeep: Optional[int] = None,
351
+ encoding: Optional[str] = None,
352
+ datefmt: Optional[str] = None,
353
+ timezone: Optional[str] = None,
354
+ streamhandler: Optional[bool] = None,
355
+ showlocation: Optional[bool] = None,
356
+ ) -> logging.Logger:
357
+ """Convenience method for creating a size rotating logger"""
358
+ return LoggerFactory.create_logger(
359
+ LoggerType.SIZE_ROTATING,
360
+ level=level,
361
+ name=name,
362
+ directory=directory,
363
+ filenames=filenames,
364
+ maxmbytes=maxmbytes,
365
+ daystokeep=daystokeep,
366
+ encoding=encoding,
367
+ datefmt=datefmt,
368
+ timezone=timezone,
369
+ streamhandler=streamhandler,
370
+ showlocation=showlocation,
371
+ )
372
+
373
+ @staticmethod
374
+ def create_timed_rotating_logger(
375
+ level: Optional[Union[LogLevel, str]] = None,
376
+ name: Optional[str] = None,
377
+ directory: Optional[str] = None,
378
+ filenames: Optional[list | tuple] = None,
379
+ when: Optional[Union[RotateWhen, str]] = None,
380
+ sufix: Optional[str] = None,
381
+ daystokeep: Optional[int] = None,
382
+ encoding: Optional[str] = None,
383
+ datefmt: Optional[str] = None,
384
+ timezone: Optional[str] = None,
385
+ streamhandler: Optional[bool] = None,
386
+ showlocation: Optional[bool] = None,
387
+ rotateatutc: Optional[bool] = None,
388
+ ) -> logging.Logger:
389
+ """Convenience method for creating a timed rotating logger"""
390
+ return LoggerFactory.create_logger(
391
+ LoggerType.TIMED_ROTATING,
392
+ level=level,
393
+ name=name,
394
+ directory=directory,
395
+ filenames=filenames,
396
+ when=when,
397
+ sufix=sufix,
398
+ daystokeep=daystokeep,
399
+ encoding=encoding,
400
+ datefmt=datefmt,
401
+ timezone=timezone,
402
+ streamhandler=streamhandler,
403
+ showlocation=showlocation,
404
+ rotateatutc=rotateatutc,
405
+ )
406
+
407
+
408
+ # Convenience functions for backward compatibility and easier usage
409
+ def create_logger(logger_type: Union[LoggerType, str], **kwargs) -> logging.Logger:
410
+ """Convenience function to create a logger using the factory"""
411
+ return LoggerFactory.create_logger(logger_type, **kwargs)
412
+
413
+
414
+ def get_or_create_logger(logger_type: Union[LoggerType, str], **kwargs) -> logging.Logger:
415
+ """Convenience function to get cached or create a logger using the factory"""
416
+ return LoggerFactory.get_or_create_logger(logger_type, **kwargs)
417
+
418
+
419
+ def basic_logger(**kwargs) -> logging.Logger:
420
+ """Convenience function to create a basic logger"""
421
+ return LoggerFactory.create_basic_logger(**kwargs)
422
+
423
+
424
+ def size_rotating_logger(**kwargs) -> logging.Logger:
425
+ """Convenience function to create a size rotating logger"""
426
+ return LoggerFactory.create_size_rotating_logger(**kwargs)
427
+
428
+
429
+ def timed_rotating_logger(**kwargs) -> logging.Logger:
430
+ """Convenience function to create a timed rotating logger"""
431
+ return LoggerFactory.create_timed_rotating_logger(**kwargs)
432
+
433
+
434
+ def clear_logger_registry() -> None:
435
+ """Convenience function to clear the logger registry with proper cleanup"""
436
+ LoggerFactory.clear_registry()
437
+
438
+
439
+ def shutdown_logger(name: str) -> bool:
440
+ """Convenience function to shut down a specific logger"""
441
+ return LoggerFactory.shutdown_logger(name)
442
+
443
+
444
+ def get_registered_loggers() -> dict[str, logging.Logger]:
445
+ """Convenience function to get all registered loggers"""
446
+ return LoggerFactory.get_registered_loggers()