#define _LOGSTREAM_H
#include <simgear/compiler.h>
+#include <simgear/debug/debug_types.h>
-#ifdef _MSC_VER
-# include <windows.h>
-#endif
+#include <sstream>
+#include <vector>
+
+// forward decls
+class SGPath;
+
+namespace simgear
+{
+
+class LogCallback
+{
+public:
+ virtual ~LogCallback() {}
+ virtual void operator()(sgDebugClass c, sgDebugPriority p,
+ const char* file, int line, const std::string& aMessage) = 0;
-#ifdef SG_HAVE_STD_INCLUDES
-# include <streambuf>
-# include <iostream>
-#else
-# include <iostream.h>
-# include <simgear/sg_traits.hxx>
-#endif
+ void setLogLevels(sgDebugClass c, sgDebugPriority p);
+protected:
+ LogCallback(sgDebugClass c, sgDebugPriority p);
-#include <simgear/debug/debug_types.h>
+ bool shouldLog(sgDebugClass c, sgDebugPriority p) const;
+private:
+ sgDebugClass m_class;
+ sgDebugPriority m_priority;
+};
-SG_USING_STD(streambuf);
-SG_USING_STD(ostream);
-SG_USING_STD(cout);
-SG_USING_STD(cerr);
-SG_USING_STD(endl);
+/**
+ * Helper force a console on platforms where it might optional, when
+ * we need to show a console. This basically means Windows at the
+ * moment - on other plaforms it's a no-op
+ */
+void requestConsole();
-#ifdef __MWERKS__
-SG_USING_STD(iostream);
-#endif
+void shutdownLogging();
-//
-// TODO:
-//
-// 1. Change output destination. Done.
-// 2. Make logbuf thread safe.
-// 3. Read environment for default debugClass and debugPriority.
-//
+} // of namespace simgear
/**
- * logbuf is an output-only streambuf with the ability to disable sets of
- * messages at runtime. Only messages with priority >= logbuf::logPriority
- * and debugClass == logbuf::logClass are output.
+ * Class to manage the debug logging stream.
*/
-#ifdef SG_NEED_STREAMBUF_HACK
-class logbuf : public __streambuf
-#else
-class logbuf : public streambuf
-#endif
+class logstream
{
public:
-
-#ifndef SG_HAVE_STD_INCLUDES
- typedef char_traits<char> traits_type;
- typedef char_traits<char>::int_type int_type;
- // typedef char_traits<char>::pos_type pos_type;
- // typedef char_traits<char>::off_type off_type;
-#endif
- // logbuf( streambuf* sb ) : sbuf(sb) {}
- /** Constructor */
- logbuf();
-
- /** Destructor */
- ~logbuf();
-
- /**
- * Is logging enabled?
- * @return true or false*/
- bool enabled() { return logging_enabled; }
-
- /**
- * Set the logging level of subsequent messages.
- * @param c debug class
- * @param p priority
- */
- void set_log_state( sgDebugClass c, sgDebugPriority p );
-
+ ~logstream();
+
+ static void initGlobalLogstream();
/**
- * Set the global logging level.
+ * Set the global log class and priority level.
* @param c debug class
* @param p priority
*/
- static void set_log_level( sgDebugClass c, sgDebugPriority p );
-
-
- /**
- * Set the allowed logging classes.
- * @param c All enabled logging classes anded together.
- */
- static void set_log_classes (sgDebugClass c);
+ void setLogLevels( sgDebugClass c, sgDebugPriority p );
+ bool would_log( sgDebugClass c, sgDebugPriority p ) const;
- /**
- * Get the logging classes currently enabled.
- * @return All enabled debug logging anded together.
- */
- static sgDebugClass get_log_classes ();
+ void logToFile( const SGPath& aPath, sgDebugClass c, sgDebugPriority p );
+ void set_log_priority( sgDebugPriority p);
+
+ void set_log_classes( sgDebugClass c);
+
+ sgDebugClass get_log_classes() const;
+
+ sgDebugPriority get_log_priority() const;
/**
- * Set the logging priority.
- * @param c The priority cutoff for logging messages.
+ * the core logging method
*/
- static void set_log_priority (sgDebugPriority p);
-
+ void log( sgDebugClass c, sgDebugPriority p,
+ const char* fileName, int line, const std::string& msg);
/**
- * Get the current logging priority.
- * @return The priority cutoff for logging messages.
+ * support for the SG_POPUP logging class
+ * set the content of the popup message
*/
- static sgDebugPriority get_log_priority ();
-
+ void popup( const std::string& msg);
/**
- * Set the stream buffer
- * @param sb stream buffer
+ * retrieve the contents of the popup message and clear it's internal
+ * content. The return value may be an empty string.
*/
- void set_sb( streambuf* sb );
-
-#ifdef _MSC_VER
- static void has_no_console() { has_console = false; }
-#endif
-
-protected:
-
- /** sync/flush */
- inline virtual int sync();
-
- /** overflow */
- int_type overflow( int ch );
- // int xsputn( const char* s, istreamsize n );
-
-private:
+ std::string get_popup();
- // The streambuf used for actual output. Defaults to cerr.rdbuf().
- static streambuf* sbuf;
-
- static bool logging_enabled;
-#ifdef _MSC_VER
- static bool has_console;
-#endif
- static sgDebugClass logClass;
- static sgDebugPriority logPriority;
-
-private:
-
- // Not defined.
- logbuf( const logbuf& );
- void operator= ( const logbuf& );
-};
-
-inline int
-logbuf::sync()
-{
-#ifdef SG_HAVE_STD_INCLUDES
- return sbuf->pubsync();
-#else
- return sbuf->sync();
-#endif
-}
-
-inline void
-logbuf::set_log_state( sgDebugClass c, sgDebugPriority p )
-{
- logging_enabled = ((c & logClass) != 0 && p >= logPriority);
-}
-
-inline logbuf::int_type
-logbuf::overflow( int c )
-{
-#ifdef _MSC_VER
- if ( logging_enabled ) {
- if ( !has_console ) {
- AllocConsole();
- freopen("conin$", "r", stdin);
- freopen("conout$", "w", stdout);
- freopen("conout$", "w", stderr);
- has_console = true;
- }
- return sbuf->sputc(c);
- }
- else
- return EOF == 0 ? 1: 0;
-#else
- return logging_enabled ? sbuf->sputc(c) : (EOF == 0 ? 1: 0);
-#endif
-}
-
-/**
- * logstream manipulator for setting the log level of a message.
- */
-struct loglevel
-{
- loglevel( sgDebugClass c, sgDebugPriority p )
- : logClass(c), logPriority(p) {}
-
- sgDebugClass logClass;
- sgDebugPriority logPriority;
-};
-
-/**
- * A helper class that ensures a streambuf and ostream are constructed and
- * destroyed in the correct order. The streambuf must be created before the
- * ostream but bases are constructed before members. Thus, making this class
- * a private base of logstream, declared to the left of ostream, we ensure the
- * correct order of construction and destruction.
- */
-struct logstream_base
-{
- // logstream_base( streambuf* sb ) : lbuf(sb) {}
- logstream_base() {}
-
- logbuf lbuf;
-};
-
-/**
- * Class to manage the debug logging stream.
- */
-class logstream : private logstream_base, public ostream
-{
-public:
/**
- * The default is to send messages to cerr.
- * @param out output stream
+ * return true if a new popup message is available. false otherwise.
*/
- logstream( ostream& out )
- // : logstream_base(out.rdbuf()),
- : logstream_base(),
- ostream(&lbuf) { lbuf.set_sb(out.rdbuf());}
-
+ bool has_popup();
+
+ /**
+ * \relates logstream
+ * Return the one and only logstream instance.
+ * We use a function instead of a global object so we are assured that cerr
+ * has been initialised.
+ * @return current logstream
+ */
+ friend logstream& sglog();
+
/**
- * Set the output stream
- * @param out output stream
+ * register a logging callback. Note callbacks are run in a
+ * dedicated thread, so callbacks which pass data to other threads
+ * must use appropriate locking.
*/
- void set_output( ostream& out ) { lbuf.set_sb( out.rdbuf() ); }
+ void addCallback(simgear::LogCallback* cb);
+
+ void removeCallback(simgear::LogCallback* cb);
- /**
- * Set the global log class and priority level.
- * @param c debug class
- * @param p priority
- */
- void setLogLevels( sgDebugClass c, sgDebugPriority p );
+private:
+ // constructor
+ logstream();
- /**
- * Output operator to capture the debug level and priority of a message.
- * @param l log level
- */
- inline ostream& operator<< ( const loglevel& l );
+ std::vector<std::string> popup_msgs;
};
-inline ostream&
-logstream::operator<< ( const loglevel& l )
-{
- lbuf.set_log_state( l.logClass, l.logPriority );
- return *this;
-}
-
-extern logstream *global_logstream;
+logstream& sglog();
-/**
- * \relates logstream
- * Return the one and only logstream instance.
- * We use a function instead of a global object so we are assured that cerr
- * has been initialised.
- * @return current logstream
- */
-inline logstream&
-sglog()
-{
- if (global_logstream == NULL) {
-
-#ifdef __APPLE__
- /**
- * There appears to be a bug in the C++ runtime in Mac OS X that
- * will crash if certain funtions are called (in this case
- * cerr.rdbuf()) during static initialization of a class. This
- * print statement is hack to kick the library in the pants so it
- * won't crash when cerr.rdbuf() is first called -DW
- **/
- cout << "Using Mac OS X hack for initializing C++ stdio..." << endl;
-#endif
- global_logstream = new logstream (cerr);
- }
-
- return *global_logstream;
-}
/** \def SG_LOG(C,P,M)
* @param P priority
* @param M message
*/
+# define SG_LOGX(C,P,M) \
+ do { if(sglog().would_log(C,P)) { \
+ std::ostringstream os; os << M; \
+ sglog().log(C, P, __FILE__, __LINE__, os.str()); \
+ if (P == SG_POPUP) sglog().popup(os.str()); \
+ } } while(0)
#ifdef FG_NDEBUG
-# define SG_LOG(C,P,M)
-#elif defined( __MWERKS__ )
-# define SG_LOG(C,P,M) ::sglog() << ::loglevel(C,P) << M << std::endl
+# define SG_LOG(C,P,M) do { if(P == SG_POPUP) SG_LOGX(C,P,M) } while(0)
#else
-# define SG_LOG(C,P,M) sglog() << loglevel(C,P) << M << endl
+# define SG_LOG(C,P,M) SG_LOGX(C,P,M)
#endif
-#define SG_STRINGIFY(x) #x
-#define SG_TOSTRING(x) SG_STRINGIFY(x)
-#define SG_ORIGIN __FILE__ ":" SG_TOSTRING(__LINE__)
+#define SG_ORIGIN __FILE__ ":" SG_STRINGIZE(__LINE__)
#endif // _LOGSTREAM_H