Console: Extend framework with intended recipient and content type metainformation

==================================================================================

Limitations of the current framework:
- Codes the translated state only for TranslatedNotification as part of the type.
- Does not code the intended recipient (user, developer, ...)

Problems:
- Some errors are intended for developers, some errors may only be intended for users,
if, for example, there is another developer error which already contains all the information.
The current framework may lead to information duplication or to showing to the user developer
information, which is perceived as annoying.
- Logs shall be in English (report view), while every message to the user (UI) shall be translated.
The current framework can only differentiate where to report based on subscription (legacy logs do not
subscribe to notifications), and for notifications, whether it is translated or not depends on the type.
It is not possible to code errors or warnings that are already translated.

Solution:
- To extend the ILogger interface with additional metainformation, indicating the intended recipient
(User, Developer, All), and the content of the message (translated, untranslated, untranslatable). The latter
is useful for dynamic strings that won't find a match in the translation framework.

Bonus:
- This extended version allows to do away with translatednotification, as now any message can be independently
marked as translated or untranslated or untraslatable.
- It is now possible to provide the right icon of severity (error, warning, info), even when it is only user intended
and already translated.
This commit is contained in:
Abdullah Tahiri
2023-05-20 21:11:45 +02:00
committed by abdullahtahiriyo
parent dc07897a29
commit e3702ac2d8
15 changed files with 115 additions and 47 deletions

View File

@@ -987,7 +987,7 @@ void Builder3D::saveToLog()
{
ILogger* obs = Base::Console().Get("StatusBar");
if (obs){
obs->SendLog("Builder3D",result.str().c_str(), Base::LogStyle::Log);
obs->SendLog("Builder3D",result.str().c_str(), Base::LogStyle::Log, Base::IntendedRecipient::Developer, Base::ContentType::Untranslatable);
}
}

View File

@@ -49,11 +49,14 @@ namespace Base {
class ConsoleEvent : public QEvent {
public:
ConsoleSingleton::FreeCAD_ConsoleMsgType msgtype;
IntendedRecipient recipient;
ContentType content;
std::string notifier;
std::string msg;
ConsoleEvent(ConsoleSingleton::FreeCAD_ConsoleMsgType type, const std::string& notifier, const std::string& msg)
: QEvent(QEvent::User), msgtype(type), notifier(notifier),msg(msg)
ConsoleEvent(ConsoleSingleton::FreeCAD_ConsoleMsgType type, IntendedRecipient recipient,
ContentType content, const std::string& notifier, const std::string& msg)
: QEvent(QEvent::User), msgtype(type), recipient(recipient), content(content), notifier(notifier), msg(msg)
{
}
~ConsoleEvent() override = default;
@@ -77,25 +80,25 @@ public:
ConsoleEvent* ce = static_cast<ConsoleEvent*>(ev);
switch (ce->msgtype) {
case ConsoleSingleton::MsgType_Txt:
Console().Notify<LogStyle::Message>(ce->notifier, ce->msg);
Console().notifyPrivate(LogStyle::Message, ce->recipient, ce->content, ce->notifier, ce->msg);
break;
case ConsoleSingleton::MsgType_Log:
Console().Notify<LogStyle::Log>(ce->notifier, ce->msg);
Console().notifyPrivate(LogStyle::Log, ce->recipient, ce->content, ce->notifier, ce->msg);
break;
case ConsoleSingleton::MsgType_Wrn:
Console().Notify<LogStyle::Warning>(ce->notifier, ce->msg);
Console().notifyPrivate(LogStyle::Warning, ce->recipient, ce->content, ce->notifier, ce->msg);
break;
case ConsoleSingleton::MsgType_Err:
Console().Notify<LogStyle::Error>(ce->notifier, ce->msg);
Console().notifyPrivate(LogStyle::Error, ce->recipient, ce->content, ce->notifier, ce->msg);
break;
case ConsoleSingleton::MsgType_Critical:
Console().Notify<LogStyle::Critical>(ce->notifier, ce->msg);
Console().notifyPrivate(LogStyle::Critical, ce->recipient, ce->content, ce->notifier, ce->msg);
break;
case ConsoleSingleton::MsgType_Notification:
Console().Notify<LogStyle::Notification>(ce->notifier, ce->msg);
Console().notifyPrivate(LogStyle::Notification, ce->recipient, ce->content, ce->notifier, ce->msg);
break;
case ConsoleSingleton::MsgType_TranslatedNotification:
Console().Notify<LogStyle::TranslatedNotification>(ce->notifier, ce->msg);
Console().notifyPrivate(LogStyle::TranslatedNotification, ce->recipient, ce->content, ce->notifier, ce->msg);
break;
}
}
@@ -286,18 +289,20 @@ void ConsoleSingleton::DetachObserver(ILogger *pcObserver)
_aclObservers.erase(pcObserver);
}
void Base::ConsoleSingleton::notifyPrivate(LogStyle category, const std::string& notifiername, const std::string& msg)
void Base::ConsoleSingleton::notifyPrivate(LogStyle category, IntendedRecipient recipient,
ContentType content, const std::string& notifiername, const std::string& msg)
{
for (std::set<ILogger * >::iterator Iter=_aclObservers.begin();Iter!=_aclObservers.end();++Iter) {
if ((*Iter)->isActive(category)) {
(*Iter)->SendLog(notifiername, msg, category); // send string to the listener
(*Iter)->SendLog(notifiername, msg, category, recipient, content); // send string to the listener
}
}
}
void ConsoleSingleton::postEvent(ConsoleSingleton::FreeCAD_ConsoleMsgType type, const std::string& notifiername, const std::string& msg)
void ConsoleSingleton::postEvent(ConsoleSingleton::FreeCAD_ConsoleMsgType type, IntendedRecipient recipient,
ContentType content, const std::string& notifiername, const std::string& msg)
{
QCoreApplication::postEvent(ConsoleOutput::getInstance(), new ConsoleEvent(type, notifiername, msg));
QCoreApplication::postEvent(ConsoleOutput::getInstance(), new ConsoleEvent(type, recipient, content, notifiername, msg));
}
ILogger *ConsoleSingleton::Get(const char *Name) const

