+} // of namespace simgear
+
+//////////////////////////////////////////////////////////////////////////////
+
+class FileLogCallback : public simgear::LogCallback
+{
+public:
+ FileLogCallback(const std::string& aPath, sgDebugClass c, sgDebugPriority p) :
+ simgear::LogCallback(c, p),
+ m_file(aPath.c_str(), std::ios_base::out | std::ios_base::trunc)
+ {
+ }
+
+ virtual void operator()(sgDebugClass c, sgDebugPriority p,
+ const char* file, int line, const std::string& message)
+ {
+ if (!shouldLog(c, p)) return;
+ m_file << debugClassToString(c) << ":" << (int) p
+ << ":" << file << ":" << line << ":" << message << std::endl;
+ }
+private:
+ std::ofstream m_file;
+};
+
+class StderrLogCallback : public simgear::LogCallback
+{
+public:
+ StderrLogCallback(sgDebugClass c, sgDebugPriority p) :
+ simgear::LogCallback(c, p)
+ {
+#ifdef SG_WINDOWS
+ AllocConsole(); // but only if we want a console
+ freopen("conin$", "r", stdin);
+ freopen("conout$", "w", stdout);
+ freopen("conout$", "w", stderr);
+#endif
+ }
+
+ virtual void operator()(sgDebugClass c, sgDebugPriority p,
+ const char* file, int line, const std::string& aMessage)
+ {
+ if (!shouldLog(c, p)) return;
+
+ fprintf(stderr, "%s\n", aMessage.c_str());
+ //fprintf(stderr, "%s:%d:%s:%d:%s\n", debugClassToString(c), p,
+ // file, line, aMessage.c_str());
+ fflush(stderr);
+ }
+};
+
+
+#ifdef SG_WINDOWS
+
+class WinDebugLogCallback : public simgear::LogCallback
+{
+public:
+ WinDebugLogCallback(sgDebugClass c, sgDebugPriority p) :
+ simgear::LogCallback(c, p)
+ {
+ }
+
+ virtual void operator()(sgDebugClass c, sgDebugPriority p,
+ const char* file, int line, const std::string& aMessage)
+ {
+ if (!shouldLog(c, p)) return;
+
+ std::ostringstream os;
+ os << debugClassToString(c) << ":" << aMessage << std::endl;
+ OutputDebugStringA(os.str().c_str());
+ }
+};
+
+#endif
+
+class LogStreamPrivate : public SGThread
+{
+private:
+ /**
+ * storage of a single log entry. Note this is not used for a persistent
+ * store, but rather for short term buffering between the submitting
+ * and output threads.
+ */
+ 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)
+ {
+ }
+
+ sgDebugClass debugClass;
+ sgDebugPriority debugPriority;
+ const char* file;
+ int line;
+ std::string message;
+ };
+
+ class PauseThread
+ {
+ public:
+ PauseThread(LogStreamPrivate* parent) : m_parent(parent)
+ {
+ m_wasRunning = m_parent->stop();
+ }
+
+ ~PauseThread()
+ {
+ if (m_wasRunning) {
+ m_parent->startLog();
+ }
+ }
+ private:
+ LogStreamPrivate* m_parent;
+ bool m_wasRunning;
+ };
+public:
+ LogStreamPrivate() :
+ m_logClass(SG_ALL),
+ m_logPriority(SG_ALERT),
+ m_isRunning(false),
+ m_consoleRequested(false)
+ {
+
+#if !defined(SG_WINDOWS)
+ m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
+ m_consoleCallbacks.push_back(m_callbacks.back());
+ m_consoleRequested = true;
+#endif
+
+#if defined (SG_WINDOWS) && !defined(NDEBUG)
+ m_callbacks.push_back(new WinDebugLogCallback(m_logClass, m_logPriority));
+ m_consoleCallbacks.push_back(m_callbacks.back());
+#endif
+ }
+
+ SGMutex m_lock;
+ SGBlockingQueue<LogEntry> m_entries;
+
+ typedef std::vector<simgear::LogCallback*> CallbackVec;
+ CallbackVec m_callbacks;
+ /// subset of callbacks which correspond to stdout / console,
+ /// and hence should dynamically reflect console logging settings
+ CallbackVec m_consoleCallbacks;
+
+ sgDebugClass m_logClass;
+ sgDebugPriority m_logPriority;
+ bool m_isRunning;
+ bool m_consoleRequested;
+
+ void startLog()
+ {
+ SGGuard<SGMutex> g(m_lock);
+ if (m_isRunning) return;
+ m_isRunning = true;
+ start();
+ }
+
+ virtual void run()
+ {
+ while (1) {
+ LogEntry entry(m_entries.pop());
+ // special marker entry detected, terminate the thread since we are
+ // making a configuration change or quitting the app
+ if ((entry.debugClass == SG_NONE) && !strcmp(entry.file, "done")) {
+ return;
+ }
+
+ // submit to each installed callback in turn
+ BOOST_FOREACH(simgear::LogCallback* cb, m_callbacks) {
+ (*cb)(entry.debugClass, entry.debugPriority,
+ entry.file, entry.line, entry.message);
+ }
+ } // of main thread loop
+ }
+
+ bool stop()
+ {
+ SGGuard<SGMutex> g(m_lock);
+ if (!m_isRunning) {
+ return false;
+ }
+
+ // log a special marker value, which will cause the thread to wakeup,
+ // and then exit
+ log(SG_NONE, SG_ALERT, "done", -1, "");
+ join();
+
+ m_isRunning = false;
+ return true;
+ }
+
+ void addCallback(simgear::LogCallback* cb)
+ {
+ PauseThread pause(this);
+ m_callbacks.push_back(cb);
+ }
+
+ void removeCallback(simgear::LogCallback* cb)
+ {
+ PauseThread pause(this);
+ CallbackVec::iterator it = std::find(m_callbacks.begin(), m_callbacks.end(), cb);
+ if (it != m_callbacks.end()) {
+ m_callbacks.erase(it);
+ }
+ }
+
+ void setLogLevels( sgDebugClass c, sgDebugPriority p )
+ {
+ PauseThread pause(this);
+ m_logPriority = p;
+ m_logClass = c;
+ BOOST_FOREACH(simgear::LogCallback* cb, m_consoleCallbacks) {
+ cb->setLogLevels(c, p);
+ }
+ }
+
+ bool would_log( sgDebugClass c, sgDebugPriority p ) const
+ {
+ if (p >= SG_INFO) return true;
+ return ((c & m_logClass) != 0 && p >= m_logPriority);
+ }
+
+ void log( sgDebugClass c, sgDebugPriority p,
+ const char* fileName, int line, const std::string& msg)
+ {
+ LogEntry entry(c, p, fileName, line, msg);
+ m_entries.push(entry);
+ }
+
+ void requestConsole()
+ {
+ PauseThread pause(this);
+ if (m_consoleRequested) {
+ return;
+ }
+
+ m_consoleRequested = true;
+ m_callbacks.push_back(new StderrLogCallback(m_logClass, m_logPriority));
+ m_consoleCallbacks.push_back(m_callbacks.back());
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+static logstream* global_logstream = NULL;
+static LogStreamPrivate* global_privateLogstream = NULL;
+
+logstream::logstream()