orionis 0.401.0__py3-none-any.whl → 0.403.0__py3-none-any.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.
- orionis/container/providers/service_provider.py +4 -4
- orionis/foundation/application.py +4 -2
- orionis/foundation/config/base.py +112 -0
- orionis/foundation/config/logging/entities/channels.py +87 -69
- orionis/foundation/config/logging/entities/chunked.py +37 -92
- orionis/foundation/config/logging/entities/daily.py +43 -81
- orionis/foundation/config/logging/entities/hourly.py +16 -71
- orionis/foundation/config/logging/entities/logging.py +37 -38
- orionis/foundation/config/logging/entities/monthly.py +19 -75
- orionis/foundation/config/logging/entities/stack.py +13 -69
- orionis/foundation/config/logging/entities/weekly.py +19 -75
- orionis/foundation/config/logging/enums/levels.py +6 -7
- orionis/foundation/config/logging/validators/__init__.py +7 -0
- orionis/foundation/config/logging/validators/level.py +54 -0
- orionis/foundation/config/logging/validators/path.py +34 -0
- orionis/foundation/providers/console_provider.py +0 -4
- orionis/foundation/providers/dumper_provider.py +0 -4
- orionis/foundation/providers/logger_provider.py +17 -0
- orionis/foundation/providers/path_resolver_provider.py +0 -4
- orionis/foundation/providers/progress_bar_provider.py +0 -4
- orionis/foundation/providers/workers_provider.py +0 -4
- orionis/metadata/framework.py +1 -1
- orionis/services/log/contracts/__init__.py +0 -0
- orionis/services/log/contracts/log_service.py +23 -0
- orionis/services/log/exceptions/__init__.py +5 -0
- orionis/services/log/exceptions/runtime.py +19 -0
- orionis/services/log/handlers/__init__.py +0 -0
- orionis/services/log/handlers/size_rotating.py +52 -0
- orionis/services/log/handlers/timed_rotating.py +53 -0
- orionis/services/log/log_service.py +188 -143
- orionis/support/facades/logger.py +15 -0
- {orionis-0.401.0.dist-info → orionis-0.403.0.dist-info}/METADATA +1 -1
- {orionis-0.401.0.dist-info → orionis-0.403.0.dist-info}/RECORD +37 -24
- {orionis-0.401.0.dist-info → orionis-0.403.0.dist-info}/WHEEL +0 -0
- {orionis-0.401.0.dist-info → orionis-0.403.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.401.0.dist-info → orionis-0.403.0.dist-info}/top_level.txt +0 -0
- {orionis-0.401.0.dist-info → orionis-0.403.0.dist-info}/zip-safe +0 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
from dataclasses import
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from orionis.foundation.config.base import BaseConfigEntity
|
|
3
|
+
from orionis.foundation.config.logging.validators import IsValidPath, IsValidLevel
|
|
2
4
|
from orionis.foundation.exceptions import OrionisIntegrityException
|
|
3
5
|
from orionis.foundation.config.logging.enums import Level
|
|
4
6
|
|
|
5
7
|
@dataclass(unsafe_hash=True, kw_only=True)
|
|
6
|
-
class Weekly:
|
|
8
|
+
class Weekly(BaseConfigEntity):
|
|
7
9
|
"""
|
|
8
10
|
Configuration entity for weekly log file management.
|
|
9
11
|
|
|
@@ -14,24 +16,24 @@ class Weekly:
|
|
|
14
16
|
"""
|
|
15
17
|
|
|
16
18
|
path: str = field(
|
|
17
|
-
default='storage/log/application.log',
|
|
18
|
-
metadata={
|
|
19
|
+
default = 'storage/log/application.log',
|
|
20
|
+
metadata = {
|
|
19
21
|
"description": "The file path where the log is stored.",
|
|
20
22
|
"default": "storage/log/application.log",
|
|
21
23
|
},
|
|
22
24
|
)
|
|
23
25
|
|
|
24
26
|
level: int | str | Level = field(
|
|
25
|
-
default=Level.INFO,
|
|
26
|
-
metadata={
|
|
27
|
-
"description": "The logging level (e.g.,
|
|
27
|
+
default = Level.INFO,
|
|
28
|
+
metadata = {
|
|
29
|
+
"description": "The logging level (e.g., DEBUG, INFO, WARNING, ERROR, CRITICAL).",
|
|
28
30
|
"default": Level.INFO,
|
|
29
31
|
},
|
|
30
32
|
)
|
|
31
33
|
|
|
32
34
|
retention_weeks: int = field(
|
|
33
|
-
default=4,
|
|
34
|
-
metadata={
|
|
35
|
+
default = 4,
|
|
36
|
+
metadata = {
|
|
35
37
|
"description": "The number of weeks to retain log files before deletion.",
|
|
36
38
|
"default": 4,
|
|
37
39
|
},
|
|
@@ -49,76 +51,18 @@ class Weekly:
|
|
|
49
51
|
Raises:
|
|
50
52
|
OrionisIntegrityException: If any attribute is invalid.
|
|
51
53
|
"""
|
|
52
|
-
# Validate 'path'
|
|
53
|
-
|
|
54
|
-
raise OrionisIntegrityException(
|
|
55
|
-
f"File cache configuration error: 'path' must be a non-empty string, got {repr(self.path)}."
|
|
56
|
-
)
|
|
54
|
+
# Validate 'path' using the IsValidPath validator
|
|
55
|
+
IsValidPath(self.path)
|
|
57
56
|
|
|
58
|
-
# Validate 'level'
|
|
59
|
-
|
|
60
|
-
if not isinstance(self.level, valid_level_types):
|
|
61
|
-
raise OrionisIntegrityException(
|
|
62
|
-
f"File cache configuration error: 'level' must be int, str, or Level enum, got {type(self.level).__name__}."
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
if isinstance(self.level, str):
|
|
66
|
-
_value = self.level.strip().upper()
|
|
67
|
-
if not _value:
|
|
68
|
-
raise OrionisIntegrityException(
|
|
69
|
-
"File cache configuration error: 'level' string cannot be empty."
|
|
70
|
-
)
|
|
71
|
-
if _value not in Level.__members__:
|
|
72
|
-
raise OrionisIntegrityException(
|
|
73
|
-
f"File cache configuration error: 'level' must be one of {list(Level.__members__.keys())}, got '{self.level}'."
|
|
74
|
-
)
|
|
75
|
-
self.level = Level[_value].value
|
|
76
|
-
elif isinstance(self.level, int):
|
|
77
|
-
valid_values = [level.value for level in Level]
|
|
78
|
-
if self.level not in valid_values:
|
|
79
|
-
raise OrionisIntegrityException(
|
|
80
|
-
f"File cache configuration error: 'level' must be one of {valid_values}, got '{self.level}'."
|
|
81
|
-
)
|
|
82
|
-
elif isinstance(self.level, Level):
|
|
83
|
-
self.level = self.level.value
|
|
57
|
+
# Validate 'level' using the IsValidLevel validator
|
|
58
|
+
IsValidLevel(self.level)
|
|
84
59
|
|
|
85
60
|
# Validate 'retention_weeks'
|
|
86
|
-
if not isinstance(self.retention_weeks, int)
|
|
61
|
+
if not isinstance(self.retention_weeks, int):
|
|
87
62
|
raise OrionisIntegrityException(
|
|
88
|
-
f"
|
|
63
|
+
f"Invalid type for 'retention_weeks': expected int, got {type(self.retention_weeks).__name__}."
|
|
89
64
|
)
|
|
90
65
|
if self.retention_weeks < 1 or self.retention_weeks > 12:
|
|
91
66
|
raise OrionisIntegrityException(
|
|
92
|
-
f"
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
def toDict(self) -> dict:
|
|
96
|
-
"""
|
|
97
|
-
Converts the Weekly configuration object to a dictionary.
|
|
98
|
-
|
|
99
|
-
Returns:
|
|
100
|
-
dict: Dictionary representation of the Weekly configuration.
|
|
101
|
-
"""
|
|
102
|
-
return asdict(self)
|
|
103
|
-
|
|
104
|
-
def getFields(self):
|
|
105
|
-
"""
|
|
106
|
-
Retrieves a list of field information for the current dataclass instance.
|
|
107
|
-
|
|
108
|
-
Returns:
|
|
109
|
-
list: A list of dictionaries, each containing details about a field:
|
|
110
|
-
- name (str): The name of the field.
|
|
111
|
-
- type (type): The type of the field.
|
|
112
|
-
- default: The default value of the field, if specified; otherwise, the value from metadata or None.
|
|
113
|
-
- metadata (mapping): The metadata associated with the field.
|
|
114
|
-
"""
|
|
115
|
-
__fields = []
|
|
116
|
-
for field in fields(self):
|
|
117
|
-
__metadata = dict(field.metadata) or {}
|
|
118
|
-
__fields.append({
|
|
119
|
-
"name": field.name,
|
|
120
|
-
"type": field.type.__name__ if hasattr(field.type, '__name__') else str(field.type),
|
|
121
|
-
"default": field.default if (field.default is not None and '_MISSING_TYPE' not in str(field.default)) else __metadata.get('default', None),
|
|
122
|
-
"metadata": __metadata
|
|
123
|
-
})
|
|
124
|
-
return __fields
|
|
67
|
+
f"'retention_weeks' must be an integer between 1 and 12 (inclusive), but got {self.retention_weeks}."
|
|
68
|
+
)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
|
+
import logging
|
|
2
3
|
|
|
3
4
|
class Level(Enum):
|
|
4
5
|
"""
|
|
5
6
|
Enumeration of standard logging levels.
|
|
6
7
|
|
|
7
8
|
Attributes:
|
|
8
|
-
NOTSET (int): No specific logging level set. Value is 0.
|
|
9
9
|
DEBUG (int): Detailed information, typically of interest only when diagnosing problems. Value is 10.
|
|
10
10
|
INFO (int): Confirmation that things are working as expected. Value is 20.
|
|
11
11
|
WARNING (int): An indication that something unexpected happened, or indicative of some problem in the near future. Value is 30.
|
|
@@ -13,9 +13,8 @@ class Level(Enum):
|
|
|
13
13
|
CRITICAL (int): A very serious error, indicating that the program itself may be unable to continue running. Value is 50.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
CRITICAL = 50
|
|
16
|
+
DEBUG = logging.DEBUG
|
|
17
|
+
INFO = logging.INFO
|
|
18
|
+
WARNING = logging.WARNING
|
|
19
|
+
ERROR = logging.ERROR
|
|
20
|
+
CRITICAL = logging.CRITICAL
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
from orionis.foundation.config.logging.enums import Level
|
|
3
|
+
from orionis.foundation.exceptions import OrionisIntegrityException
|
|
4
|
+
|
|
5
|
+
class _IsValidLevel:
|
|
6
|
+
"""
|
|
7
|
+
Validator that checks if a value is a valid logging level.
|
|
8
|
+
Accepts int, str, or Level enum.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
_level_names = {level.name for level in Level}
|
|
12
|
+
_level_values = {level.value for level in Level}
|
|
13
|
+
|
|
14
|
+
def __call__(self, value: Any) -> None:
|
|
15
|
+
"""
|
|
16
|
+
Validate the provided logging level value.
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
value : Any
|
|
20
|
+
The value to validate as a logging level. Can be an integer, string, or Level enum instance.
|
|
21
|
+
Raises
|
|
22
|
+
------
|
|
23
|
+
OrionisIntegrityException
|
|
24
|
+
If the value is not a valid logging level or not of an accepted type (int, str, or Level).
|
|
25
|
+
Notes
|
|
26
|
+
-----
|
|
27
|
+
- If `value` is an integer, it must be present in `self._level_values`.
|
|
28
|
+
- If `value` is a string, it is stripped, uppercased, and must be present in `self._level_names`.
|
|
29
|
+
- If `value` is a Level enum instance, it is accepted as valid.
|
|
30
|
+
"""
|
|
31
|
+
if isinstance(value, Level):
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
if isinstance(value, int):
|
|
35
|
+
if value not in self._level_values:
|
|
36
|
+
raise OrionisIntegrityException(
|
|
37
|
+
f"'level' must be one of {sorted(self._level_values)}, got {value}."
|
|
38
|
+
)
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
if isinstance(value, str):
|
|
42
|
+
name = value.strip().upper()
|
|
43
|
+
if name not in self._level_names:
|
|
44
|
+
raise OrionisIntegrityException(
|
|
45
|
+
f"'level' must be one of {sorted(self._level_names)}, got '{value}'."
|
|
46
|
+
)
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
raise OrionisIntegrityException(
|
|
50
|
+
f"'level' must be int, str, or Level enum, got {type(value).__name__}."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Exported singleton instance
|
|
54
|
+
IsValidLevel = _IsValidLevel()
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
from orionis.foundation.exceptions import OrionisIntegrityException
|
|
3
|
+
|
|
4
|
+
class __IsValidPath:
|
|
5
|
+
"""
|
|
6
|
+
__IsValidPath is a callable class used to validate that a given value is a non-empty string representing a file path.
|
|
7
|
+
|
|
8
|
+
Methods
|
|
9
|
+
-------
|
|
10
|
+
__call__(value: Any) -> None
|
|
11
|
+
|
|
12
|
+
Raises
|
|
13
|
+
------
|
|
14
|
+
OrionisIntegrityException
|
|
15
|
+
If the provided value is not a non-empty string representing a file path.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __call__(self, value: Any) -> None:
|
|
19
|
+
"""
|
|
20
|
+
Validates that the provided value is a non-empty string representing a file path.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
value (Any): The value to validate as a file path.
|
|
24
|
+
|
|
25
|
+
Raises:
|
|
26
|
+
OrionisIntegrityException: If the value is not a non-empty string.
|
|
27
|
+
"""
|
|
28
|
+
if not isinstance(value, str) or not value.strip():
|
|
29
|
+
raise OrionisIntegrityException(
|
|
30
|
+
f"File cache configuration error: 'path' must be a non-empty string, got {repr(value)}."
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Exported singleton instance
|
|
34
|
+
IsValidPath = __IsValidPath()
|
|
@@ -3,10 +3,6 @@ from orionis.console.output.contracts.console import IConsole
|
|
|
3
3
|
from orionis.container.providers.service_provider import ServiceProvider
|
|
4
4
|
|
|
5
5
|
class ConsoleProvider(ServiceProvider):
|
|
6
|
-
"""
|
|
7
|
-
Debug provider for the Orionis framework.
|
|
8
|
-
This provider is responsible for debugging functionalities.
|
|
9
|
-
"""
|
|
10
6
|
|
|
11
7
|
def register(self) -> None:
|
|
12
8
|
"""
|
|
@@ -3,10 +3,6 @@ from orionis.console.dumper.contracts.dump import IDebug
|
|
|
3
3
|
from orionis.container.providers.service_provider import ServiceProvider
|
|
4
4
|
|
|
5
5
|
class DumperProvider(ServiceProvider):
|
|
6
|
-
"""
|
|
7
|
-
Debug provider for the Orionis framework.
|
|
8
|
-
This provider is responsible for debugging functionalities.
|
|
9
|
-
"""
|
|
10
6
|
|
|
11
7
|
def register(self) -> None:
|
|
12
8
|
"""
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from orionis.container.providers.service_provider import ServiceProvider
|
|
2
|
+
from orionis.services.log.contracts.log_service import ILoggerService
|
|
3
|
+
from orionis.services.log.log_service import LoggerService
|
|
4
|
+
|
|
5
|
+
class LoggerProvider(ServiceProvider):
|
|
6
|
+
|
|
7
|
+
def register(self) -> None:
|
|
8
|
+
"""
|
|
9
|
+
Register services into the application container.
|
|
10
|
+
"""
|
|
11
|
+
self.app.instance(ILoggerService, LoggerService(self.app.config('logging')), alias="core.orionis.logger")
|
|
12
|
+
|
|
13
|
+
def boot(self) -> None:
|
|
14
|
+
"""
|
|
15
|
+
Perform any post-registration bootstrapping or initialization.
|
|
16
|
+
"""
|
|
17
|
+
pass
|
|
@@ -3,10 +3,6 @@ from orionis.services.paths.contracts.resolver import IResolver
|
|
|
3
3
|
from orionis.services.paths.resolver import Resolver
|
|
4
4
|
|
|
5
5
|
class PathResolverProvider(ServiceProvider):
|
|
6
|
-
"""
|
|
7
|
-
Debug provider for the Orionis framework.
|
|
8
|
-
This provider is responsible for debugging functionalities.
|
|
9
|
-
"""
|
|
10
6
|
|
|
11
7
|
def register(self) -> None:
|
|
12
8
|
"""
|
|
@@ -3,10 +3,6 @@ from orionis.console.dynamic.progress_bar import ProgressBar
|
|
|
3
3
|
from orionis.container.providers.service_provider import ServiceProvider
|
|
4
4
|
|
|
5
5
|
class ProgressBarProvider(ServiceProvider):
|
|
6
|
-
"""
|
|
7
|
-
Debug provider for the Orionis framework.
|
|
8
|
-
This provider is responsible for debugging functionalities.
|
|
9
|
-
"""
|
|
10
6
|
|
|
11
7
|
def register(self) -> None:
|
|
12
8
|
"""
|
|
@@ -3,10 +3,6 @@ from orionis.services.system.contracts.workers import IWorkers
|
|
|
3
3
|
from orionis.services.system.workers import Workers
|
|
4
4
|
|
|
5
5
|
class WorkersProvider(ServiceProvider):
|
|
6
|
-
"""
|
|
7
|
-
Debug provider for the Orionis framework.
|
|
8
|
-
This provider is responsible for debugging functionalities.
|
|
9
|
-
"""
|
|
10
6
|
|
|
11
7
|
def register(self) -> None:
|
|
12
8
|
"""
|
orionis/metadata/framework.py
CHANGED
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
class ILoggerService(ABC):
|
|
4
|
+
|
|
5
|
+
@abstractmethod
|
|
6
|
+
def info(self, message: str) -> None:
|
|
7
|
+
"""Log an informational message."""
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
@abstractmethod
|
|
11
|
+
def error(self, message: str) -> None:
|
|
12
|
+
"""Log an error message."""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
@abstractmethod
|
|
16
|
+
def warning(self, message: str) -> None:
|
|
17
|
+
"""Log a warning message."""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def debug(self, message: str) -> None:
|
|
22
|
+
"""Log a debug message."""
|
|
23
|
+
pass
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class LoggerRuntimeError(RuntimeError):
|
|
2
|
+
|
|
3
|
+
def __init__(self, msg: str):
|
|
4
|
+
"""
|
|
5
|
+
Parameters
|
|
6
|
+
----------
|
|
7
|
+
msg : str
|
|
8
|
+
Descriptive error message explaining the cause of the exception.
|
|
9
|
+
"""
|
|
10
|
+
super().__init__(msg)
|
|
11
|
+
|
|
12
|
+
def __str__(self) -> str:
|
|
13
|
+
"""
|
|
14
|
+
Returns
|
|
15
|
+
-------
|
|
16
|
+
str
|
|
17
|
+
Formatted string describing the exception.
|
|
18
|
+
"""
|
|
19
|
+
return str(self.args[0])
|
|
File without changes
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from logging.handlers import RotatingFileHandler
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
class PrefixedSizeRotatingFileHandler(RotatingFileHandler):
|
|
7
|
+
|
|
8
|
+
def rotation_filename(self, default_name):
|
|
9
|
+
"""
|
|
10
|
+
Generates a rotated log filename by prefixing the original filename with a timestamp.
|
|
11
|
+
This method takes an original file path, extracts its directory, base name, and extension,
|
|
12
|
+
and returns a new file path where the base name is prefixed with the current timestamp
|
|
13
|
+
in the format 'YYYYMMDD_HHMMSS'. If the target directory does not exist, it is created.
|
|
14
|
+
The original file path to be rotated.
|
|
15
|
+
The new file path with a timestamp prefix added to the base name.
|
|
16
|
+
Notes
|
|
17
|
+
-----
|
|
18
|
+
- The timestamp is based on the current local time.
|
|
19
|
+
- The method ensures that the parent directory for the new file exists.
|
|
20
|
+
|
|
21
|
+
Returns
|
|
22
|
+
-------
|
|
23
|
+
str
|
|
24
|
+
The new filename with a timestamp prefix in the format 'YYYYMMDD_HHMMSS'.
|
|
25
|
+
"""
|
|
26
|
+
# Split the original path to extract the base name and extension
|
|
27
|
+
if '/' in default_name:
|
|
28
|
+
parts = default_name.split('/')
|
|
29
|
+
elif '\\' in default_name:
|
|
30
|
+
parts = default_name.split('\\')
|
|
31
|
+
else:
|
|
32
|
+
parts = default_name.split(os.sep)
|
|
33
|
+
|
|
34
|
+
# Get the base name and extension
|
|
35
|
+
filename, ext = os.path.splitext(parts[-1])
|
|
36
|
+
|
|
37
|
+
# Create the path without the last part
|
|
38
|
+
path = os.path.join(*parts[:-1]) if len(parts) > 1 else ''
|
|
39
|
+
|
|
40
|
+
# Prefix the base name with a timestamp
|
|
41
|
+
prefix = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
42
|
+
|
|
43
|
+
# Join the path, prefix, and filename to create the full path
|
|
44
|
+
full_path = os.path.join(path, f"{prefix}_{filename}{ext}")
|
|
45
|
+
|
|
46
|
+
# Ensure the log directory exists
|
|
47
|
+
log_dir = Path(full_path).parent
|
|
48
|
+
if not log_dir.exists():
|
|
49
|
+
log_dir.mkdir(parents=True, exist_ok=True)
|
|
50
|
+
|
|
51
|
+
# Return the full path as a string
|
|
52
|
+
return full_path
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from logging.handlers import TimedRotatingFileHandler
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
class PrefixedTimedRotatingFileHandler(TimedRotatingFileHandler):
|
|
7
|
+
|
|
8
|
+
def rotation_filename(self, default_name):
|
|
9
|
+
"""
|
|
10
|
+
Generates a rotated log filename by prefixing the original filename with a timestamp.
|
|
11
|
+
This method takes an original file path, extracts its directory, base name, and extension,
|
|
12
|
+
and returns a new file path where the base name is prefixed with the current timestamp
|
|
13
|
+
in the format 'YYYYMMDD_HHMMSS'. If the target directory does not exist, it is created.
|
|
14
|
+
The original file path to be rotated.
|
|
15
|
+
The new file path with a timestamp prefix added to the base name.
|
|
16
|
+
Notes
|
|
17
|
+
-----
|
|
18
|
+
- The timestamp is based on the current local time.
|
|
19
|
+
- The method ensures that the parent directory for the new file exists.
|
|
20
|
+
|
|
21
|
+
Returns
|
|
22
|
+
-------
|
|
23
|
+
str
|
|
24
|
+
The new filename with a timestamp prefix in the format 'YYYYMMDD_HHMMSS'.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
# Split the original path to extract the base name and extension
|
|
28
|
+
if '/' in default_name:
|
|
29
|
+
parts = default_name.split('/')
|
|
30
|
+
elif '\\' in default_name:
|
|
31
|
+
parts = default_name.split('\\')
|
|
32
|
+
else:
|
|
33
|
+
parts = default_name.split(os.sep)
|
|
34
|
+
|
|
35
|
+
# Get the base name and extension
|
|
36
|
+
filename, ext = os.path.splitext(parts[-1])
|
|
37
|
+
|
|
38
|
+
# Create the path without the last part
|
|
39
|
+
path = os.path.join(*parts[:-1]) if len(parts) > 1 else ''
|
|
40
|
+
|
|
41
|
+
# Prefix the base name with a timestamp
|
|
42
|
+
prefix = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
43
|
+
|
|
44
|
+
# Join the path, prefix, and filename to create the full path
|
|
45
|
+
full_path = os.path.join(path, f"{prefix}_{filename}{ext}")
|
|
46
|
+
|
|
47
|
+
# Ensure the log directory exists
|
|
48
|
+
log_dir = Path(full_path).parent
|
|
49
|
+
if not log_dir.exists():
|
|
50
|
+
log_dir.mkdir(parents=True, exist_ok=True)
|
|
51
|
+
|
|
52
|
+
# Return the full path as a string
|
|
53
|
+
return full_path
|