pythonLogs 4.0.5__tar.gz → 5.0.0__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.5 → pythonlogs-5.0.0}/PKG-INFO +22 -18
- {pythonlogs-4.0.5 → pythonlogs-5.0.0}/README.md +20 -14
- pythonlogs-5.0.0/build.py +71 -0
- {pythonlogs-4.0.5 → pythonlogs-5.0.0}/pyproject.toml +9 -5
- {pythonlogs-4.0.5 → pythonlogs-5.0.0}/pythonLogs/__init__.py +1 -1
- {pythonlogs-4.0.5 → pythonlogs-5.0.0}/pythonLogs/basic_log.py +8 -1
- {pythonlogs-4.0.5 → pythonlogs-5.0.0}/pythonLogs/factory.py +75 -58
- {pythonlogs-4.0.5 → pythonlogs-5.0.0}/pythonLogs/settings.py +1 -1
- {pythonlogs-4.0.5 → pythonlogs-5.0.0}/pythonLogs/thread_safety.py +33 -17
- {pythonlogs-4.0.5 → pythonlogs-5.0.0}/pythonLogs/timed_rotating.py +1 -1
- {pythonlogs-4.0.5 → pythonlogs-5.0.0}/LICENSE +0 -0
- {pythonlogs-4.0.5 → pythonlogs-5.0.0}/pythonLogs/.env.example +0 -0
- {pythonlogs-4.0.5 → pythonlogs-5.0.0}/pythonLogs/constants.py +0 -0
- {pythonlogs-4.0.5 → pythonlogs-5.0.0}/pythonLogs/log_utils.py +0 -0
- {pythonlogs-4.0.5 → pythonlogs-5.0.0}/pythonLogs/memory_utils.py +0 -0
- {pythonlogs-4.0.5 → pythonlogs-5.0.0}/pythonLogs/size_rotating.py +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pythonLogs
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.0.0
|
|
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
|
|
7
7
|
Author: Daniel Costa
|
|
8
8
|
Author-email: danieldcsta@gmail.com
|
|
9
9
|
Maintainer: Daniel Costa
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.12,<4.0
|
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
12
|
Classifier: Environment :: Other Environment
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
@@ -15,8 +15,6 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
15
15
|
Classifier: Natural Language :: English
|
|
16
16
|
Classifier: Operating System :: OS Independent
|
|
17
17
|
Classifier: Programming Language :: Python :: 3
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
20
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
19
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
20
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
@@ -34,8 +32,10 @@ Description-Content-Type: text/markdown
|
|
|
34
32
|
[](https://pypi.python.org/pypi/pythonLogs)
|
|
35
33
|
[](https://pepy.tech/projects/pythonLogs)
|
|
36
34
|
[](https://codecov.io/gh/ddc/pythonLogs)
|
|
37
|
-
[](https://github.com/ddc/pythonLogs/actions/workflows/workflow.yml)
|
|
36
|
+
[](https://sonarcloud.io/dashboard?id=ddc_pythonLogs)
|
|
38
37
|
[](https://actions-badge.atrox.dev/ddc/pythonLogs/goto?ref=main)
|
|
38
|
+
[](https://github.com/psf/black)
|
|
39
39
|
[](https://www.python.org/downloads)
|
|
40
40
|
|
|
41
41
|
[](https://github.com/sponsors/ddc)
|
|
@@ -57,10 +57,12 @@ High-performance Python logging library with file rotation and optimized caching
|
|
|
57
57
|
- [Memory Management](#memory-management)
|
|
58
58
|
- [Flexible Configuration Options](#flexible-configuration-options)
|
|
59
59
|
- [Migration Guide](#migration-guide)
|
|
60
|
-
- [Development](#
|
|
61
|
-
- [
|
|
60
|
+
- [Development](#development)
|
|
61
|
+
- [Development](#development)
|
|
62
|
+
- [Building from Source](#building-from-source)
|
|
63
|
+
- [Running Tests](#running-tests)
|
|
62
64
|
- [License](#license)
|
|
63
|
-
- [
|
|
65
|
+
- [Support](#support)
|
|
64
66
|
|
|
65
67
|
|
|
66
68
|
|
|
@@ -547,26 +549,28 @@ timed_logger = timed_rotating_logger(level=LogLevel.WARNING, name="app", directo
|
|
|
547
549
|
- 📚 **Centralized configuration** through factory pattern
|
|
548
550
|
|
|
549
551
|
|
|
550
|
-
#
|
|
551
|
-
|
|
552
|
+
# Development
|
|
553
|
+
|
|
554
|
+
### Building from Source
|
|
552
555
|
```shell
|
|
553
556
|
poetry build -f wheel
|
|
554
557
|
```
|
|
555
558
|
|
|
556
|
-
|
|
557
|
-
# Run Tests and Get Coverage Report using Poe
|
|
559
|
+
### Running Tests
|
|
558
560
|
```shell
|
|
559
561
|
poetry update --with test
|
|
560
|
-
poe
|
|
562
|
+
poe tests
|
|
561
563
|
```
|
|
562
564
|
|
|
563
|
-
|
|
564
565
|
# License
|
|
566
|
+
|
|
565
567
|
Released under the [MIT License](LICENSE)
|
|
566
568
|
|
|
569
|
+
# Support
|
|
570
|
+
|
|
571
|
+
If you find this project helpful, consider supporting development:
|
|
567
572
|
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
+ [Paypal](https://www.paypal.com/ncp/payment/6G9Z78QHUD4RJ)
|
|
573
|
+
- [GitHub Sponsor](https://github.com/sponsors/ddc)
|
|
574
|
+
- [ko-fi](https://ko-fi.com/ddcsta)
|
|
575
|
+
- [PayPal](https://www.paypal.com/ncp/payment/6G9Z78QHUD4RJ)
|
|
572
576
|
|
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
[](https://pypi.python.org/pypi/pythonLogs)
|
|
6
6
|
[](https://pepy.tech/projects/pythonLogs)
|
|
7
7
|
[](https://codecov.io/gh/ddc/pythonLogs)
|
|
8
|
-
[](https://github.com/ddc/pythonLogs/actions/workflows/workflow.yml)
|
|
9
|
+
[](https://sonarcloud.io/dashboard?id=ddc_pythonLogs)
|
|
9
10
|
[](https://actions-badge.atrox.dev/ddc/pythonLogs/goto?ref=main)
|
|
11
|
+
[](https://github.com/psf/black)
|
|
10
12
|
[](https://www.python.org/downloads)
|
|
11
13
|
|
|
12
14
|
[](https://github.com/sponsors/ddc)
|
|
@@ -28,10 +30,12 @@ High-performance Python logging library with file rotation and optimized caching
|
|
|
28
30
|
- [Memory Management](#memory-management)
|
|
29
31
|
- [Flexible Configuration Options](#flexible-configuration-options)
|
|
30
32
|
- [Migration Guide](#migration-guide)
|
|
31
|
-
- [Development](#
|
|
32
|
-
- [
|
|
33
|
+
- [Development](#development)
|
|
34
|
+
- [Development](#development)
|
|
35
|
+
- [Building from Source](#building-from-source)
|
|
36
|
+
- [Running Tests](#running-tests)
|
|
33
37
|
- [License](#license)
|
|
34
|
-
- [
|
|
38
|
+
- [Support](#support)
|
|
35
39
|
|
|
36
40
|
|
|
37
41
|
|
|
@@ -518,25 +522,27 @@ timed_logger = timed_rotating_logger(level=LogLevel.WARNING, name="app", directo
|
|
|
518
522
|
- 📚 **Centralized configuration** through factory pattern
|
|
519
523
|
|
|
520
524
|
|
|
521
|
-
#
|
|
522
|
-
|
|
525
|
+
# Development
|
|
526
|
+
|
|
527
|
+
### Building from Source
|
|
523
528
|
```shell
|
|
524
529
|
poetry build -f wheel
|
|
525
530
|
```
|
|
526
531
|
|
|
527
|
-
|
|
528
|
-
# Run Tests and Get Coverage Report using Poe
|
|
532
|
+
### Running Tests
|
|
529
533
|
```shell
|
|
530
534
|
poetry update --with test
|
|
531
|
-
poe
|
|
535
|
+
poe tests
|
|
532
536
|
```
|
|
533
537
|
|
|
534
|
-
|
|
535
538
|
# License
|
|
539
|
+
|
|
536
540
|
Released under the [MIT License](LICENSE)
|
|
537
541
|
|
|
542
|
+
# Support
|
|
543
|
+
|
|
544
|
+
If you find this project helpful, consider supporting development:
|
|
538
545
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
+ [Paypal](https://www.paypal.com/ncp/payment/6G9Z78QHUD4RJ)
|
|
546
|
+
- [GitHub Sponsor](https://github.com/sponsors/ddc)
|
|
547
|
+
- [ko-fi](https://ko-fi.com/ddcsta)
|
|
548
|
+
- [PayPal](https://www.paypal.com/ncp/payment/6G9Z78QHUD4RJ)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
import compileall
|
|
3
|
+
import sys
|
|
4
|
+
import tomllib
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def parse_python_version_requirement():
|
|
9
|
+
"""Parse Python version requirement from pyproject.toml"""
|
|
10
|
+
pyproject_path = Path(__file__).parent / "pyproject.toml"
|
|
11
|
+
|
|
12
|
+
with open(pyproject_path, "rb") as f:
|
|
13
|
+
data = tomllib.load(f)
|
|
14
|
+
|
|
15
|
+
python_req = data["tool"]["poetry"]["dependencies"]["python"]
|
|
16
|
+
|
|
17
|
+
# Parse version constraint like "^3.12"
|
|
18
|
+
if python_req.startswith("^"):
|
|
19
|
+
min_version = python_req[1:]
|
|
20
|
+
major, minor = map(int, min_version.split("."))
|
|
21
|
+
return major, minor
|
|
22
|
+
elif python_req.startswith(">="):
|
|
23
|
+
min_version = python_req[2:]
|
|
24
|
+
major, minor = map(int, min_version.split("."))
|
|
25
|
+
return major, minor
|
|
26
|
+
else:
|
|
27
|
+
# Default fallback
|
|
28
|
+
return 3, 12
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_compatible_python_versions(min_major, min_minor):
|
|
32
|
+
"""Generate list of compatible Python versions"""
|
|
33
|
+
versions = []
|
|
34
|
+
current_major, current_minor = sys.version_info[:2]
|
|
35
|
+
|
|
36
|
+
# Start from the minimum required version
|
|
37
|
+
for major in range(min_major, min_major + 1): # Only Python 3.x for now
|
|
38
|
+
start_minor = min_minor if major == min_major else 0
|
|
39
|
+
# Go up to current Python version + 2 minor versions for future compatibility
|
|
40
|
+
end_minor = max(current_minor + 2, 12) if major == current_major else 12
|
|
41
|
+
|
|
42
|
+
for minor in range(start_minor, end_minor + 1):
|
|
43
|
+
if major == 3 and minor >= min_minor:
|
|
44
|
+
versions.append(f"{major}.{minor}")
|
|
45
|
+
|
|
46
|
+
return versions
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def build():
|
|
50
|
+
"""Build bytecode for all compatible Python versions"""
|
|
51
|
+
try:
|
|
52
|
+
min_major, min_minor = parse_python_version_requirement()
|
|
53
|
+
compatible_versions = get_compatible_python_versions(min_major, min_minor)
|
|
54
|
+
|
|
55
|
+
print(f"Building for Python versions: {', '.join(compatible_versions)}")
|
|
56
|
+
|
|
57
|
+
# Compile for current Python version
|
|
58
|
+
print(f"Compiling for Python {sys.version_info.major}.{sys.version_info.minor}")
|
|
59
|
+
compileall.compile_dir('pythonDatabases', force=True)
|
|
60
|
+
|
|
61
|
+
# Note: For actual cross-version bytecode, you'd need multiple Python interpreters
|
|
62
|
+
# This compiles with current interpreter but documents compatibility
|
|
63
|
+
print("Build completed successfully")
|
|
64
|
+
|
|
65
|
+
except Exception as e:
|
|
66
|
+
print(f"Build failed: {e}")
|
|
67
|
+
sys.exit(1)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
if __name__ == "__main__":
|
|
71
|
+
build()
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "pythonLogs"
|
|
7
|
-
version = "
|
|
7
|
+
version = "5.0.0"
|
|
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"
|
|
@@ -30,8 +30,15 @@ classifiers = [
|
|
|
30
30
|
"Natural Language :: English",
|
|
31
31
|
]
|
|
32
32
|
|
|
33
|
+
[tool.poetry.build]
|
|
34
|
+
script = "build.py"
|
|
35
|
+
generate-setup-file = false
|
|
36
|
+
|
|
37
|
+
[tool.poetry.group.test]
|
|
38
|
+
optional = true
|
|
39
|
+
|
|
33
40
|
[tool.poetry.dependencies]
|
|
34
|
-
python = "^3.
|
|
41
|
+
python = "^3.12"
|
|
35
42
|
pydantic-settings = "^2.10.1"
|
|
36
43
|
python-dotenv = "^1.1.1"
|
|
37
44
|
|
|
@@ -48,9 +55,6 @@ _coverage_xml = "coverage xml"
|
|
|
48
55
|
tests = ["_test", "_coverage_report", "_coverage_xml"]
|
|
49
56
|
test = ["tests"]
|
|
50
57
|
|
|
51
|
-
[tool.poetry.group.test]
|
|
52
|
-
optional = true
|
|
53
|
-
|
|
54
58
|
[tool.black]
|
|
55
59
|
line-length = 120
|
|
56
60
|
skip-string-normalization = true
|
|
@@ -34,7 +34,14 @@ class BasicLog:
|
|
|
34
34
|
logger.setLevel(self.level)
|
|
35
35
|
logging.Formatter.converter = get_timezone_function(self.timezone)
|
|
36
36
|
_format = get_format(self.showlocation, self.appname, self.timezone)
|
|
37
|
-
|
|
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
|
+
|
|
38
45
|
self.logger = logger
|
|
39
46
|
# Register weak reference for memory tracking
|
|
40
47
|
register_logger_weakref(logger)
|
|
@@ -3,6 +3,7 @@ import atexit
|
|
|
3
3
|
import logging
|
|
4
4
|
import threading
|
|
5
5
|
import time
|
|
6
|
+
from dataclasses import dataclass
|
|
6
7
|
from enum import Enum
|
|
7
8
|
from typing import Dict, Optional, Tuple, Union
|
|
8
9
|
from pythonLogs.basic_log import BasicLog
|
|
@@ -12,6 +13,25 @@ from pythonLogs.size_rotating import SizeRotatingLog
|
|
|
12
13
|
from pythonLogs.timed_rotating import TimedRotatingLog
|
|
13
14
|
|
|
14
15
|
|
|
16
|
+
@dataclass
|
|
17
|
+
class LoggerConfig:
|
|
18
|
+
"""Configuration class to group logger parameters"""
|
|
19
|
+
level: Optional[Union[LogLevel, str]] = None
|
|
20
|
+
name: Optional[str] = None
|
|
21
|
+
directory: Optional[str] = None
|
|
22
|
+
filenames: Optional[list | tuple] = None
|
|
23
|
+
encoding: Optional[str] = None
|
|
24
|
+
datefmt: Optional[str] = None
|
|
25
|
+
timezone: Optional[str] = None
|
|
26
|
+
streamhandler: Optional[bool] = None
|
|
27
|
+
showlocation: Optional[bool] = None
|
|
28
|
+
maxmbytes: Optional[int] = None
|
|
29
|
+
when: Optional[Union[RotateWhen, str]] = None
|
|
30
|
+
sufix: Optional[str] = None
|
|
31
|
+
rotateatutc: Optional[bool] = None
|
|
32
|
+
daystokeep: Optional[int] = None
|
|
33
|
+
|
|
34
|
+
|
|
15
35
|
class LoggerType(str, Enum):
|
|
16
36
|
"""Available logger types"""
|
|
17
37
|
BASIC = "basic"
|
|
@@ -80,7 +100,7 @@ class LoggerFactory:
|
|
|
80
100
|
|
|
81
101
|
# Check if logger already exists in the registry
|
|
82
102
|
if name in cls._logger_registry:
|
|
83
|
-
logger,
|
|
103
|
+
logger, _ = cls._logger_registry[name]
|
|
84
104
|
# Update timestamp for LRU tracking
|
|
85
105
|
cls._logger_registry[name] = (logger, time.time())
|
|
86
106
|
return logger
|
|
@@ -189,21 +209,8 @@ class LoggerFactory:
|
|
|
189
209
|
@staticmethod
|
|
190
210
|
def create_logger(
|
|
191
211
|
logger_type: Union[LoggerType, str],
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
directory: Optional[str] = None,
|
|
195
|
-
filenames: Optional[list | tuple] = None,
|
|
196
|
-
encoding: Optional[str] = None,
|
|
197
|
-
datefmt: Optional[str] = None,
|
|
198
|
-
timezone: Optional[str] = None,
|
|
199
|
-
streamhandler: Optional[bool] = None,
|
|
200
|
-
showlocation: Optional[bool] = None, # Size rotating specific
|
|
201
|
-
maxmbytes: Optional[int] = None, # Timed rotating specific
|
|
202
|
-
when: Optional[Union[RotateWhen, str]] = None,
|
|
203
|
-
sufix: Optional[str] = None,
|
|
204
|
-
rotateatutc: Optional[bool] = None,
|
|
205
|
-
# Common
|
|
206
|
-
daystokeep: Optional[int] = None,
|
|
212
|
+
config: Optional[LoggerConfig] = None,
|
|
213
|
+
**kwargs
|
|
207
214
|
) -> logging.Logger:
|
|
208
215
|
|
|
209
216
|
"""
|
|
@@ -211,20 +218,8 @@ class LoggerFactory:
|
|
|
211
218
|
|
|
212
219
|
Args:
|
|
213
220
|
logger_type: Type of logger to create (LoggerType enum or string)
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
directory: Log directory path
|
|
217
|
-
filenames: List/tuple of log filenames
|
|
218
|
-
encoding: File encoding
|
|
219
|
-
datefmt: Date format string
|
|
220
|
-
timezone: Timezone for timestamps
|
|
221
|
-
streamhandler: Enable console output
|
|
222
|
-
showlocation: Show file location in logs
|
|
223
|
-
maxmbytes: Max file size in MB (size rotating only)
|
|
224
|
-
when: When to rotate (RotateWhen enum or string: MIDNIGHT, HOURLY, DAILY, etc.)
|
|
225
|
-
sufix: Date suffix for rotated files (timed rotating only)
|
|
226
|
-
rotateatutc: Rotate at UTC time (timed rotating only)
|
|
227
|
-
daystokeep: Days to keep old logs
|
|
221
|
+
config: LoggerConfig object with logger parameters
|
|
222
|
+
**kwargs: Individual logger parameters (for backward compatibility)
|
|
228
223
|
|
|
229
224
|
Returns:
|
|
230
225
|
Configured logger instance
|
|
@@ -239,50 +234,72 @@ class LoggerFactory:
|
|
|
239
234
|
except ValueError:
|
|
240
235
|
raise ValueError(f"Invalid logger type: {logger_type}. Valid types: {[t.value for t in LoggerType]}")
|
|
241
236
|
|
|
237
|
+
# Merge config and kwargs (kwargs take precedence for backward compatibility)
|
|
238
|
+
if config is None:
|
|
239
|
+
config = LoggerConfig()
|
|
240
|
+
|
|
241
|
+
# Create a new config with kwargs overriding config values
|
|
242
|
+
final_config = LoggerConfig(
|
|
243
|
+
level=kwargs.get('level', config.level),
|
|
244
|
+
name=kwargs.get('name', config.name),
|
|
245
|
+
directory=kwargs.get('directory', config.directory),
|
|
246
|
+
filenames=kwargs.get('filenames', config.filenames),
|
|
247
|
+
encoding=kwargs.get('encoding', config.encoding),
|
|
248
|
+
datefmt=kwargs.get('datefmt', config.datefmt),
|
|
249
|
+
timezone=kwargs.get('timezone', config.timezone),
|
|
250
|
+
streamhandler=kwargs.get('streamhandler', config.streamhandler),
|
|
251
|
+
showlocation=kwargs.get('showlocation', config.showlocation),
|
|
252
|
+
maxmbytes=kwargs.get('maxmbytes', config.maxmbytes),
|
|
253
|
+
when=kwargs.get('when', config.when),
|
|
254
|
+
sufix=kwargs.get('sufix', config.sufix),
|
|
255
|
+
rotateatutc=kwargs.get('rotateatutc', config.rotateatutc),
|
|
256
|
+
daystokeep=kwargs.get('daystokeep', config.daystokeep)
|
|
257
|
+
)
|
|
258
|
+
|
|
242
259
|
# Convert enum values to strings for logger classes
|
|
243
|
-
level_str = level.value if isinstance(level, LogLevel) else level
|
|
244
|
-
when_str = when.value if isinstance(when, RotateWhen) else when
|
|
260
|
+
level_str = final_config.level.value if isinstance(final_config.level, LogLevel) else final_config.level
|
|
261
|
+
when_str = final_config.when.value if isinstance(final_config.when, RotateWhen) else final_config.when
|
|
245
262
|
|
|
246
263
|
# Create logger based on type
|
|
247
264
|
match logger_type:
|
|
248
265
|
case LoggerType.BASIC:
|
|
249
266
|
logger_instance = BasicLog(
|
|
250
267
|
level=level_str,
|
|
251
|
-
name=name,
|
|
252
|
-
encoding=encoding,
|
|
253
|
-
datefmt=datefmt,
|
|
254
|
-
timezone=timezone,
|
|
255
|
-
showlocation=showlocation, )
|
|
268
|
+
name=final_config.name,
|
|
269
|
+
encoding=final_config.encoding,
|
|
270
|
+
datefmt=final_config.datefmt,
|
|
271
|
+
timezone=final_config.timezone,
|
|
272
|
+
showlocation=final_config.showlocation, )
|
|
256
273
|
|
|
257
274
|
case LoggerType.SIZE_ROTATING:
|
|
258
275
|
logger_instance = SizeRotatingLog(
|
|
259
276
|
level=level_str,
|
|
260
|
-
name=name,
|
|
261
|
-
directory=directory,
|
|
262
|
-
filenames=filenames,
|
|
263
|
-
maxmbytes=maxmbytes,
|
|
264
|
-
daystokeep=daystokeep,
|
|
265
|
-
encoding=encoding,
|
|
266
|
-
datefmt=datefmt,
|
|
267
|
-
timezone=timezone,
|
|
268
|
-
streamhandler=streamhandler,
|
|
269
|
-
showlocation=showlocation, )
|
|
277
|
+
name=final_config.name,
|
|
278
|
+
directory=final_config.directory,
|
|
279
|
+
filenames=final_config.filenames,
|
|
280
|
+
maxmbytes=final_config.maxmbytes,
|
|
281
|
+
daystokeep=final_config.daystokeep,
|
|
282
|
+
encoding=final_config.encoding,
|
|
283
|
+
datefmt=final_config.datefmt,
|
|
284
|
+
timezone=final_config.timezone,
|
|
285
|
+
streamhandler=final_config.streamhandler,
|
|
286
|
+
showlocation=final_config.showlocation, )
|
|
270
287
|
|
|
271
288
|
case LoggerType.TIMED_ROTATING:
|
|
272
289
|
logger_instance = TimedRotatingLog(
|
|
273
290
|
level=level_str,
|
|
274
|
-
name=name,
|
|
275
|
-
directory=directory,
|
|
276
|
-
filenames=filenames,
|
|
291
|
+
name=final_config.name,
|
|
292
|
+
directory=final_config.directory,
|
|
293
|
+
filenames=final_config.filenames,
|
|
277
294
|
when=when_str,
|
|
278
|
-
sufix=sufix,
|
|
279
|
-
daystokeep=daystokeep,
|
|
280
|
-
encoding=encoding,
|
|
281
|
-
datefmt=datefmt,
|
|
282
|
-
timezone=timezone,
|
|
283
|
-
streamhandler=streamhandler,
|
|
284
|
-
showlocation=showlocation,
|
|
285
|
-
rotateatutc=rotateatutc, )
|
|
295
|
+
sufix=final_config.sufix,
|
|
296
|
+
daystokeep=final_config.daystokeep,
|
|
297
|
+
encoding=final_config.encoding,
|
|
298
|
+
datefmt=final_config.datefmt,
|
|
299
|
+
timezone=final_config.timezone,
|
|
300
|
+
streamhandler=final_config.streamhandler,
|
|
301
|
+
showlocation=final_config.showlocation,
|
|
302
|
+
rotateatutc=final_config.rotateatutc, )
|
|
286
303
|
|
|
287
304
|
case _:
|
|
288
305
|
raise ValueError(f"Unsupported logger type: {logger_type}")
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
2
|
import functools
|
|
3
3
|
import threading
|
|
4
|
-
from typing import Any, Callable, Dict,
|
|
4
|
+
from typing import Any, Callable, Dict, Type, TypeVar
|
|
5
|
+
|
|
5
6
|
|
|
6
7
|
F = TypeVar('F', bound=Callable[..., Any])
|
|
7
8
|
|
|
@@ -58,34 +59,49 @@ def thread_safe(func: F) -> F:
|
|
|
58
59
|
return wrapper
|
|
59
60
|
|
|
60
61
|
|
|
62
|
+
def _get_wrappable_methods(cls: Type) -> list:
|
|
63
|
+
"""Helper function to get methods that should be made thread-safe."""
|
|
64
|
+
return [
|
|
65
|
+
method_name for method_name in dir(cls)
|
|
66
|
+
if (callable(getattr(cls, method_name, None)) and
|
|
67
|
+
not method_name.startswith('_') and
|
|
68
|
+
method_name not in ['__enter__', '__exit__', '__init__'])
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _ensure_class_has_lock(cls: Type) -> None:
|
|
73
|
+
"""Ensure the class has a lock attribute."""
|
|
74
|
+
if not hasattr(cls, '_lock'):
|
|
75
|
+
cls._lock = threading.RLock()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _should_wrap_method(cls: Type, method_name: str, original_method: Any) -> bool:
|
|
79
|
+
"""Check if a method should be wrapped with thread safety."""
|
|
80
|
+
return (hasattr(cls, method_name) and
|
|
81
|
+
callable(original_method) and
|
|
82
|
+
not hasattr(original_method, '_thread_safe_wrapped'))
|
|
83
|
+
|
|
84
|
+
|
|
61
85
|
def auto_thread_safe(thread_safe_methods: list = None):
|
|
62
86
|
"""Class decorator that adds automatic thread safety to specified methods."""
|
|
63
87
|
|
|
64
88
|
def decorator(cls: Type) -> Type:
|
|
65
|
-
|
|
66
|
-
if not hasattr(cls, '_lock'):
|
|
67
|
-
cls._lock = threading.RLock()
|
|
89
|
+
_ensure_class_has_lock(cls)
|
|
68
90
|
|
|
69
91
|
# Store thread-safe methods list
|
|
70
92
|
if thread_safe_methods:
|
|
71
93
|
cls._thread_safe_methods = thread_safe_methods
|
|
72
94
|
|
|
73
95
|
# 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
|
-
]
|
|
96
|
+
methods_to_wrap = thread_safe_methods or _get_wrappable_methods(cls)
|
|
80
97
|
|
|
81
98
|
# Wrap each method
|
|
82
99
|
for method_name in methods_to_wrap:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
setattr(cls, method_name, wrapped_method)
|
|
100
|
+
original_method = getattr(cls, method_name, None)
|
|
101
|
+
if _should_wrap_method(cls, method_name, original_method):
|
|
102
|
+
wrapped_method = thread_safe(original_method)
|
|
103
|
+
wrapped_method._thread_safe_wrapped = True
|
|
104
|
+
setattr(cls, method_name, wrapped_method)
|
|
89
105
|
|
|
90
106
|
return cls
|
|
91
107
|
|
|
@@ -132,4 +148,4 @@ class ThreadSafeContext:
|
|
|
132
148
|
return self
|
|
133
149
|
|
|
134
150
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
135
|
-
self.lock.release()
|
|
151
|
+
self.lock.release()
|
|
@@ -10,7 +10,7 @@ from pythonLogs.log_utils import (
|
|
|
10
10
|
get_logger_and_formatter,
|
|
11
11
|
get_stream_handler,
|
|
12
12
|
gzip_file_with_sufix,
|
|
13
|
-
|
|
13
|
+
remove_old_logs,
|
|
14
14
|
)
|
|
15
15
|
from pythonLogs.memory_utils import cleanup_logger_handlers, register_logger_weakref
|
|
16
16
|
from pythonLogs.settings import get_log_settings
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|