LCOV - code coverage report
Current view: top level - lunchbox - log.cpp (source / functions) Hit Total Coverage
Test: Lunchbox Lines: 139 221 62.9 %
Date: 2018-10-03 05:33:11 Functions: 35 54 64.8 %

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

Generated by: LCOV version 1.11