Console: added tag based log support
This commit is contained in:
@@ -391,6 +391,8 @@ bool Application::closeDocument(const char* name)
|
||||
if (pos == DocMap.end()) // no such document
|
||||
return false;
|
||||
|
||||
Base::ConsoleRefreshDisabler disabler;
|
||||
|
||||
// Trigger observers before removing the document from the internal map.
|
||||
// Some observers might rely on this document still being there.
|
||||
signalDeleteDocument(*pos->second);
|
||||
@@ -1417,6 +1419,35 @@ void Application::initConfig(int argc, char ** argv)
|
||||
|
||||
LoadParameters();
|
||||
|
||||
auto loglevelParam = _pcUserParamMngr->GetGroup("BaseApp/LogLevels");
|
||||
const auto &loglevels = loglevelParam->GetIntMap();
|
||||
bool hasDefault = false;
|
||||
for(const auto &v : loglevels) {
|
||||
if(v.first == "Default") {
|
||||
#ifndef FC_DEBUG
|
||||
if(v.second>=0) {
|
||||
hasDefault = true;
|
||||
Base::Console().SetDefaultLogLevel(v.second);
|
||||
}
|
||||
#endif
|
||||
}else if(v.first == "DebugDefault") {
|
||||
#ifdef FC_DEBUG
|
||||
if(v.second>=0) {
|
||||
hasDefault = true;
|
||||
Base::Console().SetDefaultLogLevel(v.second);
|
||||
}
|
||||
#endif
|
||||
}else
|
||||
*Base::Console().GetLogLevel(v.first.c_str()) = v.second;
|
||||
}
|
||||
if(!hasDefault) {
|
||||
#ifdef FC_DEBUG
|
||||
loglevelParam->SetInt("DebugDefault",Base::Console().LogLevel(-1));
|
||||
#else
|
||||
loglevelParam->SetInt("Default",Base::Console().LogLevel(-1));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Set application tmp. directory
|
||||
mConfig["AppTempPath"] = Base::FileInfo::getTempPath();
|
||||
std::string tmpPath = _pcUserParamMngr->GetGroup("BaseApp/Preferences/General")->GetASCII("TempPath");
|
||||
|
||||
@@ -321,6 +321,9 @@ private:
|
||||
static PyObject* sRemoveDocObserver (PyObject *self,PyObject *args,PyObject *kwd);
|
||||
static PyObject* sTranslateUnit (PyObject *self,PyObject *args,PyObject *kwd);
|
||||
|
||||
static PyObject *sSetLogLevel (PyObject *self,PyObject *args,PyObject *kwd);
|
||||
static PyObject *sGetLogLevel (PyObject *self,PyObject *args,PyObject *kwd);
|
||||
|
||||
static PyMethodDef Methods[];
|
||||
|
||||
friend class ApplicationObserver;
|
||||
|
||||
@@ -129,6 +129,11 @@ PyMethodDef Application::Methods[] = {
|
||||
{"removeDocumentObserver", (PyCFunction) Application::sRemoveDocObserver ,1,
|
||||
"removeDocumentObserver() -> None\n\n"
|
||||
"Remove an added document observer."},
|
||||
{"setLogLevel", (PyCFunction) Application::sSetLogLevel, 1,
|
||||
"setLogLevel(tag, level) -- Set the log level for a string tag.\n"
|
||||
"'level' can either be string 'Log', 'Msg', 'Wrn', 'Error', or an integer value"},
|
||||
{"getLogLevel", (PyCFunction) Application::sGetLogLevel, 1,
|
||||
"getLogLevel(tag) -- Get the log level of a string tag"},
|
||||
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
@@ -604,3 +609,90 @@ PyObject* Application::sRemoveDocObserver(PyObject * /*self*/, PyObject *args,Py
|
||||
Py_Return;
|
||||
} PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject *Application::sSetLogLevel(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/)
|
||||
{
|
||||
char *tag;
|
||||
PyObject *pcObj;
|
||||
if (!PyArg_ParseTuple(args, "sO", &tag, &pcObj))
|
||||
return NULL;
|
||||
PY_TRY{
|
||||
int l;
|
||||
if (PyString_Check(pcObj)) {
|
||||
const char *pstr = PyString_AsString(pcObj);
|
||||
if(strcmp(pstr,"Log") == 0)
|
||||
l = FC_LOGLEVEL_LOG;
|
||||
else if(strcmp(pstr,"Warning") == 0)
|
||||
l = FC_LOGLEVEL_WARN;
|
||||
else if(strcmp(pstr,"Message") == 0)
|
||||
l = FC_LOGLEVEL_MSG;
|
||||
else if(strcmp(pstr,"Error") == 0)
|
||||
l = FC_LOGLEVEL_ERR;
|
||||
else if(strcmp(pstr,"Trace") == 0)
|
||||
l = FC_LOGLEVEL_TRACE;
|
||||
else if(strcmp(pstr,"Default") == 0)
|
||||
l = FC_LOGLEVEL_DEFAULT;
|
||||
else {
|
||||
Py_Error(Base::BaseExceptionFreeCADError,
|
||||
"Unknown Log Level (use 'Default', 'Error', 'Warning', 'Message', 'Log', 'Trace' or an integer)");
|
||||
return NULL;
|
||||
}
|
||||
}else
|
||||
l = PyLong_AsLong(pcObj);
|
||||
GetApplication().GetParameterGroupByPath("User parameter:BaseApp/LogLevels")->SetInt(tag,l);
|
||||
if(strcmp(tag,"Default") == 0) {
|
||||
#ifndef FC_DEBUG
|
||||
if(l>=0) Base::Console().SetDefaultLogLevel(l);
|
||||
#endif
|
||||
}else if(strcmp(tag,"DebugDefault") == 0) {
|
||||
#ifdef FC_DEBUG
|
||||
if(l>=0) Base::Console().SetDefaultLogLevel(l);
|
||||
#endif
|
||||
}else
|
||||
*Base::Console().GetLogLevel(tag) = l;
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}PY_CATCH;
|
||||
}
|
||||
|
||||
PyObject *Application::sGetLogLevel(PyObject * /*self*/, PyObject *args, PyObject * /*kwd*/)
|
||||
{
|
||||
char *tag;
|
||||
if (!PyArg_ParseTuple(args, "s", &tag))
|
||||
return NULL;
|
||||
|
||||
PY_TRY{
|
||||
int l = -1;
|
||||
if(strcmp(tag,"Default")==0) {
|
||||
#ifdef FC_DEBUG
|
||||
l = _pcUserParamMngr->GetGroup("BaseApp/LogLevels")->GetInt(tag,-1);
|
||||
#endif
|
||||
}else if(strcmp(tag,"DebugDefault")) {
|
||||
#ifndef FC_DEBUG
|
||||
l = _pcUserParamMngr->GetGroup("BaseApp/LogLevels")->GetInt(tag,-1);
|
||||
#endif
|
||||
}else{
|
||||
int *pl = Base::Console().GetLogLevel(tag,false);
|
||||
l = pl?*pl:-1;
|
||||
}
|
||||
// For performance reason, we only output integer value
|
||||
return Py_BuildValue("i",Base::Console().LogLevel(l));
|
||||
|
||||
// switch(l) {
|
||||
// case FC_LOGLEVEL_LOG:
|
||||
// return Py_BuildValue("s","Log");
|
||||
// case FC_LOGLEVEL_WARN:
|
||||
// return Py_BuildValue("s","Warning");
|
||||
// case FC_LOGLEVEL_ERR:
|
||||
// return Py_BuildValue("s","Error");
|
||||
// case FC_LOGLEVEL_MSG:
|
||||
// return Py_BuildValue("s","Message");
|
||||
// case FC_LOGLEVEL_TRACE:
|
||||
// return Py_BuildValue("s","Trace");
|
||||
// default:
|
||||
// return Py_BuildValue("i",l);
|
||||
// }
|
||||
} PY_CATCH;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "Console.h"
|
||||
#include "Exception.h"
|
||||
#include "PyObjectBase.h"
|
||||
#include <QCoreApplication>
|
||||
|
||||
using namespace Base;
|
||||
|
||||
@@ -50,7 +51,12 @@ using namespace Base;
|
||||
|
||||
|
||||
ConsoleSingleton::ConsoleSingleton(void)
|
||||
:_bVerbose(false)
|
||||
:_bVerbose(false),_bCanRefresh(true)
|
||||
#ifdef FC_DEBUG
|
||||
,_defaultLogLevel(FC_LOGLEVEL_LOG)
|
||||
#else
|
||||
,_defaultLogLevel(FC_LOGLEVEL_MSG)
|
||||
#endif
|
||||
{
|
||||
|
||||
}
|
||||
@@ -354,6 +360,24 @@ ConsoleObserver *ConsoleSingleton::Get(const char *Name) const
|
||||
return 0;
|
||||
}
|
||||
|
||||
int *ConsoleSingleton::GetLogLevel(const char *tag, bool create) {
|
||||
if(!tag) tag = "";
|
||||
if(_logLevels.find(tag) != _logLevels.end())
|
||||
return &_logLevels[tag];
|
||||
if(!create) return 0;
|
||||
int &ret = _logLevels[tag];
|
||||
ret = -1;
|
||||
return &ret;
|
||||
}
|
||||
|
||||
void ConsoleSingleton::Refresh() {
|
||||
if(_bCanRefresh)
|
||||
QCoreApplication::sendPostedEvents();
|
||||
}
|
||||
|
||||
void ConsoleSingleton::EnableRefresh(bool enable) {
|
||||
_bCanRefresh = enable;
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
// Singleton stuff
|
||||
@@ -781,3 +805,24 @@ int RedirectStdError::sync()
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
std::stringstream &LogLevel::prefix(std::stringstream &str, const char *src, int line) {
|
||||
static FC_TIME_POINT s_tstart;
|
||||
static bool s_timing = false;
|
||||
if(print_time) {
|
||||
if(!s_timing) {
|
||||
s_timing = true;
|
||||
_FC_TIME_INIT(s_tstart);
|
||||
}
|
||||
auto tnow = std::chrono::FC_TIME_CLOCK::now();
|
||||
auto d = std::chrono::duration_cast<FC_DURATION>(tnow-s_tstart);
|
||||
str << d.count() << ' ';
|
||||
}
|
||||
if(print_tag) str << '<' << tag << "> ";
|
||||
if(print_src) {
|
||||
const char *_f = std::strrchr(src, '/');
|
||||
str << (_f?_f+1:src)<<"("<<line<<"): ";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
@@ -31,11 +31,14 @@
|
||||
// Std. configurations
|
||||
#include <Base/PyExport.h>
|
||||
#include <Base/Stream.h>
|
||||
|
||||
//#pragma warning(disable: 4786) // specifier longer then 255 chars
|
||||
#include <assert.h>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <chrono>
|
||||
|
||||
//**************************************************************************
|
||||
// Loging levels
|
||||
@@ -49,6 +52,382 @@
|
||||
# undef FC_LOGUPDATECHAIN
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/** \page LogLevelPage Tag based log helpers
|
||||
* Simple tag based log and timing helper macros and functions.
|
||||
*
|
||||
* \section Motivation
|
||||
*
|
||||
* FreeCAD Base::Console() is capable of outputing to different targets, and has
|
||||
* some basic enable/disable control of different types of logs. There is,
|
||||
* however, no easy 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 permantly 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.
|
||||
*
|
||||
* \section SampleUsage Sample Usage
|
||||
*
|
||||
* 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 begining of your C++ source,
|
||||
* you need to initialize the log level of your chosen tag using,
|
||||
*
|
||||
* \code{.c}
|
||||
* FC_LOG_LEVEL_INIT(tag)
|
||||
* \endcode
|
||||
*
|
||||
* It makes sense to use the same tag in multiple files for easy log level
|
||||
* control. Please check \ref Customization if You want more than one tag inside
|
||||
* the same source file.
|
||||
*
|
||||
* Predefined log levels are,
|
||||
*
|
||||
* \code{.c}
|
||||
* #define FC_LOGLEVEL_ERR 0
|
||||
* #define FC_LOGLEVEL_WARN 1
|
||||
* #define FC_LOGLEVEL_MSG 2
|
||||
* #define FC_LOGLEVEL_LOG 3
|
||||
* #define FC_LOGLEVEL_TRACE 4
|
||||
* \endcode
|
||||
*
|
||||
* Bigger log level integer values have lower priorities. There is a special log
|
||||
* level,
|
||||
*
|
||||
* \code{.c}
|
||||
* #define FC_LOGLEVEL_DEFAULT -1
|
||||
* \endcode
|
||||
*
|
||||
* 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 \c Base::Console().SetDefaultLogLevel(). Python
|
||||
* developers/end-users can configure the default log level by calling
|
||||
*
|
||||
* \code{.py}
|
||||
* FreeCAD.setLogLevel('Default',level)
|
||||
* FreeCAD.setLogLevel('DebugDefault',level)
|
||||
* \endcode
|
||||
*
|
||||
* where \c level is either a string of value <tt>Error, Warning, Message, Log,
|
||||
* Trace</tt> or an integer value. By default, on release build, the default log
|
||||
* level is \c FC_LOGLEVEL_MSG, and on debug build, \c FC_LOGLEVEL_LOG.
|
||||
*
|
||||
* Python code can call \c FreeCAD.setLogLevel(tag,level) to configure any other
|
||||
* tag log levels, and \c 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.
|
||||
*
|
||||
* \code{.c}
|
||||
* FC_LOG_LEVEL_INIT(tag, print_tag=true, print_src=false,
|
||||
* print_time=false, add_eol=true, refresh=false)
|
||||
* \endcode
|
||||
*
|
||||
* You can dynamically modify the log style as well, by changing these
|
||||
* variables before the actual log output macro. See \ref Customization for
|
||||
* more details
|
||||
*
|
||||
* \code{.c}
|
||||
* FC_LOG_INSTANCE.refresh = true; // print time for each log, default false.
|
||||
* FC_LOG_INSTANCE.print_src = true; // print file and line number, default false.
|
||||
* FC_LOG_INSTANCE.print_tag = false; // do not print tag, default true
|
||||
* FC_LOG_INSTANCE.add_eol = false; // do not add eol
|
||||
* FC_LOG_INSTANCE.refresh = true; // refresh GUI after each log
|
||||
* \endcode
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* \code
|
||||
* FC_ERR(msg)
|
||||
* FC_WARN(msg)
|
||||
* FC_MSG(msg)
|
||||
* FC_LOG(msg)
|
||||
* FC_TRACE(msg)
|
||||
* \endcode
|
||||
*
|
||||
* The logging macros correspond to existing Base::Console() output functions,
|
||||
* and \c FC_TRACE uses Base::Console().Log(), same as \c FC_LOG. These macros
|
||||
* checks the log level defined in \c FC_LOG_LEVEL_INIT to decide whether to
|
||||
* print log or not. \c msg here shall be a C++ streaming expression. End of
|
||||
* line will be automatically appended by default.
|
||||
*
|
||||
* \code
|
||||
* FC_ERR("error: " << code << ". exiting")
|
||||
* \endcode
|
||||
*
|
||||
* \section TimingHelper Timing Helpers
|
||||
*
|
||||
* This set of macros is for helping C++ code to time lengthy operations.
|
||||
* Examples:
|
||||
*
|
||||
* \code{.c}
|
||||
* void operation() {
|
||||
* FC_TIME_INIT(t);
|
||||
*
|
||||
* //do stuff
|
||||
*
|
||||
* FC_TIME_LOG(t,"operation done.");
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* This will output in console something like,
|
||||
*
|
||||
* \code
|
||||
* operation done. time: 1.12s
|
||||
* \endcode
|
||||
*
|
||||
* Every time you call \c FC_TIME_LOG it will calculate the time duration
|
||||
* between this call and the last \c FC_TIME_LOG or \c FC_TIME_INIT. Time
|
||||
* variable \c t will then be updated to the current time. You can also use
|
||||
* <tt>FC_TIME_MSG, FC_TIME_TRACE</tt> similar to <tt>FC_MSG and FC_TRACE</tt>.
|
||||
*
|
||||
* To time operation in multiple stages,
|
||||
*
|
||||
* \code{.cpp}
|
||||
* void operation() {
|
||||
* FC_TIME_INIT2(t,t1);
|
||||
*
|
||||
* //do stage 1
|
||||
*
|
||||
* FC_TIME_LOG(t1,"stage1");
|
||||
*
|
||||
* //do stage 2
|
||||
*
|
||||
* FC_TIME_LOG(t1,"stage2");
|
||||
*
|
||||
* // do other stuff
|
||||
*
|
||||
* FC_TIME_LOG(t,"total");
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* Will output something like,
|
||||
* \code
|
||||
* stage1 time: 1.2s
|
||||
* stage2 time: 2.3s
|
||||
* total time: 4.0s
|
||||
* \endcode
|
||||
*
|
||||
* To time operation in multiple functions,
|
||||
*
|
||||
* \code{.cpp}
|
||||
* class Timing {
|
||||
* FC_DURATION_DECLARE(d1)
|
||||
* FC_DURATION_DECLARE(d1_1)
|
||||
* FC_DURATION_DECLARE(d1_2)
|
||||
* FC_DURATION_DECLARE(d2);
|
||||
*
|
||||
* Timing() {
|
||||
* FC_DURATION_INIT(d1);
|
||||
* FC_DURATION_INIT(d1_1);
|
||||
* FC_DURATION_INIT(d1_2);
|
||||
* FC_DURATION_INIT(d2);
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* void operation1(Timing &timing) {
|
||||
*
|
||||
* FC_TIME_INIT(t);
|
||||
*
|
||||
* for(...) {
|
||||
* FC_TIME_INIT(t1);
|
||||
*
|
||||
* //do setp 1
|
||||
*
|
||||
* FC_DURATION_PLUS(timing.d1_1,t1);
|
||||
*
|
||||
* // do step 2
|
||||
*
|
||||
* FC_DURATION_PLUS(timing.d1_2,t1);
|
||||
* }
|
||||
*
|
||||
* // do other stuff
|
||||
*
|
||||
* FC_DRUATION_PLUS(timing.d1, t);
|
||||
* }
|
||||
*
|
||||
* void operation2(Timing &timing) {
|
||||
*
|
||||
* FC_TIME_INIT(t);
|
||||
*
|
||||
* // do stuff
|
||||
*
|
||||
* FC_DRUATION_PLUS(timing.d2, t);
|
||||
* }
|
||||
*
|
||||
* void operation() {
|
||||
*
|
||||
* Timing timing;
|
||||
*
|
||||
* FC_TIME_INIT(t);
|
||||
*
|
||||
* for(...) {
|
||||
* operation1(timing);
|
||||
*
|
||||
* // do other stuff
|
||||
*
|
||||
* operation2(timing);
|
||||
* }
|
||||
*
|
||||
* FC_DURATION_LOG(timing.d1_1,"operation 1 step 1");
|
||||
* FC_DURATION_LOG(timing.d1_2,"operation 1 step 2");
|
||||
* FC_DURATION_LOG(timing.d1,"operation 1");
|
||||
* FC_DURATION_LOG(timing.d2,"operation 2");
|
||||
* FC_TIME_LOG(t,"operation total");
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* You can also use <tt>FC_DURATION_MSG, FC_DURATION_TRACE</tt> as usual.
|
||||
*
|
||||
* If you use only macros provided here to do timing, the entire timing code
|
||||
* can be complied out by defining \c FC_LOG_NO_TIMING before including
|
||||
* \c App/Console.h.
|
||||
*
|
||||
* \section Customization
|
||||
*
|
||||
* 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 arbitary 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
|
||||
* \c 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 \c instance_name
|
||||
*
|
||||
* \code
|
||||
* _FC_LOG_LEVEL_INIT(instance_name,tag)
|
||||
* \endcode
|
||||
*
|
||||
* Then, define a second set of logging macros as
|
||||
*
|
||||
* \code{.c}
|
||||
* #define MY_MSG(_msg) _FC_PRINT(instance_name,FC_LOGLEVEL_MSG,Message,_msg)
|
||||
* #define MY_WARN(_msg) _FC_PRINT(instance_name,FC_LOGLEVEL_WARN,Warning,_msg)
|
||||
* #define MY_ERR(_msg) _FC_PRINT(instance_name,FC_LOGLEVEL_ERR,Error,_msg)
|
||||
* #define MY_LOG(_msg) _FC_PRINT(instance_name,FC_LOGLEVEL_LOG,Log,_msg)
|
||||
* #define MY_TRACE(_msg) _FC_PRINT(instance_name,FC_LOGLEVEL_TRACE,Log,_msg)
|
||||
* \endcode
|
||||
*
|
||||
* Note, replace \c 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 \c _instance is the static loglevel instance name (default is
|
||||
* \c FC_LOG_INSTANCE), and \c _l is the log level constant to be checked,
|
||||
* \c _func is the Base::Console() function to print the log.
|
||||
*
|
||||
*/
|
||||
|
||||
#define FC_LOGLEVEL_DEFAULT -1
|
||||
#define FC_LOGLEVEL_ERR 0
|
||||
#define FC_LOGLEVEL_WARN 1
|
||||
#define FC_LOGLEVEL_MSG 2
|
||||
#define FC_LOGLEVEL_LOG 3
|
||||
#define FC_LOGLEVEL_TRACE 4
|
||||
|
||||
#define _FC_LOG_LEVEL_INIT(_name,_tag,...) \
|
||||
static Base::LogLevel _name(_tag,## __VA_ARGS__);
|
||||
|
||||
#ifndef FC_LOG_INSTANCE
|
||||
# define FC_LOG_INSTANCE _s_fclvl
|
||||
#endif
|
||||
|
||||
#define FC_LOG_LEVEL_INIT(_tag,...) \
|
||||
_FC_LOG_LEVEL_INIT(FC_LOG_INSTANCE, _tag, ## __VA_ARGS__)
|
||||
|
||||
#define _FC_PRINT(_instance,_l,_func,_msg) do{\
|
||||
if(_instance.isEnabled(_l)) {\
|
||||
std::stringstream str;\
|
||||
_instance.prefix(str,__FILE__,__LINE__) << _msg;\
|
||||
if(_instance.add_eol) \
|
||||
str<<std::endl;\
|
||||
Base::Console()._func("%s",str.str().c_str());\
|
||||
if(_instance.refresh) Base::Console().Refresh();\
|
||||
}\
|
||||
}while(0)
|
||||
|
||||
#define FC_MSG(_msg) _FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_MSG,Message,_msg)
|
||||
#define FC_WARN(_msg) _FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_WARN,Warning,_msg)
|
||||
#define FC_ERR(_msg) _FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_ERR,Error,_msg)
|
||||
#define FC_LOG(_msg) _FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_LOG,Log,_msg)
|
||||
#define FC_TRACE(_msg) _FC_PRINT(FC_LOG_INSTANCE,FC_LOGLEVEL_TRACE,Log,_msg)
|
||||
#define FC_XYZ(_pt) '('<<(_pt).X()<<", " << (_pt).Y()<<", " << (_pt).Z()<<')'
|
||||
#define FC_XY(_pt) '('<<(_pt).x<<", " << (_pt).y<<')'
|
||||
|
||||
#ifndef FC_LOG_NO_TIMING
|
||||
# define FC_TIME_CLOCK high_resolution_clock
|
||||
# define FC_TIME_POINT std::chrono::FC_TIME_CLOCK::time_point
|
||||
# define FC_DURATION std::chrono::duration<double>
|
||||
|
||||
# define _FC_TIME_INIT(_t) _t=std::chrono::FC_TIME_CLOCK::now()
|
||||
# define FC_TIME_INIT(_t) FC_TIME_POINT _FC_TIME_INIT(_t)
|
||||
# define FC_TIME_INIT2(_t1,_t2) FC_TIME_INIT(_t1),_t2=_t1
|
||||
# define FC_TIME_INIT3(_t1,_t2,_t3) FC_TIME_INIT(_t1),_t2=_t1,_t3=_t1
|
||||
|
||||
# define _FC_DURATION_PRINT(_l,_d,_msg) \
|
||||
FC_##_l(_msg<< " time: " << _d.count()<<'s');
|
||||
|
||||
# define FC_DURATION_MSG(_d,_msg) _FC_DURATION_PRINT(MSG,_d,_msg)
|
||||
# define FC_DURATION_LOG(_d,_msg) _FC_DURATION_PRINT(LOG,_d,_msg)
|
||||
# define FC_DURATION_TRACE(_d,_msg) _FC_DURATION_PRINT(TRACE,_d,_msg)
|
||||
|
||||
# define _FC_TIME_PRINT(_l,_t,_msg) \
|
||||
_FC_DURATION_PRINT(_l,Base::GetDuration(_t),_msg);
|
||||
|
||||
# define FC_TIME_MSG(_t,_msg) _FC_TIME_PRINT(MSG,_t,_msg)
|
||||
# define FC_TIME_LOG(_t,_msg) _FC_TIME_PRINT(LOG,_t,_msg)
|
||||
# define FC_TIME_TRACE(_t,_msg) _FC_TIME_PRINT(TRACE,_t,_msg)
|
||||
|
||||
# define FC_DURATION_DECLARE(_d) FC_DURATION _d
|
||||
# define FC_DURATION_DECLARE2(_d,_d1) FC_DURATION_DECLARE(_d),_d1
|
||||
# define FC_DURATION_DECLARE3(_d,_d1) FC_DURATION_DECLARE2(_d,_d1),_d2
|
||||
|
||||
# define FC_DURATION_INIT(_d) _d=FC_DURATION(0)
|
||||
# define FC_DURATION_INIT2(_d,_d1) _d=_d1=FC_DURATION(0)
|
||||
# define FC_DURATION_INIT3(_d,_d1,_d2) _d=_d1=_d2=FC_DURATION(0)
|
||||
|
||||
# define FC_DURATION_DECL_INIT(_d) FC_DURATION _d(0)
|
||||
# define FC_DURATION_DECL_INIT2(_d,_d1) FC_DURATION_DECL_INIT(_d),_d1(0)
|
||||
# define FC_DURATION_DECL_INIT3(_d,_d1) FC_DURATION_DECL_INIT2(_d,_d1),_d3(0)
|
||||
|
||||
# define FC_DURATION_PLUS(_d,_t) _d += Base::GetDuration(_t)
|
||||
|
||||
#else //FC_LOG_NO_TIMING
|
||||
# define FC_TIME_POINT
|
||||
# define _FC_TIME_INIT(...) do{}while(0)
|
||||
# define FC_TIME_INIT(...) do{}while(0)
|
||||
# define FC_TIME_INIT2(...) do{}while(0)
|
||||
# define FC_TIME_INIT3(...) do{}while(0)
|
||||
# define _FC_DURATION_PRINT(...) do{}while(0)
|
||||
# define _FC_TIME(_t) do{}while(0)
|
||||
# define FC_DURATION_PRINT(...) do{}while(0)
|
||||
# define FC_DURATION
|
||||
# define FC_DURATION_INIT(...) do{}while(0)
|
||||
# define FC_DURATION_INIT1(...) do{}while(0)
|
||||
# define FC_DURATION_INIT2(...) do{}while(0)
|
||||
# define FC_DURATION_DECLARE(...)
|
||||
# define FC_DURATION_DECLARE1(...)
|
||||
# define FC_DURATION_DECLARE2(...)
|
||||
# define FC_DURATION_DECL_INIT(...) do{}while(0)
|
||||
# define FC_DURATION_DECL_INIT2(...) do{}while(0)
|
||||
# define FC_DURATION_DECL_INIT3(...) do{}while(0)
|
||||
# define FC_DURATION_PLUS(...) do{}while(0)
|
||||
|
||||
#endif //FC_LOG_NO_TIMING
|
||||
|
||||
namespace Base {
|
||||
class ConsoleSingleton;
|
||||
@@ -59,6 +438,16 @@ typedef unsigned int ConsoleMsgFlags;
|
||||
|
||||
namespace Base {
|
||||
|
||||
#ifndef FC_LOG_NO_TIMING
|
||||
inline FC_DURATION GetDuration(FC_TIME_POINT &t)
|
||||
{
|
||||
auto tnow = std::chrono::FC_TIME_CLOCK::now();
|
||||
auto d = std::chrono::duration_cast<FC_DURATION>(tnow-t);
|
||||
t = tnow;
|
||||
return d;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** The console observer class
|
||||
* This class distribute the Messages issued to the FCConsole class.
|
||||
* If you need to catch some of the Messages you need to inherit from
|
||||
@@ -144,6 +533,16 @@ public:
|
||||
/// Enables or disables message types of a certain console observer
|
||||
bool IsMsgTypeEnabled(const char* sObs, FreeCAD_ConsoleMsgType type) const;
|
||||
|
||||
int *GetLogLevel(const char *tag, bool create=true);
|
||||
|
||||
void SetDefaultLogLevel(int level) {
|
||||
_defaultLogLevel = level;
|
||||
}
|
||||
|
||||
inline int LogLevel(int level) const{
|
||||
return level<0?_defaultLogLevel:level;
|
||||
}
|
||||
|
||||
/// singleton
|
||||
static ConsoleSingleton &Instance(void);
|
||||
|
||||
@@ -152,6 +551,9 @@ public:
|
||||
|
||||
static PyMethodDef Methods[];
|
||||
|
||||
void Refresh();
|
||||
void EnableRefresh(bool enable);
|
||||
|
||||
protected:
|
||||
// python exports goes here +++++++++++++++++++++++++++++++++++++++++++
|
||||
// static python wrapper of the exported functions
|
||||
@@ -163,6 +565,7 @@ protected:
|
||||
static PyObject *sPyGetStatus(PyObject *self,PyObject *args,PyObject *kwd);
|
||||
|
||||
bool _bVerbose;
|
||||
bool _bCanRefresh;
|
||||
|
||||
// Singleton!
|
||||
ConsoleSingleton(void);
|
||||
@@ -181,6 +584,9 @@ private:
|
||||
|
||||
// observer list
|
||||
std::set<ConsoleObserver * > _aclObservers;
|
||||
|
||||
std::map<std::string, int> _logLevels;
|
||||
int _defaultLogLevel;
|
||||
};
|
||||
|
||||
/** Access to the Console
|
||||
@@ -191,6 +597,47 @@ inline ConsoleSingleton &Console(void){
|
||||
return ConsoleSingleton::Instance();
|
||||
}
|
||||
|
||||
class BaseExport ConsoleRefreshDisabler {
|
||||
public:
|
||||
ConsoleRefreshDisabler() {
|
||||
Console().EnableRefresh(false);
|
||||
}
|
||||
|
||||
~ConsoleRefreshDisabler() {
|
||||
Console().EnableRefresh(true);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** LogLevel helper class */
|
||||
class BaseExport LogLevel {
|
||||
public:
|
||||
std::string tag;
|
||||
int &lvl;
|
||||
bool print_tag;
|
||||
bool print_src;
|
||||
bool print_time;
|
||||
bool add_eol;
|
||||
bool refresh;
|
||||
|
||||
LogLevel(const char *tag, bool print_tag=true, bool print_src=false,
|
||||
bool print_time=false, bool add_eol=true, bool refresh=false)
|
||||
:tag(tag),lvl(*Console().GetLogLevel(tag))
|
||||
,print_tag(print_tag),print_src(print_src),print_time(print_time)
|
||||
,add_eol(add_eol),refresh(refresh)
|
||||
{}
|
||||
|
||||
bool isEnabled(int l) {
|
||||
return l<=level();
|
||||
}
|
||||
|
||||
int level() const {
|
||||
return Console().LogLevel(lvl);
|
||||
}
|
||||
|
||||
std::stringstream &prefix(std::stringstream &str, const char *src, int line);
|
||||
};
|
||||
|
||||
|
||||
//=========================================================================
|
||||
// some special observers
|
||||
|
||||
Reference in New Issue
Block a user