Rotary Logger  1.0.2
The middleware rotary logger
Loading...
Searching...
No Matches
constants.py
Go to the documentation of this file.
1"""
2# +==== BEGIN rotary_logger =================+
3# LOGO:
4# ..........####...####..........
5# ......###.....#.#########......
6# ....##........#.###########....
7# ...#..........#.############...
8# ...#..........#.#####.######...
9# ..#.....##....#.###..#...####..
10# .#.....#.##...#.##..##########.
11# #.....##########....##...######
12# #.....#...##..#.##..####.######
13# .#...##....##.#.##..###..#####.
14# ..#.##......#.#.####...######..
15# ..#...........#.#############..
16# ..#...........#.#############..
17# ...##.........#.############...
18# ......#.......#.#########......
19# .......#......#.########.......
20# .........#####...#####.........
21# /STOP
22# PROJECT: rotary_logger
23# FILE: constants.py
24# CREATION DATE: 29-10-2025
25# LAST Modified: 12:24:31 27-03-2026
26# DESCRIPTION:
27# A module that provides a universal python light on iops way of logging to files your program execution.
28# /STOP
29# COPYRIGHT: (c) Asperguide
30# PURPOSE: This is the file that contains the values that are not meant to change, this is regardless of the downstream code
31# // AR
32# +==== END rotary_logger =================+
33"""
34
35import os
36import sys
37from enum import Enum
38from pathlib import Path
39from dataclasses import dataclass, field
40from typing import IO, Optional, Dict, Any, Final, TYPE_CHECKING
41from io import TextIOWrapper
42
43if TYPE_CHECKING:
44 from .rotary_logger_cls import FileInstance
45
46# Stream redirection
47MIM_STDOUT = sys.stdout
48MIM_STDERR = sys.stderr
49MIM_STDIN = sys.stdin
50sys.stdout = MIM_STDOUT
51sys.stdin = MIM_STDIN
52sys.stderr = MIM_STDERR
53
54# Enable unbuffered output to ensure logs are captured immediately when redirected to files
55# This is critical when stdout/stderr are redirected (e.g., to a file)
56if not sys.flags.optimize or 'PYTHONUNBUFFERED' not in os.environ:
57 os.environ['PYTHONUNBUFFERED'] = '1'
58 sys.stdout.reconfigure(line_buffering=True) # type: ignore
59 sys.stderr.reconfigure(line_buffering=True) # type: ignore
60
61IS_A_TTY: bool = sys.stdout.isatty()
62IS_PIPE: bool = not IS_A_TTY
63
64MODULE_NAME: str = "[Rotary Logger]"
65
66ERROR: int = 1
67SUCCESS: int = 0
68
69SPACE: str = " "
70
71B1: int = 1 # bytes
72KB1: int = B1 * 1024 # bytes
73MB1: int = KB1 * KB1 # bytes
74GB1: int = MB1 * KB1 # bytes
75TB1: int = GB1 * KB1 # bytes
76
77STDIN: str = "stdin"
78STDOUT: str = "stdout"
79STDERR: str = "stderr"
80STDUNKNOWN: str = "stdunknown"
81
82
83class StdMode(Enum):
84 """Enumeration of the standard stream identifiers.
85
86 Used throughout the package to distinguish which standard stream a
87 TeeStream instance is wrapping, and to look up the correct folder
88 name and log prefix for that stream.
89 """
90 STDIN = STDIN
91 STDOUT = STDOUT
92 STDERR = STDERR
93 STDUNKNOWN = STDUNKNOWN
94
95
96LOG_FOLDER_BASE_NAME: str = "logs"
97
98FOLDER_STDOUT: str = "stdout"
99FOLDER_STDERR: str = "stderr"
100FOLDER_STDIN: str = "stdin"
101FOLDER_STDUNKNOWN: str = "std_unknown"
102
103CORRECT_FOLDER: Dict[StdMode, str] = {
104 StdMode.STDIN: FOLDER_STDIN,
105 StdMode.STDOUT: FOLDER_STDOUT,
106 StdMode.STDERR: FOLDER_STDERR,
107 StdMode.STDUNKNOWN: FOLDER_STDUNKNOWN,
108}
109
110BUFFER_FLUSH_SIZE: int = 8 * KB1 # flush every 8 KB
111
112DEFAULT_ENCODING: str = "utf-8"
113DEFAULT_LOG_MAX_FILE_SIZE: int = 2 * GB1 # MB ~ 2 GB
114DEFAULT_LOG_BUFFER_FLUSH_SIZE: int = BUFFER_FLUSH_SIZE
115DEFAULT_LOG_FOLDER: Path = Path(__file__).parent / LOG_FOLDER_BASE_NAME
116
117
118ERROR_MODE_WARN: str = "Warn"
119ERROR_MODE_WARN_NO_PIPE: str = "Warn No pipe"
120ERROR_MODE_EXIT: str = "Exit"
121ERROR_MODE_EXIT_NO_PIPE: str = "Exit No pipe"
122
123
124class ErrorMode(Enum):
125 """Tee error handling policy enumeration.
126
127 Members represent different policies for handling broken-pipe
128 situations (e.g. warn, exit, or variants for when stdout/stderr
129 is a pipe).
130 """
131 WARN = ERROR_MODE_WARN
132 WARN_NO_PIPE = ERROR_MODE_WARN_NO_PIPE
133 EXIT = ERROR_MODE_EXIT
134 EXIT_NO_PIPE = ERROR_MODE_EXIT_NO_PIPE
135
136
137FILE_LOG_DATE_FORMAT: str = "%Y_%m_%dT%Hh%Mm%Ss"
138
139
140@dataclass
142 """Container for an open log file.
143
144 Attributes:
145 path: the Path to the file on disk.
146 descriptor: the open file object (text mode) or None.
147 written_bytes: number of bytes already written to the file.
148 """
149 path: Optional[Path] = None
150 descriptor: Optional[IO[Any]] = None
151 written_bytes: int = 0
152
153
154@dataclass
155class Prefix:
156 """Flags describing which streams should be prefixed when mirrored.
157
158 Each flag is a boolean indicating whether the corresponding
159 standard stream (stdin/stdout/stderr) should receive a textual
160 prefix when written to disk.
161 """
162 std_in: bool = False
163 std_out: bool = False
164 std_err: bool = False
165
166
167BROKEN_PIPE_ERROR: str = f"{MODULE_NAME} Broken pipe on stdout"
168
169PREFIX_STDOUT: str = "[STDOUT]"
170PREFIX_STDERR: str = "[STDERR]"
171PREFIX_STDIN: str = "[STDIN]"
172PREFIX_STDUNKNOWN: str = "[STDUNKNOWN]"
173CORRECT_PREFIX: Dict[StdMode, str] = {
174 StdMode.STDIN: PREFIX_STDIN,
175 StdMode.STDOUT: PREFIX_STDOUT,
176 StdMode.STDERR: PREFIX_STDERR,
177 StdMode.STDUNKNOWN: PREFIX_STDUNKNOWN
178}
179
180PREFIX_FUNCTION_CALL_EMPTY: str = ""
181PREFIX_FUNCTION_CALL_WRITE: str = "[WRITE]"
182PREFIX_FUNCTION_CALL_WRITELINES: str = "[WRITELINES]"
183PREFIX_FUNCTION_CALL_FLUSH: str = "[FLUSH]"
184PREFIX_FUNCTION_CALL_READ: str = "[READ]"
185PREFIX_FUNCTION_CALL_READLINE: str = "[READLINE]"
186PREFIX_FUNCTION_CALL_READLINES: str = "[READLINES]"
187
188
190 """Optional per-call prefix appended to log entries for precise tracing.
191
192 When enabled, each log entry is tagged with the name of the stream
193 method that produced it (e.g. [WRITE], [READLINE]), making it easy
194 to distinguish writes from reads in a shared log file.
195 """
196 EMPTY = PREFIX_FUNCTION_CALL_EMPTY
197 WRITE = PREFIX_FUNCTION_CALL_WRITE
198 WRITELINES = PREFIX_FUNCTION_CALL_WRITELINES
199 FLUSH = PREFIX_FUNCTION_CALL_FLUSH
200 READ = PREFIX_FUNCTION_CALL_READ
201 READLINE = PREFIX_FUNCTION_CALL_READLINE
202 READLINES = PREFIX_FUNCTION_CALL_READLINES
203
204
205LOG_TO_FILE_ENV: bool = os.environ.get(
206 "LOG_TO_FILE",
207 "true"
208).lower() in ("1", "true", "yes")
209
210RAW_LOG_FOLDER_ENV: str = os.environ.get(
211 "LOG_FOLDER_NAME",
212 str(DEFAULT_LOG_FOLDER)
213)
214
215
216@dataclass
218 """Container for the FileInstance objects corresponding to each stream.
219
220 Attributes:
221 stdout: the FileInstance for stdout, or None if not capturing.
222 stderr: the FileInstance for stderr, or None if not capturing.
223 stdin: the FileInstance for stdin, or None if not capturing.
224 """
225 stdout: Optional["FileInstance"] = None
226 stderr: Optional["FileInstance"] = None
227 stdin: Optional["FileInstance"] = None
228 stdunknown: Optional["FileInstance"] = None
229 merged_streams: Dict[StdMode, bool] = field(default_factory=lambda: {
230 StdMode.STDIN: False,
231 StdMode.STDOUT: False,
232 StdMode.STDERR: False,
233 StdMode.STDUNKNOWN: False
234 })
235
236
237@dataclass(frozen=True)
239 """
240 The settings for the logger to know if it can output the line for the given level.
241 """
242 program_log: bool = True
243 success: bool = True
244 info: bool = True
245 warning: bool = True
246 error: bool = True
247 critical: bool = True
248 debug: bool = True
249
250
251RAW_STDIN: Final[Optional[TextIOWrapper]] = sys.__stdin__
252RAW_STDOUT: Final[Optional[TextIOWrapper]] = sys.__stdout__
253RAW_STDERR: Final[Optional[TextIOWrapper]] = sys.__stderr__