From Maciej Krol,"With advent of GUI applications in OSG there is a growing need to redirect notification messages to third party systems. For example windows applications do not have console output, it would be appropriate to redirect notifications to GUI widget or debug output. I have revamped notification system to fit this need. New notification stream is using NotifyHandler as a message sink. Handler is called whenever stream is synchronized (i.e. after <<std::endl). Standard streams std::cout and std::cerr are no longer used although by default StandardNotifyHandler is a message sink. Custom notification handler can be set with osg::setNotifyHandler(NotifyHandler *) function.

Two implementations of NotifyHandler are currently available:
- StandardNotifyHandler, calls fputs(message, stderr) for severity <= WARN and fputs(message, stdout) for severity > WARN
- WinDebugNotifyHandler, windows users can redirect notifications to windows debug output, notifications can be viewed in output window of the debugger i.e. MSVC or DebugView (http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx) (see screenshot).

I have seen on osg-users that some people do std::cerr.rdbuf(otherStream.rdbuf()) to redirect notifications. This trick will no longer work since osg::notify() returns internal osg::NotifyStream not std::cout or std::cerr. You can use osg::notify().rdbuf(otherStream.rdbuf()) to do this instead.

Additionally I've made some minor fixes:
- Minor imrovements to osg::notify documentation
- NullStream could crash by deleting stream buffer other than default NullStreamBuffer in the destructor i.e. after osg::notify(osg::DEBUG_FP).rdbuf(otherStream.rdbuf())"
This commit is contained in:
Robert Osfield 2009-05-18 12:04:07 +00:00
parent 7dead3f52d
commit d38fb904d3
2 changed files with 191 additions and 43 deletions

View File

