-
Notifications
You must be signed in to change notification settings - Fork 22
Logging #368
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
base: main
Are you sure you want to change the base?
Logging #368
Conversation
ondrejkrejci
left a comment
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.
Huge work @NikoOinonen ! I have many comments there. It seems to me, that we are slightly inconsistent with what is debug, warning and info. Still after some comments, I am more for approval, as we can change those later.
| if self.verbose > 1: | ||
| print("updateView time [s]", time.perf_counter() - t0) | ||
| logger.debug(f"FFViewer.updateView {ic} {iz} {data.shape}") | ||
| perf_logger.info(f"updateView time [s] {time.perf_counter() - t0}") |
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.
What is the perf_logger here? is it about performance - and that is instead of verbose > 1 ?
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.
I previously had the performance benchmark prints at higher verbose level, but now it's in a separate system that can be enabled as needed. Basically performance logger prints lines with info on the amount of time taken for various tasks, for example:
[2025-11-04 09:40:57,436 - ppafm.perf.AFMulator] evalAFM [s]: 0.05937764700001935
| xyzs = self.parent.xyzs - lvec[0] | ||
| atomstring = io.primcoords2Xsf(self.parent.Zs, xyzs.T, lvec) | ||
| io.saveXSF(fileName, data, lvec, head=atomstring, verbose=0) | ||
| io.saveXSF(fileName, data, lvec, head=atomstring) |
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.
Is it the information about verbosity send through somehow?
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.
The verbosity level is now handled globally by setting the logger logging level, so there is no need to pass it manually everywhere.
| from . import cpp_utils | ||
| from .logging_utils import get_logger | ||
|
|
||
| logger = get_logger("GridUtils") |
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.
Why the file/routine type has to be passed to the logger function?
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.
It's just a label that allows differentiating between messages coming from different modules if needed. The name is used in the log format with %(name)s. For example setting PPAFM_LOG_FORMAT="%(name) - %(message)s" will print lines like:
ppafm.HighLevel - Saving Lennard-Jones force field to xsf
The ppafm. will be prepended to the name because the logger is hierarchically derived from a logger with the name ppafm.
| if verbose > 0: | ||
| print(" zTips : ", zTips) | ||
| logger.info(">>BEGIN: relaxedScan3D_omp()") | ||
| logger.debug(f" zTips : {zTips}") |
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.
I understand why, but it is just funny, that one, of the verbose > 0 is info and other is debug.
|
|
||
| if level is None: | ||
| try: | ||
| level = os.environ["PPAFM_LOG_LEVEL"] |
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.
Do we set this how? During a pip installation?
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.
They are normal environment variables that you can set as you run your calculation, either for the whole session with export PPAFM_LOG_LEVEL=DEBUG or just for the one run by prepending, e.g.:
PPAFM_LOG_LEVEL=DEBUG ./run.sh
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.
We should add this to our WIKI.
ppafm/ml/CorrectionLoop.py
Outdated
| looper.startLoop(molecule, atomMap, bondMap, lvecMap, AFMRef) | ||
| ErrConv = 0.1 | ||
| print("# ------ To Loop ") | ||
| logger.debug("# ------ To Loop ") |
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.
Sounds like info to me.
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.
Changed it.
| if bCopy: | ||
| FE = self.downloadFF(FE) | ||
| if bFinish: | ||
| if bFinish or perf_log_enabled(): |
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.
I am wondering, if bFinish is still necessary in the function.
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.
It's maybe not that useful, but it's doing a different thing. You could possibly want to wait for the computation to finish even when not logging the performance.
| print(" LOCAL_MEM_SIZE = ", device.get_info( cl.device_info.LOCAL_MEM_SIZE ) / 4, " float32") | ||
| print(" MAX_CONSTANT_BUFFER_SIZE = ", device.get_info( cl.device_info.MAX_CONSTANT_BUFFER_SIZE ) / 4, " float32") | ||
| print(" MAX_WORK_GROUP_SIZE = ", device.get_info( cl.device_info.MAX_WORK_GROUP_SIZE )) | ||
| logger.info("---------------------------------------------------------------") |
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.
Ha, Ha, I would have many things from here, such as speed and mem sizes as debug.
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.
We don't actually use this function anywhere in the code at the moment. It's more of a developer testing thing for figuring out what capability the device has. I think all of the things here should be at the same log level, whether it's info or debug.
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.
maybe somebody more skilled with this like @yakutovicha should have look at this. I do not see there any damage, but to be honest, I am not fully understanding what is going on there.
ondrejkrejci
left a comment
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.
Looks good to me, Just we should put to the WIKI how to set the PPAFM_LOG_LEVEL=DEBUG and what are the possible levels.
Fixes #214
This changes all print statements in the Python code to use the built-in logging module instead. The logging events are divided into different levels, see https://docs.python.org/3/library/logging.html#levels, so that the user can control the amount and format of the output from ppafm. I set the default level at INFO and only left high level info at that level so that more detailed output is at DEBUG level which is not printed by default. You can see in the code if you agree with my judgement and we can adjust.
There is still some output coming from the C++ code which just goes to stdout, but that's for another time. I also didn't touch the
devmodule and some of the CLI scripts that are not part of the ppafm pip package.Controlling the logging output
In the code the logging level is controlled by using differently named methods for each level in the logger objects that I defined for each module. For example
logger.info("This is important information"), orlogger.debug("This is debug information").The output at runtime can be controlled two different ways:
configure_loggingfunction in the newlogging_utilsmodulePPAFM_LOG_LEVELsets the log level. All message more severe than the set level are printed. For examplePPAFM_LOG_LEVEL=DEBUGto print everything.PPAFM_LOG_PATHsets the file where the log is printed. If not specified (default), then the log goes to stdout. For examplePPAFM_LOG_PATH=my_ppafm_run.log.PPAFM_LOG_FORMATsets the format of printed log messages. There are several keywords you can use, see: https://docs.python.org/3/library/logging.html#logrecord-attributes. By default only the plain message is printed for info or "WARNING/ERROR: message" for more severe, or if the log level is at DEBUG then more detailed message with timestamp and the originating module name is printed. For examplePPAFM_LOG_FORMAT="%(asctime)s - %(message)s".PPAFM_LOG_PERFORMANCEcontrols whether timings for benchmarking are logged. Mostly relevant for the OpenCL code for now. Setting any value to the variable enables the log, for examplePPAFM_LOG_PERFORMANCE=1.We could add arguments to the CLI scripts as well, but I left it out for now since we are intending to overhaul that system anyways.