Get better log in your extensions thanks to the CamiTK Log System
Starting from CamiTK 4.1, the log system was entirely redesigned. The new log system is more powerful, very flexible and can trace anything on the console as well as in a log file.
Forget about all this std::cout
and other way of “debugging” that clutters and slowdown your code, and let start with the new log system.
The aims of the log system in CamiTK are:
- to simplify and give a flexible way to log message in any application
- to be minimally invasive in the source code
- to help bug tracking and report
- to help new development by providing automatically generated debugging information
- to suppress any need to use
qDebug
macro orstd::cout
/std::cerr
printf
Basics
Using the CamiTK log system is very easy, all you need to do is include the Log.h
header and use one of the CAMITK_...(QString)
macro.
Code example
Examples in an Action
or Component
based class:
|
|
As you can see in the above example, there is four types of log messages, explained in the table below.
log message | description |
---|---|
ERROR |
error messages should be used only when a problem occurs which cannot be taken care of by the source code (e.g. input data are invalid and preventive action is taken, execution might result in unfinished process / undefined behaviour, or a crash…) |
WARNING |
warning messages should be used when something went wrong but is not critical to the application state but might require some corrective or alternative action by the user |
INFO |
information messages should be used to show normal processing, stage or intermediate steps or display some useful data values |
TRACE |
trace messages should be used to trace anything useful for debugging or log post-processing |
These log macros will generate a compilation error in some context (i.e., in static methods and in classes that do not derived from QObject
). In this case you need to use the ALT
version of the macros instead, see the corresponding section below.
Note about the IF versions
All the CamiTK log macro also have an IF version. In this case the message is only logged if the first parameter of the macro is evaluated to true during execution. This can be useful when debugging code inside a loop or display a log message in specific cases.Log level
This is where log messages get flexible: when running a CamiTK application (such as camitk-imp
), log messages will be displayed depending on the current log level.
There is four types of current log level as described in this table:
log level | description |
---|---|
TRACE |
all types of messages are logged |
INFO |
INFO, WARNING and ERROR messages are logged |
WARNING |
only WARNING and ERROR messages are logged |
ERROR |
only error messages are logged |
NONE |
in this case, no message are logged at all. Logger is completely silent |
The current log level can be changed interactively (see the log console section) as well as programmatically (see the corresponding good practice section).
To change the log level programmatically, at any stage, use Log::getLogger()->setLogLevel(...)
.
A message box level is also available: any message equals or above the message box level will be shown in a modal QMessageBox
dialog. This is very handy to define the level of interaction required in an application:
- if the application requires a user, it is a good practice to show all message above warning into a
QMessageBox
modal dialog - if the application should run without any user interaction (e.g., a test application) or manages the warning and error differently, then the message box level should be set to
NONE
Format of a log message
A log message has always the same format (at least in CamiTK default logger):
yyyy-MM-dd HH:mm:ss.zzz [LOG_LEVEL] [DEBUG_INFO] message
^----- part #1 -------^ ^--- #2 --^ ^--- #3 ---^ ^--- #4 ---^
There are four parts:
- The first part is the time stamp of the message (at what date time exactly it is being printed). Note that you can choose to hide the time stamp if you wish.
- The message level: it is either
ERROR
,WARNING
,INFO
orTRACE
- The debug information: depending on your OS, compiler, and context, it generally contains the name of the class, method, and line number of the log message emitter. Note that you can choose to hide the debug information if you wish
- The message itself: this is the
QString
parameter given to the log macro.
Note that if the log macro is used inside a CamiTK inherited class (e.g., Action, Component,…), then the message is prepend with a specific text (for example, if a log macro is called from a camitk::Action
or any class inheriting from camitk::Action
, then "Action 'TheActionName' - "
is automatically added at the beginning of the log message string)
Class inheriting from Action
, Component
, Viewer
, Application
, MainWindow
, ComponentExtension
, ActionExtension
and ViewerExtension
are currently supported.
Log console
The log console is automatically available in camitk-imp
. It can be shown/displayed using the Help → Toggle Log Console
menu or by clicking on the console icon at the left of the progress bar.
It displays all the log messages above the current log level:
Moreover it allows you to modify the current log level and to show or hide the time stamp or debug information:
- click on to set the current log level to
NONE
- click on to set the current log level to
ERROR
- click on to set the current log level to
WARNING
- click on to set the current log level to
INFO
- click on to set the current log level to
TRACE
- click on to hide/show the debug information
- click on to hide/show the time stamp
There are other ways to change the current log level in camitk-imp
:
- You can set the log parameters in the application settings dialog
- You can test or play with the log parameters using the “Logger Parameters” action
Good practice, tips and tricks
Debugging with CAMITK_TRACE
CAMITK_TRACE
is a great tool to be used for temporarily debugging in replacement of std::cout or std::cerr.
As debug information can be turned on, you will get the exact location of the log message, and
You can also use CAMITK_TRACE_IF((test),message)
for instance to display a message for a given value of a loop iterator or
if a given parameter or attribute has a specific value you are currently testing.
It can also be used for information about normal process current step/stage.
No more QMessageBox
or modal dialogs
CAMITK_WARNING
or CAMITK_ERROR
are to be used instead of QMessageBox
.
The main advantage is that in environment where there is no user available to close the message dialog (such as a test environment) or where specific actions have to be taken when a warning or an error arise (such a clinical test environment) then the application is not blocked and can take the right course of actions.
This increase dramatically your code portability.
QMessageBox
should be reserved of asking the user some question.
When the user interaction is really required, you may therefore use a QMessageBox
, but please mark your need with specific comment above the QMessageBox
declaration, such as:
// CCC Exception: Use a QMessageBox::question as the user interaction is required
or
// CCC Exception: Use a QMessageBox::warning instead of CAMITK_WARNING as the user interaction is required
(CCC stands for CamiTK Coding Convention)
Using QMessageBox
is not recommended as it interrupt the user flow and might reduce the extensibility of your extension. There are a lot of other ways to get some information from the user (parameters in action or properties in component for instance, check the property system of CamiTK).
Think twice before you add a QMessageBox
or a modal dialog to your application (e.g., sometimes a default value or a CamiTK property may be better).
Changing the current log level programmatically
To change the log level at any stage in your code (for instance to display the TRACE
message only for a portion of the code), use the setLogLevel(..)
method:
...
CAMITK_TRACE("This message might not be seen. If current log level is higher than TRACE, then this message is not printed.")
// store the current log level
InterfaceLogger::LogLevel appCurrentLogLevel = Log::getLogger()->getLogLevel();
// change the current log level temporarily, after this line, any TRACE message is shown
Log::getLogger()->setLogLevel(TRACE);
...
CAMITK_TRACE("This message is guaranteed to be visible")
...
// Restore previous state
Log::getLogger()->setLogLevel(appCurrentLogLevel);
Other things can be changed programmatically (click on the arrow for more details):
Show/hide the time stamp
To show or hide the time stamp, just use theLog::getLogger()->setTimeStampInformation(bool)
Show/hide the debug information
To show or hide the debug information, just use theLog::getLogger()->setDebugInformation(bool)
Log to a file
Every log messages can be written in a log file. The filename is determined automatically using a time stamp, but you can modify the directory of the log file.Using CamiTK log system in static methods and in classes that do not derived from QObject
You need to use the specific ALT
(alternative) log macros in the following cases:
- inside a static method
- inside a class that does not derived from
QObject
- inside a function (such as a
main(..)
)
If you use the normal macros and get a compilation error such as:
error: invalid use of ‘this’ in non-member function
error: no matching function for call to ‘camitk::InterfaceLogger::log(QString, camitk::InterfaceLogger::LogLevel, const char [93], const char [84], int, NameOfANonQObjectDerivedClass*)’
It means you need to use the ALT
macros instead.
But don’t worry, it just means you have to suffix the normal CamiTK log macros with _ALT
, as in the following example:
// in header
class MyClassA : public QWidget {
Q_OBJECT // this class inherits from QObject through QWidget
void methodA();
static void methodB();
}
...
class MyClassB {
// this class does not inherits from QObject
void methodC();
}
...
// in implementation
void MyClassA::methodA() {
CAMITK_TRACE("`this` pointer is available, detailed information can be fetched by introspection.")
}
void MyClassA::methodB() {
CAMITK_TRACE_ALT("In static method, introspection is not available. Use the ALT macros instead.")
}
...
void MyClassB::methodC() {
CAMITK_TRACE_ALT("In non-Qt inherited class, introspection cannot be based on Qt mechanism. Use the ALT macros instead.")
}
In static method, the autoreference pointer this
is not available, therefore helper methods cannot easily determine the class name and introspection is not guaranteed.
Both Action
and Component
CamiTK class derived from Qt’s QObject
. The log system can therefore takes advantage of Qt introspection mechanism to fetch more debugging information. This is not available for classes that do not derived from QObject
.
Log message in your Action
In a CamiTK Action
, it is recommended to issue a CAMITK_WARNING
in case the the return status is ABORTED
(unless the user voluntarily did cancel/abort the action)
New application
If you create your own application, you can change and override the default log settings. You can also create a new logger completely if the default CamiTK logger does not suit your needs.
Translation of the GUI visible messages
It is a good practice to wrap your messages into a tr(..)
, so that they can be easily translated to another language. In order to collect all the strings properly and allow translators to re-arrange the sentence
structures with respect to the grammar of the targeted language, use tr()
in conjunction with arg()
CAMITK_TRACE(tr("File %1 saved").arg(filename))
// and if there are more than one parameter use the tr("%1 %2 %3...").arg(..,..,..), e.g.,
CAMITK_TRACE(tr("File %1 saved in directory %2").arg(filename,dirName))
Note that for non-Qt derived class, you can add Q_DECLARE_TR_FUNCTIONS
to the class declaration
...
#include <QCoreApplication>
...
class MyClass {
Q_DECLARE_TR_FUNCTIONS(MyClass) // add at the beginning before any public/private/protected section
...
}
Then it can be used directly in the class:
...
CAMITK_TRACE_ALT(tr("Parsing %1 files...").arg(QString::number(fileCount)))
...
Troubleshooting compilation error on MSVC
A (legacy) MS Windows header defines ERROR
as a preprocessor macro. This header is unfortunately sometimes
included in low-level library code. This will clash with the CAMITK_ERROR
macro.
If you use the CAMITK_ERROR
or CAMITK_ERROR_ALT
macro in your code and get these compilation errors:
error C2589: 'constant': illegal token on right side of '::'
or
error C2059: syntax error: '::'
Move your #include <Log.h>
line at the end of your #include
lines.
Therefore #include <Log.h>
should be the last #include
directive.
No semi-column at the end of a macro line
It is not recommended to add a semi-column sign (;
) at the end of a log macro instruction.
As the the macro command will be replaced by the proper C++ code, including an ending semi-column, the extra semi-column sign will add an extra NOP.
Moreover if the log macro are completely disabled using the CAMITK_DISABLE_LOG
definition flag at compile time, the the semi-column will still be present (and transformed into a useless NOP).
Stylishly speaking, omitting the semi-column also remind the developer that the log messages are created by a corresponding preprocessor macro.
Pretty print pointer value
Sometimes it is quite handy to print the address where a pointer myPointer
is pointing to. In order to do that you, you can use the CamiTK helper macro CAMITK_PRINT_POINTER
.
For instance, to print the pointer myPointer
declared as AnyClass*
, you can write:
CAMITK_INFO(tr("checking ") + CAMITK_PRINT_POINTER(myPointer))
This will print:
checking myPointer=0x00005632e5fd5af0
For your information, the macro is defined like this:
#define CAMITK_PRINT_POINTER(PTR) (QString(#PTR) + QString("=0x%1").arg((quintptr)PTR, QT_POINTER_SIZE * 2, 16, QChar('0')))
To go further
A lot of other things are possible with the CamiTK log system:
- log all messages to a file
- show or hide the time stamp or debugging information programmatically
- …
→ Make sure to read the Log
and CamiTKLogger
class API documentation to go further and improve your productivity.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.