X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fdebug%2Flogstream.cxx;h=4290f5ca9d0c3fc6a60c9df3e073501053504563;hb=8c38f799adf4f4ece8c79dd1e6ff5a75d85a17dc;hp=255eae07a88542f5580d6f48e3ae72d5cabd49ad;hpb=dcb95d131bc6aef1abe25d1f415e309f06e52436;p=simgear.git diff --git a/simgear/debug/logstream.cxx b/simgear/debug/logstream.cxx index 255eae07..4290f5ca 100644 --- a/simgear/debug/logstream.cxx +++ b/simgear/debug/logstream.cxx @@ -20,70 +20,423 @@ // // $Id$ +#include + #include "logstream.hxx" -logstream *global_logstream = NULL; +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include -bool logbuf::logging_enabled = true; -sgDebugClass logbuf::logClass = SG_NONE; -sgDebugPriority logbuf::logPriority = SG_INFO; -streambuf* logbuf::sbuf = NULL; +#ifdef SG_WINDOWS +// for AllocConsole, OutputDebugString + #include "windows.h" +#endif -logbuf::logbuf() +const char* debugClassToString(sgDebugClass c) { -// if ( sbuf == NULL ) -// sbuf = cerr.rdbuf(); + 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"; + default: return "unknown"; + } } -logbuf::~logbuf() +////////////////////////////////////////////////////////////////////////////// + +namespace simgear +{ + +LogCallback::LogCallback(sgDebugClass c, sgDebugPriority p) : + m_class(c), + m_priority(p) { - if ( sbuf ) - sync(); } -void -logbuf::set_sb( streambuf* sb ) +bool LogCallback::shouldLog(sgDebugClass c, sgDebugPriority p) const { - if ( sbuf ) - sync(); + return ((c & m_class) != 0 && p >= m_priority); +} - sbuf = sb; +void LogCallback::setLogLevels( sgDebugClass c, sgDebugPriority p ) +{ + m_priority = p; + m_class = c; +} + +} // 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 m_entries; + + typedef std::vector 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 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 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() +{ + global_privateLogstream = new LogStreamPrivate; + global_privateLogstream->startLog(); } void -logbuf::set_log_level( sgDebugClass c, sgDebugPriority p ) +logstream::setLogLevels( sgDebugClass c, sgDebugPriority p ) { - logClass = c; - logPriority = p; + global_privateLogstream->setLogLevels(c, p); +} + +void +logstream::addCallback(simgear::LogCallback* cb) +{ + global_privateLogstream->addCallback(cb); +} + +void +logstream::removeCallback(simgear::LogCallback* cb) +{ + global_privateLogstream->removeCallback(cb); } void -logbuf::set_log_classes (sgDebugClass c) +logstream::log( sgDebugClass c, sgDebugPriority p, + const char* fileName, int line, const std::string& msg) +{ + global_privateLogstream->log(c, p, fileName, line, msg); +} + +bool +logstream::would_log( sgDebugClass c, sgDebugPriority p ) const { - logClass = c; + return global_privateLogstream->would_log(c,p); } sgDebugClass -logbuf::get_log_classes () +logstream::get_log_classes() const { - return logClass; + return global_privateLogstream->m_logClass; +} + +sgDebugPriority +logstream::get_log_priority() const +{ + return global_privateLogstream->m_logPriority; } void -logbuf::set_log_priority (sgDebugPriority p) +logstream::set_log_priority( sgDebugPriority p) { - logPriority = p; + global_privateLogstream->setLogLevels(global_privateLogstream->m_logClass, p); +} + +void +logstream::set_log_classes( sgDebugClass c) +{ + global_privateLogstream->setLogLevels(c, global_privateLogstream->m_logPriority); } -sgDebugPriority -logbuf::get_log_priority () +logstream& +sglog() { - return logPriority; + // Force initialization of cerr. + static std::ios_base::Init initializer; + + // http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf + // in the absence of portable memory barrier ops in Simgear, + // let's keep this correct & safe + static SGMutex m; + SGGuard g(m); + + if( !global_logstream ) + global_logstream = new logstream(); + return *global_logstream; } void -logstream::setLogLevels( sgDebugClass c, sgDebugPriority p ) +logstream::logToFile( const SGPath& aPath, sgDebugClass c, sgDebugPriority p ) +{ + global_privateLogstream->addCallback(new FileLogCallback(aPath.str(), c, p)); +} + +namespace simgear +{ + +void requestConsole() { - logbuf::set_log_level( c, p ); + sglog(); // force creation + global_privateLogstream->requestConsole(); } +} // of namespace simgear