LCOV - code coverage report
Current view: top level - lunchbox - log.cpp (source / functions) Hit Total Coverage
Test: Lunchbox Lines: 142 224 63.4 %
Date: 2016-11-11 05:21:33 Functions: 34 53 64.2 %

          Line data    Source code
       1             : 
       2             : /* Copyright (c) 2005-2013, Stefan Eilemann <eile@equalizergraphics.com>
       3             :  *               2011-2012, Daniel Nachbaur <danielnachbaur@gmail.com>
       4             :  *
       5             :  * This library is free software; you can redistribute it and/or modify it under
       6             :  * the terms of the GNU Lesser General Public License version 2.1 as published
       7             :  * by the Free Software Foundation.
       8             :  *
       9             :  * This library is distributed in the hope that it will be useful, but WITHOUT
      10             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
      11             :  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
      12             :  * details.
      13             :  *
      14             :  * You should have received a copy of the GNU Lesser General Public License
      15             :  * along with this library; if not, write to the Free Software Foundation, Inc.,
      16             :  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      17             :  */
      18             : 
      19             : #include <pthread.h>
      20             : 
      21             : #include "log.h"
      22             : 
      23             : #include "clock.h"
      24             : #include "perThread.h"
      25             : #include "scopedMutex.h"
      26             : #include "thread.h"
      27             : 
      28             : #include <cstdio>
      29             : #include <cstdlib>
      30             : #include <fstream>
      31             : 
      32             : #ifdef _MSC_VER
      33             : #  include <process.h>
      34             : #  define atoll _atoi64
      35             : #  define snprintf _snprintf
      36             : #  define getpid _getpid
      37             : #else
      38             : #  include <unistd.h>
      39             : #endif
      40             : 
      41             : namespace lunchbox
      42             : {
      43             : static unsigned getLogTopics();
      44          24 : static Clock    _defaultClock;
      45             : static Clock*   _clock = &_defaultClock;
      46          24 : static Lock     _lock; // The write lock
      47             : 
      48             : const size_t LENGTH_PID = 5;
      49             : const size_t LENGTH_THREAD = 8;
      50             : const size_t LENGTH_FILE = 29;
      51             : const size_t LENGTH_TIME = 6;
      52             : 
      53             : namespace detail
      54             : {
      55             : /** @internal The string buffer used for logging. */
      56             : class Log : public std::streambuf
      57             : {
      58             : public:
      59        1671 :     explicit Log( std::ostream& stream )
      60        1671 :         : _indent(0), _blocked(0), _noHeader(0),
      61        1671 :           _newLine(true), _stream(stream)
      62             :     {
      63        1671 :         _file[0] = 0;
      64        1671 :         setThreadName( "Unknown" );
      65        1671 :     }
      66             : 
      67        3342 :     virtual ~Log() {}
      68             : 
      69           0 :     void indent() { ++_indent; }
      70           0 :     void exdent() { --_indent; }
      71             : 
      72          23 :     void disableFlush() { ++_blocked; assert( _blocked < 100 ); }
      73          23 :     void enableFlush()
      74             :     {
      75          23 :         assert( _blocked );// Too many enableFlush on log stream
      76          23 :         --_blocked;
      77          23 :     }
      78             : 
      79          23 :     void disableHeader() { ++_noHeader; } // use counted variable to allow
      80          23 :     void enableHeader()  { --_noHeader; } //   nested enable/disable calls
      81             : 
      82        3358 :     void setThreadName( const std::string& name )
      83             :     {
      84        3358 :         LBASSERT( !name.empty( ));
      85        3358 :         _thread = name.substr( 0, LENGTH_THREAD );
      86        3358 :     }
      87             : 
      88           0 :     const std::string& getThreadName() const { return _thread; }
      89             : 
      90        1678 :     void setLogInfo( const char* f, const int line )
      91             :     {
      92        1678 :         LBASSERT( f );
      93        3356 :         std::string file( f );
      94        1678 :         const size_t length = file.length();
      95             : 
      96        1678 :         if( length > LENGTH_FILE )
      97        1678 :             file = file.substr( length - LENGTH_FILE, length );
      98             : 
      99        1678 :         snprintf( _file, LENGTH_FILE+6, "%29s:%-4d", file.c_str(), line );
     100        1678 :     }
     101             : 
     102             : protected:
     103       86143 :     int_type overflow( Log::int_type c ) override
     104             :     {
     105       86143 :         if( c == EOF )
     106           0 :             return EOF;
     107             : 
     108       86143 :         if( _newLine )
     109             :         {
     110        1701 :             if( !_noHeader )
     111             :             {
     112        1678 :                 if( lunchbox::Log::level > LOG_INFO )
     113        1678 :                     _stringStream << std::right << std::setw( LENGTH_PID )
     114        3356 :                                   << getpid() << "."
     115        1678 :                                   << std::left << std::setw( LENGTH_THREAD )
     116        3356 :                                   << _thread << " " << _file << " "
     117        3356 :                                   << std::right << std::setw( LENGTH_TIME )
     118        3356 :                                   << _clock->getTime64() << " ";
     119             :                 else
     120           0 :                     _stringStream << std::right << std::setw( LENGTH_TIME )
     121           0 :                                   << _clock->getTime64() << " ";
     122             :             }
     123             : 
     124        1701 :             for( int i=0; i<_indent; ++i )
     125           0 :                 _stringStream << "    ";
     126        1701 :             _newLine = false;
     127             :         }
     128             : 
     129       86143 :         _stringStream << (char)c;
     130       86143 :         return c;
     131             :     }
     132             : 
     133        5014 :     int sync() override
     134             :     {
     135        5014 :         if( !_blocked )
     136             :         {
     137        9982 :             const std::string& string = _stringStream.str();
     138             :             {
     139        9982 :                 ScopedMutex< lunchbox::Lock > mutex( _lock );
     140        4991 :                 _stream.write( string.c_str(), string.length( ));
     141        4989 :                 _stream.rdbuf()->pubsync();
     142             :             }
     143        4989 :             _stringStream.str( "" );
     144             :         }
     145        5012 :         _newLine = true;
     146        5012 :         return 0;
     147             :     }
     148             : 
     149             : private:
     150             :     Log( const Log& );
     151             :     Log& operator = ( const Log& );
     152             : 
     153             :     /** Short thread name. */
     154             :     std::string _thread;
     155             : 
     156             :     /** The current file logging. */
     157             :     char _file[35];
     158             : 
     159             :     /** The current indentation level. */
     160             :     int _indent;
     161             : 
     162             :     /** Flush reference counter. */
     163             :     int _blocked;
     164             : 
     165             :     /** The header disable counter. */
     166             :     int _noHeader;
     167             : 
     168             :     /** The flag that a new line has started. */
     169             :     bool _newLine;
     170             : 
     171             :     /** The temporary buffer. */
     172             :     std::ostringstream _stringStream;
     173             : 
     174             :     /** The wrapped ostream. */
     175             :     std::ostream& _stream;
     176             : };
     177             : }
     178             : 
     179             : namespace
     180             : {
     181         144 : class LogTable
     182             : {
     183             : public:
     184         144 :     LogTable( const LogLevel _level, const std::string& _name )
     185         144 :             : level( _level ), name( _name ) {}
     186             : 
     187             :     LogLevel level;
     188             :     std::string name;
     189             : };
     190             : 
     191             : #define LOG_TABLE_ENTRY( name ) LogTable( LOG_ ## name, std::string( #name ))
     192             : #define LOG_TABLE_SIZE (6)
     193             : 
     194          48 : static LogTable _logTable[ LOG_TABLE_SIZE ] =
     195             : {
     196          48 :     LOG_TABLE_ENTRY( ERROR ),
     197             :     LogTable( LOG_ERROR, "WARN" ),
     198          48 :     LOG_TABLE_ENTRY( INFO ),
     199          48 :     LOG_TABLE_ENTRY( DEBUG ),
     200          48 :     LOG_TABLE_ENTRY( VERB ),
     201          48 :     LOG_TABLE_ENTRY( ALL )
     202         264 : };
     203             : }
     204             : 
     205          24 : int      Log::level  = Log::getLogLevel( getenv( "LB_LOG_LEVEL" ));
     206          24 : unsigned Log::topics = getLogTopics();
     207             : 
     208          24 : static PerThread< Log > _logInstance;
     209             : 
     210             : #ifdef NDEBUG
     211             : static std::ostream* _logStream = &std::cout;
     212             : #else
     213             : static std::ostream* _logStream = &std::cerr;
     214             : #endif
     215             : static std::ostream* _logFile = 0;
     216             : 
     217        1671 : Log::Log()
     218        1671 :     : std::ostream( new detail::Log( getOutput( )))
     219        3342 :     , impl_( dynamic_cast< detail::Log* >( rdbuf( )))
     220        1671 : {}
     221             : 
     222        5013 : Log::~Log()
     223             : {
     224        1671 :     impl_->pubsync();
     225        1671 :     delete impl_;
     226        3340 : }
     227             : 
     228           0 : void Log::indent()
     229             : {
     230           0 :     impl_->indent();
     231           0 : }
     232             : 
     233           0 : void Log::exdent()
     234             : {
     235           0 :     impl_->exdent();
     236           0 : }
     237             : 
     238          23 : void Log::disableFlush()
     239             : {
     240          23 :     impl_->disableFlush();
     241          23 : }
     242             : 
     243          23 : void Log::enableFlush()
     244             : {
     245          23 :     impl_->enableFlush();
     246          23 : }
     247             : 
     248        1642 : void Log::forceFlush()
     249             : {
     250        1642 :     impl_->pubsync();
     251        1642 : }
     252             : 
     253          23 : void Log::disableHeader()
     254             : {
     255          23 :     impl_->disableHeader();
     256          23 : }
     257             : 
     258          23 : void Log::enableHeader()
     259             : {
     260          23 :     impl_->enableHeader();
     261          23 : }
     262             : 
     263        1678 : void Log::setLogInfo( const char* file, const int line )
     264             : {
     265        1678 :     impl_->setLogInfo( file, line );
     266        1678 : }
     267             : 
     268        1687 : void Log::setThreadName( const std::string& name )
     269             : {
     270        1687 :     impl_->setThreadName( name );
     271        1687 : }
     272             : 
     273           0 : const std::string& Log::getThreadName() const
     274             : {
     275           0 :     return impl_->getThreadName();
     276             : }
     277             : 
     278          48 : int Log::getLogLevel( const char* text )
     279             : {
     280          48 :     if( text )
     281             :     {
     282           0 :         const int num = atoi( text );
     283           0 :         if( num > 0 && num <= LOG_ALL )
     284           0 :             return num;
     285             : 
     286           0 :         for( uint32_t i = 0; i < LOG_TABLE_SIZE; ++i )
     287           0 :             if( _logTable[i].name == text )
     288           0 :                 return _logTable[i].level;
     289             :     }
     290             : 
     291             : #ifdef NDEBUG
     292             :     return LOG_INFO;
     293             : #else
     294          48 :     return LOG_DEBUG;
     295             : #endif
     296             : }
     297             : 
     298           4 : std::string& Log::getLogLevelString()
     299             : {
     300          16 :     for( uint32_t i=0; i<LOG_TABLE_SIZE; ++i )
     301          16 :         if( _logTable[i].level == level )
     302           4 :                 return _logTable[i].name;
     303             : 
     304           0 :     return _logTable[0].name;
     305             : }
     306             : 
     307          24 : unsigned getLogTopics()
     308             : {
     309          24 :     Log::level = Log::getLogLevel( getenv( "LB_LOG_LEVEL" ));
     310          24 :     const char *env = getenv( "LB_LOG_TOPICS" );
     311             : 
     312          24 :     if( env )
     313           0 :         return atoll(env);
     314             : 
     315          24 :     if( Log::level == LOG_ALL )
     316           0 :         return LOG_ANY;
     317             : 
     318             : #ifdef NDEBUG
     319             :     return 0;
     320             : #else
     321          24 :     return LOG_BUG;
     322             : #endif
     323             : }
     324             : 
     325        6642 : Log& Log::instance()
     326             : {
     327        6642 :     Log* log = _logInstance.get();
     328        6637 :     if( !log )
     329             :     {
     330        1671 :         log = new Log();
     331        1671 :         _logInstance = log;
     332             :         static bool first = true;
     333        1671 :         if( first && lunchbox::Log::level > LOG_INFO )
     334             :         {
     335          23 :             first = false;
     336          23 :             log->disableHeader();
     337          23 :             log->disableFlush();
     338          23 :             *log << std::setw( LENGTH_PID ) << std::right << "PID" << "."
     339          46 :                  << std::setw( LENGTH_THREAD ) << std::left << "Thread " << "|"
     340          46 :                  << std::setw( LENGTH_FILE+5 ) << " Filename:line " << "|"
     341          23 :                  << std::right << std::setw( LENGTH_TIME ) << " ms " << "|"
     342          46 :                  << " Message" << std::endl;
     343          23 :             log->enableFlush();
     344          23 :             log->enableHeader();
     345             :         }
     346             :     }
     347             : 
     348        6637 :     return *log;
     349             : }
     350             : 
     351        1678 : Log& Log::instance( const char* file, const int line )
     352             : {
     353        1678 :     Log& log = instance();
     354        1678 :     log.setLogInfo( file, line );
     355        1678 :     return log;
     356             : }
     357             : 
     358        1647 : void Log::exit()
     359             : {
     360        1647 :     Log* log = _logInstance.get();
     361        1647 :     _logInstance = 0;
     362        1647 :     delete log;
     363        1646 : }
     364             : 
     365           5 : void Log::reset()
     366             : {
     367           5 :     exit();
     368             : 
     369           5 :     delete _logFile;
     370           5 :     _logFile = 0;
     371             : 
     372             : #ifdef NDEBUG
     373             :     _logStream = &std::cout;
     374             : #else
     375           5 :     _logStream = &std::cerr;
     376             : #endif
     377           5 : }
     378             : 
     379           0 : void Log::setOutput( std::ostream& stream )
     380             : {
     381           0 :     _logStream = &stream;
     382           0 :     exit();
     383           0 : }
     384             : 
     385           0 : bool Log::setOutput( const std::string& file )
     386             : {
     387           0 :     std::ofstream* newLog = new std::ofstream( file.c_str( ));
     388             : 
     389           0 :     if( !newLog->is_open( ))
     390             :     {
     391           0 :         LBERROR << "Can't open log file " << file << ": " << sysError
     392           0 :                 << std::endl;
     393           0 :         delete newLog;
     394           0 :         return false;
     395             :     }
     396             : 
     397           0 :     LBDEBUG << "Redirect log to " << file << std::endl;
     398           0 :     setOutput( *newLog );
     399             : 
     400           0 :     delete _logFile;
     401           0 :     _logFile = newLog;
     402           0 :     return true;
     403             : }
     404             : 
     405           0 : void Log::setClock( Clock* clock )
     406             : {
     407           0 :     if( clock )
     408           0 :         _clock = clock;
     409             :     else
     410           0 :         _clock = &_defaultClock;
     411           0 : }
     412             : 
     413           0 : const Clock& Log::getClock()
     414             : {
     415           0 :     return *_clock;
     416             : }
     417             : 
     418        1671 : std::ostream& Log::getOutput()
     419             : {
     420        1671 :     return *_logStream;
     421             : }
     422             : 
     423           0 : std::ostream& indent( std::ostream& os )
     424             : {
     425           0 :     Log* log = dynamic_cast<Log*>(&os);
     426           0 :     if( log )
     427           0 :         log->indent();
     428           0 :     return os;
     429             : }
     430           0 : std::ostream& exdent( std::ostream& os )
     431             : {
     432           0 :     Log* log = dynamic_cast<Log*>(&os);
     433           0 :     if( log )
     434           0 :         log->exdent();
     435           0 :     return os;
     436             : }
     437             : 
     438           0 : std::ostream& disableFlush( std::ostream& os )
     439             : {
     440           0 :     Log* log = dynamic_cast<Log*>(&os);
     441           0 :     if( log )
     442           0 :         log->disableFlush();
     443           0 :     return os;
     444             : }
     445           0 : std::ostream& enableFlush( std::ostream& os )
     446             : {
     447           0 :     Log* log = dynamic_cast<Log*>(&os);
     448           0 :     if( log )
     449           0 :         log->enableFlush();
     450           0 :     return os;
     451             : }
     452           0 : std::ostream& forceFlush( std::ostream& os )
     453             : {
     454           0 :     Log* log = dynamic_cast<Log*>(&os);
     455           0 :     if( log )
     456           0 :         log->forceFlush();
     457           0 :     return os;
     458             : }
     459             : 
     460           0 : std::ostream& disableHeader( std::ostream& os )
     461             : {
     462           0 :     Log* log = dynamic_cast<Log*>(&os);
     463           0 :     if( log )
     464           0 :         log->disableHeader();
     465           0 :     return os;
     466             : }
     467           0 : std::ostream& enableHeader( std::ostream& os )
     468             : {
     469           0 :     Log* log = dynamic_cast<Log*>(&os);
     470           0 :     if( log )
     471           0 :         log->enableHeader();
     472           0 :     return os;
     473             : }
     474             : 
     475          72 : }

Generated by: LCOV version 1.11