Refactor and rename ConsoleObserver...

...Renamed to "ILogger", to designate that this is an Interface for a
Logger. This "Interface" is pure virtual, so that it cannot be
instantiated directly. This makes it clear that it is intended to be
derived.

Finally, got rid of all the individual log-style methods and replaced
with SendLog. The idea here is that day-to-day users will only interact
with ILogger through ConsoleSingleton (or, likely, LoggerSingleton in
the future). This singleton will manage an arbirtary collection of
ILogger, and call SendLog with the appropriate parameters based on what
the user requests.

Therefore, the singleton itself will have the individual Log, Message,
Error, etc... methods, while stil allowing us to simplify the code base
of ILogger and its derived classes.
This commit is contained in:
ezzieyguywuf
2019-09-10 00:27:36 -04:00
committed by wmayer
parent 7416055dbb
commit 9fcc18b08e
12 changed files with 392 additions and 417 deletions

View File

@@ -300,13 +300,15 @@ void Builder3D::addTransformation(const Base::Vector3f& translation, const Base:
*/
void Builder3D::saveToLog(void)
{
result << "} ";
// Note: The string can become very long, so that ConsoleSingelton::Log() will internally
// truncate the string which causes Inventor to fail to interpret the truncated string.
// So, we send the string directly to the observer that handles the Inventor stuff.
//Console().Log("Vdbg: %s \n",result.str().c_str());
ConsoleObserver* obs = Base::Console().Get("StatusBar");
if (obs) obs->Log(result.str().c_str());
result << "} ";
// Note: The string can become very long, so that ConsoleSingelton::Log() will internally
// truncate the string which causes Inventor to fail to interpret the truncated string.
// So, we send the string directly to the observer that handles the Inventor stuff.
//Console().Log("Vdbg: %s \n",result.str().c_str());
ILogger* obs = Base::Console().Get("StatusBar");
if (obs != nullptr){
obs->SendLog(result.str().c_str(), Base::LogStyle::Log);
}
}
/**

View File

@@ -134,7 +134,7 @@ ConsoleSingleton::ConsoleSingleton(void)
ConsoleSingleton::~ConsoleSingleton()
{
ConsoleOutput::destruct();
for(std::set<ConsoleObserver * >::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();++Iter)
for(std::set<ILogger * >::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();++Iter)
delete (*Iter);
}
@@ -167,7 +167,7 @@ void ConsoleSingleton::UnsetConsoleMode(ConsoleMode m)
* @code
* // switch off warnings and error messages
* ConsoleMsgFlags ret = Base::Console().SetEnabledMsgType("myObs",
* ConsoleMsgType::MsgType_Wrn|ConsoleMsgType::MsgType_Err, false);
* Base:ConsoleSingleton::MsgType_Wrn|Base::ConsoleSingleton::MsgType_Err, false);
* // do something without notifying observer myObs
* ...
* // restore the former configuration again
@@ -178,7 +178,7 @@ void ConsoleSingleton::UnsetConsoleMode(ConsoleMode m)
*/
ConsoleMsgFlags ConsoleSingleton::SetEnabledMsgType(const char* sObs, ConsoleMsgFlags type, bool b)
{
ConsoleObserver* pObs = Get(sObs);
ILogger* pObs = Get(sObs);
if ( pObs ){
ConsoleMsgFlags flags=0;
@@ -211,7 +211,7 @@ ConsoleMsgFlags ConsoleSingleton::SetEnabledMsgType(const char* sObs, ConsoleMsg
bool ConsoleSingleton::IsMsgTypeEnabled(const char* sObs, FreeCAD_ConsoleMsgType type) const
{
ConsoleObserver* pObs = Get(sObs);
ILogger* pObs = Get(sObs);
if (pObs) {
switch (type) {
case MsgType_Txt:
@@ -359,12 +359,12 @@ const char* ConsoleSingleton::Time(void)
// Observer stuff
/** Attaches an Observer to Console
* Use this method to attach a ConsoleObserver derived class to
* Use this method to attach a ILogger derived class to
* the Console. After the observer is attached all messages will also
* be forwarded to it.
* @see ConsoleObserver
* @see ILogger
*/
void ConsoleSingleton::AttachObserver(ConsoleObserver *pcObserver)
void ConsoleSingleton::AttachObserver(ILogger *pcObserver)
{
// double insert !!
assert(_aclObservers.find(pcObserver) == _aclObservers.end() );
@@ -373,51 +373,51 @@ void ConsoleSingleton::AttachObserver(ConsoleObserver *pcObserver)
}
/** Detaches an Observer from Console
* Use this method to detach a ConsoleObserver derived class.
* Use this method to detach a ILogger derived class.
* After detaching you can destruct the Observer or reinsert it later.
* @see ConsoleObserver
* @see ILogger
*/
void ConsoleSingleton::DetachObserver(ConsoleObserver *pcObserver)
void ConsoleSingleton::DetachObserver(ILogger *pcObserver)
{
_aclObservers.erase(pcObserver);
}
void ConsoleSingleton::NotifyMessage(const char *sMsg)
{
for(std::set<ConsoleObserver * >::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();++Iter) {
for(std::set<ILogger * >::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();++Iter) {
if((*Iter)->bMsg)
(*Iter)->Message(sMsg); // send string to the listener
(*Iter)->SendLog(sMsg, LogStyle::Message); // send string to the listener
}
}
void ConsoleSingleton::NotifyWarning(const char *sMsg)
{
for(std::set<ConsoleObserver * >::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();++Iter) {
for(std::set<ILogger * >::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();++Iter) {
if((*Iter)->bWrn)
(*Iter)->Warning(sMsg); // send string to the listener
(*Iter)->SendLog(sMsg, LogStyle::Warning); // send string to the listener
}
}
void ConsoleSingleton::NotifyError(const char *sMsg)
{
for(std::set<ConsoleObserver * >::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();++Iter) {
for(std::set<ILogger * >::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();++Iter) {
if((*Iter)->bErr)
(*Iter)->Error(sMsg); // send string to the listener
(*Iter)->SendLog(sMsg, LogStyle::Error); // send string to the listener
}
}
void ConsoleSingleton::NotifyLog(const char *sMsg)
{
for(std::set<ConsoleObserver * >::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();++Iter) {
for(std::set<ILogger * >::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();++Iter) {
if((*Iter)->bLog)
(*Iter)->Log(sMsg); // send string to the listener
(*Iter)->SendLog(sMsg, LogStyle::Log); // send string to the listener
}
}
ConsoleObserver *ConsoleSingleton::Get(const char *Name) const
ILogger *ConsoleSingleton::Get(const char *Name) const
{
const char* OName;
for(std::set<ConsoleObserver * >::const_iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();++Iter) {
for(std::set<ILogger * >::const_iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();++Iter) {
OName = (*Iter)->Name(); // get the name
if(OName && strcmp(OName,Name) == 0)
return *Iter;
@@ -681,7 +681,7 @@ PyObject *ConsoleSingleton::sPyGetStatus(PyObject * /*self*/, PyObject *args)
PY_TRY{
bool b=false;
ConsoleObserver *pObs = Instance().Get(pstr1);
ILogger *pObs = Instance().Get(pstr1);
if(!pObs)
{
Py_INCREF(Py_None);
@@ -710,7 +710,7 @@ PyObject *ConsoleSingleton::sPySetStatus(PyObject * /*self*/, PyObject *args)
return NULL; // NULL triggers exception
PY_TRY{
ConsoleObserver *pObs = Instance().Get(pstr1);
ILogger *pObs = Instance().Get(pstr1);
if(pObs)
{
if(strcmp(pstr2,"Log") == 0)
@@ -736,6 +736,9 @@ PyObject *ConsoleSingleton::sPySetStatus(PyObject * /*self*/, PyObject *args)
//=========================================================================
// some special observers
Base::ILogger::~ILogger()
{}
ConsoleObserverFile::ConsoleObserverFile(const char *sFileName)
: cFileStream(Base::FileInfo(sFileName)) // can be in UTF8
{
@@ -751,31 +754,29 @@ ConsoleObserverFile::~ConsoleObserverFile()
cFileStream.close();
}
void ConsoleObserverFile::Warning(const char *sWarn)
void ConsoleObserverFile::SendLog(const std::string& msg, LogStyle level)
{
cFileStream << "Wrn: " << sWarn;
cFileStream.flush();
std::string prefix = "";
switch(level){
case LogStyle::Warning:
prefix = "Wrn: ";
break;
case LogStyle::Message:
prefix = "Msg: ";
break;
case LogStyle::Error:
prefix = "Err: ";
break;
case LogStyle::Log:
prefix = "Log: ";
break;
}
if (prefix.empty() == false){
cFileStream << prefix << msg;
cFileStream.flush();
}
}
void ConsoleObserverFile::Message(const char *sMsg)
{
cFileStream << "Msg: " << sMsg;
cFileStream.flush();
}
void ConsoleObserverFile::Error (const char *sErr)
{
cFileStream << "Err: " << sErr;
cFileStream.flush();
}
void ConsoleObserverFile::Log (const char *sLog)
{
cFileStream << "Log: " << sLog;
cFileStream.flush();
}
ConsoleObserverStd::ConsoleObserverStd() :
# if defined(FC_OS_WIN32)
useColorStderr(true)
@@ -792,6 +793,24 @@ ConsoleObserverStd::~ConsoleObserverStd()
{
}
void ConsoleObserverStd::SendLog(const std::string& msg, LogStyle level)
{
switch(level){
case LogStyle::Warning:
this->Warning(msg.c_str());
break;
case LogStyle::Message:
this->Message(msg.c_str());
break;
case LogStyle::Error:
this->Error(msg.c_str());
break;
case LogStyle::Log:
this->Log(msg.c_str());
break;
}
}
void ConsoleObserverStd::Message(const char *sMsg)
{
printf("%s",sMsg);

View File

@@ -22,11 +22,7 @@
* Juergen Riegel 2002 *
***************************************************************************/
#ifndef BASE_CONSOLE_H
#define BASE_CONSOLE_H
#pragma once
// Std. configurations
#include <Base/PyExport.h>
@@ -443,11 +439,12 @@
#endif //FC_LOG_NO_TIMING
//TODO: Get rid of this forward-declaration
namespace Base {
class ConsoleSingleton;
class ConsoleSingleton;
} // namespace Base
typedef Base::ConsoleSingleton ConsoleMsgType;
//TODO: Get rid of this typedef
typedef unsigned int ConsoleMsgFlags;
namespace Base {
@@ -462,220 +459,223 @@ namespace Base {
}
#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
* this class and implement some of the methods.
* @see Console
*/
class BaseExport ConsoleObserver
{
public:
ConsoleObserver()
:bErr(true),bMsg(true),bLog(true),bWrn(true){}
virtual ~ConsoleObserver() {}
/// get calls when a Warning is issued
virtual void Warning(const char *){}
/// get calls when a Message is issued
virtual void Message(const char *){}
/// get calls when a Error is issued
virtual void Error (const char *)=0;
/// get calls when a Log Message is issued
virtual void Log (const char *){}
virtual const char *Name(void){return 0L;}
bool bErr,bMsg,bLog,bWrn;
};
/** The console class
* This class manage all the stdio stuff. This includes
* Messages, Warnings, Log entries and Errors. The incoming
* Messages are distributed with the FCConsoleObserver. The
* FCConsole class itself makes no IO, it's more like a manager.
* \par
* ConsoleSingleton is a singleton! That means you can access the only
* instance of the class from every where in c++ by simply using:
* \code
* #include <Base/Console.h>
* Base::Console().Log("Stage: %d",i);
* \endcode
* \par
* ConsoleSingleton is able to switch between several modes to, e.g. switch
* the logging on or off, or treat Warnings as Errors, and so on...
* @see ConsoleObserver
*/
class BaseExport ConsoleSingleton
{
public:
static const unsigned int BufferSize = 4024;
// exported functions goes here +++++++++++++++++++++++++++++++++++++++
/// Prints a Message
virtual void Message ( const char * pMsg, ... ) ;
/// Prints a warning Message
virtual void Warning ( const char * pMsg, ... ) ;
/// Prints a error Message
virtual void Error ( const char * pMsg, ... ) ;
/// Prints a log Message
virtual void Log ( const char * pMsg, ... ) ;
// observer processing
void NotifyMessage(const char *sMsg);
void NotifyWarning(const char *sMsg);
void NotifyError (const char *sMsg);
void NotifyLog (const char *sMsg);
/// Delivers a time/date string
const char* Time(void);
/// Attaches an Observer to FCConsole
void AttachObserver(ConsoleObserver *pcObserver);
/// Detaches an Observer from FCConsole
void DetachObserver(ConsoleObserver *pcObserver);
/// enumaration for the console modes
enum ConsoleMode{
Verbose = 1, // suppress Log messages
};
enum ConnectionMode {
Direct = 0,
Queued =1
/** Used to identify log level*/
enum class LogStyle{
Warning,
Message,
Error,
Log
};
enum FreeCAD_ConsoleMsgType {
MsgType_Txt = 1,
MsgType_Log = 2, // ConsoleObserverStd sends this and higher to stderr
MsgType_Wrn = 4,
MsgType_Err = 8
/** The Logger Interface
* This class describes an Interface for logging within FreeCAD. If you want to add a new
* "sink" to FreeCAD's logging mechanism, then inherit this class. You'll also need to
* register your derived class with ConsoleSingleton.
*
* @see ConsoleSingleton
*/
class BaseExport ILogger
{
public:
ILogger()
:bErr(true),bMsg(true),bLog(true),bWrn(true){};
virtual ~ILogger() = 0;
/** Used to send a Log message at the given level.
*/
virtual void SendLog(const std::string& msg, LogStyle level) = 0;
virtual const char *Name(void){return 0L;}
bool bErr,bMsg,bLog,bWrn;
};
/// Change mode
void SetConsoleMode(ConsoleMode m);
/// Change mode
void UnsetConsoleMode(ConsoleMode m);
/// Enables or disables message types of a certain console observer
ConsoleMsgFlags SetEnabledMsgType(const char* sObs, ConsoleMsgFlags type, bool b);
/// Enables or disables message types of a certain console observer
bool IsMsgTypeEnabled(const char* sObs, FreeCAD_ConsoleMsgType type) const;
void SetConnectionMode(ConnectionMode mode);
int *GetLogLevel(const char *tag, bool create=true);
/** The console class
* This class manage all the stdio stuff. This includes
* Messages, Warnings, Log entries and Errors. The incoming
* Messages are distributed with the FCConsoleObserver. The
* FCConsole class itself makes no IO, it's more like a manager.
* \par
* ConsoleSingleton is a singleton! That means you can access the only
* instance of the class from every where in c++ by simply using:
* \code
* #include <Base/Console.h>
* Base::Console().Log("Stage: %d",i);
* \endcode
* \par
* ConsoleSingleton is able to switch between several modes to, e.g. switch
* the logging on or off, or treat Warnings as Errors, and so on...
* @see ConsoleObserver
*/
class BaseExport ConsoleSingleton
{
void SetDefaultLogLevel(int level) {
_defaultLogLevel = level;
public:
static const unsigned int BufferSize = 4024;
// exported functions goes here +++++++++++++++++++++++++++++++++++++++
/// Prints a Message
virtual void Message ( const char * pMsg, ... ) ;
/// Prints a warning Message
virtual void Warning ( const char * pMsg, ... ) ;
/// Prints a error Message
virtual void Error ( const char * pMsg, ... ) ;
/// Prints a log Message
virtual void Log ( const char * pMsg, ... ) ;
// observer processing
void NotifyMessage(const char *sMsg);
void NotifyWarning(const char *sMsg);
void NotifyError (const char *sMsg);
void NotifyLog (const char *sMsg);
/// Delivers a time/date string
const char* Time(void);
/// Attaches an Observer to FCConsole
void AttachObserver(ILogger *pcObserver);
/// Detaches an Observer from FCConsole
void DetachObserver(ILogger *pcObserver);
/// enumaration for the console modes
enum ConsoleMode{
Verbose = 1, // suppress Log messages
};
enum ConnectionMode {
Direct = 0,
Queued =1
};
enum FreeCAD_ConsoleMsgType {
MsgType_Txt = 1,
MsgType_Log = 2, // ConsoleObserverStd sends this and higher to stderr
MsgType_Wrn = 4,
MsgType_Err = 8
};
/// Change mode
void SetConsoleMode(ConsoleMode m);
/// Change mode
void UnsetConsoleMode(ConsoleMode m);
/// Enables or disables message types of a certain console observer
ConsoleMsgFlags SetEnabledMsgType(const char* sObs, ConsoleMsgFlags type, bool b);
/// Enables or disables message types of a certain console observer
bool IsMsgTypeEnabled(const char* sObs, FreeCAD_ConsoleMsgType type) const;
void SetConnectionMode(ConnectionMode mode);
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);
// retrieval of an observer by name
ILogger *Get(const char *Name) const;
static PyMethodDef Methods[];
void Refresh();
void EnableRefresh(bool enable);
protected:
// python exports goes here +++++++++++++++++++++++++++++++++++++++++++
// static python wrapper of the exported functions
static PyObject *sPyLog (PyObject *self,PyObject *args);
static PyObject *sPyMessage (PyObject *self,PyObject *args);
static PyObject *sPyWarning (PyObject *self,PyObject *args);
static PyObject *sPyError (PyObject *self,PyObject *args);
static PyObject *sPySetStatus(PyObject *self,PyObject *args);
static PyObject *sPyGetStatus(PyObject *self,PyObject *args);
bool _bVerbose;
bool _bCanRefresh;
ConnectionMode connectionMode;
// Singleton!
ConsoleSingleton(void);
virtual ~ConsoleSingleton();
private:
// singleton
static void Destruct(void);
static ConsoleSingleton *_pcSingleton;
// observer list
std::set<ILogger * > _aclObservers;
std::map<std::string, int> _logLevels;
int _defaultLogLevel;
friend class ConsoleOutput;
};
/** Access to the Console
* This method is used to gain access to the one and only instance of
* the ConsoleSingleton class.
*/
inline ConsoleSingleton &Console(void){
return ConsoleSingleton::Instance();
}
inline int LogLevel(int level) const{
return level<0?_defaultLogLevel:level;
}
class BaseExport ConsoleRefreshDisabler {
public:
ConsoleRefreshDisabler() {
Console().EnableRefresh(false);
}
/// singleton
static ConsoleSingleton &Instance(void);
// retrieval of an observer by name
ConsoleObserver *Get(const char *Name) const;
static PyMethodDef Methods[];
void Refresh();
void EnableRefresh(bool enable);
protected:
// python exports goes here +++++++++++++++++++++++++++++++++++++++++++
// static python wrapper of the exported functions
static PyObject *sPyLog (PyObject *self,PyObject *args);
static PyObject *sPyMessage (PyObject *self,PyObject *args);
static PyObject *sPyWarning (PyObject *self,PyObject *args);
static PyObject *sPyError (PyObject *self,PyObject *args);
static PyObject *sPySetStatus(PyObject *self,PyObject *args);
static PyObject *sPyGetStatus(PyObject *self,PyObject *args);
bool _bVerbose;
bool _bCanRefresh;
ConnectionMode connectionMode;
// Singleton!
ConsoleSingleton(void);
virtual ~ConsoleSingleton();
private:
// singleton
static void Destruct(void);
static ConsoleSingleton *_pcSingleton;
// observer list
std::set<ConsoleObserver * > _aclObservers;
std::map<std::string, int> _logLevels;
int _defaultLogLevel;
friend class ConsoleOutput;
};
/** Access to the Console
* This method is used to gain access to the one and only instance of
* the ConsoleSingleton class.
*/
inline ConsoleSingleton &Console(void){
return ConsoleSingleton::Instance();
}
class BaseExport ConsoleRefreshDisabler {
public:
ConsoleRefreshDisabler() {
Console().EnableRefresh(false);
}
~ConsoleRefreshDisabler() {
Console().EnableRefresh(true);
}
};
~ConsoleRefreshDisabler() {
Console().EnableRefresh(true);
}
};
/** LogLevel helper class */
class BaseExport LogLevel {
public:
std::string tag;
int &lvl;
bool print_tag;
int print_src;
bool print_time;
bool add_eol;
bool refresh;
/** LogLevel helper class */
class BaseExport LogLevel {
public:
std::string tag;
int &lvl;
bool print_tag;
int print_src;
bool print_time;
bool add_eol;
bool refresh;
LogLevel(const char *tag, bool print_tag=true, int print_src=0,
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)
{}
LogLevel(const char *tag, bool print_tag=true, int print_src=0,
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();
}
bool isEnabled(int l) {
return l<=level();
}
int level() const {
return Console().LogLevel(lvl);
}
int level() const {
return Console().LogLevel(lvl);
}
std::stringstream &prefix(std::stringstream &str, const char *src, int line);
};
std::stringstream &prefix(std::stringstream &str, const char *src, int line);
};
//=========================================================================
// some special observers
//=========================================================================
// some special observers
/** The LoggingConsoleObserver class
* This class is used by the main modules to write Console messages and logs to a file
*/
class BaseExport ConsoleObserverFile : public ConsoleObserver
/** The LoggingConsoleObserver class
* This class is used by the main modules to write Console messages and logs to a file
*/
class BaseExport ConsoleObserverFile : public ILogger
{
public:
ConsoleObserverFile(const char *sFileName);
virtual ~ConsoleObserverFile();
virtual void Warning(const char *sWarn);
virtual void Message(const char *sMsg);
virtual void Error (const char *sErr);
virtual void Log (const char *sLog);
~ConsoleObserverFile() override;
void SendLog(const std::string& message, LogStyle level) override;
const char* Name(void){return "File";}
protected:
@@ -685,18 +685,20 @@ protected:
/** The CmdConsoleObserver class
* This class is used by the main modules to write Console messages and logs the system con.
*/
class BaseExport ConsoleObserverStd: public ConsoleObserver
class BaseExport ConsoleObserverStd: public ILogger
{
public:
ConsoleObserverStd();
virtual ~ConsoleObserverStd();
virtual void Warning(const char *sWarn);
virtual void Message(const char *sMsg);
virtual void Error (const char *sErr);
virtual void Log (const char *sErr);
~ConsoleObserverStd() override;
void SendLog(const std::string& message, LogStyle level) override;
const char* Name(void){return "Console";}
protected:
bool useColorStderr;
private:
void Warning(const char *sWarn);
void Message(const char *sMsg);
void Error (const char *sErr);
void Log (const char *sErr);
};
class BaseExport RedirectStdOutput : public std::streambuf
@@ -740,5 +742,3 @@ private:
} // namespace Base
#endif // BASE_CONSOLE_H