New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pass progress bar to logger #313
Comments
Yes, please see this and this. And if you like the solution, please give us a like here! |
I solved it like this:
|
This could be tidied up even further if from tqdm import tqdm
class stream(object):
def write(self, s):
tqdm.write(s)
def flush():
pass NB: edited Then, instead of having to define your own class with a special import logging
from tqdm import stream
logger = logging.getLogger()
logger.addHandler(logging.StreamHandler(stream)) If we wanted to be really cheeky about it, the only thing which actually needs doing is for the It would be great if either a TqdmStream (for use with a StreamHandler) or a TqdmLogHandler could be added to the library - saves users digging through old StackOverflow posts which are all confusing variations on the same theme! EDIT: It turns out the python docs may be lying about the fact that StreamHandler only needs an object with Therefore, I think the best course of action is to have a single TqdmHandler for use with logging. Would it need its own buffering scheme? |
Just a quick update on this. Currently I'm using: import tqdm
import logging
import time
class TqdmStream(object):
@classmethod
def write(_, msg):
tqdm.tqdm.write(msg, end='')
# is this required?
# @classmethod
# def flush(_):
# pass
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG, stream=TqdmStream)
log = logging.getLogger(__name__)
log.info("loop")
# for x in tqdm.trange(1, 101, mininterval=10):
for x in tqdm.trange(1, 101):
time.sleep(.2)
if not x % 20:
log.debug("in loop %d" % x)
log.info("done") does anyone have any objections to adding this version of |
@casperdcl - does that work for you, even with It would be nice to have a TqdmStream instead of a TqdmHandler so that it can be used with basicConfig as you demonstrate, but it might be safer use use a TqdmHandler: I'm using this from tqdm import tqdm
class TqdmHandler(logging.Handler):
def emit(self, record):
try:
msg = self.format(record)
tqdm.write(msg) # , file=sys.stderr)
self.flush()
except (KeyboardInterrupt, SystemExit):
raise
except:
self.handleError(record) from a |
Are you certain? There's no way a |
Is there an updated and recommended way to interface tqdm with logging please? |
So, how tqdm work with the logging model, to put the log both in Screen and File? I tried #313 (comment) , but it only supports screen. And use stream and handler at the same time is not allowed. |
@casperdcl example of TqdmStream breaks if the message contains a line feed (not printing anything after the line feed). Related issue is #724 Leaving the Best way I've found to fix it is by adding a line feed at the end of every message. But that's kind of an aweful solution and we still got an unwanted line feed. |
I used this for my custom logging module: import tqdm
import time
import contextlib
from debug_tools import getLogger
log = getLogger()
class DummyTqdmFile(object):
""" Dummy file-like that will write to tqdm
https://github.com/tqdm/tqdm/issues/313
"""
file = None
def __init__(self, file):
self.file = file
def write(self, x):
# Avoid print() second call (useless \n)
if len(x.rstrip()) > 0:
tqdm.tqdm.write(x, file=self.file, end='')
def flush(self):
return getattr(self.file, "flush", lambda: None)()
@contextlib.contextmanager
def std_out_err_redirect_tqdm(log):
try:
if log._file:
original_file = log._file.stream
log._file.stream = DummyTqdmFile( original_file )
yield original_file
elif log._stream:
original_stream = log._stream.stream
log._stream.stream = DummyTqdmFile( original_stream )
yield original_stream
except Exception as exc:
raise exc
finally:
if log._file:
log._file.stream = original_file
elif log._stream:
log._stream.stream = original_stream
if __name__ == "__main__":
log.info("loop")
with std_out_err_redirect_tqdm( log ) as outputstream:
for x in tqdm.trange( 1, 16, file=outputstream, dynamic_ncols=True ):
time.sleep(.2)
if not x % 5:
log.debug("in loop %d\nnew line" % x)
log.info("done") |
yes |
Might anyone have any insights on how we can ensure when outputting to logging we can print the progress bar on single line.
Below seems to work in pycharm but not via the terminal on mac
was also exploring perhaps printing on the same line using logger formatter this partially works on the terminal but feels hacky. There's absolutely nothing wrong printing on a newline, for my use-case just looks better to load the progress bar in a single line using the python logger.
|
Not sure if it is helpful, what I have done in the past is to extend tqdm: import logging
from tqdm import tqdm
LOGGER = logging.getLogger(__name__)
class logging_tqdm(tqdm):
def __init__(
self,
*args,
logger: logging.Logger = None,
mininterval: float = 1,
bar_format: str = '{desc}{percentage:3.0f}%{r_bar}',
desc: str = 'progress: ',
**kwargs):
self._logger = logger
super().__init__(
*args,
mininterval=mininterval,
bar_format=bar_format,
desc=desc,
**kwargs
)
@property
def logger(self):
if self._logger is not None:
return self._logger
return LOGGER
def display(self, msg=None, pos=None):
if not self.n:
# skip progress bar before having processed anything
return
if not msg:
msg = self.__str__()
self.logger.info('%s', msg) It has some different more log-friendly defaults. |
@de-code how to actually use this?
doesn't show anything. |
fixed by #1155 (tqdm>=4.60.0); please see https://github.com/tqdm/tqdm#redirecting-logging |
@casperdcl I believe this issue is to do the opposite, send the tqdm output to logging (#1155 was about sending the logging to tqdm) Happy to also create a PR with the #313 (comment) |
Ah srry, right 🤦 - please do! |
@brtasavpatel you need to configure logging first. e.g. import logging
logging.basicConfig(level='INFO') Then you could also add some delay (sleep) to see the effect more (otherwise a single line will be shown), e.g.: from time import sleep
for item in logging_tqdm(range(10)):
sleep(0.3) |
I use this variant for tqdm 4.61.0
|
Sorry to bother, but I wonder has this issue been addressed? |
@de-code I was wondering if you could tell me how to log other info while using this, and also how to use a handler with it that has specified formatting. So far, my attempts are getting duplicated output, and lines which do not keep my specified formatting: handler = WatchedFileHandler('example.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(name)s %(lineno)d: %(message)s')
handler.setFormatter(formatter)
LOGGER.addHandler(handler)
...
LOGGER.info('outputting other important program info') |
Ah, sorry to bother you, I just realized that handlers are mutually exclusive with |
It is also possible to send
|
I have optimize the solution from rostepifanov above.
|
Just to clarify - these last couple of comments are about writing your tqdm output to a logger, rather than writing your log output using tqdm? That's a separate concern and should be in a different issue. The original issue was motivated by a case like import logging
from tqdm import tqdm
logger = logging.getLogger(__name__)
for item in tqdm([1,2,3,4,5])
logger.info("processing %s", item)
logging.basicConfig(level=logging.INFO) Which, by default, disrupts the progress bar by printing out in the middle of it. Discussion not relevant to this issue should be taken elsewhere. |
Extension of previously posted code to minimal workable snippet :
|
Hi,
Can the tqdm.tqdm progress bar to be passed into a logger?
The text was updated successfully, but these errors were encountered: