libyui
3.0.10
|
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 }