I want to use Python’s logging library to overwrite the previous console line. It should display ...
on the console while the process is running, then overwrite it with OK
once the process completes, as shown in this Python code.
import time
for i in range(10):
print(f'Progress: {i+1}/10 ...', end='')
time.sleep(1)
print(f'rProgress: {i+1}/10 OK')
I want to achieve the same result using the logging
library, but my current solution feels too verbose and un-pythonic. Does anyone have a better approach? Thanks!
import logging
import sys
import time
class OverwriteStreamHandler(logging.StreamHandler):
def __init__(self, stream, overwrite=False):
super().__init__(stream)
self.overwrite = overwrite
def emit(self, record):
msg = self.format(record)
stream = self.stream
if self.overwrite:
stream.write('r' + msg)
else:
stream.write(msg)
stream.flush()
def create_logger(name, overwrite=False):
handler = OverwriteStreamHandler(sys.stdout, overwrite=overwrite)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(message)s')
handler.setFormatter(formatter)
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
logger.addHandler(handler)
return logger
logger1 = create_logger('logger1')
logger2 = create_logger('logger2', overwrite=True)
for i in range(10):
logger1.info(f'Progress: {i+1}/10 ...')
time.sleep(1)
logger2.info(f'Progress: {i+1}/10 Okn')
2
There are 2 options:
- Add a handler with empty terminator and manually add the line terminator to each message.
- Create an additional logger with
terminator='r'
configured at the handler.
In both cases all line lengths must be of the longest message to avoid output garbage. Pad with spaces if needed.
Option 1
import logging
import time
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logging.basicConfig(level=logging.INFO)
def get_logger(name, formatter, terminator='n'):
lggr = logging.getLogger(name)
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
ch.setFormatter(formatter)
ch.terminator = terminator
lggr.addHandler(ch)
lggr.propagate = False
return ch, lggr
ch1, logger1 = get_logger("SO_log1", formatter, terminator='')
logger1.info('Startedn')
for i in range(10):
msg = f'Progress: {i+1:>2}/10 {"..."}r'
logger1.info(msg)
time.sleep(0.21)
msg = f'Progress: {i+1:>2}/10 {"Ok":>3}r'
if i == 10:
msg += "n"
logger1.info(msg)
# additional delay to get a chance to see the Ok msg
time.sleep(0.21)
logger1.info('n')
Output where date and last 9 characters of message will alternate
2024-12-18 23:44:32,535 - SO_log1 - INFO - Started
2024-12-18 23:44:34,638 - SO_log1 - INFO - Progress: 10/10 Ok
Option 2
Use 2 loggers and set line terminator on the second handler
import logging
import time
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logging.basicConfig(level=logging.INFO)
def get_logger(name, formatter, terminator='n'):
lggr = logging.getLogger(name)
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
ch.setFormatter(formatter)
ch.terminator = terminator
lggr.addHandler(ch)
lggr.propagate = False
return ch, lggr
ch1, logger1 = get_logger("SO_log1", formatter)
ch2, logger2 = get_logger("SO_log2", formatter, terminator='r')
logger1.info('Started opt 2')
for i in range(10):
msg = f'Progress: {i+1:>2}/10 {"..."}'
logger2.info(msg)
time.sleep(0.21)
msg = f'Progress: {i+1:>2}/10 {"Ok":>3}'
#logger2.info(msg)
if i < 10:
logger2.info(msg)
else:
logger1.info(msg)
time.sleep(0.21)
logger1.info("n")
Output 2
2024-12-19 00:03:38,262 - SO_log1 - INFO - Started opt 2
2024-12-19 00:03:42,467 - SO_log1 - INFO - Progress: 10/10 Ok