-
Notifications
You must be signed in to change notification settings - Fork 178
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
Setup Python logging + convert print statements to logger calls #558
Conversation
yay. green. |
rebased |
* Setup the standard Python logging module via main.py with cli argument so that different log levels can be chosen. * Move the main.py into ./src/seedsigner folder, to be able to importable for unit-testing. * Also define a cli entrypoint for main as the becomes possible with the main.py move. * Create a symlink from./src/main.py -> ./src/seedsigner/main.py so that seedsigner can be started without any changes to seedsigner-os * Add uni-tests for the main.py regarding the default log level and changing the log level. * Converts all existing print statements to `logger.info` calls (INFO is the default log level) * Produces tab aligned log message like `2024-05-15 17:21:45.385 INFO: Starting Seedsigner with: {'loglevel': 'INFO'}` * The sub seconds display + the option to choose the loglevel help to debug the application and also see how long certain code needs There are many many commented out `print` statements in the code base that could be converted to `logger.debug(...)` calls and thus be shown additionally via running `main.py -l DEBUG`, but that is for next PRs as all those commented out print statements can be potential broken by referencing non existing variables etc. This is a move towards more Pythonic ways of doing: The logging framework with its central configurability, options to set levels, the ability to activate certain log lines by choosing a different log level (without un-commenting/commenting print statements), the possibility to the see how much times is between log calls (without extra instrumenting the code) etc is all in all very convenient. The help output on cli: ``` usage: main.py [-h] [-l {CRITICAL,FATAL,ERROR,WARN,WARNING,INFO,DEBUG,NOTSET}] options: -h, --help show this help message and exit -l {CRITICAL,FATAL,ERROR,WARN,WARNING,INFO,DEBUG,NOTSET}, --loglevel {CRITICAL,FATAL,ERROR,WARN,WARNING,INFO,DEBUG,NOTSET} Set the log level (default: INFO) ```
rebased |
src/seedsigner/main.py
Outdated
) | ||
|
||
args = parser.parse_args(sys_argv) | ||
logging.basicConfig( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That there is no "filename" param, does this guarantee (even into the future) that messages logged will always end up on stdout?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm... how to guarantee this? The Python stdlib logging framework since forever logs to stderr if nothing else is given to the logging.basicConfig()
call. A change of behaviour like logging to a not further specified file would be a huge break for many many applications that handle secrets, credentials, tokens etc.
Added a unit test in a1d3489 that actually verifies that the log is written to the stderr.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
stderr, of course, thank you for the correction.
And that it would raise alarms elsewhere eases my mind.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yay. the added testcase is now green. and I also replaced the logging.basicConfig
with explicit calls to have better control over what is exactly setup here.
I'm torn on the concept of logging between:
My worry is that currently, the sdcard is mounted if the user left it in. Thank you for all of your great prs... and in advance for settling my paranoid (because I don't know better) nerves. |
@jdlcdl right... both the existing print statements AND the new logging statements can be problematic IF the console output (stdout/stderr) is redirected to a file on sd-card:
|
|
||
|
||
@patch("seedsigner.main.Controller") | ||
def test_main__logging__writes_to_stderr(patched_controller, capsys): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<3
Logging > print: ACK Recommend we drop mention of see: https://stackoverflow.com/a/31170408/11611893 Later TODO: Provide explicit style / convention guidance for devs in a README. |
One thing I can't think through is: what kinds of logging should be INFO vs DEBUG? I think the question is a bit different for us than typical projects since our devs are the only ones who'll ever see any of these messages. So in that sense, EVERY log line is intended to aid debugging. I guess for now the best we can do is default to INFO and then over time make decisions about what log chatter we want to quiet down and demote to DEBUG. |
Also filter out |
Moving Because I also dislike symlinks in repos in general. Moving I'm reading up on |
@kdmukai Added another commit that 1. moves the main.py back 2. eliminates the introduced symlink 3. still gets the added tests working by doing an import trick :) I cautiously converted all existing print statements to |
src/main.py
Outdated
root_logger.setLevel(logging.getLevelName(args.loglevel)) | ||
console_handler = logging.StreamHandler(sys.stderr) | ||
console_handler.setFormatter( | ||
logging.Formatter("%(asctime)s.%(msecs)03d %(levelname)s:\t%(message)s") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: the .%(msecs)03d
is redundant as asctime
includes millis already.
I'd find something like this format more useful:
"%(asctime)s %(levelname)5s [%(name)s %(funcName)s (%(lineno)d)]: %(message)s"
2024-07-08 18:25:01,437 INFO [__main__ main (45)]: Starting Seedsigner with: {'loglevel': 'INFO'}
2024-07-08 18:25:06,255 INFO [seedsigner.controller activate_toast (428)]: Controller: starting RemoveSDCardToastManagerThread
2024-07-08 18:25:06,512 INFO [seedsigner.gui.toast run (124)]: RemoveSDCardToastManagerThread: started
2024-07-08 18:25:06,514 INFO [seedsigner.controller start (308)]: back_stack: []
2024-07-08 18:25:06,519 INFO [seedsigner.controller start (312)]: Executing MainMenuView()
2024-07-08 18:25:09,577 INFO [seedsigner.gui.toast run (136)]: RemoveSDCardToastManagerThread: Acquiring lock
2024-07-08 18:25:09,582 INFO [seedsigner.gui.toast run (138)]: RemoveSDCardToastManagerThread: Lock acquired
2024-07-08 18:25:09,589 INFO [seedsigner.gui.toast run (166)]: RemoveSDCardToastManagerThread: Showing toast
2024-07-08 18:25:11,799 INFO [seedsigner.gui.toast run (145)]: RemoveSDCardToastManagerThread: Exiting due to user input
2024-07-08 18:25:11,804 INFO [seedsigner.gui.toast run (178)]: RemoveSDCardToastManagerThread: exiting
2024-07-08 18:25:11,856 INFO [seedsigner.gui.toast run (182)]: RemoveSDCardToastManagerThread: restored previous screen state
2024-07-08 18:25:12,243 INFO [seedsigner.controller start (358)]: Appending next destination: ToolsMenuView()
2024-07-08 18:25:12,247 INFO [seedsigner.controller start (363)]: ------------------------------
2024-07-08 18:25:12,252 INFO [seedsigner.controller start (308)]: back_stack: [
0: ToolsMenuView()
]
2024-07-08 18:25:12,259 INFO [seedsigner.controller start (312)]: Executing ToolsMenuView()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uh. thanks for the redundant msec finding, don't know why i didn't see that... this seems to have changed, I remember that is was required when working with older Python versions... having msecs help to accurately see how much time is required between certain calls.
Good idea to add funcname + lineno etc to the log line... (I am used to have all those fields via json loggers, which are not feasible here for the console) ... went for "%(asctime)s %(levelname)8s [%(name)s %(funcName)s (%(lineno)d)]: %(message)s"
(=changed the 5s to 8s spacing so that all the potential loglevel are right aligned .. longest name is CRITICAL
), see 5a4ae50
Final minor nits noted above, but otherwise very happy to ACK this! Very nice quality-of-life improvement here, @dbast. |
tACK. Great contribution as usual @dbast! |
seedsigner
cli entrypoint for main as that becomes possible with the main.py move.logger.info
calls (INFO is the default log level)2024-05-15 17:21:45.385 INFO: Starting Seedsigner with: {'loglevel': 'INFO'}
There are many many commented out
print
statements in the code base that could be converted tologger.debug(...)
calls and thus be shown additionally via runningmain.py -l DEBUG
, but that is for next PRs as all those commented out print statements can be potential broken by referencing non existing variables etc.This is a move towards more Pythonic ways of doing: The logging framework with its central configurability, options to set levels, the ability to activate certain log lines by choosing a different log level (without un-commenting/commenting print statements), the possibility to the see how much times is between log calls (without extra instrumenting the code) etc is all in all very convenient.
The help output on cli:
Description
Describe the change simply. Provide a reason for the change.
Include screenshots of any new or modified screens (or at least explain why they were omitted)
This pull request is categorized as a:
Checklist
pytest
and made sure all unit tests pass before sumbitting the PRIf you modified or added functionality/workflow, did you add new unit tests?
I have tested this PR on the following platforms/os:
Note: Keep your changes limited in scope; if you uncover other issues or improvements along the way, ideally submit those as a separate PR. The more complicated the PR the harder to review, test, and merge.