View File

@@ -474,6 +474,18 @@ enum class LogStyle{
TranslatedNotification, // Special message for already translated notifications to the user (e.g. educational)
};
enum class IntendedRecipient {
All, // All recipients, Developers and Users (One notification covers all)
User, // User only (notification intended only for a user)
Developer, // Developer only (notification intended only for a developer)
};
enum class ContentType {
Untranslated, // Not translated, but translatable
Translated, // Already translated
Untranslatable, // Cannot and should not be translated (Dynamic content, trace,...)
};
/** 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
@@ -490,7 +502,8 @@ public:
/** Used to send a Log message at the given level.
*/
virtual void SendLog(const std::string& notifiername, const std::string& msg, LogStyle level) = 0;
virtual void SendLog(const std::string& notifiername, const std::string& msg, LogStyle level,
IntendedRecipient recipient, ContentType content) = 0;
/**
* Returns whether a LogStyle category is active or not
@@ -559,7 +572,10 @@ public:
Notification can be direct or via queue.
*/
template <LogStyle, typename... Args>
template <Base::LogStyle,
Base::IntendedRecipient = Base::IntendedRecipient::All,
Base::ContentType = Base::ContentType::Untranslated,
typename... Args>
inline void Send( const std::string & notifiername, const char * pMsg, Args&&... args );
/// Prints a Message
@@ -587,19 +603,19 @@ public:
/// Prints a Message with source indication
template <typename... Args>
inline void Message (const std::string &, const char * pMsg, Args&&... args);
inline void Message (const std::string & notifier, const char * pMsg, Args&&... args);
/// Prints a warning Message with source indication
template <typename... Args>
inline void Warning (const std::string &, const char * pMsg, Args&&... args);
inline void Warning (const std::string & notifier, const char * pMsg, Args&&... args);
/// Prints a error Message with source indication
template <typename... Args>
inline void Error (const std::string &, const char * pMsg, Args&&... args);
inline void Error (const std::string & notifier, const char * pMsg, Args&&... args);
/// Prints a log Message with source indication
template <typename... Args>
inline void Log (const std::string &, const char * pMsg, Args&&... args);
inline void Log (const std::string & notifier, const char * pMsg, Args&&... args);
/// Prints a Critical Message with source indication
template <typename... Args>
inline void Critical (const std::string &, const char * pMsg, Args&&... args);
inline void Critical (const std::string & notifier, const char * pMsg, Args&&... args);
/// Sends a User Notification with source indication
template <typename... Args>
inline void UserNotification( const std::string & notifier, const char * pMsg, Args&&... args );
@@ -608,7 +624,9 @@ public:
inline void UserTranslatedNotification( const std::string & notifier, const char * pMsg, Args&&... args );
// Notify a message directly to observers
template <LogStyle>
template <Base::LogStyle,
Base::IntendedRecipient = Base::IntendedRecipient::All,
Base::ContentType = Base::ContentType::Untranslated>
inline void Notify(const std::string & notifiername, const std::string & msg);
/// Attaches an Observer to FCConsole
@@ -691,8 +709,10 @@ protected:
virtual ~ConsoleSingleton();
private:
void postEvent(ConsoleSingleton::FreeCAD_ConsoleMsgType type, const std::string& notifiername, const std::string& msg);
void notifyPrivate(LogStyle category, const std::string& notifiername, const std::string& msg);
void postEvent(ConsoleSingleton::FreeCAD_ConsoleMsgType type, IntendedRecipient recipient,
ContentType content, const std::string& notifiername, const std::string& msg);
void notifyPrivate(LogStyle category, IntendedRecipient recipient, ContentType content,
const std::string& notifiername, const std::string& msg);
// singleton
static void Destruct();
@@ -873,26 +893,31 @@ inline void Base::ConsoleSingleton::Log( const std::string & notifier, const cha
Send<Base::LogStyle::Log>(notifier, pMsg, std::forward<Args>(args)...);
}
template <Base::LogStyle category, typename... Args>
template <Base::LogStyle category,
Base::IntendedRecipient recipient /*= Base::IntendedRecipient::All*/,
Base::ContentType contenttype /*= Base::ContentType::Untranslated*/,
typename... Args>
inline void Base::ConsoleSingleton::Send( const std::string & notifiername, const char * pMsg, Args&&... args )
{
std::string format = fmt::sprintf(pMsg, args...);
if (connectionMode == Direct) {
Notify<category>(notifiername,format);
Notify<category, recipient, contenttype>(notifiername,format);
}
else {
auto type = getConsoleMsg(category);
postEvent(type, notifiername, format);
postEvent(type, recipient, contenttype, notifiername, format);
}
}
template <Base::LogStyle category>
template <Base::LogStyle category,
Base::IntendedRecipient recipient /*= Base::IntendedRecipient::All*/,
Base::ContentType contenttype /*= Base::ContentType::Untranslated*/>
inline void Base::ConsoleSingleton::Notify(const std::string & notifiername, const std::string & msg)
{
notifyPrivate(category, notifiername, msg);
notifyPrivate(category, recipient, contenttype, notifiername, msg);
}
#if defined(__clang__)

