libyui  3.0.10
/usr/src/RPM/BUILD/libyui-3.0.10/src/YUILog.cc
00001 /*
00002   Copyright (C) 2000-2012 Novell, Inc
00003   This library is free software; you can redistribute it and/or modify
00004   it under the terms of the GNU Lesser General Public License as
00005   published by the Free Software Foundation; either version 2.1 of the
00006   License, or (at your option) version 3.0 of the License. This library
00007   is distributed in the hope that it will be useful, but WITHOUT ANY
00008   WARRANTY; without even the implied warranty of MERCHANTABILITY or 
00009   FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
00010   License for more details. You should have received a copy of the GNU
00011   Lesser General Public License along with this library; if not, write
00012   to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
00013   Floor, Boston, MA 02110-1301 USA
00014 */
00015 
00016 
00017 /*-/
00018 
00019   File:         YUILog.cc
00020 
00021   Author:       Stefan Hundhammer <sh@suse.de>
00022 
00023 /-*/
00024 
00025 
00026 #include <string.h>
00027 
00028 #include <ostream>
00029 #include <fstream>
00030 #include <vector>
00031 #include <pthread.h>
00032 
00033 #define YUILogComponent "ui"
00034 #include "YUILog.h"
00035 
00036 #include "YUIException.h"
00037 
00038 
00039 static void stdLogger( YUILogLevel_t    logLevel,
00040                        const char *     logComponent,
00041                        const char *     sourceFileName,
00042                        int              sourceLineNo,
00043                        const char *     sourceFunctionName,
00044                        const char *     message );
00045 
00046 static std::ostream * stdLogStream = &std::cerr;
00047 
00048 
00049 /**
00050  * Stream buffer class that will use the YUILog's logger function.
00051  *
00052  * See also http://blogs.awesomeplay.com/elanthis/archives/2007/12/10/
00053  **/
00054 class YUILogBuffer: public std::streambuf
00055 {
00056     friend class YUILog;
00057 
00058 public:
00059 
00060     /**
00061      * Constructor.
00062      **/
00063     YUILogBuffer()
00064         : logComponent( 0 )
00065         , sourceFileName( 0 )
00066         , lineNo( 0 )
00067         , functionName( 0 )
00068         {}
00069 
00070     /**
00071      * Destructor.
00072      **/
00073     virtual ~YUILogBuffer()
00074         { flush(); }
00075 
00076     /**
00077      * Write (no more than maxLength characters of) a sequence of characters
00078      * and return the number of characters written.
00079      *
00080      * Reimplemented from std::streambuf.
00081      * This is called for all output operations on the associated ostream.
00082      **/
00083     virtual std::streamsize xsputn( const char * sequence, std::streamsize maxLength );
00084 
00085     /**
00086      * Write one character in case of buffer overflow.
00087      *
00088      * Reimplemented from std::streambuf.
00089      **/
00090     virtual int overflow( int ch = EOF );
00091 
00092     /**
00093      * Write (no more than maxLength characters of) a sequence of characters
00094      * and return the number of characters written.
00095      *
00096      * This is the actual worker function that uses the YUILog::loggerFunction to
00097      * actually write characters.
00098      **/
00099     std::streamsize writeBuffer( const char * sequence, std::streamsize seqLen );
00100 
00101     /**
00102      * Flush the output buffer: Write any data unwritten so far.
00103      **/
00104     void flush();
00105 
00106 
00107 private:
00108 
00109     YUILogLevel_t       logLevel;
00110     const char *        logComponent;
00111     const char *        sourceFileName;
00112     int                 lineNo;
00113     const char *        functionName;
00114 
00115     std::string         buffer;
00116 };
00117 
00118 
00119 
00120 std::streamsize
00121 YUILogBuffer::writeBuffer( const char * sequence, std::streamsize seqLen )
00122 {
00123     // Add new character sequence
00124 
00125     if ( seqLen > 0 )
00126         buffer += std::string( sequence, seqLen );
00127 
00128     //
00129     // Output buffer contents line by line
00130     //
00131 
00132     std::size_t start       = 0;
00133     std::size_t newline_pos = 0;
00134 
00135     while ( start < buffer.length() &&
00136             ( newline_pos = buffer.find_first_of( '\n', start ) ) != std::string::npos )
00137     {
00138         YUILoggerFunction loggerFunction = YUILog::loggerFunction( true ); // never return 0
00139 
00140         std::string line = buffer.substr( start, newline_pos - start );
00141 
00142         loggerFunction( logLevel, logComponent,
00143                         YUILog::basename( sourceFileName ).c_str(), lineNo, functionName,
00144                         line.c_str() );
00145 
00146         start = newline_pos + 1;
00147     }
00148 
00149     if ( start < buffer.length() )
00150         buffer = buffer.substr( start, std::string::npos );
00151     else
00152         buffer.clear();
00153 
00154     return seqLen;
00155 }
00156 
00157 
00158 std::streamsize
00159 YUILogBuffer::xsputn( const char * sequence, std::streamsize maxLength )
00160 {
00161     return writeBuffer( sequence, maxLength );
00162 }
00163 
00164 
00165 int
00166 YUILogBuffer::overflow( int ch )
00167 {
00168     if ( ch != EOF )
00169     {
00170         char sequence = ch;
00171         writeBuffer( &sequence, 1 );
00172     }
00173 
00174     return 0;
00175 }
00176 
00177 
00178 void YUILogBuffer::flush()
00179 {
00180     writeBuffer( "\n", 1 );
00181 }
00182 
00183 
00184 
00185 
00186 
00187 /**
00188  * Helper class: Per-thread logging information.
00189  *
00190  * Multiple threads can easily clobber each others' half-done logging.
00191  * A naive approach to prevent this would be to lock a mutex when a thread
00192  * starts logging and unlock it when it's done logging. But that "when it's
00193  * done" condition might never come true. std::endl or a newline in the output
00194  * stream would be one indication, but there is no way to make sure there
00195  * always is such a delimiter. If it is forgotten and that thread (that still
00196  * has the mutex locked) runs into a waiting condition itself (e.g., UI thread
00197  * synchronization with pipes), there would be a deadlock.
00198  *
00199  * So this much safer approach was chosen: Give each thread its own logging
00200  * infrastructure, i.e., its own log stream and its own log buffer.
00201  *
00202  * Sure, in bad cases the logger function might still be executed in parallel
00203  * and thus clobber a line or two of log output. But that's merely bad output
00204  * formatting, not writing another thread's data structures without control -
00205  * which can easily happen if multiple threads are working on the same output
00206  * buffer, i.e. manipulate the same string.
00207  **/
00208 struct YPerThreadLogInfo
00209 {
00210     /**
00211      * Constructor
00212      **/
00213     YPerThreadLogInfo()
00214         : threadHandle( pthread_self() )
00215         , logBuffer()
00216         , logStream( &logBuffer )
00217     {
00218         // std::cerr << "New thread with ID " << hex << threadHandle << dec << std::endl;
00219     }
00220 
00221     /**
00222      * Destructor
00223      **/
00224     ~YPerThreadLogInfo()
00225     {
00226         logBuffer.flush();
00227     }
00228 
00229     /**
00230      * Check if this per-thread logging information belongs to the specified thread.
00231      **/
00232     bool isThread( pthread_t otherThreadHandle )
00233     {
00234         return pthread_equal( otherThreadHandle, this->threadHandle );
00235     }
00236 
00237 
00238     //
00239     // Data members
00240     //
00241 
00242     pthread_t           threadHandle;
00243     YUILogBuffer        logBuffer;
00244     std::ostream        logStream;
00245 };
00246 
00247 
00248 
00249 
00250 struct YUILogPrivate
00251 {
00252     /**
00253      * Constructor
00254      **/
00255     YUILogPrivate()
00256         : loggerFunction( stdLogger )
00257         , enableDebugLoggingHook( 0 )
00258         , debugLoggingEnabledHook( 0 )
00259         , enableDebugLogging( false )
00260         {}
00261 
00262     /**
00263      * Destructor
00264      **/
00265     ~YUILogPrivate()
00266     {
00267         for ( unsigned i=0; i < threadLogInfo.size(); i++ )
00268             delete threadLogInfo[i];
00269     }
00270 
00271     /**
00272      * Find the per-thread logging information for the current thread.
00273      * Create a new one if it doesn't exist yet.
00274      **/
00275     YPerThreadLogInfo * findCurrentThread()
00276     {
00277         pthread_t thisThread = pthread_self();
00278 
00279         // Search backwards: Slight optimization for the UI.
00280         // The UI thread does the most logging, but it is created after the
00281         // main thread.
00282 
00283         for ( std::vector<YPerThreadLogInfo *>::reverse_iterator it = threadLogInfo.rbegin();
00284               it != threadLogInfo.rend();
00285               ++it )
00286         {
00287             if ( (*it)->isThread( thisThread ) )
00288                 return (*it);
00289         }
00290 
00291         YPerThreadLogInfo * newThreadLogInfo = new YPerThreadLogInfo();
00292         threadLogInfo.push_back( newThreadLogInfo );
00293 
00294         return newThreadLogInfo;
00295     }
00296 
00297     //
00298     // Data members
00299     //
00300 
00301     std::string                         logFileName;
00302     std::ofstream                       stdLogStream;
00303     YUILoggerFunction                   loggerFunction;
00304     YUIEnableDebugLoggingFunction       enableDebugLoggingHook;
00305     YUIDebugLoggingEnabledFunction      debugLoggingEnabledHook;
00306     bool                                enableDebugLogging;
00307 
00308     std::vector<YPerThreadLogInfo *>    threadLogInfo;
00309 };
00310 
00311 
00312 
00313 
00314 YUILog::YUILog()
00315     : priv( new YUILogPrivate() )
00316 {
00317     YUI_CHECK_NEW( priv );
00318 }
00319 
00320 
00321 YUILog::~YUILog()
00322 {
00323     if ( priv->stdLogStream.is_open() )
00324         priv->stdLogStream.close();
00325 }
00326 
00327 
00328 YUILog *
00329 YUILog::instance()
00330 {
00331     static YUILog * instance = 0;
00332 
00333     if ( ! instance )
00334     {
00335         instance = new YUILog();
00336         YUI_CHECK_NEW( instance );
00337     }
00338 
00339     return instance;
00340 }
00341 
00342 
00343 bool
00344 YUILog::setLogFileName( const std::string & logFileName )
00345 {
00346     instance()->priv->logFileName = logFileName;
00347 
00348     std::ofstream & logStream = instance()->priv->stdLogStream;
00349 
00350     if ( logStream.is_open() )
00351         logStream.close();
00352 
00353     bool success = true;
00354 
00355     if ( logFileName.empty() ) // log to stderr again
00356     {
00357         stdLogStream = &std::cerr;
00358     }
00359     else
00360     {
00361         logStream.open( logFileName.c_str(), std::ios_base::app );
00362         success = logStream.good();
00363 
00364         if ( success )
00365         {
00366             stdLogStream = &( instance()->priv->stdLogStream );
00367         }
00368         else
00369         {
00370             std::cerr << "ERROR: Can't open log file " << logFileName << std::endl;
00371             stdLogStream = &std::cerr;
00372         }
00373     }
00374 
00375     return success;
00376 }
00377 
00378 
00379 std::string
00380 YUILog::logFileName()
00381 {
00382     return instance()->priv->logFileName;
00383 }
00384 
00385 
00386 void
00387 YUILog::enableDebugLogging( bool debugLogging )
00388 {
00389     instance()->priv->enableDebugLogging = debugLogging;
00390 
00391     if ( instance()->priv->enableDebugLoggingHook )
00392         instance()->priv->enableDebugLoggingHook( debugLogging );
00393 }
00394 
00395 
00396 bool
00397 YUILog::debugLoggingEnabled()
00398 {
00399     if ( instance()->priv->debugLoggingEnabledHook )
00400         return instance()->priv->debugLoggingEnabledHook();
00401     else
00402         return instance()->priv->enableDebugLogging;
00403 }
00404 
00405 
00406 void
00407 YUILog::setLoggerFunction( YUILoggerFunction loggerFunction )
00408 {
00409     if ( ! loggerFunction )
00410         loggerFunction = stdLogger;
00411 
00412     instance()->priv->loggerFunction = loggerFunction;
00413 }
00414 
00415 
00416 YUILoggerFunction
00417 YUILog::loggerFunction( bool returnStdLogger )
00418 {
00419     YUILoggerFunction logger = instance()->priv->loggerFunction;
00420 
00421     if ( logger == stdLogger && ! returnStdLogger )
00422         logger = 0;
00423 
00424     return logger;
00425 }
00426 
00427 
00428 void
00429 YUILog::setEnableDebugLoggingHooks( YUIEnableDebugLoggingFunction  enableFunction,
00430                                     YUIDebugLoggingEnabledFunction isEnabledFunction )
00431 {
00432     instance()->priv->enableDebugLoggingHook  = enableFunction;
00433     instance()->priv->debugLoggingEnabledHook = isEnabledFunction;
00434 }
00435 
00436 
00437 YUIEnableDebugLoggingFunction
00438 YUILog::enableDebugLoggingHook()
00439 {
00440     return instance()->priv->enableDebugLoggingHook;
00441 }
00442 
00443 
00444 YUIDebugLoggingEnabledFunction
00445 YUILog::debugLoggingEnabledHook()
00446 {
00447     return instance()->priv->debugLoggingEnabledHook;
00448 }
00449 
00450 
00451 std::ostream &
00452 YUILog::log( YUILogLevel_t      logLevel,
00453              const char *       logComponent,
00454              const char *       sourceFileName,
00455              int                lineNo,
00456              const char *       functionName )
00457 {
00458     YPerThreadLogInfo * threadLogInfo = priv->findCurrentThread();
00459 
00460     if ( ! threadLogInfo->logBuffer.buffer.empty() )    // Leftovers from previous logging?
00461     {
00462         if ( threadLogInfo->logBuffer.logLevel != logLevel ||
00463              threadLogInfo->logBuffer.lineNo   != lineNo   ||
00464              strcmp( threadLogInfo->logBuffer.logComponent,   logComponent   ) != 0 ||
00465              strcmp( threadLogInfo->logBuffer.sourceFileName, sourceFileName ) != 0 ||
00466              strcmp( threadLogInfo->logBuffer.functionName,   functionName   ) != 0   )
00467         {
00468             threadLogInfo->logBuffer.flush();
00469         }
00470     }
00471 
00472     threadLogInfo->logBuffer.logLevel           = logLevel;
00473     threadLogInfo->logBuffer.logComponent       = logComponent;
00474     threadLogInfo->logBuffer.sourceFileName     = sourceFileName;
00475     threadLogInfo->logBuffer.lineNo             = lineNo;
00476     threadLogInfo->logBuffer.functionName       = functionName;
00477 
00478     return threadLogInfo->logStream;
00479 }
00480 
00481 
00482 std::ostream &
00483 YUILog::debug( const char * logComponent, const char * sourceFileName, int lineNo, const char * functionName )
00484 {
00485     return instance()->log( YUI_LOG_DEBUG, logComponent, sourceFileName, lineNo, functionName );
00486 }
00487 
00488 
00489 std::ostream &
00490 YUILog::milestone( const char * logComponent, const char * sourceFileName, int lineNo, const char * functionName )
00491 {
00492     return instance()->log( YUI_LOG_MILESTONE, logComponent, sourceFileName, lineNo, functionName );
00493 }
00494 
00495 
00496 std::ostream &
00497 YUILog::warning( const char * logComponent, const char * sourceFileName, int lineNo, const char * functionName )
00498 {
00499     return instance()->log( YUI_LOG_WARNING, logComponent, sourceFileName, lineNo, functionName );
00500 }
00501 
00502 
00503 std::ostream &
00504 YUILog::error( const char * logComponent, const char * sourceFileName, int lineNo, const char * functionName )
00505 {
00506     return instance()->log( YUI_LOG_ERROR, logComponent, sourceFileName, lineNo, functionName );
00507 }
00508 
00509 
00510 
00511 std::string
00512 YUILog::basename( const std::string & fileNameWithPath )
00513 {
00514     std::size_t lastSlashPos = fileNameWithPath.find_last_of( '/' );
00515 
00516     std::string fileName =
00517         ( lastSlashPos == std::string::npos ) ?
00518         fileNameWithPath :
00519         fileNameWithPath.substr( lastSlashPos+1 );
00520 
00521     return fileName;
00522 }
00523 
00524 
00525 
00526 static void
00527 stdLogger( YUILogLevel_t        logLevel,
00528            const char *         logComponent,
00529            const char *         sourceFileName,
00530            int                  sourceLineNo,
00531            const char *         sourceFunctionName,
00532            const char *         message )
00533 {
00534     const char * logLevelStr = "";
00535 
00536     switch ( logLevel )
00537     {
00538         case YUI_LOG_DEBUG:
00539             if ( ! YUILog::debugLoggingEnabled() )
00540                 return;
00541 
00542             logLevelStr = "dbg";
00543             break;
00544 
00545         case YUI_LOG_MILESTONE: logLevelStr = "_M_";    break;
00546         case YUI_LOG_WARNING:   logLevelStr = "WRN";    break;
00547         case YUI_LOG_ERROR:     logLevelStr = "ERR";    break;
00548     }
00549 
00550     if ( ! logComponent )
00551         logComponent = "??";
00552 
00553     if ( ! sourceFileName )
00554         sourceFileName = "??";
00555 
00556     if ( ! sourceFunctionName )
00557         sourceFunctionName = "??";
00558 
00559     if ( ! message )
00560         message = "";
00561 
00562     (*stdLogStream) << "<" << logLevelStr  << "> "
00563                     << "[" << logComponent << "] "
00564                     << sourceFileName   << ":" << sourceLineNo << " "
00565                     << sourceFunctionName       << "(): "
00566                     << message
00567                     << std::endl;
00568 }
 All Classes Functions Variables Enumerations Friends