Add MANDATORY_INFO log priority.

New log-level to avoid using ‘ALERT’ for expected mandatory messages,
since I want to do error collection on real alerts in a future commit.

As part of this, split logstream.hxx into separate header files, and
add a new virtual hook to LogCallback which takes the complete log-entry
This commit is contained in:
James Turner 2020-09-06 14:56:55 +01:00 committed by Automatic Release Builder
parent 3e804605b7
commit 3753c62783
11 changed files with 319 additions and 171 deletions

View File

@ -26,7 +26,7 @@
#include <vector> #include <vector>
#include <memory> // for std::unique_ptr #include <memory> // for std::unique_ptr
#include <simgear/debug/logstream.hxx> #include <simgear/debug/LogCallback.hxx>
namespace simgear namespace simgear
{ {

View File

@ -1,7 +1,10 @@
include (SimGearComponent) include (SimGearComponent)
set(HEADERS debug_types.h logstream.hxx BufferedLogCallback.hxx OsgIoCapture.hxx) set(HEADERS debug_types.h
set(SOURCES logstream.cxx BufferedLogCallback.cxx) logstream.hxx BufferedLogCallback.hxx OsgIoCapture.hxx
LogCallback.hxx LogEntry.hxx)
set(SOURCES logstream.cxx BufferedLogCallback.cxx
LogCallback.cxx LogEntry.cxx)
simgear_component(debug debug "${SOURCES}" "${HEADERS}") simgear_component(debug debug "${SOURCES}" "${HEADERS}")

View File

@ -0,0 +1,116 @@
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "LogCallback.hxx"
using namespace simgear;
LogCallback::LogCallback(sgDebugClass c, sgDebugPriority p) : m_class(c),
m_priority(p)
{
}
void LogCallback::operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage)
{
// override me
}
bool LogCallback::doProcessEntry(const LogEntry& e)
{
return false;
}
void LogCallback::processEntry(const LogEntry& e)
{
if (doProcessEntry(e))
return; // derived class used the new API
// call the old API
(*this)(e.debugClass, e.debugPriority, e.file, e.line, e.message);
}
bool LogCallback::shouldLog(sgDebugClass c, sgDebugPriority p) const
{
if ((c & m_class) != 0 && p >= m_priority)
return true;
if (c == SG_OSG) // always have OSG logging as it OSG logging is configured separately.
return true;
return false;
}
void LogCallback::setLogLevels(sgDebugClass c, sgDebugPriority p)
{
m_priority = p;
m_class = c;
}
const char* LogCallback::debugPriorityToString(sgDebugPriority p)
{
switch (p) {
case SG_DEV_ALERT:
case SG_ALERT:
return "ALRT";
case SG_BULK: return "BULK";
case SG_DEBUG: return "DBUG";
case SG_MANDATORY_INFO:
case SG_INFO:
return "INFO";
case SG_POPUP: return "POPU";
case SG_DEV_WARN:
case SG_WARN:
return "WARN";
default: return "UNKN";
}
}
const char* LogCallback::debugClassToString(sgDebugClass c)
{
switch (c) {
case SG_NONE: return "none";
case SG_TERRAIN: return "terrain";
case SG_ASTRO: return "astro";
case SG_FLIGHT: return "flight";
case SG_INPUT: return "input";
case SG_GL: return "opengl";
case SG_VIEW: return "view";
case SG_COCKPIT: return "cockpit";
case SG_GENERAL: return "general";
case SG_MATH: return "math";
case SG_EVENT: return "event";
case SG_AIRCRAFT: return "aircraft";
case SG_AUTOPILOT: return "autopilot";
case SG_IO: return "io";
case SG_CLIPPER: return "clipper";
case SG_NETWORK: return "network";
case SG_ATC: return "atc";
case SG_NASAL: return "nasal";
case SG_INSTR: return "instruments";
case SG_SYSTEMS: return "systems";
case SG_AI: return "ai";
case SG_ENVIRONMENT: return "environment";
case SG_SOUND: return "sound";
case SG_NAVAID: return "navaid";
case SG_GUI: return "gui";
case SG_TERRASYNC: return "terrasync";
case SG_PARTICLES: return "particles";
case SG_HEADLESS: return "headless";
case SG_OSG: return "OSG";
default: return "unknown";
}
}

View File

@ -0,0 +1,56 @@
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#pragma once
#include <string>
#include "LogEntry.hxx"
#include "debug_types.h"
namespace simgear {
class LogCallback
{
public:
virtual ~LogCallback() = default;
// newer API: return true if you handled the message, otherwise
// the old API will be called
virtual bool doProcessEntry(const LogEntry& e);
// old API, kept for compatability
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage);
void setLogLevels(sgDebugClass c, sgDebugPriority p);
void processEntry(const LogEntry& e);
protected:
LogCallback(sgDebugClass c, sgDebugPriority p);
bool shouldLog(sgDebugClass c, sgDebugPriority p) const;
static const char* debugClassToString(sgDebugClass c);
static const char* debugPriorityToString(sgDebugPriority p);
private:
sgDebugClass m_class;
sgDebugPriority m_priority;
};
} // namespace simgear