View File

@@ -58,9 +58,12 @@ ConsoleObserverFile::~ConsoleObserverFile()
cFileStream.close();
}
void ConsoleObserverFile::SendLog(const std::string& notifiername, const std::string& msg, LogStyle level)
void ConsoleObserverFile::SendLog(const std::string& notifiername, const std::string& msg, LogStyle level,
IntendedRecipient recipient, ContentType content)
{
(void) notifiername;
(void) recipient;
(void) content;
std::string prefix;
switch(level){
@@ -101,9 +104,12 @@ ConsoleObserverStd::ConsoleObserverStd() :
ConsoleObserverStd::~ConsoleObserverStd() = default;
void ConsoleObserverStd::SendLog(const std::string& notifiername, const std::string& msg, LogStyle level)
void ConsoleObserverStd::SendLog(const std::string& notifiername, const std::string& msg, LogStyle level,
IntendedRecipient recipient, ContentType content)
{
(void) notifiername;
(void) recipient;
(void) content;
switch(level){
case LogStyle::Warning:

View File

@@ -42,7 +42,8 @@ public:
explicit ConsoleObserverFile(const char *sFileName);
~ConsoleObserverFile() override;
void SendLog(const std::string& notifiername, const std::string& message, LogStyle level) override;
void SendLog(const std::string& notifiername, const std::string& msg, LogStyle level,
IntendedRecipient recipient, ContentType content) override;
const char* Name() override {return "File";}
protected:
@@ -57,7 +58,8 @@ class BaseExport ConsoleObserverStd: public ILogger
public:
ConsoleObserverStd();
~ConsoleObserverStd() override;
void SendLog(const std::string& notifiername, const std::string& message, LogStyle level) override;
void SendLog(const std::string& notifiername, const std::string& msg, LogStyle level,
IntendedRecipient recipient, ContentType content) override;
const char* Name() override {return "Console";}
protected:
bool useColorStderr;

View File

@@ -728,9 +728,12 @@ public:
TestConsoleObserver() : matchMsg(0), matchWrn(0), matchErr(0), matchLog(0), matchCritical(0)
{
}
void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level) override{
void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level,
Base::IntendedRecipient recipient, Base::ContentType content) override{
(void) notifiername;
(void) recipient;
(void) content;
QMutexLocker ml(&mutex);

View File

@@ -81,10 +81,15 @@ GUIConsole::~GUIConsole (void)
FreeConsole();
}
void GUIConsole::SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level)
void GUIConsole::SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level,
Base::IntendedRecipient recipient, Base::ContentType content)
{
(void) notifiername;
// Do not log translated messages, or messages intended only to the user to std log
if(recipient == Base::IntendedRecipient::User || content == Base::ContentType::Translated)
return;
int color = -1;
switch(level){
case Base::LogStyle::Warning:
@@ -116,9 +121,12 @@ void GUIConsole::SendLog(const std::string& notifiername, const std::string& msg
// safely ignore GUIConsole::s_nMaxLines and GUIConsole::s_nRefCount
GUIConsole::GUIConsole () {}
GUIConsole::~GUIConsole () {}
void GUIConsole::SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level)
void GUIConsole::SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level,
Base::IntendedRecipient recipient, Base::ContentType content)
{
(void) notifiername;
(void) recipient;
(void) content;
switch(level){
case Base::LogStyle::Warning:

View File

@@ -47,7 +47,8 @@ public:
GUIConsole();
/// Destructor
~GUIConsole() override;
void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level) override;
void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level,
Base::IntendedRecipient recipient, Base::ContentType content) override;
const char* Name() override {return "GUIConsole";}
protected:

View File

@@ -2252,9 +2252,12 @@ void StatusBarObserver::OnChange(Base::Subject<const char*> &rCaller, const char
}
}
void StatusBarObserver::SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level)
void StatusBarObserver::SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level,
Base::IntendedRecipient recipient, Base::ContentType content)
{
(void) notifiername;
(void) recipient;
(void) content;
int messageType = -1;
switch(level){

View File

@@ -370,7 +370,8 @@ public:
/** Observes its parameter group. */
void OnChange(Base::Subject<const char*> &rCaller, const char * sReason) override;
void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level) override;
void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level,
Base::IntendedRecipient recipient, Base::ContentType content) override;
/// name of the observer
const char *Name() override {return "StatusBar";}