@ -15,6 +15,7 @@
#define OSG_NOTIFY 1
#include <osg/Export>
#include <osg/Referenced> // for NotifyHandler
#include <ostream>
@ -23,8 +24,8 @@ namespace osg {
/** Range of notify levels from DEBUG_FP through to FATAL, ALWAYS
* is reserved for forcing the absorption of all messages. The
* keywords are also used verbatim when specified by the environmental
* variable OSGNOTIFYLEVEL. See documentation on osg::notify() for
* further details.
* variable OSGNOTIFYLEVEL or OSG_NOTIFY_LEVEL.
* See documentation on osg::notify() for further details.
*/
enum NotifySeverity {
ALWAYS=0,
@ -37,7 +38,7 @@ enum NotifySeverity {
};
/** set the notify level, overriding the default or the value set by
* the environmental variable OSGNOTIFYLEVEL.
* the environmental variable OSGNOTIFYLEVEL or OSG_NOTIFY_LEVEL.
*/
extern OSG_EXPORT void setNotifyLevel(NotifySeverity severity);
@ -53,22 +54,79 @@ extern OSG_EXPORT bool initNotifyLevel();
/** notify messaging function for providing fatal through to verbose
* debugging messages. Level of messages sent to the console can
* be controlled by setting the NotifyLevel either within your
* application or via the an environmental variable. For instance
* setenv OSGNOTIFYLEVEL DEBUG (for tsh), export OSGNOTIFYLEVEL=DEBUG
* (for bourne shell) or set OSGNOTIFYLEVEL=DEBUG (for Windows) all
* tell the osg to redirect all debugging and more important messages
* to the console (useful for debugging :-) setting ALWAYS will force
* application or via the an environmental variable i.e.
* - setenv OSGNOTIFYLEVEL DEBUG (for tsh)
* - export OSGNOTIFYLEVEL=DEBUG (for bourne shell)
* - set OSGNOTIFYLEVEL=DEBUG (for Windows)
*
* All tell the osg to redirect all debugging and more important messages
* to the notification stream (useful for debugging) setting ALWAYS will force
* all messages to be absorbed, which might be appropriate for final
* applications. Default NotifyLevel is NOTICE. Check the enum
* NotifySeverity for full range of possibilities. To use the notify
* #NotifySeverity for full range of possibilities. To use the notify
* with your code simply use the notify function as a normal file
* stream (like cout) i.e osg::notify(osg::DEBUG) << "Hello Bugs!"<<endl;
* stream (like std::cout) i.e
* @code
* osg::notify(osg::DEBUG) << "Hello Bugs!" << std::endl;
* @endcode
* @see setNotifyLevel, setNotifyHandler
*/
extern OSG_EXPORT std::ostream& notify(const NotifySeverity severity);
inline std::ostream& notify(void) { return notify(osg::INFO); }
/** Handler processing output of notification stream. It acts as a sink to
* notification messages. It is called when notification stream needs to be
* synchronized (i.e. after osg::notify() << std::endl).
* StandardNotifyHandler is used by default, it writes notifications to stderr
* (severity <= WARN) or stdout (severity > WARN).
* Notifications can be redirected to other sinks such as GUI widgets or
* windows debugger (WinDebugNotifyHandler) with custom handlers.
* Use setNotifyHandler to set custom handler.
* Note that osg notification API is not thread safe although notification
* handler is called from many threads. When incorporating handlers into GUI
* widgets you must take care of thread safety on your own.
* @see setNotifyHandler
*/
class OSG_EXPORT NotifyHandler : public osg::Referenced
{
public:
virtual void notify(osg::NotifySeverity severity, const char *message) = 0;
};
/** Set notification handler, by default StandardNotifyHandler is used.
* @see NotifyHandler
*/
extern OSG_EXPORT void setNotifyHandler(NotifyHandler *handler);
/** Get currrent notification handler. */
extern OSG_EXPORT NotifyHandler *getNotifyHandler();
/** Redirects notification stream to stderr (severity <= WARN) or stdout (severity > WARN).
* The fputs() function is used to write messages to standard files. Note that
* std::out and std::cerr streams are not used.
* @see setNotifyHandler
*/
class OSG_EXPORT StandardNotifyHandler : public NotifyHandler
{
public:
void notify(osg::NotifySeverity severity, const char *message);
};
#if defined(WIN32) && !defined(__CYGWIN__)
/** Redirects notification stream to windows debugger with use of
* OuputDebugString functions.
* @see setNotifyHandler
*/
class OSG_EXPORT WinDebugNotifyHandler : public NotifyHandler
{
public:
void notify(osg::NotifySeverity severity, const char *message);
};
#endif
}
#endif

View File

@ -12,16 +12,95 @@
*/
#include <osg/Notify>
#include <osg/ApplicationUsage>
#include <osg/ref_ptr>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include <sstream>
#include <iostream>
#include <fstream>
using namespace std;
namespace osg
{
class NullStreamBuffer : public std::streambuf
{
private:
std::streamsize xsputn(const std::streambuf::char_type *str, std::streamsize n)
{
return n;
}
};
struct NullStream : public std::ostream
{
public:
NullStream():
std::ostream(&_buffer) {}
protected:
NullStreamBuffer _buffer;
};
/** Stream buffer calling notify handler when buffer is synchronized (usually on std::endl).
* Stream stores last notification severity to pass it to handler call.
*/
struct NotifyStreamBuffer : public std::stringbuf
{
NotifyStreamBuffer() : _severity(osg::NOTICE)
{
}
void setNotifyHandler(osg::NotifyHandler *handler) { _handler = handler; }
osg::NotifyHandler *getNotifyHandler() const { return _handler.get(); }
/** Sets severity for next call of notify handler */
void setCurrentSeverity(osg::NotifySeverity severity) { _severity = severity; }
osg::NotifySeverity getCurrentSeverity() const { return _severity; }
private:
int sync()
{
sputc(0); // string termination
if (_handler.valid())
_handler->notify(_severity, pbase());
pubseekpos(0, std::ios_base::out); // or str(std::string())
return 0;
}
osg::ref_ptr<osg::NotifyHandler> _handler;
osg::NotifySeverity _severity;
};
struct NotifyStream : public std::ostream
{
public:
NotifyStream():
std::ostream(&_buffer) {}
void setCurrentSeverity(osg::NotifySeverity severity)
{
_buffer.setCurrentSeverity(severity);
}
osg::NotifySeverity getCurrentSeverity() const
{
return _buffer.getCurrentSeverity();
}
protected:
NotifyStreamBuffer _buffer;
};
}
using namespace osg;
static osg::ApplicationUsageProxy Notify_e0(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE, "OSG_NOTIFY_LEVEL <mode>", "FATAL | WARN | NOTICE | DEBUG_INFO | DEBUG_FP | DEBUG | INFO | ALWAYS");
osg::NotifySeverity g_NotifyLevel = osg::NOTICE;
osg::NullStream g_NullStream;
osg::NotifyStream g_NotifyStream;
void osg::setNotifyLevel(osg::NotifySeverity severity)
{
@ -36,6 +115,19 @@ osg::NotifySeverity osg::getNotifyLevel()
return g_NotifyLevel;
}
void osg::setNotifyHandler(osg::NotifyHandler *handler)
{
osg::NotifyStreamBuffer *buffer = static_cast<osg::NotifyStreamBuffer *>(g_NotifyStream.rdbuf());
if (buffer)
buffer->setNotifyHandler(handler);
}
osg::NotifyHandler *getNotifyHandler()
{
osg::initNotifyLevel();
osg::NotifyStreamBuffer *buffer = static_cast<osg::NotifyStreamBuffer *>(g_NotifyStream.rdbuf());
return buffer ? buffer->getNotifyHandler() : 0;
}
bool osg::initNotifyLevel()
{
@ -75,6 +167,11 @@ bool osg::initNotifyLevel()
}
// Setup standard notify handler
osg::NotifyStreamBuffer *buffer = dynamic_cast<osg::NotifyStreamBuffer *>(g_NotifyStream.rdbuf());
if (buffer && !buffer->getNotifyHandler())
buffer->setNotifyHandler(new StandardNotifyHandler);
s_NotifyInit = true;
return true;
@ -86,45 +183,38 @@ bool osg::isNotifyEnabled( osg::NotifySeverity severity )
return severity<=g_NotifyLevel;
}
class NullStreamBuffer : public std::streambuf
{
private:
virtual streamsize xsputn (const char_type*, streamsize n)
{
return n;
}
};
struct NullStream : public std::ostream
{
NullStream():
std::ostream(new NullStreamBuffer) {}
virtual ~NullStream()
{
delete rdbuf();
rdbuf(0);
}
};
std::ostream& osg::notify(const osg::NotifySeverity severity)
{
// set up global notify null stream for inline notify
static NullStream s_NotifyNulStream;
static bool initialized = false;
if (!initialized)
{
std::cerr<<""; // dummy op to force construction of cerr, before a reference is passed back to calling code.
std::cout<<""; // dummy op to force construction of cout, before a reference is passed back to calling code.
initialized = osg::initNotifyLevel();
}
if (severity<=g_NotifyLevel)
{
if (severity<=osg::WARN) return std::cerr;
else return std::cout;
g_NotifyStream.setCurrentSeverity(severity);
return g_NotifyStream;
}
return s_NotifyNulStream;
return g_NullStream;
}
void osg::StandardNotifyHandler::notify(osg::NotifySeverity severity, const char *message)
{
if (severity <= osg::WARN)
fputs(message, stderr);
else
fputs(message, stdout);
}
#if defined(WIN32) && !defined(__CYGWIN__)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
void osg::WinDebugNotifyHandler::notify(osg::NotifySeverity severity, const char *message)
{
OutputDebugStringA(message);
}
#endif