Logging system buffers startup messages.

This allows adding log callbacks easier during startup, without 
missing the initial messages, which might be very important.
This commit is contained in:
James Turner
2017-01-30 07:43:35 +01:00
parent 866f85064a
commit ed3ba67925
2 changed files with 60 additions and 21 deletions
+54 -21
View File
@@ -181,9 +181,9 @@ class LogStreamPrivate : public SGThread
{ {
private: private:
/** /**
* storage of a single log entry. Note this is not used for a persistent * storage of a single log entry. This is used to pass log entries from
* store, but rather for short term buffering between the submitting * the various threads to the logging thread, and also to store the startup
* and output threads. * entries
*/ */
class LogEntry class LogEntry
{ {
@@ -195,19 +195,24 @@ private:
{ {
} }
sgDebugClass debugClass; const sgDebugClass debugClass;
sgDebugPriority debugPriority; const sgDebugPriority debugPriority;
const char* file; const char* file;
int line; const int line;
std::string message; const std::string message;
}; };
/**
* RAII object to pause the logging thread if it's running, and restart it.
* used to safely make configuration changes.
*/
class PauseThread class PauseThread
{ {
public: public:
PauseThread(LogStreamPrivate* parent) : m_parent(parent) PauseThread(LogStreamPrivate* parent)
: m_parent(parent)
, m_wasRunning(m_parent->stop())
{ {
m_wasRunning = m_parent->stop();
} }
~PauseThread() ~PauseThread()
@@ -218,17 +223,13 @@ private:
} }
private: private:
LogStreamPrivate* m_parent; LogStreamPrivate* m_parent;
bool m_wasRunning; const bool m_wasRunning;
}; };
public: public:
LogStreamPrivate() : LogStreamPrivate() :
m_logClass(SG_ALL), m_logClass(SG_ALL),
m_logPriority(SG_ALERT), m_logPriority(SG_ALERT)
#if defined (SG_WINDOWS)
m_stdout_isRedirectedAlready(false),
m_stderr_isRedirectedAlready(false),
#endif
m_isRunning(false)
{ {
#if defined (SG_WINDOWS) #if defined (SG_WINDOWS)
/* /*
@@ -332,7 +333,7 @@ public:
~LogStreamPrivate() ~LogStreamPrivate()
{ {
BOOST_FOREACH(simgear::LogCallback* cb, m_callbacks) { for (simgear::LogCallback* cb : m_callbacks) {
delete cb; delete cb;
} }
} }
@@ -340,6 +341,10 @@ public:
SGMutex m_lock; SGMutex m_lock;
SGBlockingQueue<LogEntry> m_entries; SGBlockingQueue<LogEntry> m_entries;
// log entries posted during startup
std::vector<LogEntry> m_startupEntries;
bool m_startupLogging = false;
typedef std::vector<simgear::LogCallback*> CallbackVec; typedef std::vector<simgear::LogCallback*> CallbackVec;
CallbackVec m_callbacks; CallbackVec m_callbacks;
/// subset of callbacks which correspond to stdout / console, /// subset of callbacks which correspond to stdout / console,
@@ -348,11 +353,11 @@ public:
sgDebugClass m_logClass; sgDebugClass m_logClass;
sgDebugPriority m_logPriority; sgDebugPriority m_logPriority;
bool m_isRunning; bool m_isRunning = false;
#if defined (SG_WINDOWS) #if defined (SG_WINDOWS)
// track whether the console was redirected on launch (in the constructor, which is called early on) // track whether the console was redirected on launch (in the constructor, which is called early on)
bool m_stderr_isRedirectedAlready; bool m_stderr_isRedirectedAlready = false;
bool m_stdout_isRedirectedAlready; bool m_stdout_isRedirectedAlready = false;
#endif #endif
void startLog() void startLog()
@@ -363,6 +368,16 @@ public:
start(); start();
} }
void setStartupLoggingEnabled(bool on)
{
if (m_startupLogging == on) {
return;
}
m_startupLogging = on;
m_startupEntries.clear();
}
virtual void run() virtual void run()
{ {
while (1) { while (1) {
@@ -373,8 +388,14 @@ public:
return; return;
} }
if (m_startupLogging) {
// save to the startup list for not-yet-added callbacks to
// pull down on startup
m_startupEntries.push_back(entry);
}
// submit to each installed callback in turn // submit to each installed callback in turn
BOOST_FOREACH(simgear::LogCallback* cb, m_callbacks) { for (simgear::LogCallback* cb : m_callbacks) {
(*cb)(entry.debugClass, entry.debugPriority, (*cb)(entry.debugClass, entry.debugPriority,
entry.file, entry.line, entry.message); entry.file, entry.line, entry.message);
} }
@@ -401,6 +422,13 @@ public:
{ {
PauseThread pause(this); PauseThread pause(this);
m_callbacks.push_back(cb); m_callbacks.push_back(cb);
// we clear startup entries not using this, so always safe to run
// this code, container will simply be empty
for (auto entry : m_startupEntries) {
(*cb)(entry.debugClass, entry.debugPriority,
entry.file, entry.line, entry.message);
}
} }
void removeCallback(simgear::LogCallback* cb) void removeCallback(simgear::LogCallback* cb)
@@ -557,6 +585,11 @@ logstream::logToFile( const SGPath& aPath, sgDebugClass c, sgDebugPriority p )
global_privateLogstream->addCallback(new FileLogCallback(aPath, c, p)); global_privateLogstream->addCallback(new FileLogCallback(aPath, c, p));
} }
void logstream::setStartupLoggingEnabled(bool enabled)
{
global_privateLogstream->setStartupLoggingEnabled(enabled);
}
namespace simgear namespace simgear
{ {
+6
View File
@@ -134,6 +134,12 @@ public:
void removeCallback(simgear::LogCallback* cb); void removeCallback(simgear::LogCallback* cb);
/**
* optionally record all entries and submit them to new log callbacks that
* are added. This allows simplified logging configuration, but still including
* early startup information in all logs.
*/
void setStartupLoggingEnabled(bool enabled);
private: private:
// constructor // constructor
logstream(); logstream();