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