Simple tag based log and timing helper macros and functions.
FreeCAD Base::Console() is capable of outputting to different targets, and has some basic enable/disable control of different types of logs. There is, however, no easy way to use logging facility for various FC modules. This set of helper macros and function is aimed to provide a unified logging (and timing) interface. The interface is mainly designed for C++ code. Python code can also take some advantage of log level control interface. The developer can now leave their logging code permanently active in the source code without impact on performance, and the log can be easily turned on/off dynamically using Python console for debugging purpose, even in release build.
A set of macros is provided to ease the usage of tag based log. All the macros are defined in <Base/Console.h>. At the beginning of your C++ source, you need to initialize the log level of your chosen tag using,
It makes sense to use the same tag in multiple files for easy log level control. Please check Customization if You want more than one tag inside the same source file.
Predefined log levels are,
Bigger log level integer values have lower priorities. There is a special log level,
Actually, any negative log level behave the same, which is for tags that are not previously configured by user. The actual log level applied is controlled by Base::Console()
.SetDefaultLogLevel(). Python developers/end-users can configure the default log level by calling
where level
is either a string of value Error, Warning, Message, Log, Trace
or an integer value. By default, on release build, the default log level is FC_LOGLEVEL_MSG
, and on debug build, FC_LOGLEVEL_LOG
.
Python code can call FreeCAD.setLogLevel(tag,level)
to configure any other tag log levels, and FreeCAD.getLogLevel(tag)
, which outputs only integer log level.
You can fine tune how the log is output by passing extra parameters to #FC_LOG_LEVEL_INIT(). All the extra parameters are boolean value, which are shown blew along with their default values.
You can dynamically modify the log style as well, by changing these variables before the actual log output macro. See Customization for more details
Be careful with 'refresh' option. Its current implementation calls QCoreApplication::sendPostedEvent() which may cause some unexpected behavior, especially when called inside a destructor.
The actual logging macros are
The logging macros correspond to existing Base::Console() output functions, and FC_TRACE
uses Base::Console().Log(), same as FC_LOG
. These macros checks the log level defined in FC_LOG_LEVEL_INIT
to decide whether to print log or not. msg
here shall be a C++ streaming expression. End of line will be automatically appended by default.
This set of macros is for helping C++ code to time lengthy operations. Examples:
This will output in console something like,
Every time you call FC_TIME_LOG
it will calculate the time duration between this call and the last FC_TIME_LOG
or FC_TIME_INIT
. Time variable t
will then be updated to the current time. You can also use FC_TIME_MSG, FC_TIME_TRACE
similar to FC_MSG and FC_TRACE
.
To time operation in multiple stages,
Will output something like,
To time operation in multiple functions,
You can also use FC_DURATION_MSG, FC_DURATION_TRACE
as usual.
If you use only macros provided here to do timing, the entire timing code can be compiled out by defining FC_LOG_NO_TIMING
before including App/Console.h
.
Most of the logging facilities are exposed through macros. This section briefs how they are implemented under the hood in case you want customization. A new function GetLogLevel(tag) is added to Base::Console() to let C++ developer query a log level for an arbitrary string tag. The function returns a pointer to an integer representing the log level. Python developer or end-user can set/get the same tag based log level using FreeCAD.setLogLevel/getLogLevel. Any change to the log level is reflected through the pointer returned by Base::Console().GetLogLevel(). What FC_LOG_LEVEL_INIT(tag)
does is to define a class Base::LogLevel, and then a file static instance of that class to store the pointer to the desired tag log level. The class and instance name is predefined. Various log macros will check that instance to query log level. If you some how want to have more than one tag inside the same source file, use the following macros to define a second instance of name instance_name
Then, define a second set of logging macros as
Note, replace instance_name
with your actual desired name.
You can also define your own log levels the same way. Macro #_FC_PRINT(_instance,_l,_func,_msg) checks to see if the log shall proceed, where _instance
is the static loglevel instance name (default is FC_LOG_INSTANCE
), and _l
is the log level constant to be checked, _func
is the Base::Console() function to print the log.