orionis 0.401.0__py3-none-any.whl → 0.402.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.402.0.dist-info}/METADATA +1 -1
- {orionis-0.401.0.dist-info → orionis-0.402.0.dist-info}/RECORD +37 -24
- {orionis-0.401.0.dist-info → orionis-0.402.0.dist-info}/WHEEL +0 -0
- {orionis-0.401.0.dist-info → orionis-0.402.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.401.0.dist-info → orionis-0.402.0.dist-info}/top_level.txt +0 -0
- {orionis-0.401.0.dist-info → orionis-0.402.0.dist-info}/zip-safe +0 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
from dataclasses import
|
|
2
|
-
from datetime import time
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from datetime import datetime, time
|
|
3
|
+
from orionis.foundation.config.base import BaseConfigEntity
|
|
4
|
+
from orionis.foundation.config.logging.validators import IsValidPath, IsValidLevel
|
|
3
5
|
from orionis.foundation.exceptions import OrionisIntegrityException
|
|
4
6
|
from orionis.foundation.config.logging.enums import Level
|
|
5
7
|
|
|
6
8
|
@dataclass(unsafe_hash=True, kw_only=True)
|
|
7
|
-
class Daily:
|
|
9
|
+
class Daily(BaseConfigEntity):
|
|
8
10
|
"""
|
|
9
11
|
Represents the configuration for daily log file rotation.
|
|
10
12
|
|
|
@@ -16,34 +18,34 @@ class Daily:
|
|
|
16
18
|
"""
|
|
17
19
|
|
|
18
20
|
path: str = field(
|
|
19
|
-
default='storage/log/application.log',
|
|
20
|
-
metadata={
|
|
21
|
+
default = 'storage/log/application.log',
|
|
22
|
+
metadata = {
|
|
21
23
|
"description": "The file path where the log is stored.",
|
|
22
24
|
"default": "storage/log/application.log",
|
|
23
25
|
},
|
|
24
26
|
)
|
|
25
27
|
|
|
26
28
|
level: int | str | Level = field(
|
|
27
|
-
default=Level.INFO,
|
|
28
|
-
metadata={
|
|
29
|
-
"description": "The logging level (e.g.,
|
|
29
|
+
default = Level.INFO,
|
|
30
|
+
metadata = {
|
|
31
|
+
"description": "The logging level (e.g., DEBUG, INFO, WARNING, ERROR, CRITICAL).",
|
|
30
32
|
"default": Level.INFO,
|
|
31
33
|
},
|
|
32
34
|
)
|
|
33
35
|
|
|
34
36
|
retention_days: int = field(
|
|
35
|
-
default=7,
|
|
36
|
-
metadata={
|
|
37
|
+
default = 7,
|
|
38
|
+
metadata = {
|
|
37
39
|
"description": "The number of days to retain log files before deletion.",
|
|
38
40
|
"default": 7,
|
|
39
41
|
},
|
|
40
42
|
)
|
|
41
43
|
|
|
42
|
-
at: time = field(
|
|
43
|
-
default=time(0, 0),
|
|
44
|
+
at: time | str = field(
|
|
45
|
+
default = time(0, 0).strftime("%H:%M"),
|
|
44
46
|
metadata={
|
|
45
47
|
"description": "The time of day when the log rotation should occur.",
|
|
46
|
-
"default": time(0, 0),
|
|
48
|
+
"default": time(0, 0).strftime("%H:%M"),
|
|
47
49
|
},
|
|
48
50
|
)
|
|
49
51
|
|
|
@@ -54,82 +56,42 @@ class Daily:
|
|
|
54
56
|
Raises:
|
|
55
57
|
OrionisIntegrityException: If any attribute is invalid.
|
|
56
58
|
"""
|
|
57
|
-
# Validate 'path'
|
|
58
|
-
if not isinstance(self.path, str) or not self.path.strip():
|
|
59
|
-
raise OrionisIntegrityException(
|
|
60
|
-
f"File cache configuration error: 'path' must be a non-empty string, got {repr(self.path)}."
|
|
61
|
-
)
|
|
62
59
|
|
|
63
|
-
# Validate '
|
|
64
|
-
|
|
65
|
-
if not isinstance(self.level, valid_level_types):
|
|
66
|
-
raise OrionisIntegrityException(
|
|
67
|
-
f"File cache configuration error: 'level' must be int, str, or Level enum, got {type(self.level).__name__}."
|
|
68
|
-
)
|
|
60
|
+
# Validate 'path' using the IsValidPath validator
|
|
61
|
+
IsValidPath(self.path)
|
|
69
62
|
|
|
70
|
-
#
|
|
71
|
-
|
|
72
|
-
_value = self.level.strip().upper()
|
|
73
|
-
if not _value:
|
|
74
|
-
raise OrionisIntegrityException(
|
|
75
|
-
"File cache configuration error: 'level' string cannot be empty."
|
|
76
|
-
)
|
|
77
|
-
if _value not in Level.__members__:
|
|
78
|
-
raise OrionisIntegrityException(
|
|
79
|
-
f"File cache configuration error: 'level' must be one of {list(Level.__members__.keys())}, got '{self.level}'."
|
|
80
|
-
)
|
|
81
|
-
self.level = Level[_value].value
|
|
82
|
-
elif isinstance(self.level, int):
|
|
83
|
-
valid_values = [level.value for level in Level]
|
|
84
|
-
if self.level not in valid_values:
|
|
85
|
-
raise OrionisIntegrityException(
|
|
86
|
-
f"File cache configuration error: 'level' must be one of {valid_values}, got '{self.level}'."
|
|
87
|
-
)
|
|
88
|
-
elif isinstance(self.level, Level):
|
|
89
|
-
self.level = self.level.value
|
|
63
|
+
# Validate 'level' using the IsValidLevel validator
|
|
64
|
+
IsValidLevel(self.level)
|
|
90
65
|
|
|
91
66
|
# Validate 'retention_days'
|
|
92
|
-
if not isinstance(self.retention_days, int)
|
|
67
|
+
if not isinstance(self.retention_days, int):
|
|
93
68
|
raise OrionisIntegrityException(
|
|
94
|
-
f"
|
|
69
|
+
f"'retention_days' must be an integer, got {type(self.retention_days).__name__}."
|
|
95
70
|
)
|
|
96
|
-
|
|
97
|
-
# Validate 'at'
|
|
98
|
-
if not isinstance(self.at, time):
|
|
71
|
+
if not (1 <= self.retention_days <= 90):
|
|
99
72
|
raise OrionisIntegrityException(
|
|
100
|
-
f"
|
|
73
|
+
f"'retention_days' must be between 1 and 90, got {self.retention_days}."
|
|
101
74
|
)
|
|
102
75
|
|
|
103
|
-
#
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
Converts the Daily object to a dictionary representation.
|
|
109
|
-
|
|
110
|
-
Returns:
|
|
111
|
-
dict: A dictionary containing the dataclass fields and their values.
|
|
112
|
-
"""
|
|
113
|
-
return asdict(self)
|
|
76
|
+
# Validate 'at' must be a time instance or a valid "HH:MM" string
|
|
77
|
+
if not isinstance(self.at, (time, str)):
|
|
78
|
+
raise OrionisIntegrityException(
|
|
79
|
+
f"'at' must be a datetime.time instance or a 'HH:MM' string, got {type(self.at).__name__}."
|
|
80
|
+
)
|
|
114
81
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
82
|
+
# Validate and normalize 'at'
|
|
83
|
+
if isinstance(self.at, str):
|
|
84
|
+
try:
|
|
85
|
+
parsed_time = datetime.strptime(self.at, "%H:%M").time()
|
|
86
|
+
self.at = parsed_time
|
|
87
|
+
except ValueError:
|
|
88
|
+
raise OrionisIntegrityException(
|
|
89
|
+
f"'at' must be a valid time string in 'HH:MM' format, got '{self.at}'."
|
|
90
|
+
)
|
|
91
|
+
elif not isinstance(self.at, time):
|
|
92
|
+
raise OrionisIntegrityException(
|
|
93
|
+
f"'at' must be a datetime.time instance or a 'HH:MM' string, got {type(self.at).__name__}."
|
|
94
|
+
)
|
|
118
95
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
- name (str): The name of the field.
|
|
122
|
-
- type (type): The type of the field.
|
|
123
|
-
- default: The default value of the field, if specified; otherwise, the value from metadata or None.
|
|
124
|
-
- metadata (mapping): The metadata associated with the field.
|
|
125
|
-
"""
|
|
126
|
-
__fields = []
|
|
127
|
-
for field in fields(self):
|
|
128
|
-
__metadata = dict(field.metadata) or {}
|
|
129
|
-
__fields.append({
|
|
130
|
-
"name": field.name,
|
|
131
|
-
"type": field.type.__name__ if hasattr(field.type, '__name__') else str(field.type),
|
|
132
|
-
"default": field.default if (field.default is not None and '_MISSING_TYPE' not in str(field.default)) else __metadata.get('default', None),
|
|
133
|
-
"metadata": __metadata
|
|
134
|
-
})
|
|
135
|
-
return __fields
|
|
96
|
+
# Normalize 'at' to "HH:MM" format
|
|
97
|
+
self.at = self.at.strftime("%H:%M")
|
|
@@ -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 IsValidLevel, IsValidPath
|
|
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 Hourly:
|
|
8
|
+
class Hourly(BaseConfigEntity):
|
|
7
9
|
"""
|
|
8
10
|
Represents the configuration for hourly log file management.
|
|
9
11
|
|
|
@@ -14,24 +16,24 @@ class Hourly:
|
|
|
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_hours: int = field(
|
|
33
|
-
default=24,
|
|
34
|
-
metadata={
|
|
35
|
+
default = 24,
|
|
36
|
+
metadata = {
|
|
35
37
|
"description": "The number of hours to retain log files before deletion.",
|
|
36
38
|
"default": 24,
|
|
37
39
|
},
|
|
@@ -47,38 +49,12 @@ class Hourly:
|
|
|
47
49
|
- 'level' must be an int, str, or Level enum, and a valid value.
|
|
48
50
|
- 'retention_hours' must be an integer between 1 and 168.
|
|
49
51
|
"""
|
|
50
|
-
# Validate 'path'
|
|
51
|
-
if not isinstance(self.path, str) or not self.path.strip():
|
|
52
|
-
raise OrionisIntegrityException(
|
|
53
|
-
f"File cache configuration error: 'path' must be a non-empty string, got {repr(self.path)}."
|
|
54
|
-
)
|
|
55
52
|
|
|
56
|
-
# Validate '
|
|
57
|
-
|
|
58
|
-
if not isinstance(self.level, valid_level_types):
|
|
59
|
-
raise OrionisIntegrityException(
|
|
60
|
-
f"File cache configuration error: 'level' must be int, str, or Level enum, got {type(self.level).__name__}."
|
|
61
|
-
)
|
|
53
|
+
# Validate 'path' using the IsValidPath validator
|
|
54
|
+
IsValidPath(self.path)
|
|
62
55
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if not _value:
|
|
66
|
-
raise OrionisIntegrityException(
|
|
67
|
-
"File cache configuration error: 'level' string cannot be empty."
|
|
68
|
-
)
|
|
69
|
-
if _value not in Level.__members__:
|
|
70
|
-
raise OrionisIntegrityException(
|
|
71
|
-
f"File cache configuration error: 'level' must be one of {list(Level.__members__.keys())}, got '{self.level}'."
|
|
72
|
-
)
|
|
73
|
-
self.level = Level[_value].value
|
|
74
|
-
elif isinstance(self.level, int):
|
|
75
|
-
valid_values = [level.value for level in Level]
|
|
76
|
-
if self.level not in valid_values:
|
|
77
|
-
raise OrionisIntegrityException(
|
|
78
|
-
f"File cache configuration error: 'level' must be one of {valid_values}, got '{self.level}'."
|
|
79
|
-
)
|
|
80
|
-
elif isinstance(self.level, Level):
|
|
81
|
-
self.level = self.level.value
|
|
56
|
+
# Validate 'level' using the IsValidLevel validator
|
|
57
|
+
IsValidLevel(self.level)
|
|
82
58
|
|
|
83
59
|
# Validate 'retention_hours'
|
|
84
60
|
if not isinstance(self.retention_hours, int) or self.retention_hours < 0:
|
|
@@ -88,35 +64,4 @@ class Hourly:
|
|
|
88
64
|
if self.retention_hours < 1 or self.retention_hours > 168:
|
|
89
65
|
raise OrionisIntegrityException(
|
|
90
66
|
f"File cache configuration error: 'retention_hours' must be between 1 and 168, got {self.retention_hours}."
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
def toDict(self) -> dict:
|
|
94
|
-
"""
|
|
95
|
-
Converts the Hourly object to a dictionary representation.
|
|
96
|
-
|
|
97
|
-
Returns:
|
|
98
|
-
dict: A dictionary containing all the dataclass fields and their values.
|
|
99
|
-
"""
|
|
100
|
-
return asdict(self)
|
|
101
|
-
|
|
102
|
-
def getFields(self):
|
|
103
|
-
"""
|
|
104
|
-
Retrieves a list of field information for the current dataclass instance.
|
|
105
|
-
|
|
106
|
-
Returns:
|
|
107
|
-
list: A list of dictionaries, each containing details about a field:
|
|
108
|
-
- name (str): The name of the field.
|
|
109
|
-
- type (type): The type of the field.
|
|
110
|
-
- default: The default value of the field, if specified; otherwise, the value from metadata or None.
|
|
111
|
-
- metadata (mapping): The metadata associated with the field.
|
|
112
|
-
"""
|
|
113
|
-
__fields = []
|
|
114
|
-
for field in fields(self):
|
|
115
|
-
__metadata = dict(field.metadata) or {}
|
|
116
|
-
__fields.append({
|
|
117
|
-
"name": field.name,
|
|
118
|
-
"type": field.type.__name__ if hasattr(field.type, '__name__') else str(field.type),
|
|
119
|
-
"default": field.default if (field.default is not None and '_MISSING_TYPE' not in str(field.default)) else __metadata.get('default', None),
|
|
120
|
-
"metadata": __metadata
|
|
121
|
-
})
|
|
122
|
-
return __fields
|
|
67
|
+
)
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
from dataclasses import dataclass, field,
|
|
1
|
+
from dataclasses import dataclass, field, fields
|
|
2
|
+
from orionis.foundation.config.base import BaseConfigEntity
|
|
2
3
|
from orionis.foundation.config.logging.entities.channels import Channels
|
|
3
4
|
from orionis.foundation.exceptions import OrionisIntegrityException
|
|
4
5
|
|
|
5
6
|
@dataclass(unsafe_hash=True, kw_only=True)
|
|
6
|
-
class Logging:
|
|
7
|
+
class Logging(BaseConfigEntity):
|
|
7
8
|
"""
|
|
8
9
|
Represents the logging system configuration.
|
|
9
10
|
|
|
@@ -18,21 +19,39 @@ class Logging:
|
|
|
18
19
|
default="stack",
|
|
19
20
|
metadata={
|
|
20
21
|
"description": "The default logging channel to use.",
|
|
21
|
-
"default": "stack"
|
|
22
|
+
"default": "stack"
|
|
22
23
|
}
|
|
23
24
|
)
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
|
|
26
|
+
channels: Channels | dict = field(
|
|
27
|
+
default_factory = lambda: Channels(),
|
|
26
28
|
metadata={
|
|
27
29
|
"description": "A collection of available logging channels.",
|
|
28
|
-
"default":
|
|
30
|
+
"default": lambda: Channels().toDict()
|
|
29
31
|
}
|
|
30
32
|
)
|
|
31
33
|
|
|
32
34
|
def __post_init__(self):
|
|
33
35
|
"""
|
|
34
|
-
Validates the
|
|
36
|
+
Validates the logging configuration after dataclass initialization by ensuring
|
|
37
|
+
the default channel and channels configuration are properly formatted and valid.
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
None
|
|
41
|
+
Raises
|
|
42
|
+
------
|
|
43
|
+
OrionisIntegrityException
|
|
44
|
+
If the default channel is not a string or doesn't match available channel options.
|
|
45
|
+
OrionisIntegrityException
|
|
46
|
+
If the channels configuration is malformed or cannot be converted to a Channels instance.
|
|
47
|
+
OrionisIntegrityException
|
|
48
|
+
If the channels property is not a Channels instance or a dictionary.
|
|
49
|
+
Notes
|
|
50
|
+
-----
|
|
51
|
+
This method performs the following validations:
|
|
52
|
+
- Ensures 'default' is a string matching available channel options from Channels fields
|
|
35
53
|
"""
|
|
54
|
+
|
|
36
55
|
options = [field.name for field in fields(Channels)]
|
|
37
56
|
if not isinstance(self.default, str) or self.default not in options:
|
|
38
57
|
raise OrionisIntegrityException(
|
|
@@ -40,34 +59,14 @@ class Logging:
|
|
|
40
59
|
)
|
|
41
60
|
|
|
42
61
|
if not isinstance(self.channels, Channels):
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"""
|
|
55
|
-
Retrieves a list of field information for the current dataclass instance.
|
|
56
|
-
|
|
57
|
-
Returns:
|
|
58
|
-
list: A list of dictionaries, each containing details about a field:
|
|
59
|
-
- name (str): The name of the field.
|
|
60
|
-
- type (type): The type of the field.
|
|
61
|
-
- default: The default value of the field, if specified; otherwise, the value from metadata or None.
|
|
62
|
-
- metadata (mapping): The metadata associated with the field.
|
|
63
|
-
"""
|
|
64
|
-
__fields = []
|
|
65
|
-
for field in fields(self):
|
|
66
|
-
__metadata = dict(field.metadata) or {}
|
|
67
|
-
__fields.append({
|
|
68
|
-
"name": field.name,
|
|
69
|
-
"type": field.type.__name__ if hasattr(field.type, '__name__') else str(field.type),
|
|
70
|
-
"default": field.default if (field.default is not None and '_MISSING_TYPE' not in str(field.default)) else __metadata.get('default', None),
|
|
71
|
-
"metadata": __metadata
|
|
72
|
-
})
|
|
73
|
-
return __fields
|
|
62
|
+
if isinstance(self.channels, dict):
|
|
63
|
+
try:
|
|
64
|
+
self.channels = Channels(**self.channels)
|
|
65
|
+
except TypeError as e:
|
|
66
|
+
raise OrionisIntegrityException(
|
|
67
|
+
f"Invalid channels configuration: {e}"
|
|
68
|
+
)
|
|
69
|
+
else:
|
|
70
|
+
raise OrionisIntegrityException(
|
|
71
|
+
"The 'channels' property must be an instance of Channels or a dictionary."
|
|
72
|
+
)
|
|
@@ -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 Monthly:
|
|
8
|
+
class Monthly(BaseConfigEntity):
|
|
7
9
|
"""
|
|
8
10
|
Configuration entity for monthly log file management.
|
|
9
11
|
|
|
@@ -14,23 +16,23 @@ class Monthly:
|
|
|
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_months: int = field(
|
|
33
|
-
default=4,
|
|
35
|
+
default = 4,
|
|
34
36
|
metadata={
|
|
35
37
|
"description": "The number of months to retain log files before deletion.",
|
|
36
38
|
"default": 4,
|
|
@@ -47,76 +49,18 @@ class Monthly:
|
|
|
47
49
|
- 'level' must be an int, str, or Level enum, and a valid logging level.
|
|
48
50
|
- 'retention_months' must be an integer between 1 and 12 (inclusive).
|
|
49
51
|
"""
|
|
50
|
-
# Validate 'path'
|
|
51
|
-
|
|
52
|
-
raise OrionisIntegrityException(
|
|
53
|
-
f"File cache configuration error: 'path' must be a non-empty string, got {repr(self.path)}."
|
|
54
|
-
)
|
|
52
|
+
# Validate 'path' using the IsValidPath validator
|
|
53
|
+
IsValidPath(self.path)
|
|
55
54
|
|
|
56
|
-
# Validate 'level'
|
|
57
|
-
|
|
58
|
-
if not isinstance(self.level, valid_level_types):
|
|
59
|
-
raise OrionisIntegrityException(
|
|
60
|
-
f"File cache configuration error: 'level' must be int, str, or Level enum, got {type(self.level).__name__}."
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
if isinstance(self.level, str):
|
|
64
|
-
_value = self.level.strip().upper()
|
|
65
|
-
if not _value:
|
|
66
|
-
raise OrionisIntegrityException(
|
|
67
|
-
"File cache configuration error: 'level' string cannot be empty."
|
|
68
|
-
)
|
|
69
|
-
if _value not in Level.__members__:
|
|
70
|
-
raise OrionisIntegrityException(
|
|
71
|
-
f"File cache configuration error: 'level' must be one of {list(Level.__members__.keys())}, got '{self.level}'."
|
|
72
|
-
)
|
|
73
|
-
self.level = Level[_value].value
|
|
74
|
-
elif isinstance(self.level, int):
|
|
75
|
-
valid_values = [level.value for level in Level]
|
|
76
|
-
if self.level not in valid_values:
|
|
77
|
-
raise OrionisIntegrityException(
|
|
78
|
-
f"File cache configuration error: 'level' must be one of {valid_values}, got '{self.level}'."
|
|
79
|
-
)
|
|
80
|
-
elif isinstance(self.level, Level):
|
|
81
|
-
self.level = self.level.value
|
|
55
|
+
# Validate 'level' using the IsValidLevel validator
|
|
56
|
+
IsValidLevel(self.level)
|
|
82
57
|
|
|
83
58
|
# Validate 'retention_months'
|
|
84
|
-
if not isinstance(self.retention_months, int)
|
|
59
|
+
if not isinstance(self.retention_months, int):
|
|
85
60
|
raise OrionisIntegrityException(
|
|
86
|
-
f"
|
|
61
|
+
f"Invalid type for 'retention_months': expected int, got {type(self.retention_months).__name__}."
|
|
87
62
|
)
|
|
88
|
-
if
|
|
63
|
+
if not (1 <= self.retention_months <= 12):
|
|
89
64
|
raise OrionisIntegrityException(
|
|
90
|
-
f"
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
def toDict(self) -> dict:
|
|
94
|
-
"""
|
|
95
|
-
Converts the Monthly object to a dictionary representation.
|
|
96
|
-
|
|
97
|
-
Returns:
|
|
98
|
-
dict: A dictionary containing all dataclass fields and their values.
|
|
99
|
-
"""
|
|
100
|
-
return asdict(self)
|
|
101
|
-
|
|
102
|
-
def getFields(self):
|
|
103
|
-
"""
|
|
104
|
-
Retrieves a list of field information for the current dataclass instance.
|
|
105
|
-
|
|
106
|
-
Returns:
|
|
107
|
-
list: A list of dictionaries, each containing details about a field:
|
|
108
|
-
- name (str): The name of the field.
|
|
109
|
-
- type (type): The type of the field.
|
|
110
|
-
- default: The default value of the field, if specified; otherwise, the value from metadata or None.
|
|
111
|
-
- metadata (mapping): The metadata associated with the field.
|
|
112
|
-
"""
|
|
113
|
-
__fields = []
|
|
114
|
-
for field in fields(self):
|
|
115
|
-
__metadata = dict(field.metadata) or {}
|
|
116
|
-
__fields.append({
|
|
117
|
-
"name": field.name,
|
|
118
|
-
"type": field.type.__name__ if hasattr(field.type, '__name__') else str(field.type),
|
|
119
|
-
"default": field.default if (field.default is not None and '_MISSING_TYPE' not in str(field.default)) else __metadata.get('default', None),
|
|
120
|
-
"metadata": __metadata
|
|
121
|
-
})
|
|
122
|
-
return __fields
|
|
65
|
+
f"'retention_months' must be an integer between 1 and 12 (inclusive), got {self.retention_months}."
|
|
66
|
+
)
|
|
@@ -1,25 +1,26 @@
|
|
|
1
|
-
from dataclasses import
|
|
2
|
-
from orionis.foundation.
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from orionis.foundation.config.base import BaseConfigEntity
|
|
3
|
+
from orionis.foundation.config.logging.validators import IsValidLevel, IsValidPath
|
|
3
4
|
from orionis.foundation.config.logging.enums import Level
|
|
4
5
|
|
|
5
6
|
@dataclass(unsafe_hash=True, kw_only=True)
|
|
6
|
-
class Stack:
|
|
7
|
+
class Stack(BaseConfigEntity):
|
|
7
8
|
"""
|
|
8
9
|
Represents the configuration for a logging stack, including the log file path and logging level.
|
|
9
10
|
"""
|
|
10
11
|
|
|
11
12
|
path: str = field(
|
|
12
|
-
default='storage/log/application.log',
|
|
13
|
-
metadata={
|
|
13
|
+
default = 'storage/log/application.log',
|
|
14
|
+
metadata = {
|
|
14
15
|
"description": "The file path where the log is stored.",
|
|
15
16
|
"default": "storage/log/application.log",
|
|
16
17
|
},
|
|
17
18
|
)
|
|
18
19
|
|
|
19
20
|
level: int | str | Level = field(
|
|
20
|
-
default=Level.INFO,
|
|
21
|
-
metadata={
|
|
22
|
-
"description": "The logging level (e.g.,
|
|
21
|
+
default = Level.INFO,
|
|
22
|
+
metadata = {
|
|
23
|
+
"description": "The logging level (e.g., DEBUG, INFO, WARNING, ERROR, CRITICAL).",
|
|
23
24
|
"default": Level.INFO,
|
|
24
25
|
},
|
|
25
26
|
)
|
|
@@ -31,66 +32,9 @@ class Stack:
|
|
|
31
32
|
Raises:
|
|
32
33
|
OrionisIntegrityException: If 'path' is not a non-empty string, or if 'level' is not a valid type or value.
|
|
33
34
|
"""
|
|
34
|
-
# Validate 'path'
|
|
35
|
-
if not isinstance(self.path, str) or not self.path.strip():
|
|
36
|
-
raise OrionisIntegrityException(
|
|
37
|
-
f"File cache configuration error: 'path' must be a non-empty string, got {repr(self.path)}."
|
|
38
|
-
)
|
|
39
35
|
|
|
40
|
-
# Validate '
|
|
41
|
-
|
|
42
|
-
if not isinstance(self.level, valid_level_types):
|
|
43
|
-
raise OrionisIntegrityException(
|
|
44
|
-
f"File cache configuration error: 'level' must be int, str, or Level enum, got {type(self.level).__name__}."
|
|
45
|
-
)
|
|
36
|
+
# Validate 'path' using the IsValidPath validator
|
|
37
|
+
IsValidPath(self.path)
|
|
46
38
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if not _value:
|
|
50
|
-
raise OrionisIntegrityException(
|
|
51
|
-
"File cache configuration error: 'level' string cannot be empty."
|
|
52
|
-
)
|
|
53
|
-
if _value not in Level.__members__:
|
|
54
|
-
raise OrionisIntegrityException(
|
|
55
|
-
f"File cache configuration error: 'level' must be one of {list(Level.__members__.keys())}, got '{self.level}'."
|
|
56
|
-
)
|
|
57
|
-
self.level = Level[_value].value
|
|
58
|
-
elif isinstance(self.level, int):
|
|
59
|
-
valid_values = [level.value for level in Level]
|
|
60
|
-
if self.level not in valid_values:
|
|
61
|
-
raise OrionisIntegrityException(
|
|
62
|
-
f"File cache configuration error: 'level' must be one of {valid_values}, got '{self.level}'."
|
|
63
|
-
)
|
|
64
|
-
elif isinstance(self.level, Level):
|
|
65
|
-
self.level = self.level.value
|
|
66
|
-
|
|
67
|
-
def toDict(self) -> dict:
|
|
68
|
-
"""
|
|
69
|
-
Converts the Stack instance to a dictionary representation.
|
|
70
|
-
|
|
71
|
-
Returns:
|
|
72
|
-
dict: A dictionary containing all fields of the Stack instance.
|
|
73
|
-
"""
|
|
74
|
-
return asdict(self)
|
|
75
|
-
|
|
76
|
-
def getFields(self):
|
|
77
|
-
"""
|
|
78
|
-
Retrieves a list of field information for the current dataclass instance.
|
|
79
|
-
|
|
80
|
-
Returns:
|
|
81
|
-
list: A list of dictionaries, each containing details about a field:
|
|
82
|
-
- name (str): The name of the field.
|
|
83
|
-
- type (type): The type of the field.
|
|
84
|
-
- default: The default value of the field, if specified; otherwise, the value from metadata or None.
|
|
85
|
-
- metadata (mapping): The metadata associated with the field.
|
|
86
|
-
"""
|
|
87
|
-
__fields = []
|
|
88
|
-
for field in fields(self):
|
|
89
|
-
__metadata = dict(field.metadata) or {}
|
|
90
|
-
__fields.append({
|
|
91
|
-
"name": field.name,
|
|
92
|
-
"type": field.type.__name__ if hasattr(field.type, '__name__') else str(field.type),
|
|
93
|
-
"default": field.default if (field.default is not None and '_MISSING_TYPE' not in str(field.default)) else __metadata.get('default', None),
|
|
94
|
-
"metadata": __metadata
|
|
95
|
-
})
|
|
96
|
-
return __fields
|
|
39
|
+
# Validate 'level' using the IsValidLevel validator
|
|
40
|
+
IsValidLevel(self.level)
|