View File

@@ -214,8 +214,8 @@ public:
/// Function that is called by the console interface for this observer with the message
/// information
void SendLog(const std::string& notifiername, const std::string& msg,
Base::LogStyle level) override;
void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level,
Base::IntendedRecipient recipient, Base::ContentType content) override;
/// Name of the observer
const char* Name() override
@@ -242,8 +242,8 @@ NotificationAreaObserver::~NotificationAreaObserver()
Base::Console().DetachObserver(this);
}
void NotificationAreaObserver::SendLog(const std::string& notifiername, const std::string& msg,
Base::LogStyle level)
void NotificationAreaObserver::SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level,
Base::IntendedRecipient recipient, Base::ContentType content)
{
// 1. As notification system is shared with report view and others, the expectation is that any
// individual error and warning message will end in "\n". This means the string must be stripped
@@ -253,6 +253,9 @@ void NotificationAreaObserver::SendLog(const std::string& notifiername, const st
// "\n", as this generates problems with the translation system. Then the string must be
// stripped of "\n" before translation.
(void) recipient;
(void) content;
auto simplifiedstring =
QString::fromStdString(msg)
.trimmed();// remove any leading and trailing whitespace character ('\n')

View File

@@ -472,9 +472,12 @@ void ReportOutput::restoreFont()
setFont(serifFont);
}
void ReportOutput::SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level)
void ReportOutput::SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level,
Base::IntendedRecipient recipient, Base::ContentType content)
{
(void) notifiername;
(void) recipient;
(void) content;
ReportHighlighter::Paragraph style = ReportHighlighter::LogText;
switch (level) {

View File

@@ -141,7 +141,8 @@ public:
/** Observes its parameter group. */
void OnChange(Base::Subject<const char*> &rCaller, const char * sReason) override;
void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level) override;
void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level,
Base::IntendedRecipient recipient, Base::ContentType content) override;
/// returns the name for observer handling
const char* Name() override {return "ReportOutput";}

View File

@@ -176,9 +176,12 @@ public:
{
return "SplashObserver";
}
void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level) override
void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level,
Base::IntendedRecipient recipient, Base::ContentType content) override
{
Q_UNUSED(notifiername)
Q_UNUSED(recipient)
Q_UNUSED(content)
#ifdef FC_DEBUG
Log(msg.c_str());

View File

@@ -40,9 +40,13 @@ public:
void flush() {buffer.str("");buffer.clear();}
void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level) override{
void SendLog(const std::string& notifiername, const std::string& msg, Base::LogStyle level,
Base::IntendedRecipient recipient, Base::ContentType content) override{
(void) notifiername;
(void) msg;
(void) recipient;
(void) content;
switch(level){
case Base::LogStyle::Warning:
buffer << "WRN";