nuclear@0: /* nuclear@0: --------------------------------------------------------------------------- nuclear@0: Open Asset Import Library (assimp) nuclear@0: --------------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the following nuclear@0: conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: --------------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** @file DefaultLogger.cpp nuclear@0: * @brief Implementation of DefaultLogger (and Logger) nuclear@0: */ nuclear@0: nuclear@0: #include "AssimpPCH.h" nuclear@0: #include "DefaultIOSystem.h" nuclear@0: nuclear@0: // Default log streams nuclear@0: #include "Win32DebugLogStream.h" nuclear@0: #include "StdOStreamLogStream.h" nuclear@0: #include "FileLogStream.h" nuclear@0: nuclear@0: #ifndef ASSIMP_BUILD_SINGLETHREADED nuclear@0: # include nuclear@0: # include nuclear@0: nuclear@0: boost::mutex loggerMutex; nuclear@0: #endif nuclear@0: nuclear@0: namespace Assimp { nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: NullLogger DefaultLogger::s_pNullLogger; nuclear@0: Logger *DefaultLogger::m_pLogger = &DefaultLogger::s_pNullLogger; nuclear@0: nuclear@0: static const unsigned int SeverityAll = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging; nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: // Represents a log-stream + its error severity nuclear@0: struct LogStreamInfo nuclear@0: { nuclear@0: unsigned int m_uiErrorSeverity; nuclear@0: LogStream *m_pStream; nuclear@0: nuclear@0: // Constructor nuclear@0: LogStreamInfo( unsigned int uiErrorSev, LogStream *pStream ) : nuclear@0: m_uiErrorSeverity( uiErrorSev ), nuclear@0: m_pStream( pStream ) nuclear@0: { nuclear@0: // empty nuclear@0: } nuclear@0: nuclear@0: // Destructor nuclear@0: ~LogStreamInfo() nuclear@0: { nuclear@0: delete m_pStream; nuclear@0: } nuclear@0: }; nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: // Construct a default log stream nuclear@0: LogStream* LogStream::createDefaultStream(aiDefaultLogStream streams, nuclear@0: const char* name /*= "AssimpLog.txt"*/, nuclear@0: IOSystem* io /*= NULL*/) nuclear@0: { nuclear@0: switch (streams) nuclear@0: { nuclear@0: // This is a platform-specific feature nuclear@0: case aiDefaultLogStream_DEBUGGER: nuclear@0: #ifdef WIN32 nuclear@0: return new Win32DebugLogStream(); nuclear@0: #else nuclear@0: return NULL; nuclear@0: #endif nuclear@0: nuclear@0: // Platform-independent default streams nuclear@0: case aiDefaultLogStream_STDERR: nuclear@0: return new StdOStreamLogStream(std::cerr); nuclear@0: case aiDefaultLogStream_STDOUT: nuclear@0: return new StdOStreamLogStream(std::cout); nuclear@0: case aiDefaultLogStream_FILE: nuclear@0: return (name && *name ? new FileLogStream(name,io) : NULL); nuclear@0: default: nuclear@0: // We don't know this default log stream, so raise an assertion nuclear@0: ai_assert(false); nuclear@0: nuclear@0: }; nuclear@0: nuclear@0: // For compilers without dead code path detection nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: // Creates the only singleton instance nuclear@0: Logger *DefaultLogger::create(const char* name /*= "AssimpLog.txt"*/, nuclear@0: LogSeverity severity /*= NORMAL*/, nuclear@0: unsigned int defStreams /*= aiDefaultLogStream_DEBUGGER | aiDefaultLogStream_FILE*/, nuclear@0: IOSystem* io /*= NULL*/) nuclear@0: { nuclear@0: // enter the mutex here to avoid concurrency problems nuclear@0: #ifndef ASSIMP_BUILD_SINGLETHREADED nuclear@0: boost::mutex::scoped_lock lock(loggerMutex); nuclear@0: #endif nuclear@0: nuclear@0: if (m_pLogger && !isNullLogger() ) nuclear@0: delete m_pLogger; nuclear@0: nuclear@0: m_pLogger = new DefaultLogger( severity ); nuclear@0: nuclear@0: // Attach default log streams nuclear@0: // Stream the log to the MSVC debugger? nuclear@0: if (defStreams & aiDefaultLogStream_DEBUGGER) nuclear@0: m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_DEBUGGER)); nuclear@0: nuclear@0: // Stream the log to COUT? nuclear@0: if (defStreams & aiDefaultLogStream_STDOUT) nuclear@0: m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_STDOUT)); nuclear@0: nuclear@0: // Stream the log to CERR? nuclear@0: if (defStreams & aiDefaultLogStream_STDERR) nuclear@0: m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_STDERR)); nuclear@0: nuclear@0: // Stream the log to a file nuclear@0: if (defStreams & aiDefaultLogStream_FILE && name && *name) nuclear@0: m_pLogger->attachStream( LogStream::createDefaultStream(aiDefaultLogStream_FILE,name,io)); nuclear@0: nuclear@0: return m_pLogger; nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: void Logger::debug(const char* message) { nuclear@0: nuclear@0: // SECURITY FIX: otherwise it's easy to produce overruns since nuclear@0: // sometimes importers will include data from the input file nuclear@0: // (i.e. node names) in their messages. nuclear@0: if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) { nuclear@0: ai_assert(false); nuclear@0: return; nuclear@0: } nuclear@0: return OnDebug(message); nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: void Logger::info(const char* message) { nuclear@0: nuclear@0: // SECURITY FIX: see above nuclear@0: if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) { nuclear@0: ai_assert(false); nuclear@0: return; nuclear@0: } nuclear@0: return OnInfo(message); nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: void Logger::warn(const char* message) { nuclear@0: nuclear@0: // SECURITY FIX: see above nuclear@0: if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) { nuclear@0: ai_assert(false); nuclear@0: return; nuclear@0: } nuclear@0: return OnWarn(message); nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: void Logger::error(const char* message) { nuclear@0: nuclear@0: // SECURITY FIX: see above nuclear@0: if (strlen(message)>MAX_LOG_MESSAGE_LENGTH) { nuclear@0: ai_assert(false); nuclear@0: return; nuclear@0: } nuclear@0: return OnError(message); nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: void DefaultLogger::set( Logger *logger ) nuclear@0: { nuclear@0: // enter the mutex here to avoid concurrency problems nuclear@0: #ifndef ASSIMP_BUILD_SINGLETHREADED nuclear@0: boost::mutex::scoped_lock lock(loggerMutex); nuclear@0: #endif nuclear@0: nuclear@0: if (!logger)logger = &s_pNullLogger; nuclear@0: if (m_pLogger && !isNullLogger() ) nuclear@0: delete m_pLogger; nuclear@0: nuclear@0: DefaultLogger::m_pLogger = logger; nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: bool DefaultLogger::isNullLogger() nuclear@0: { nuclear@0: return m_pLogger == &s_pNullLogger; nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: // Singleton getter nuclear@0: Logger *DefaultLogger::get() nuclear@0: { nuclear@0: return m_pLogger; nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: // Kills the only instance nuclear@0: void DefaultLogger::kill() nuclear@0: { nuclear@0: // enter the mutex here to avoid concurrency problems nuclear@0: #ifndef ASSIMP_BUILD_SINGLETHREADED nuclear@0: boost::mutex::scoped_lock lock(loggerMutex); nuclear@0: #endif nuclear@0: nuclear@0: if (m_pLogger == &s_pNullLogger)return; nuclear@0: delete m_pLogger; nuclear@0: m_pLogger = &s_pNullLogger; nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: // Debug message nuclear@0: void DefaultLogger::OnDebug( const char* message ) nuclear@0: { nuclear@0: if ( m_Severity == Logger::NORMAL ) nuclear@0: return; nuclear@0: nuclear@0: char msg[MAX_LOG_MESSAGE_LENGTH*2]; nuclear@0: ::sprintf(msg,"Debug, T%i: %s", GetThreadID(), message ); nuclear@0: nuclear@0: WriteToStreams( msg, Logger::Debugging ); nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: // Logs an info nuclear@0: void DefaultLogger::OnInfo( const char* message ) nuclear@0: { nuclear@0: char msg[MAX_LOG_MESSAGE_LENGTH*2]; nuclear@0: ::sprintf(msg,"Info, T%i: %s", GetThreadID(), message ); nuclear@0: nuclear@0: WriteToStreams( msg , Logger::Info ); nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: // Logs a warning nuclear@0: void DefaultLogger::OnWarn( const char* message ) nuclear@0: { nuclear@0: char msg[MAX_LOG_MESSAGE_LENGTH*2]; nuclear@0: ::sprintf(msg,"Warn, T%i: %s", GetThreadID(), message ); nuclear@0: nuclear@0: WriteToStreams( msg, Logger::Warn ); nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: // Logs an error nuclear@0: void DefaultLogger::OnError( const char* message ) nuclear@0: { nuclear@0: char msg[MAX_LOG_MESSAGE_LENGTH*2]; nuclear@0: ::sprintf(msg,"Error, T%i: %s", GetThreadID(), message ); nuclear@0: nuclear@0: WriteToStreams( msg, Logger::Err ); nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: // Will attach a new stream nuclear@0: bool DefaultLogger::attachStream( LogStream *pStream, unsigned int severity ) nuclear@0: { nuclear@0: if (!pStream) nuclear@0: return false; nuclear@0: nuclear@0: if (0 == severity) { nuclear@0: severity = Logger::Info | Logger::Err | Logger::Warn | Logger::Debugging; nuclear@0: } nuclear@0: nuclear@0: for ( StreamIt it = m_StreamArray.begin(); nuclear@0: it != m_StreamArray.end(); nuclear@0: ++it ) nuclear@0: { nuclear@0: if ( (*it)->m_pStream == pStream ) nuclear@0: { nuclear@0: (*it)->m_uiErrorSeverity |= severity; nuclear@0: return true; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: LogStreamInfo *pInfo = new LogStreamInfo( severity, pStream ); nuclear@0: m_StreamArray.push_back( pInfo ); nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: // Detatch a stream nuclear@0: bool DefaultLogger::detatchStream( LogStream *pStream, unsigned int severity ) nuclear@0: { nuclear@0: if (!pStream) nuclear@0: return false; nuclear@0: nuclear@0: if (0 == severity) { nuclear@0: severity = SeverityAll; nuclear@0: } nuclear@0: nuclear@0: for ( StreamIt it = m_StreamArray.begin(); nuclear@0: it != m_StreamArray.end(); nuclear@0: ++it ) nuclear@0: { nuclear@0: if ( (*it)->m_pStream == pStream ) nuclear@0: { nuclear@0: (*it)->m_uiErrorSeverity &= ~severity; nuclear@0: if ( (*it)->m_uiErrorSeverity == 0 ) nuclear@0: { nuclear@0: // don't delete the underlying stream 'cause the caller gains ownership again nuclear@0: (**it).m_pStream = NULL; nuclear@0: delete *it; nuclear@0: m_StreamArray.erase( it ); nuclear@0: break; nuclear@0: } nuclear@0: return true; nuclear@0: } nuclear@0: } nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: // Constructor nuclear@0: DefaultLogger::DefaultLogger(LogSeverity severity) nuclear@0: nuclear@0: : Logger ( severity ) nuclear@0: , noRepeatMsg (false) nuclear@0: , lastLen( 0 ) nuclear@0: { nuclear@0: lastMsg[0] = '\0'; nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: // Destructor nuclear@0: DefaultLogger::~DefaultLogger() nuclear@0: { nuclear@0: for ( StreamIt it = m_StreamArray.begin(); it != m_StreamArray.end(); ++it ) { nuclear@0: // also frees the underlying stream, we are its owner. nuclear@0: delete *it; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: // Writes message to stream nuclear@0: void DefaultLogger::WriteToStreams(const char *message, nuclear@0: ErrorSeverity ErrorSev ) nuclear@0: { nuclear@0: ai_assert(NULL != message); nuclear@0: nuclear@0: // Check whether this is a repeated message nuclear@0: if (! ::strncmp( message,lastMsg, lastLen-1)) nuclear@0: { nuclear@0: if (!noRepeatMsg) nuclear@0: { nuclear@0: noRepeatMsg = true; nuclear@0: message = "Skipping one or more lines with the same contents\n"; nuclear@0: } nuclear@0: else return; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: // append a new-line character to the message to be printed nuclear@0: lastLen = ::strlen(message); nuclear@0: ::memcpy(lastMsg,message,lastLen+1); nuclear@0: ::strcat(lastMsg+lastLen,"\n"); nuclear@0: nuclear@0: message = lastMsg; nuclear@0: noRepeatMsg = false; nuclear@0: ++lastLen; nuclear@0: } nuclear@0: for ( ConstStreamIt it = m_StreamArray.begin(); nuclear@0: it != m_StreamArray.end(); nuclear@0: ++it) nuclear@0: { nuclear@0: if ( ErrorSev & (*it)->m_uiErrorSeverity ) nuclear@0: (*it)->m_pStream->write( message); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: // Returns thread id, if not supported only a zero will be returned. nuclear@0: unsigned int DefaultLogger::GetThreadID() nuclear@0: { nuclear@0: // fixme: we can get this value via boost::threads nuclear@0: #ifdef WIN32 nuclear@0: return (unsigned int)::GetCurrentThreadId(); nuclear@0: #else nuclear@0: return 0; // not supported nuclear@0: #endif nuclear@0: } nuclear@0: nuclear@0: // ---------------------------------------------------------------------------------- nuclear@0: nuclear@0: } // !namespace Assimp