Reputation: 5492
First of all, my platform:
$ for ix in "uname -s" "python3 --version"; do echo "$ix: " $($ix); done
uname -s: MINGW64_NT-10.0-19045
python3 --version: Python 3.11.9
I would like in my PyQt5 application to have a logfile, where all terminal loging printouts to stdout or stderr would be save; I came up with this example test.py
:
#!/usr/bin/env python
import sys, os
# based on https://stackoverflow.com/q/14906787
class LoggerFile(object):
# "a" would append: here we want to restart log each time
log = open("logfile.log", "w")
def __init__(self):
self.terminal = None
self.id = None
def write(self, message):
self.terminal.write(message)
self.log.write(message)
def flush(self):
# > this flush method is needed for python 3 compatibility.
# > this handles the flush command by doing nothing.
# > you might want to specify some extra behavior here.
# MUST have this, to have logfile populated when running under PyQt5!
# But even then - logfile is populated only after application exits!
self.terminal.flush()
self.log.flush()
class LoggerOut(LoggerFile):
def __init__(self):
self.terminal = sys.stdout
self.id = "out"
class LoggerErr(LoggerFile):
def __init__(self):
self.terminal = sys.stderr
self.id = "err"
# REMEMBER TO ASSIGN!:
sys.stdout = LoggerOut()
sys.stderr = LoggerErr()
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
print("Hello from MainWindow")
self.setWindowTitle("My App")
button = QPushButton("Press me!")
button.clicked.connect(self.button_clicked)
self.setCentralWidget(button)
def button_clicked(self, s):
print("click", s)
def main(argv=None):
print("Hello from main")
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
if __name__ == '__main__':
main()
Once the "logfile.log" has been created, I can follow its changes in realtime by running tail -f logfile.log
in another terminal.
So, this is what happens when I run python3 test.py
from a terminal; I get these printouts immedately:
$ python3 test.py
Hello from main
Hello from MainWindow
... and the window starts up:
... and at this time, I can see from tail -f logfile.log
the following:
$ tail -f logfile.log
...
tail: logfile.log: file truncated
Good, so a new empty file got created, as desired - but notice no printouts yet.
Then I click on the button a few times - the python3
terminal shows printouts immediately/in real-time:
$ python3 test.py
Hello from main
Hello from MainWindow
click False
click False
... however the tail -f logfile.log
still prints nothing!
Finally, I close the window - the python3
command exits in its terminal, and only afterwards, do we see the printout in the tail -f logfile.log
terminal:
tail: logfile.log: file truncated
Hello from main
Hello from MainWindow
click False
click False
It's as if PyQt5 buffers all stdout/stderr output until the application exits - specifically for the log file writing (but not for terminal streams, stdout/stderr writing) !? And that, in spite of the explicit flush in the LoggerFile class?
(EDIT: moving the reassignments sys.stdout = LoggerOut()
after window = MainWindow()
has no effect on this file buffering behavior)
So, is it possible - and if so, how - to get such a "tee"-d log printout output to both terminal (stdout/stderr) and to file, which is unbuffered (i.e. writes are executed as soon as possible) for both terminal and file printouts?
Upvotes: 0
Views: 26