libyui  3.10.0
YUILog.cc
1 /*
2  Copyright (C) 2000-2012 Novell, Inc
3  This library is free software; you can redistribute it and/or modify
4  it under the terms of the GNU Lesser General Public License as
5  published by the Free Software Foundation; either version 2.1 of the
6  License, or (at your option) version 3.0 of the License. This library
7  is distributed in the hope that it will be useful, but WITHOUT ANY
8  WARRANTY; without even the implied warranty of MERCHANTABILITY or
9  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
10  License for more details. You should have received a copy of the GNU
11  Lesser General Public License along with this library; if not, write
12  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
13  Floor, Boston, MA 02110-1301 USA
14 */
15 
16 
17 /*-/
18 
19  File: YUILog.cc
20 
21  Author: Stefan Hundhammer <sh@suse.de>
22 
23 /-*/
24 
25 
26 #include <string.h>
27 
28 #include <ostream>
29 #include <fstream>
30 #include <vector>
31 #include <pthread.h>
32 
33 #define YUILogComponent "ui"
34 #include "YUILog.h"
35 
36 #include "YUIException.h"
37 
38 using std::string;
39 using std::ostream;
40 using std::cerr;
41 
42 
43 static void stdLogger( YUILogLevel_t logLevel,
44  const char * logComponent,
45  const char * sourceFileName,
46  int sourceLineNo,
47  const char * sourceFunctionName,
48  const char * message );
49 
50 static ostream * stdLogStream = &cerr;
51 
52 
53 /**
54  * Stream buffer class that will use the YUILog's logger function.
55  *
56  * See also http://blogs.awesomeplay.com/elanthis/archives/2007/12/10/
57  **/
58 class YUILogBuffer: public std::streambuf
59 {
60  friend class YUILog;
61 
62 public:
63 
64  /**
65  * Constructor.
66  **/
68  : logComponent( 0 )
69  , sourceFileName( 0 )
70  , lineNo( 0 )
71  , functionName( 0 )
72  {}
73 
74  /**
75  * Destructor.
76  **/
77  virtual ~YUILogBuffer()
78  { flush(); }
79 
80  /**
81  * Write (no more than maxLength characters of) a sequence of characters
82  * and return the number of characters written.
83  *
84  * Reimplemented from streambuf.
85  * This is called for all output operations on the associated ostream.
86  **/
87  virtual std::streamsize xsputn( const char * sequence, std::streamsize maxLength );
88 
89  /**
90  * Write one character in case of buffer overflow.
91  *
92  * Reimplemented from streambuf.
93  **/
94  virtual int overflow( int ch = EOF );
95 
96  /**
97  * Write (no more than maxLength characters of) a sequence of characters
98  * and return the number of characters written.
99  *
100  * This is the actual worker function that uses the YUILog::loggerFunction to
101  * actually write characters.
102  **/
103  std::streamsize writeBuffer( const char * sequence, std::streamsize seqLen );
104 
105  /**
106  * Flush the output buffer: Write any data unwritten so far.
107  **/
108  void flush();
109 
110 
111 private:
112 
113  YUILogLevel_t logLevel;
114  const char * logComponent;
115  const char * sourceFileName;
116  int lineNo;
117  const char * functionName;
118 
119  string buffer;
120 };
121 
122 
123 
124 std::streamsize
125 YUILogBuffer::writeBuffer( const char * sequence, std::streamsize seqLen )
126 {
127  // Add new character sequence
128 
129  if ( seqLen > 0 )
130  buffer += string( sequence, seqLen );
131 
132  //
133  // Output buffer contents line by line
134  //
135 
136  size_t start = 0;
137  size_t newline_pos = 0;
138 
139  while ( start < buffer.length() &&
140  ( newline_pos = buffer.find_first_of( '\n', start ) ) != string::npos )
141  {
142  YUILoggerFunction loggerFunction = YUILog::loggerFunction( true ); // never return 0
143 
144  string line = buffer.substr( start, newline_pos - start );
145 
146  loggerFunction( logLevel, logComponent,
147  YUILog::basename( sourceFileName ).c_str(), lineNo, functionName,
148  line.c_str() );
149 
150  start = newline_pos + 1;
151  }
152 
153  if ( start < buffer.length() )
154  buffer = buffer.substr( start, string::npos );
155  else
156  buffer.clear();
157 
158  return seqLen;
159 }
160 
161 
162 std::streamsize
163 YUILogBuffer::xsputn( const char * sequence, std::streamsize maxLength )
164 {
165  return writeBuffer( sequence, maxLength );
166 }
167 
168 
169 int
171 {
172  if ( ch != EOF )
173  {
174  char sequence = ch;
175  writeBuffer( &sequence, 1 );
176  }
177 
178  return 0;
179 }
180 
181 
183 {
184  writeBuffer( "\n", 1 );
185 }
186 
187 
188 
189 
190 
191 /**
192  * Helper class: Per-thread logging information.
193  *
194  * Multiple threads can easily clobber each others' half-done logging.
195  * A naive approach to prevent this would be to lock a mutex when a thread
196  * starts logging and unlock it when it's done logging. But that "when it's
197  * done" condition might never come true. endl or a newline in the output
198  * stream would be one indication, but there is no way to make sure there
199  * always is such a delimiter. If it is forgotten and that thread (that still
200  * has the mutex locked) runs into a waiting condition itself (e.g., UI thread
201  * synchronization with pipes), there would be a deadlock.
202  *
203  * So this much safer approach was chosen: Give each thread its own logging
204  * infrastructure, i.e., its own log stream and its own log buffer.
205  *
206  * Sure, in bad cases the logger function might still be executed in parallel
207  * and thus clobber a line or two of log output. But that's merely bad output
208  * formatting, not writing another thread's data structures without control -
209  * which can easily happen if multiple threads are working on the same output
210  * buffer, i.e. manipulate the same string.
211  **/
213 {
214  /**
215  * Constructor
216  **/
218  : threadHandle( pthread_self() )
219  , logBuffer()
220  , logStream( &logBuffer )
221  {
222  // cerr << "New thread with ID " << hex << threadHandle << dec << endl;
223  }
224 
225  /**
226  * Destructor
227  **/
229  {
230  logBuffer.flush();
231  }
232 
233  /**
234  * Check if this per-thread logging information belongs to the specified thread.
235  **/
236  bool isThread( pthread_t otherThreadHandle )
237  {
238  return pthread_equal( otherThreadHandle, this->threadHandle );
239  }
240 
241 
242  //
243  // Data members
244  //
245 
246  pthread_t threadHandle;
247  YUILogBuffer logBuffer;
248  ostream logStream;
249 };
250 
251 
252 
253 
255 {
256  /**
257  * Constructor
258  **/
260  : loggerFunction( stdLogger )
261  , enableDebugLoggingHook( 0 )
262  , debugLoggingEnabledHook( 0 )
263  , enableDebugLogging( false )
264  {}
265 
266  /**
267  * Destructor
268  **/
270  {
271  for ( unsigned i=0; i < threadLogInfo.size(); i++ )
272  delete threadLogInfo[i];
273  }
274 
275  /**
276  * Find the per-thread logging information for the current thread.
277  * Create a new one if it doesn't exist yet.
278  **/
280  {
281  pthread_t thisThread = pthread_self();
282 
283  // Search backwards: Slight optimization for the UI.
284  // The UI thread does the most logging, but it is created after the
285  // main thread.
286 
287  for ( std::vector<YPerThreadLogInfo *>::reverse_iterator it = threadLogInfo.rbegin();
288  it != threadLogInfo.rend();
289  ++it )
290  {
291  if ( (*it)->isThread( thisThread ) )
292  return (*it);
293  }
294 
295  YPerThreadLogInfo * newThreadLogInfo = new YPerThreadLogInfo();
296  threadLogInfo.push_back( newThreadLogInfo );
297 
298  return newThreadLogInfo;
299  }
300 
301  //
302  // Data members
303  //
304 
305  string logFileName;
306  std::ofstream stdLogStream;
307  YUILoggerFunction loggerFunction;
308  YUIEnableDebugLoggingFunction enableDebugLoggingHook;
309  YUIDebugLoggingEnabledFunction debugLoggingEnabledHook;
310  bool enableDebugLogging;
311 
312  std::vector<YPerThreadLogInfo *> threadLogInfo;
313 };
314 
315 
316 
317 
318 YUILog::YUILog()
319  : priv( new YUILogPrivate() )
320 {
321  YUI_CHECK_NEW( priv );
322 }
323 
324 
325 YUILog::~YUILog()
326 {
327  if ( priv->stdLogStream.is_open() )
328  priv->stdLogStream.close();
329 }
330 
331 
332 YUILog *
334 {
335  static YUILog * instance = 0;
336 
337  if ( ! instance )
338  {
339  instance = new YUILog();
340  YUI_CHECK_NEW( instance );
341  }
342 
343  return instance;
344 }
345 
346 
347 bool
348 YUILog::setLogFileName( const string & logFileName )
349 {
350  instance()->priv->logFileName = logFileName;
351 
352  std::ofstream & logStream = instance()->priv->stdLogStream;
353 
354  if ( logStream.is_open() )
355  logStream.close();
356 
357  bool success = true;
358 
359  if ( logFileName.empty() ) // log to stderr again
360  {
361  stdLogStream = &cerr;
362  }
363  else
364  {
365  logStream.open( logFileName.c_str(), std::ios_base::app );
366  success = logStream.good();
367 
368  if ( success )
369  {
370  stdLogStream = &( instance()->priv->stdLogStream );
371  }
372  else
373  {
374  cerr << "ERROR: Can't open log file " << logFileName << endl;
375  stdLogStream = &cerr;
376  }
377  }
378 
379  return success;
380 }
381 
382 
383 string
385 {
386  return instance()->priv->logFileName;
387 }
388 
389 
390 void
391 YUILog::enableDebugLogging( bool debugLogging )
392 {
393  instance()->priv->enableDebugLogging = debugLogging;
394 
395  if ( instance()->priv->enableDebugLoggingHook )
396  instance()->priv->enableDebugLoggingHook( debugLogging );
397 }
398 
399 
400 bool
402 {
403  if ( instance()->priv->debugLoggingEnabledHook )
404  return instance()->priv->debugLoggingEnabledHook();
405  else
406  return instance()->priv->enableDebugLogging;
407 }
408 
409 
410 void
411 YUILog::setLoggerFunction( YUILoggerFunction loggerFunction )
412 {
413  if ( ! loggerFunction )
414  loggerFunction = stdLogger;
415 
416  instance()->priv->loggerFunction = loggerFunction;
417 }
418 
419 
420 YUILoggerFunction
421 YUILog::loggerFunction( bool returnStdLogger )
422 {
423  YUILoggerFunction logger = instance()->priv->loggerFunction;
424 
425  if ( logger == stdLogger && ! returnStdLogger )
426  logger = 0;
427 
428  return logger;
429 }
430 
431 
432 void
433 YUILog::setEnableDebugLoggingHooks( YUIEnableDebugLoggingFunction enableFunction,
434  YUIDebugLoggingEnabledFunction isEnabledFunction )
435 {
436  instance()->priv->enableDebugLoggingHook = enableFunction;
437  instance()->priv->debugLoggingEnabledHook = isEnabledFunction;
438 }
439 
440 
441 YUIEnableDebugLoggingFunction
443 {
444  return instance()->priv->enableDebugLoggingHook;
445 }
446 
447 
448 YUIDebugLoggingEnabledFunction
450 {
451  return instance()->priv->debugLoggingEnabledHook;
452 }
453 
454 
455 ostream &
456 YUILog::log( YUILogLevel_t logLevel,
457  const char * logComponent,
458  const char * sourceFileName,
459  int lineNo,
460  const char * functionName )
461 {
462  YPerThreadLogInfo * threadLogInfo = priv->findCurrentThread();
463 
464  if ( ! threadLogInfo->logBuffer.buffer.empty() ) // Leftovers from previous logging?
465  {
466  if ( threadLogInfo->logBuffer.logLevel != logLevel ||
467  threadLogInfo->logBuffer.lineNo != lineNo ||
468  strcmp( threadLogInfo->logBuffer.logComponent, logComponent ) != 0 ||
469  strcmp( threadLogInfo->logBuffer.sourceFileName, sourceFileName ) != 0 ||
470  strcmp( threadLogInfo->logBuffer.functionName, functionName ) != 0 )
471  {
472  threadLogInfo->logBuffer.flush();
473  }
474  }
475 
476  threadLogInfo->logBuffer.logLevel = logLevel;
477  threadLogInfo->logBuffer.logComponent = logComponent;
478  threadLogInfo->logBuffer.sourceFileName = sourceFileName;
479  threadLogInfo->logBuffer.lineNo = lineNo;
480  threadLogInfo->logBuffer.functionName = functionName;
481 
482  return threadLogInfo->logStream;
483 }
484 
485 
486 ostream &
487 YUILog::debug( const char * logComponent, const char * sourceFileName, int lineNo, const char * functionName )
488 {
489  return instance()->log( YUI_LOG_DEBUG, logComponent, sourceFileName, lineNo, functionName );
490 }
491 
492 
493 ostream &
494 YUILog::milestone( const char * logComponent, const char * sourceFileName, int lineNo, const char * functionName )
495 {
496  return instance()->log( YUI_LOG_MILESTONE, logComponent, sourceFileName, lineNo, functionName );
497 }
498 
499 
500 ostream &
501 YUILog::warning( const char * logComponent, const char * sourceFileName, int lineNo, const char * functionName )
502 {
503  return instance()->log( YUI_LOG_WARNING, logComponent, sourceFileName, lineNo, functionName );
504 }
505 
506 
507 ostream &
508 YUILog::error( const char * logComponent, const char * sourceFileName, int lineNo, const char * functionName )
509 {
510  return instance()->log( YUI_LOG_ERROR, logComponent, sourceFileName, lineNo, functionName );
511 }
512 
513 
514 
515 string
516 YUILog::basename( const string & fileNameWithPath )
517 {
518  size_t lastSlashPos = fileNameWithPath.find_last_of( '/' );
519 
520  string fileName =
521  ( lastSlashPos == string::npos ) ?
522  fileNameWithPath :
523  fileNameWithPath.substr( lastSlashPos+1 );
524 
525  return fileName;
526 }
527 
528 
529 
530 static void
531 stdLogger( YUILogLevel_t logLevel,
532  const char * logComponent,
533  const char * sourceFileName,
534  int sourceLineNo,
535  const char * sourceFunctionName,
536  const char * message )
537 {
538  const char * logLevelStr = "";
539 
540  switch ( logLevel )
541  {
542  case YUI_LOG_DEBUG:
543  if ( ! YUILog::debugLoggingEnabled() )
544  return;
545 
546  logLevelStr = "dbg";
547  break;
548 
549  case YUI_LOG_MILESTONE: logLevelStr = "_M_"; break;
550  case YUI_LOG_WARNING: logLevelStr = "WRN"; break;
551  case YUI_LOG_ERROR: logLevelStr = "ERR"; break;
552  }
553 
554  if ( ! logComponent )
555  logComponent = "??";
556 
557  if ( ! sourceFileName )
558  sourceFileName = "??";
559 
560  if ( ! sourceFunctionName )
561  sourceFunctionName = "??";
562 
563  if ( ! message )
564  message = "";
565 
566  (*stdLogStream) << "<" << logLevelStr << "> "
567  << "[" << logComponent << "] "
568  << sourceFileName << ":" << sourceLineNo << " "
569  << sourceFunctionName << "(): "
570  << message
571  << endl;
572 }
YUILog::debugLoggingEnabled
static bool debugLoggingEnabled()
Return 'true' if debug logging is enabled, 'false' if not.
Definition: YUILog.cc:401
YPerThreadLogInfo
Helper class: Per-thread logging information.
Definition: YUILog.cc:213
YPerThreadLogInfo::isThread
bool isThread(pthread_t otherThreadHandle)
Check if this per-thread logging information belongs to the specified thread.
Definition: YUILog.cc:236
YUILog::basename
static std::string basename(const std::string &fileNameWithPath)
Return the base name without path from a file name with path.
Definition: YUILog.cc:516
YUILogBuffer::writeBuffer
std::streamsize writeBuffer(const char *sequence, std::streamsize seqLen)
Write (no more than maxLength characters of) a sequence of characters and return the number of charac...
Definition: YUILog.cc:125
YUILog::setEnableDebugLoggingHooks
static void setEnableDebugLoggingHooks(YUIEnableDebugLoggingFunction enableFunction, YUIDebugLoggingEnabledFunction isEnabledFunction)
Set the hook functions to enable/disable debug logging and to query if debug logging is enabled:
Definition: YUILog.cc:433
YUILog::logFileName
static std::string logFileName()
Return the current log file name or an empty string if stderr is used.
Definition: YUILog.cc:384
YUILog::setLogFileName
static bool setLogFileName(const std::string &logFileName)
Set the log file name to be used with the standard logger function.
Definition: YUILog.cc:348
YUILogBuffer::flush
void flush()
Flush the output buffer: Write any data unwritten so far.
Definition: YUILog.cc:182
YUILogBuffer::YUILogBuffer
YUILogBuffer()
Constructor.
Definition: YUILog.cc:67
YUILog::log
std::ostream & log(YUILogLevel_t logLevel, const char *logComponent, const char *sourceFileName, int lineNo, const char *functionName)
Generic log function.
Definition: YUILog.cc:456
YUILog::loggerFunction
static YUILoggerFunction loggerFunction(bool returnStdLogger=false)
Return the UI logger function.
Definition: YUILog.cc:421
YUILog::debugLoggingEnabledHook
static YUIDebugLoggingEnabledFunction debugLoggingEnabledHook()
Return the hook function that checks if debug logging is enabled or 0 if no such hook function is set...
Definition: YUILog.cc:449
YUILogPrivate::~YUILogPrivate
~YUILogPrivate()
Destructor.
Definition: YUILog.cc:269
YUILog::enableDebugLogging
static void enableDebugLogging(bool debugLogging=true)
Enable or disable debug logging.
Definition: YUILog.cc:391
YUILogBuffer::overflow
virtual int overflow(int ch=EOF)
Write one character in case of buffer overflow.
Definition: YUILog.cc:170
YUILogBuffer::xsputn
virtual std::streamsize xsputn(const char *sequence, std::streamsize maxLength)
Write (no more than maxLength characters of) a sequence of characters and return the number of charac...
Definition: YUILog.cc:163
YUILogPrivate
Definition: YUILog.cc:255
YUILogBuffer::~YUILogBuffer
virtual ~YUILogBuffer()
Destructor.
Definition: YUILog.cc:77
YUILogPrivate::YUILogPrivate
YUILogPrivate()
Constructor.
Definition: YUILog.cc:259
YUILogBuffer
Stream buffer class that will use the YUILog's logger function.
Definition: YUILog.cc:59
YUILog::debug
static std::ostream & debug(const char *logComponent, const char *sourceFileName, int lineNo, const char *functionName)
Logging functions for each log level.
Definition: YUILog.cc:487
YPerThreadLogInfo::YPerThreadLogInfo
YPerThreadLogInfo()
Constructor.
Definition: YUILog.cc:217
YUILog::instance
static YUILog * instance()
Return the singleton object for this class.
Definition: YUILog.cc:333
YUILog
UI logging.
Definition: YUILog.h:100
YPerThreadLogInfo::~YPerThreadLogInfo
~YPerThreadLogInfo()
Destructor.
Definition: YUILog.cc:228
YUILogPrivate::findCurrentThread
YPerThreadLogInfo * findCurrentThread()
Find the per-thread logging information for the current thread.
Definition: YUILog.cc:279
YUILog::enableDebugLoggingHook
static YUIEnableDebugLoggingFunction enableDebugLoggingHook()
Return the hook function that enables or disables debug logging or 0 if no such hook function is set.
Definition: YUILog.cc:442
YUILog::setLoggerFunction
static void setLoggerFunction(YUILoggerFunction loggerFunction)
Set the UI logger function.
Definition: YUILog.cc:411