View File

@ -0,0 +1,45 @@
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include <simgear_config.h>
#include "LogEntry.hxx"
#include <cstring> // for strdup
namespace simgear {
LogEntry::~LogEntry()
{
if (freeFilename) {
free(const_cast<char*>(file));
}
}
LogEntry::LogEntry(const LogEntry& c) : debugClass(c.debugClass),
debugPriority(c.debugPriority),
originalPriority(c.originalPriority),
file(c.file),
line(c.line),
message(c.message),
freeFilename(c.freeFilename)
{
if (c.freeFilename) {
file = strdup(c.file);
}
}
} // namespace simgear

View File

@ -0,0 +1,54 @@
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Library General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#pragma once
#include <string>
#include "debug_types.h"
namespace simgear {
/**
* storage of a single log entry. This is used to pass log entries from
* the various threads to the logging thread, and also to store the startup
* entries
*/
class LogEntry
{
public:
LogEntry(sgDebugClass c, sgDebugPriority p,
sgDebugPriority op,
const char* f, int l, const std::string& msg) : debugClass(c), debugPriority(p), originalPriority(op),
file(f), line(l),
message(msg)
{
}
LogEntry(const LogEntry& c);
LogEntry& operator=(const LogEntry& c) = delete;
~LogEntry();
const sgDebugClass debugClass;
const sgDebugPriority debugPriority;
const sgDebugPriority originalPriority;
const char* file;
const int line;
const std::string message;
bool freeFilename = false; ///< if true, we own, and therefore need to free, the memory pointed to by 'file'
};
} // namespace simgear

View File

@ -2,8 +2,7 @@
#include <osg/Notify> #include <osg/Notify>
using namespace osg; #include <simgear/debug/logstream.hxx>
/** /**
* merge OSG output into our logging system, so it gets recorded to file, * merge OSG output into our logging system, so it gets recorded to file,

View File

@ -1,3 +1,5 @@
#pragma once
/** \file debug_types.h /** \file debug_types.h
* Define the various logging classes and priorities * Define the various logging classes and priorities
*/ */
@ -62,6 +64,7 @@ typedef enum {
// SG_ABORT // Abandon ship (core) // SG_ABORT // Abandon ship (core)
SG_DEV_WARN, // Warning for developers, translated to other priority SG_DEV_WARN, // Warning for developers, translated to other priority
SG_DEV_ALERT // Alert for developers, translated SG_DEV_ALERT, // Alert for developers, translated
} sgDebugPriority;
SG_MANDATORY_INFO // information, but should always be shown
} sgDebugPriority;

View File

