pythonLogs 4.0.3__tar.gz → 4.0.4__tar.gz
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.
- {pythonlogs-4.0.3 → pythonlogs-4.0.4}/PKG-INFO +1 -59
- {pythonlogs-4.0.3 → pythonlogs-4.0.4}/README.md +0 -58
- {pythonlogs-4.0.3 → pythonlogs-4.0.4}/pyproject.toml +1 -1
- {pythonlogs-4.0.3 → pythonlogs-4.0.4}/pythonLogs/basic_log.py +3 -5
- {pythonlogs-4.0.3 → pythonlogs-4.0.4}/pythonLogs/size_rotating.py +3 -5
- pythonlogs-4.0.4/pythonLogs/thread_safety.py +135 -0
- {pythonlogs-4.0.3 → pythonlogs-4.0.4}/pythonLogs/timed_rotating.py +3 -5
- {pythonlogs-4.0.3 → pythonlogs-4.0.4}/LICENSE +0 -0
- {pythonlogs-4.0.3 → pythonlogs-4.0.4}/pythonLogs/.env.example +0 -0
- {pythonlogs-4.0.3 → pythonlogs-4.0.4}/pythonLogs/__init__.py +0 -0
- {pythonlogs-4.0.3 → pythonlogs-4.0.4}/pythonLogs/constants.py +0 -0
- {pythonlogs-4.0.3 → pythonlogs-4.0.4}/pythonLogs/factory.py +0 -0
- {pythonlogs-4.0.3 → pythonlogs-4.0.4}/pythonLogs/log_utils.py +0 -0
- {pythonlogs-4.0.3 → pythonlogs-4.0.4}/pythonLogs/memory_utils.py +0 -0
- {pythonlogs-4.0.3 → pythonlogs-4.0.4}/pythonLogs/settings.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pythonLogs
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.4
|
|
4
4
|
Summary: High-performance Python logging library with file rotation and optimized caching for better performance
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: python3,python-3,python,log,logging,logger,logutils,log-utils,pythonLogs
|
|
@@ -58,7 +58,6 @@ High-performance Python logging library with file rotation and optimized caching
|
|
|
58
58
|
- [Memory Management](#memory-management)
|
|
59
59
|
- [Flexible Configuration Options](#flexible-configuration-options)
|
|
60
60
|
- [Migration Guide](#migration-guide)
|
|
61
|
-
- [Performance Improvements](#performance-improvements)
|
|
62
61
|
- [Development](#source-code)
|
|
63
62
|
- [Run Tests and Get Coverage Report using Poe](#run-tests-and-get-coverage-report-using-poe)
|
|
64
63
|
- [License](#license)
|
|
@@ -476,40 +475,6 @@ registered = LoggerFactory.get_registered_loggers()
|
|
|
476
475
|
print(f"Currently registered: {list(registered.keys())}")
|
|
477
476
|
```
|
|
478
477
|
|
|
479
|
-
## Thread-Safe Operations
|
|
480
|
-
All memory management operations are thread-safe and can be used safely in multi-threaded applications:
|
|
481
|
-
|
|
482
|
-
```python
|
|
483
|
-
import threading
|
|
484
|
-
from pythonLogs import size_rotating_logger, clear_logger_registry
|
|
485
|
-
|
|
486
|
-
def worker_function(worker_id):
|
|
487
|
-
# Each thread can safely create and use loggers
|
|
488
|
-
logger = size_rotating_logger(
|
|
489
|
-
name=f"worker_{worker_id}",
|
|
490
|
-
directory="/app/logs"
|
|
491
|
-
)
|
|
492
|
-
|
|
493
|
-
with logger as log:
|
|
494
|
-
log.info(f"Worker {worker_id} started")
|
|
495
|
-
# Automatic cleanup per thread
|
|
496
|
-
|
|
497
|
-
# Create multiple threads - all operations are thread-safe
|
|
498
|
-
threads = []
|
|
499
|
-
for i in range(10):
|
|
500
|
-
thread = threading.Thread(target=worker_function, args=(i,))
|
|
501
|
-
threads.append(thread)
|
|
502
|
-
thread.start()
|
|
503
|
-
|
|
504
|
-
# Wait for completion and clean up
|
|
505
|
-
for thread in threads:
|
|
506
|
-
thread.join()
|
|
507
|
-
|
|
508
|
-
# Safe to clear registry from main thread
|
|
509
|
-
clear_logger_registry()
|
|
510
|
-
```
|
|
511
|
-
|
|
512
|
-
|
|
513
478
|
# Flexible Configuration Options
|
|
514
479
|
You can use either enums (for type safety) or strings (for simplicity):
|
|
515
480
|
|
|
@@ -582,25 +547,6 @@ timed_logger = timed_rotating_logger(level=LogLevel.WARNING, name="app", directo
|
|
|
582
547
|
- 🔧 **Cleaner API** without manual `.init()` calls
|
|
583
548
|
- 📚 **Centralized configuration** through factory pattern
|
|
584
549
|
|
|
585
|
-
# Performance Improvements
|
|
586
|
-
|
|
587
|
-
## Benchmarks
|
|
588
|
-
The factory pattern with optimizations provides significant performance improvements:
|
|
589
|
-
|
|
590
|
-
| Feature | Improvement | Benefit |
|
|
591
|
-
|---------|-------------|---------|
|
|
592
|
-
| Logger Registry | 90%+ faster | Cached logger instances |
|
|
593
|
-
| Settings Caching | ~85% faster | Reused configuration objects |
|
|
594
|
-
| Directory Validation | ~75% faster | Cached permission checks |
|
|
595
|
-
| Timezone Operations | ~60% faster | Cached timezone functions |
|
|
596
|
-
|
|
597
|
-
## Performance Test Results
|
|
598
|
-
```python
|
|
599
|
-
# Create 100 loggers - Performance comparison
|
|
600
|
-
# Legacy method: ~0.045 seconds
|
|
601
|
-
# Factory pattern: ~0.004 seconds
|
|
602
|
-
# Improvement: 91% faster ⚡
|
|
603
|
-
```
|
|
604
550
|
|
|
605
551
|
# Source Code
|
|
606
552
|
### Build
|
|
@@ -609,7 +555,6 @@ poetry build -f wheel
|
|
|
609
555
|
```
|
|
610
556
|
|
|
611
557
|
|
|
612
|
-
|
|
613
558
|
# Run Tests and Get Coverage Report using Poe
|
|
614
559
|
```shell
|
|
615
560
|
poetry update --with test
|
|
@@ -617,13 +562,10 @@ poe test
|
|
|
617
562
|
```
|
|
618
563
|
|
|
619
564
|
|
|
620
|
-
|
|
621
565
|
# License
|
|
622
566
|
Released under the [MIT License](LICENSE)
|
|
623
567
|
|
|
624
568
|
|
|
625
|
-
|
|
626
|
-
|
|
627
569
|
# Buy me a cup of coffee
|
|
628
570
|
+ [GitHub Sponsor](https://github.com/sponsors/ddc)
|
|
629
571
|
+ [ko-fi](https://ko-fi.com/ddcsta)
|
|
@@ -28,7 +28,6 @@ High-performance Python logging library with file rotation and optimized caching
|
|
|
28
28
|
- [Memory Management](#memory-management)
|
|
29
29
|
- [Flexible Configuration Options](#flexible-configuration-options)
|
|
30
30
|
- [Migration Guide](#migration-guide)
|
|
31
|
-
- [Performance Improvements](#performance-improvements)
|
|
32
31
|
- [Development](#source-code)
|
|
33
32
|
- [Run Tests and Get Coverage Report using Poe](#run-tests-and-get-coverage-report-using-poe)
|
|
34
33
|
- [License](#license)
|
|
@@ -446,40 +445,6 @@ registered = LoggerFactory.get_registered_loggers()
|
|
|
446
445
|
print(f"Currently registered: {list(registered.keys())}")
|
|
447
446
|
```
|
|
448
447
|
|
|
449
|
-
## Thread-Safe Operations
|
|
450
|
-
All memory management operations are thread-safe and can be used safely in multi-threaded applications:
|
|
451
|
-
|
|
452
|
-
```python
|
|
453
|
-
import threading
|
|
454
|
-
from pythonLogs import size_rotating_logger, clear_logger_registry
|
|
455
|
-
|
|
456
|
-
def worker_function(worker_id):
|
|
457
|
-
# Each thread can safely create and use loggers
|
|
458
|
-
logger = size_rotating_logger(
|
|
459
|
-
name=f"worker_{worker_id}",
|
|
460
|
-
directory="/app/logs"
|
|
461
|
-
)
|
|
462
|
-
|
|
463
|
-
with logger as log:
|
|
464
|
-
log.info(f"Worker {worker_id} started")
|
|
465
|
-
# Automatic cleanup per thread
|
|
466
|
-
|
|
467
|
-
# Create multiple threads - all operations are thread-safe
|
|
468
|
-
threads = []
|
|
469
|
-
for i in range(10):
|
|
470
|
-
thread = threading.Thread(target=worker_function, args=(i,))
|
|
471
|
-
threads.append(thread)
|
|
472
|
-
thread.start()
|
|
473
|
-
|
|
474
|
-
# Wait for completion and clean up
|
|
475
|
-
for thread in threads:
|
|
476
|
-
thread.join()
|
|
477
|
-
|
|
478
|
-
# Safe to clear registry from main thread
|
|
479
|
-
clear_logger_registry()
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
|
|
483
448
|
# Flexible Configuration Options
|
|
484
449
|
You can use either enums (for type safety) or strings (for simplicity):
|
|
485
450
|
|
|
@@ -552,25 +517,6 @@ timed_logger = timed_rotating_logger(level=LogLevel.WARNING, name="app", directo
|
|
|
552
517
|
- 🔧 **Cleaner API** without manual `.init()` calls
|
|
553
518
|
- 📚 **Centralized configuration** through factory pattern
|
|
554
519
|
|
|
555
|
-
# Performance Improvements
|
|
556
|
-
|
|
557
|
-
## Benchmarks
|
|
558
|
-
The factory pattern with optimizations provides significant performance improvements:
|
|
559
|
-
|
|
560
|
-
| Feature | Improvement | Benefit |
|
|
561
|
-
|---------|-------------|---------|
|
|
562
|
-
| Logger Registry | 90%+ faster | Cached logger instances |
|
|
563
|
-
| Settings Caching | ~85% faster | Reused configuration objects |
|
|
564
|
-
| Directory Validation | ~75% faster | Cached permission checks |
|
|
565
|
-
| Timezone Operations | ~60% faster | Cached timezone functions |
|
|
566
|
-
|
|
567
|
-
## Performance Test Results
|
|
568
|
-
```python
|
|
569
|
-
# Create 100 loggers - Performance comparison
|
|
570
|
-
# Legacy method: ~0.045 seconds
|
|
571
|
-
# Factory pattern: ~0.004 seconds
|
|
572
|
-
# Improvement: 91% faster ⚡
|
|
573
|
-
```
|
|
574
520
|
|
|
575
521
|
# Source Code
|
|
576
522
|
### Build
|
|
@@ -579,7 +525,6 @@ poetry build -f wheel
|
|
|
579
525
|
```
|
|
580
526
|
|
|
581
527
|
|
|
582
|
-
|
|
583
528
|
# Run Tests and Get Coverage Report using Poe
|
|
584
529
|
```shell
|
|
585
530
|
poetry update --with test
|
|
@@ -587,13 +532,10 @@ poe test
|
|
|
587
532
|
```
|
|
588
533
|
|
|
589
534
|
|
|
590
|
-
|
|
591
535
|
# License
|
|
592
536
|
Released under the [MIT License](LICENSE)
|
|
593
537
|
|
|
594
538
|
|
|
595
|
-
|
|
596
|
-
|
|
597
539
|
# Buy me a cup of coffee
|
|
598
540
|
+ [GitHub Sponsor](https://github.com/sponsors/ddc)
|
|
599
541
|
+ [ko-fi](https://ko-fi.com/ddcsta)
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "pythonLogs"
|
|
7
|
-
version = "4.0.
|
|
7
|
+
version = "4.0.4"
|
|
8
8
|
description = "High-performance Python logging library with file rotation and optimized caching for better performance"
|
|
9
9
|
license = "MIT"
|
|
10
10
|
readme = "README.md"
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
2
|
import logging
|
|
3
|
-
import threading
|
|
4
3
|
from typing import Optional
|
|
5
4
|
from pythonLogs.log_utils import get_format, get_level, get_timezone_function
|
|
6
5
|
from pythonLogs.memory_utils import cleanup_logger_handlers, register_logger_weakref
|
|
7
6
|
from pythonLogs.settings import get_log_settings
|
|
7
|
+
from pythonLogs.thread_safety import auto_thread_safe
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
@auto_thread_safe(['init', '_cleanup_logger'])
|
|
10
11
|
class BasicLog:
|
|
11
12
|
"""Basic logger with context manager support for automatic resource cleanup."""
|
|
12
13
|
|
|
@@ -27,8 +28,6 @@ class BasicLog:
|
|
|
27
28
|
self.timezone = timezone or _settings.timezone
|
|
28
29
|
self.showlocation = showlocation or _settings.show_location
|
|
29
30
|
self.logger = None
|
|
30
|
-
# Instance-level lock for thread safety
|
|
31
|
-
self._lock = threading.Lock()
|
|
32
31
|
|
|
33
32
|
def init(self):
|
|
34
33
|
logger = logging.getLogger(self.appname)
|
|
@@ -54,8 +53,7 @@ class BasicLog:
|
|
|
54
53
|
|
|
55
54
|
def _cleanup_logger(self, logger: logging.Logger) -> None:
|
|
56
55
|
"""Clean up logger resources by closing all handlers with thread safety."""
|
|
57
|
-
|
|
58
|
-
cleanup_logger_handlers(logger)
|
|
56
|
+
cleanup_logger_handlers(logger)
|
|
59
57
|
|
|
60
58
|
@staticmethod
|
|
61
59
|
def cleanup_logger(logger: logging.Logger) -> None:
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import logging.handlers
|
|
3
3
|
import os
|
|
4
4
|
import re
|
|
5
|
-
import threading
|
|
6
5
|
from typing import Optional
|
|
7
6
|
from pythonLogs.constants import MB_TO_BYTES
|
|
8
7
|
from pythonLogs.log_utils import (
|
|
@@ -18,8 +17,10 @@ from pythonLogs.log_utils import (
|
|
|
18
17
|
)
|
|
19
18
|
from pythonLogs.memory_utils import cleanup_logger_handlers, register_logger_weakref
|
|
20
19
|
from pythonLogs.settings import get_log_settings
|
|
20
|
+
from pythonLogs.thread_safety import auto_thread_safe
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
@auto_thread_safe(['init', '_cleanup_logger'])
|
|
23
24
|
class SizeRotatingLog:
|
|
24
25
|
"""Size-based rotating logger with context manager support for automatic resource cleanup."""
|
|
25
26
|
def __init__(
|
|
@@ -49,8 +50,6 @@ class SizeRotatingLog:
|
|
|
49
50
|
self.streamhandler = streamhandler or _settings.stream_handler
|
|
50
51
|
self.showlocation = showlocation or _settings.show_location
|
|
51
52
|
self.logger = None
|
|
52
|
-
# Instance-level lock for thread safety
|
|
53
|
-
self._lock = threading.Lock()
|
|
54
53
|
|
|
55
54
|
def init(self):
|
|
56
55
|
check_filename_instance(self.filenames)
|
|
@@ -98,8 +97,7 @@ class SizeRotatingLog:
|
|
|
98
97
|
|
|
99
98
|
def _cleanup_logger(self, logger: logging.Logger) -> None:
|
|
100
99
|
"""Clean up logger resources by closing all handlers with thread safety."""
|
|
101
|
-
|
|
102
|
-
cleanup_logger_handlers(logger)
|
|
100
|
+
cleanup_logger_handlers(logger)
|
|
103
101
|
|
|
104
102
|
@staticmethod
|
|
105
103
|
def cleanup_logger(logger: logging.Logger) -> None:
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
import functools
|
|
3
|
+
import threading
|
|
4
|
+
from typing import Any, Callable, Dict, TypeVar, Type
|
|
5
|
+
|
|
6
|
+
F = TypeVar('F', bound=Callable[..., Any])
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ThreadSafeMeta(type):
|
|
10
|
+
"""Metaclass that automatically adds thread safety to class methods."""
|
|
11
|
+
|
|
12
|
+
def __new__(mcs, name: str, bases: tuple, namespace: Dict[str, Any], **kwargs):
|
|
13
|
+
# Create the class first
|
|
14
|
+
cls = super().__new__(mcs, name, bases, namespace)
|
|
15
|
+
|
|
16
|
+
# Add a class-level lock if not already present
|
|
17
|
+
if not hasattr(cls, '_lock'):
|
|
18
|
+
cls._lock = threading.RLock()
|
|
19
|
+
|
|
20
|
+
# Get methods that should be thread-safe (exclude private/dunder methods)
|
|
21
|
+
thread_safe_methods = getattr(cls, '_thread_safe_methods', None)
|
|
22
|
+
if thread_safe_methods is None:
|
|
23
|
+
# Auto-detect public methods that modify state
|
|
24
|
+
thread_safe_methods = [
|
|
25
|
+
method_name for method_name in namespace
|
|
26
|
+
if (callable(getattr(cls, method_name, None)) and
|
|
27
|
+
not method_name.startswith('_') and
|
|
28
|
+
method_name not in ['__enter__', '__exit__', '__init__'])
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
# Wrap each method with automatic locking
|
|
32
|
+
for method_name in thread_safe_methods:
|
|
33
|
+
if hasattr(cls, method_name):
|
|
34
|
+
original_method = getattr(cls, method_name)
|
|
35
|
+
if callable(original_method):
|
|
36
|
+
wrapped_method = thread_safe(original_method)
|
|
37
|
+
setattr(cls, method_name, wrapped_method)
|
|
38
|
+
|
|
39
|
+
return cls
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def thread_safe(func: F) -> F:
|
|
43
|
+
"""Decorator that automatically adds thread safety to methods."""
|
|
44
|
+
|
|
45
|
+
@functools.wraps(func)
|
|
46
|
+
def wrapper(self, *args, **kwargs):
|
|
47
|
+
# Use instance lock if available, otherwise class lock
|
|
48
|
+
lock = getattr(self, '_lock', None)
|
|
49
|
+
if lock is None:
|
|
50
|
+
# Check if class has lock, if not create one
|
|
51
|
+
if not hasattr(self.__class__, '_lock'):
|
|
52
|
+
self.__class__._lock = threading.RLock()
|
|
53
|
+
lock = self.__class__._lock
|
|
54
|
+
|
|
55
|
+
with lock:
|
|
56
|
+
return func(self, *args, **kwargs)
|
|
57
|
+
|
|
58
|
+
return wrapper
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def auto_thread_safe(thread_safe_methods: list = None):
|
|
62
|
+
"""Class decorator that adds automatic thread safety to specified methods."""
|
|
63
|
+
|
|
64
|
+
def decorator(cls: Type) -> Type:
|
|
65
|
+
# Add lock to class if not present
|
|
66
|
+
if not hasattr(cls, '_lock'):
|
|
67
|
+
cls._lock = threading.RLock()
|
|
68
|
+
|
|
69
|
+
# Store thread-safe methods list
|
|
70
|
+
if thread_safe_methods:
|
|
71
|
+
cls._thread_safe_methods = thread_safe_methods
|
|
72
|
+
|
|
73
|
+
# Get methods to make thread-safe
|
|
74
|
+
methods_to_wrap = thread_safe_methods or [
|
|
75
|
+
method_name for method_name in dir(cls)
|
|
76
|
+
if (callable(getattr(cls, method_name, None)) and
|
|
77
|
+
not method_name.startswith('_') and
|
|
78
|
+
method_name not in ['__enter__', '__exit__', '__init__'])
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
# Wrap each method
|
|
82
|
+
for method_name in methods_to_wrap:
|
|
83
|
+
if hasattr(cls, method_name):
|
|
84
|
+
original_method = getattr(cls, method_name)
|
|
85
|
+
if callable(original_method) and not hasattr(original_method, '_thread_safe_wrapped'):
|
|
86
|
+
wrapped_method = thread_safe(original_method)
|
|
87
|
+
wrapped_method._thread_safe_wrapped = True
|
|
88
|
+
setattr(cls, method_name, wrapped_method)
|
|
89
|
+
|
|
90
|
+
return cls
|
|
91
|
+
|
|
92
|
+
return decorator
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class AutoThreadSafe:
|
|
96
|
+
"""Base class that provides automatic thread safety for all public methods."""
|
|
97
|
+
|
|
98
|
+
def __init__(self):
|
|
99
|
+
if not hasattr(self, '_lock'):
|
|
100
|
+
self._lock = threading.RLock()
|
|
101
|
+
|
|
102
|
+
def __init_subclass__(cls, **kwargs):
|
|
103
|
+
super().__init_subclass__(**kwargs)
|
|
104
|
+
|
|
105
|
+
# Add class-level lock
|
|
106
|
+
if not hasattr(cls, '_lock'):
|
|
107
|
+
cls._lock = threading.RLock()
|
|
108
|
+
|
|
109
|
+
# Auto-wrap public methods
|
|
110
|
+
for attr_name in dir(cls):
|
|
111
|
+
if not attr_name.startswith('_'):
|
|
112
|
+
attr = getattr(cls, attr_name)
|
|
113
|
+
if callable(attr) and not hasattr(attr, '_thread_safe_wrapped'):
|
|
114
|
+
wrapped_attr = thread_safe(attr)
|
|
115
|
+
wrapped_attr._thread_safe_wrapped = True
|
|
116
|
+
setattr(cls, attr_name, wrapped_attr)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def synchronized_method(func: F) -> F:
|
|
120
|
+
"""Decorator for individual methods that need thread safety."""
|
|
121
|
+
return thread_safe(func)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class ThreadSafeContext:
|
|
125
|
+
"""Context manager for thread-safe operations."""
|
|
126
|
+
|
|
127
|
+
def __init__(self, lock: threading.Lock):
|
|
128
|
+
self.lock = lock
|
|
129
|
+
|
|
130
|
+
def __enter__(self):
|
|
131
|
+
self.lock.acquire()
|
|
132
|
+
return self
|
|
133
|
+
|
|
134
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
135
|
+
self.lock.release()
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
2
|
import logging.handlers
|
|
3
3
|
import os
|
|
4
|
-
import threading
|
|
5
4
|
from typing import Optional
|
|
6
5
|
from pythonLogs.log_utils import (
|
|
7
6
|
check_directory_permissions,
|
|
@@ -15,8 +14,10 @@ from pythonLogs.log_utils import (
|
|
|
15
14
|
)
|
|
16
15
|
from pythonLogs.memory_utils import cleanup_logger_handlers, register_logger_weakref
|
|
17
16
|
from pythonLogs.settings import get_log_settings
|
|
17
|
+
from pythonLogs.thread_safety import auto_thread_safe
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
@auto_thread_safe(['init', '_cleanup_logger'])
|
|
20
21
|
class TimedRotatingLog:
|
|
21
22
|
"""
|
|
22
23
|
Time-based rotating logger with context manager support for automatic resource cleanup.
|
|
@@ -60,8 +61,6 @@ class TimedRotatingLog:
|
|
|
60
61
|
self.showlocation = showlocation or _settings.show_location
|
|
61
62
|
self.rotateatutc = rotateatutc or _settings.rotate_at_utc
|
|
62
63
|
self.logger = None
|
|
63
|
-
# Instance-level lock for thread safety
|
|
64
|
-
self._lock = threading.Lock()
|
|
65
64
|
|
|
66
65
|
def init(self):
|
|
67
66
|
check_filename_instance(self.filenames)
|
|
@@ -107,8 +106,7 @@ class TimedRotatingLog:
|
|
|
107
106
|
|
|
108
107
|
def _cleanup_logger(self, logger: logging.Logger) -> None:
|
|
109
108
|
"""Clean up logger resources by closing all handlers with thread safety."""
|
|
110
|
-
|
|
111
|
-
cleanup_logger_handlers(logger)
|
|
109
|
+
cleanup_logger_handlers(logger)
|
|
112
110
|
|
|
113
111
|
@staticmethod
|
|
114
112
|
def cleanup_logger(logger: logging.Logger) -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|