Skip to content

Commit b1cfaee

Browse files
FEAT: Adding the Logger Class to Main Template
1 parent 471cc2e commit b1cfaee

File tree

1 file changed

+156
-0
lines changed

1 file changed

+156
-0
lines changed

Main Template/Logger.py

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
r"""
2+
================================================================================
3+
Logger Utility Module
4+
================================================================================
5+
Author : Breno Farias da Silva
6+
Created : 2025-12-11
7+
Description :
8+
This module implements a dual-channel logging system designed to capture
9+
all console output produced by Python scripts while preserving ANSI colors
10+
in the terminal and removing them from the log file.
11+
12+
It provides consistent, color-safe logging across interactive terminals,
13+
background executions, CI pipelines, Makefile pipelines, and nohup/systemd
14+
environments.
15+
16+
Key features include:
17+
- Automatic ANSI color stripping for log files
18+
- Full compatibility with interactive and non-interactive terminals
19+
- Mirrored output: terminal (colored) + log file (clean)
20+
- Optional integration by assigning it to sys.stdout/sys.stderr
21+
22+
Usage:
23+
1. Create a Logger instance:
24+
25+
from logger import Logger
26+
logger = Logger("./Logs/output.log", clean=True)
27+
28+
2. (Optional) Redirect all stdout/stderr to the logger:
29+
30+
sys.stdout = logger
31+
sys.stderr = logger
32+
33+
3. Print normally:
34+
35+
print("\x1b[92mHello World\x1b[0m")
36+
37+
Terminal shows colored output.
38+
Log file receives the same text without ANSI escape sequences.
39+
40+
Outputs:
41+
- <path>.log file with fully sanitized (color-free) log output
42+
- Real-time terminal output with correct ANSI handling
43+
44+
TODOs:
45+
- Timestamp prefixing for each log line
46+
- File rotation or size-based log splitting
47+
- CLI flag to force color on/off
48+
- Optional JSON-structured logs
49+
50+
Dependencies:
51+
- Python >= 3.8
52+
- colorama (optional but recommended for Windows)
53+
54+
Assumptions & Notes:
55+
- ANSI escape sequences follow the regex: \x1B\[[0-9;]*[a-zA-Z]
56+
- Log file always stores clean output
57+
- When stdout is not a TTY, color output is automatically disabled
58+
"""
59+
60+
import os # For interacting with the filesystem
61+
import re # For stripping ANSI escape sequences
62+
import sys # For replacing stdout/stderr
63+
64+
# Regex Constants:
65+
ANSI_ESCAPE_REGEX = re.compile(r"\x1B\[[0-9;]*[a-zA-Z]") # Pattern to remove ANSI colors
66+
67+
# Classes Definitions:
68+
69+
class Logger:
70+
"""
71+
Simple logger class that prints colored messages to the terminal and
72+
writes a cleaned (ANSI-stripped) version to a log file.
73+
74+
Usage:
75+
logger = Logger("./Logs/output.log", clean=True)
76+
logger.info("\x1b[92mHello world\x1b[0m")
77+
78+
:param logfile_path: Path to the log file.
79+
:param clean: If True, truncate the log file on init; otherwise append.
80+
"""
81+
82+
def __init__(self, logfile_path, clean=False):
83+
"""
84+
Initialize the Logger.
85+
86+
:param self: Instance of the Logger class.
87+
:param logfile_path: Path to the log file.
88+
:param clean: If True, truncate the log file on init; otherwise append.
89+
"""
90+
91+
self.logfile_path = logfile_path # Store log file path
92+
93+
parent = os.path.dirname(logfile_path) # Ensure log directory exists
94+
if parent and not os.path.exists(parent): # Create parent directories if needed
95+
os.makedirs(parent, exist_ok=True) # Safe creation
96+
97+
mode = "w" if clean else "a" # Choose file mode based on 'clean' flag
98+
self.logfile = open(logfile_path, mode, encoding="utf-8") # Open log file
99+
self.is_tty = sys.stdout.isatty() # Verify if stdout is a TTY
100+
101+
def write(self, message):
102+
"""
103+
Internal method to write messages to both terminal and log file.
104+
105+
:param self: Instance of the Logger class.
106+
:param message: The message to log.
107+
"""
108+
109+
if message is None: # Ignore None messages
110+
return # Early exit
111+
112+
out = str(message) # Convert message to string
113+
if not out.endswith("\n"): # Ensure newline termination
114+
out += "\n" # Append newline if missing
115+
116+
clean_out = ANSI_ESCAPE_REGEX.sub("", out) # Strip ANSI sequences for log file
117+
118+
try: # Write to log file
119+
self.logfile.write(clean_out) # Write cleaned message
120+
self.logfile.flush() # Ensure immediate write
121+
except Exception: # Fail silently to avoid breaking user code
122+
pass # Silent fail
123+
124+
try: # Write to terminal: colored when TTY, cleaned otherwise
125+
if self.is_tty: # Terminal supports colors
126+
sys.__stdout__.write(out) # Write colored message
127+
sys.__stdout__.flush() # Flush immediately
128+
else: # Terminal does not support colors
129+
sys.__stdout__.write(clean_out) # Write cleaned message
130+
sys.__stdout__.flush() # Flush immediately
131+
except Exception: # Fail silently to avoid breaking user code
132+
pass # Silent fail
133+
134+
def flush(self):
135+
"""
136+
Flush the log file.
137+
138+
:param self: Instance of the Logger class.
139+
"""
140+
141+
try: # Flush log file buffer
142+
self.logfile.flush() # Flush log file
143+
except Exception: # Fail silently
144+
pass # Silent fail
145+
146+
def close(self):
147+
"""
148+
Close the log file.
149+
150+
:param self: Instance of the Logger class.
151+
"""
152+
153+
try: # Close log file
154+
self.logfile.close() # Close log file
155+
except Exception: # Fail silently
156+
pass # Silent fail

0 commit comments

Comments
 (0)