@ -35,6 +35,7 @@
#include <simgear/threads/SGThread.hxx> #include <simgear/threads/SGThread.hxx>
#include <simgear/threads/SGQueue.hxx> #include <simgear/threads/SGQueue.hxx>
#include "LogCallback.hxx"
#include <simgear/io/iostreams/sgstream.hxx> #include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/misc/sg_path.hxx> #include <simgear/misc/sg_path.hxx>
#include <simgear/misc/strutils.hxx> #include <simgear/misc/strutils.hxx>
@ -47,86 +48,9 @@
#include <io.h> #include <io.h>
#endif #endif
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
namespace simgear
{
LogCallback::LogCallback(sgDebugClass c, sgDebugPriority p) :
m_class(c),
m_priority(p)
{
}
bool LogCallback::shouldLog(sgDebugClass c, sgDebugPriority p) const
{
if ((c & m_class) != 0 && p >= m_priority)
return true;
if (c == SG_OSG) // always have OSG logging as it OSG logging is configured separately.
return true;
return false;
}
void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p )
{
m_priority = p;
m_class = c;
}
const char* LogCallback::debugPriorityToString(sgDebugPriority p)
{
switch (p) {
case SG_ALERT: return "ALRT";
case SG_BULK: return "BULK";
case SG_DEBUG: return "DBUG";
case SG_INFO: return "INFO";
case SG_POPUP: return "POPU";
case SG_WARN: return "WARN";
default: return "UNKN";
}
}
const char* LogCallback::debugClassToString(sgDebugClass c)
{
switch (c) {
case SG_NONE: return "none";
case SG_TERRAIN: return "terrain";
case SG_ASTRO: return "astro";
case SG_FLIGHT: return "flight";
case SG_INPUT: return "input";
case SG_GL: return "opengl";
case SG_VIEW: return "view";
case SG_COCKPIT: return "cockpit";
case SG_GENERAL: return "general";
case SG_MATH: return "math";
case SG_EVENT: return "event";
case SG_AIRCRAFT: return "aircraft";
case SG_AUTOPILOT: return "autopilot";
case SG_IO: return "io";
case SG_CLIPPER: return "clipper";
case SG_NETWORK: return "network";
case SG_ATC: return "atc";
case SG_NASAL: return "nasal";
case SG_INSTR: return "instruments";
case SG_SYSTEMS: return "systems";
case SG_AI: return "ai";
case SG_ENVIRONMENT:return "environment";
case SG_SOUND: return "sound";
case SG_NAVAID: return "navaid";
case SG_GUI: return "gui";
case SG_TERRASYNC: return "terrasync";
case SG_PARTICLES: return "particles";
case SG_HEADLESS: return "headless";
case SG_OSG: return "OSG";
default: return "unknown";
}
}
} // of namespace simgear
//////////////////////////////////////////////////////////////////////////////
class FileLogCallback : public simgear::LogCallback class FileLogCallback : public simgear::LogCallback
{ {
@ -196,8 +120,8 @@ public:
} }
#endif #endif
virtual void operator()(sgDebugClass c, sgDebugPriority p, void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage) const char* file, int line, const std::string& aMessage) override
{ {
if (!shouldLog(c, p)) return; if (!shouldLog(c, p)) return;
//fprintf(stderr, "%s\n", aMessage.c_str()); //fprintf(stderr, "%s\n", aMessage.c_str());
@ -226,8 +150,8 @@ public:
{ {
} }
virtual void operator()(sgDebugClass c, sgDebugPriority p, void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage) const char* file, int line, const std::string& aMessage) override
{ {
if (!shouldLog(c, p)) return; if (!shouldLog(c, p)) return;
@ -242,29 +166,6 @@ public:
class logstream::LogStreamPrivate : public SGThread class logstream::LogStreamPrivate : public SGThread
{ {
private: private:
/**
* storage of a single log entry. This is used to pass log entries from
* the various threads to the logging thread, and also to store the startup
* entries
*/
class LogEntry
{
public:
LogEntry(sgDebugClass c, sgDebugPriority p,
const char* f, int l, const std::string& msg) :
debugClass(c), debugPriority(p), file(f), line(l),
message(msg)
{
}
const sgDebugClass debugClass;
const sgDebugPriority debugPriority;
const char* file;
const int line;
const std::string message;
bool freeFilename = false;
};
/** /**
* RAII object to pause the logging thread if it's running, and restart it. * RAII object to pause the logging thread if it's running, and restart it.
* used to safely make configuration changes. * used to safely make configuration changes.
@ -412,10 +313,10 @@ public:
} }
std::mutex m_lock; std::mutex m_lock;
SGBlockingQueue<LogEntry> m_entries; SGBlockingQueue<simgear::LogEntry> m_entries;
// log entries posted during startup // log entries posted during startup
std::vector<LogEntry> m_startupEntries; std::vector<simgear::LogEntry> m_startupEntries;
bool m_startupLogging = false; bool m_startupLogging = false;
typedef std::vector<simgear::LogCallback*> CallbackVec; typedef std::vector<simgear::LogCallback*> CallbackVec;
@ -438,6 +339,8 @@ public:
// test suite mode. // test suite mode.
bool m_testMode = false; bool m_testMode = false;
std::vector<std::string> _popupMessages;
void startLog() void startLog()
{ {
std::lock_guard<std::mutex> g(m_lock); std::lock_guard<std::mutex> g(m_lock);
@ -461,16 +364,13 @@ public:
void clearStartupEntriesLocked() void clearStartupEntriesLocked()
{ {
std::for_each(m_startupEntries.begin(), m_startupEntries.end(), [](const LogEntry& e) {
if (e.freeFilename) free(const_cast<char*>(e.file));
});
m_startupEntries.clear(); m_startupEntries.clear();
} }
void run() override void run() override
{ {
while (1) { while (1) {
LogEntry entry(m_entries.pop()); simgear::LogEntry entry(m_entries.pop());
// special marker entry detected, terminate the thread since we are // special marker entry detected, terminate the thread since we are
// making a configuration change or quitting the app // making a configuration change or quitting the app
if ((entry.debugClass == SG_NONE) && entry.file && !strcmp(entry.file, "done")) { if ((entry.debugClass == SG_NONE) && entry.file && !strcmp(entry.file, "done")) {
@ -486,14 +386,7 @@ public:
} }
// submit to each installed callback in turn // submit to each installed callback in turn
for (simgear::LogCallback* cb : m_callbacks) { for (simgear::LogCallback* cb : m_callbacks) {
(*cb)(entry.debugClass, entry.debugPriority, cb->processEntry(entry);
entry.file, entry.line, entry.message);
}
// frrr the filename if required. For startup entries
// we wait and do this later
if (!m_startupLogging && entry.freeFilename) {
free(const_cast<char*>(entry.file));
} }
} // of main thread loop } // of main thread loop
} }
@ -523,9 +416,8 @@ public:
// we clear startup entries not using this, so always safe to run // we clear startup entries not using this, so always safe to run
// this code, container will simply be empty // this code, container will simply be empty
for (auto entry : m_startupEntries) { for (const auto& entry : m_startupEntries) {
(*cb)(entry.debugClass, entry.debugPriority, cb->processEntry(entry);
entry.file, entry.line, entry.message);
} }
} }
@ -576,12 +468,13 @@ public:
const char* fileName, int line, const std::string& msg, const char* fileName, int line, const std::string& msg,
bool freeFilename) bool freeFilename)
{ {
p = translatePriority(p); auto tp = translatePriority(p);
if (!m_fileLine) { if (!m_fileLine) {
/* This prevents output of file:line in StderrLogCallback. */ /* This prevents output of file:line in StderrLogCallback. */
line = -line; line = -line;
} }
LogEntry entry(c, p, fileName, line, msg);
simgear::LogEntry entry(c, tp, p, fileName, line, msg);
entry.freeFilename = freeFilename; entry.freeFilename = freeFilename;
m_entries.push(entry); m_entries.push(entry);
} }
@ -613,8 +506,8 @@ logstream::logstream()
logstream::~logstream() logstream::~logstream()
{ {
popup_msgs.clear();
d->stop(); d->stop();
d.reset();
} }
void void
@ -717,17 +610,18 @@ void logstream::hexdump(sgDebugClass c, sgDebugPriority p, const char* fileName,
void void
logstream::popup( const std::string& msg) logstream::popup( const std::string& msg)
{ {
popup_msgs.push_back(msg); std::lock_guard<std::mutex> g(d->m_lock);
d->_popupMessages.push_back(msg);
} }
std::string std::string
logstream::get_popup() logstream::get_popup()
{ {
std::string rv = ""; std::string rv;
if (!popup_msgs.empty()) std::lock_guard<std::mutex> g(d->m_lock);
{ if (!d->_popupMessages.empty()) {
rv = popup_msgs.front(); rv = d->_popupMessages.front();
popup_msgs.erase(popup_msgs.begin()); d->_popupMessages.erase(d->_popupMessages.begin());
} }
return rv; return rv;
} }
@ -735,7 +629,8 @@ logstream::get_popup()
bool bool
logstream::has_popup() logstream::has_popup()
{ {
return (popup_msgs.size() > 0) ? true : false; std::lock_guard<std::mutex> g(d->m_lock);
return !d->_popupMessages.empty();
} }
bool bool

View File

@ -38,26 +38,7 @@ class SGPath;
namespace simgear namespace simgear
{ {
class LogCallback class LogCallback;
{
public:
virtual ~LogCallback() {}
virtual void operator()(sgDebugClass c, sgDebugPriority p,
const char* file, int line, const std::string& aMessage) = 0;
void setLogLevels(sgDebugClass c, sgDebugPriority p);
protected:
LogCallback(sgDebugClass c, sgDebugPriority p);
bool shouldLog(sgDebugClass c, sgDebugPriority p) const;
static const char* debugClassToString(sgDebugClass c);
static const char* debugPriorityToString(sgDebugPriority p);
private:
sgDebugClass m_class;
sgDebugPriority m_priority;
};
/** /**
* Helper force a console on platforms where it might optional, when * Helper force a console on platforms where it might optional, when
* we need to show a console. This basically means Windows at the * we need to show a console. This basically means Windows at the
@ -198,8 +179,6 @@ private:
// constructor // constructor
logstream(); logstream();
std::vector<std::string> popup_msgs;
class LogStreamPrivate; class LogStreamPrivate;
std::unique_ptr<LogStreamPrivate> d; std::unique_ptr<LogStreamPrivate> d;

View File

@ -448,9 +448,7 @@ bool SGTerraSync::WorkerThread::start()
_stop = false; _stop = false;
_state = TerrasyncThreadState(); // clean state _state = TerrasyncThreadState(); // clean state
// not really an alert - but we want to (always) see this message, so user is SG_LOG(SG_TERRASYNC, SG_MANDATORY_INFO,
// aware we're downloading scenery (and using bandwidth).
SG_LOG(SG_TERRASYNC,SG_ALERT,
"Starting automatic scenery download/synchronization to '" << _local_dir << "'."); "Starting automatic scenery download/synchronization to '" << _local_dir << "'.");
SGThread::start(); SGThread::start();