Skip to content
Snippets Groups Projects
ErrLog.hh 19.7 KiB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
#ifndef ERRLOG_HH
#define ERRLOG_HH

//--------------------------------------------------------------------------
// Description:
//	Class ErrLogger:
//		Interface description for the abstract logging interface
//
// BaBar Usage:
//
//	BaBar policy for using ErrLogger is to interact with it only
//	through the ErrMsg macro.
//
//	ErrMsg(severity) << "the message" << endmsg;
//
//	The error message must be terminated by endmsg as above.
//
//	The ErrMsg macro returns an ostream& so that it is possible
//	to include any constructs that can be sent to cout or cerr.
//	For example, the string classes and user-defined operator<<
//	are available to users while using ErrMsg.
//
// General Usage:
//
//	Available constructs are a logging call:
//
//		ErrLog::msg(severity, facility, code) << "Foo" << endmsg;
//
//	and a test to see if a particular logging call will do anything:
//
//		if (ErrLog::logging(severity, facility, code)) ...
//
//	All error messages should be completed with "endmsg".  For multiple-
//	line messages use '\n' for line breaks in preference to endl.  The 
//      endmsg command will supply a final "endl" for error loggers for which
//      it's appropriate (e.g., if writing to the console).  Users should in 
//      particular NOT terminate messages with "some text" << endl << endmsg;.
//
//
//	"severity" is a member of an enum; see the definition below
//	"facility" names the source of the message; in the ErrMsg() form,
//                 this is set to be the filename and line number
//	"code"     an integer distinguishing the individual errors possible;
//	           no meanings are predefined at this level
//
//
// Environment:
//	Software developed for the BaBar Detector at the SLAC B-Factory.
//
// Author List:
//      Bob Jacobsen	(original author)
//	Scott Metzler	(addition of ErrStream)
//	Gregory Dubois-Felsmann   (ErrOpt; conversion of "code" to int)
//      $Id: ErrLog.hh,v 1.1.1.1 2005/03/29 17:04:19 steinke Exp $
//
// Copyright Information:
//	Copyright (C) 1998  Lawrence Berkeley National Laboratory
//	Copyright (C) 1998-2004  California Institute of Technology
//
//------------------------------------------------------------------------

//-----------------
// C/C++ Headers --
//-----------------
#include <assert.h>
#include <iostream>

//------------------------------------
// Collaborating Class Declarations --
//------------------------------------
class ErrStream;
class ErrOptParser;
template <class T> class ErrOptRef;


// Use the ErrMsg and ErrLogging macros for interacting with the error logger
// in a syntactically economical way.

// The next two macros allow the substitution of the value of the 
// __LINE__ macro as a quoted character string instead of an integer.
// As odd as this may seem, it's a well-established ISO C trick.

// 1) replaced by its argument, treated as a quoted string, without any
//    nested expansions applied (they are suppressed by the "#" operator).
#define ERRLINE_HACK_1(line)   #line

// 2) allows its argument (which will be "__LINE__") to have macro expansion
//    performed on it.  Hence HACK_2(__LINE__) becomes, e.g., HACK_1(76).
#define ERRLINE_HACK_2(line)   ERRLINE_HACK_1(line)


#ifdef ErrMsg
#undef ErrMsg
#endif
#define ErrMsg(sev) ErrLog::msg( ErrLog::sev, __FILE__ "(" ERRLINE_HACK_2(__LINE__) ")", 0 )

#ifdef ErrLogging
#undef ErrLogging
#endif
#define ErrLogging(sev) ErrLog::logging( ErrLog::sev, __FILE__ "(" ERRLINE_HACK_2(__LINE__) ")", 0 )

// [June 2002 note]  The old versions of these macros expanded to 
// ( ErrLog::sev, __FILE__, __LINE__ ) and so invoked the (..., int) forms
// of msg() and logging().  But at run time these forms were immediately
// vectored, by converting the code to a string, into the (..., const char*)
// forms, in which then in practice in all relevant ErrLog implementations the
// "code" string, containing the line number, was appended to the facility.
//
// The new forms of these macros preserve this behavior -- the line number
// is concatenated to the file name -- on the theory that the line number
// makes a silly code number and that the file+line combination is 
// reasonable to treat as a unit.


// The actual class

class ErrLog {
public:

  /**
   *  Message severity levels.
   */
  enum Severity { debugging=-1, 
		  trace=0, 
		  routine, 
		  warning, 
		  error,
		  severe,
		  fatal, 
		  MinSeverity=debugging,
		  MaxSeverity=fatal };

  //	fatal:		A condition exists that casts doubt on the integrity
  //                    of processing and the reliability of any results.
  //                    No recovery strategy can be attempted, because the
  //                    state of the program may be indeterminate.
  //
  //                    It is intended that "fatal" errors be reported
  //                    primarily when it is likely that the condition was
  //                    caused by incorrect code, or when the root cause cannot
  //                    be determined.  Dealing with the error is likely to
  //                    require debugging or other expert investigation.
  // 
  //                    In most implementations, this will be configured to
  //                    result in application termination with ::abort() 
  //                    immediately after the error message is completed with
  //                    "endmsg", producing a core dump usable to obtain a
  //                    traceback.
  //
  //			Programmers should generally not call ::abort() or
  //                    ::exit() themselves following the message.

  //    severe:         A condition exists such that the general ability of
  //                    the program to continue work is compromised: the
  //                    present computation and most or all subsequent ones
  //                    are likely to fail or produce erroneous results.
  //
  //                    It is intended that "severe" errors be reported 
  //                    primarily when external conditions (command line
  //                    arguments, control files, data files, the contents of
  //                    databases) are identified to be incompatible with
  //                    proceeding.  The programmer should ensure that the
  //                    message written is as informative as possible.
  //
  //                    In most implementations, this will be configured to
  //                    result in application termination immediately after
  //                    the error message is completed with "endmsg" and
  //                    delivered, with a non-zero exit status.  The ErrLog
  //                    implementation is free to determine the exit status
  //                    to use, from the range 1-126.  It may take the "code"
  //                    value from the ErrLog::msg( , , code ) into account.
  //
  //			Programmers should generally not call ::abort() or
  //                    ::exit() themselves following the message.

  //	error:		A condition exists such that requested result or
  //			action can not be produced.  However, future 
  //                    processing may be able to succeed.

  //	warning:	The result is produced, but may not be
  //			what's desired due to an unexpected condition

  //	routine:	Nothing known to be wrong with the result;
  //			messages that are always produced in normal
  //			operation

  //	trace:		Messages about the flow of program control
  //			and which optional operations took place.
  //			(This is the default if nothing is defined)
  
  //	debugging:	Information in addition to the above


  /**
   *  This constant is the exit code to be used when a message severity
   *  is such that an exit(!=0) -- as opposed to an abort() -- is merited,
   *  but no message-specific exit code can be determined from the 
   *  message parameters.
   */
  enum { DefaultExitCode=125 };

//-----------------------------------------------------------------------
// Static public member functions ---------------------------------------
//-----------------------------------------------------------------------

public:

  // These member functions are the public interface to error logging.

  /**
   *  Returns a stream to which to write a message, with the parameters
   *  given.  The message must be terminated later by performing the
   *  "<< endmsg" operation on the resulting stream.  Actual output may
   *  not occur until the endmsg is executed.  The meaning of the "facility"
   *  and "code" parameters is not defined by the ErrLog base class, nor is
   *  is guaranteed that their values will be reflected in any output
   *  generated for the message.
   *
   *  The "facility" pointer must be guaranteed by the caller to remain
   *  valid until the message is completed with "endmsg".  (I.e., this
   *  function is not guaranteed to make a copy of the string.)
   */
  static std::ostream& msg( ErrLog::Severity severity, 
		       const char* facility, 
		       int code );

  /** 
   *  DEPRECATED
   *  Backward-compatibility function for an old API.
   *  Calls through to msg( severity, facilty + code, 0 )
   *  Do not use in new code.
   */
  static std::ostream& msg( ErrLog::Severity severity, 
			    const char* facility, 
			    const char* code );

  /**
   *  Tests whether actual logging output is enabled for a specific set
   *  of message parameters.  This function should be used before sending
   *  complex output to the result of ErrMsg() or ErrLog::msg(), to avoid
   *  the cost of executing the stream insertion operations (binary-to-
   *  string conversions) for the message, only to discard their output.
   */
  inline
  static bool logging( ErrLog::Severity severity, 
		       const char* facility, 
		       int code );

  /** 
   *  DEPRECATED
   *  Backward-compatibility function for an old API.
   *  Calls through to logging( severity, facilty + code, 0 )
   *  Do not use in new code.
   */
  static bool logging( ErrLog::Severity severity, 
		       const char* facility, 
		       const char* code );


  // The following are options controlling the global configuration of
  // the static "front end" to error logging.

  /**
   *  Disables the automatic warning message generated when a new ErrLog::msg()
   *  call is made before the previous one has been terminated with "endmsg".
   *  The use of this option is *strongly* discouraged; the correct response
   *  to such warnings is to fix the nested messages.
   */
  static void turnOffWarnings();


  // The following are utility functions that are probably of interest only
  // implementations (i.e., could be protected) but expose no internals and
  // are harmless, and maybe useful, to make public.

  /**
   *  Returns a static string corresponding to the enum member name of a
   *  severity level.  I.e., severityName(ErrLog::error) == "error".
   */
  static const char* severityName( ErrLog::Severity );

  /**
   *  Returns a single uppercase letter corresponding to the enum member name
   *  of a severity level.  I.e., severityName(ErrLog::error) == 'E'.
   */
  static char        severityLetter( ErrLog::Severity );



//-----------------------------------------------------------------------
// Instance public member functions -------------------------------------
//-----------------------------------------------------------------------

public:

  /**
   *  Provides parsing of a comma-delimited list of error logger
   *  behavior control options, in getsubopt() format.  The effects of
   *  the parsing depend on the specific ErrLog implementation and are
   *  established by it at constructor time using the _errOptParser()
   *  and _configureOptions() methods.
   */ 
  bool parseOpts( const char* optarg );


//-----------------------------------------------------------------------
// Friendship declarations ----------------------------------------------
//-----------------------------------------------------------------------

  friend class ErrStream;


//-----------------------------------------------------------------------
// Instance protected member functions ----------------------------------
//-----------------------------------------------------------------------

protected:

  // These members provide a basic set of functions for dealing with a
  // specific instance of ErrLog or a subclass.  They are really intended
  // only for use in configuring the active instance just after it's been
  // created.

  /**
   *  Protected constructor, for use in initializing subclasses.
   *
   *  @param minSeverity Minimum severity of messages that will be logged
   *                     by the default implementation.
   */
  ErrLog( ErrLog::Severity minSeverity = ErrLog::debugging );

  /**
   *  A virtual destructor is provided, but note that the quasi-singleton
   *  design of this class cannot guarantee that the active ErrLog instance
   *  will be deleted at application termination.
   */
  virtual ~ErrLog();

  // Functions used with ErrOptParser.

  virtual bool _configureOptions();
  ErrOptParser& _errOptParser() { return *_itsErrOptParser; }


//-----------------------------------------------------------------------
// Instance protected virtual member functions --------------------------
//-----------------------------------------------------------------------

protected:

  // These members provide the polymorphic interface for customizing the
  // behavior of an ErrLogger implementation.

  /** 
   *  Establishes an std::ostream that is to be used for logging message text,
   *  to be terminated with "endmsg".  Pure virtual.
   */
  virtual ErrStream& doMsg( ErrLog::Severity severity, 
			    const char* facility, 
			    int code ) = 0;

  /**
   *  Callback from ErrStream when a message has been completed, allowing
   *  the logger to decide what to do.
   *
   *  @param text   The full formatted message text, if the ErrStream is a
   *                "buffering" stream, or 0 (null pointer) if it is a
   *                "wrapper" stream, with output actually appearing as 
   *                each stream-output (<<) operation happens.
   *  @param stream The ErrStream to which the message had been directed.
   *                It's provided so that end-of-message output (std::endl or
   *                something more) can be written if appropriate.
   */
  virtual void doEndmsg( const char* text, ErrStream& stream ) = 0;

  /**
   *  Tests whether a message will actually be logged for a particular
   *  set of message parameters.  A default implementation is provided,
   *  based only on a test of the severity w.r.t. a minimum level, a
   *  static member of the ErrLog base class.
   *
   *  The sort of thing a subclass might do here is apply a "verbosity"
   *  control in addition to the severity test, perhaps suppressing
   *  repeated messages with the same parameters.
   */
  virtual bool doLogging( ErrLog::Severity severity, 
			  const char* facility, 
			  int code );


//-----------------------------------------------------------------------
// Static protected member functions ------------------------------------
//-----------------------------------------------------------------------

protected:

  // These members provide tools for ErrLog implementations to use.

  /** 
   *  ErrLog is a form of singleton.  Polymorphism of its static methods is
   *  arranged by calling implementation instances through this pointer.
   */
  inline static ErrLog* _implementation() { return _implInstance; }

  /** 
   *  ErrLog is a form of singleton.  The constructor of any subclass must
   *  register itself as the active instance by calling this function.
   */
  inline static void _setImplementation( ErrLog* implementation );

  /**
   *  Get the appropriate default stream for a given severity level.
   *  The facility and code arguments are not used.
   */
  static ErrStream* _getDefaultStream( ErrLog::Severity, const char*, int );

  /**
   *  Apply the logging test for a given severity level.
   *  The facility and code arguments are not used.
   */
  inline static bool _getDefaultLogging( ErrLog::Severity, const char*, int );

  /**
   *  Write a set of message parameters through into an ErrStream.
   *  This function exists so that subclasses of ErrLog can benefit
   *  from its friendship relationship with ErrStream.
   */
  static void _setParameters( ErrStream&, 
			      ErrLog::Severity, const char*, int );

  /**
   *  Evaluated and executes the default termination strategy for errors:
   *  abort() on fatal, exit(!=0) on severe, with the exit code derived from
   *  the message's "code" value.
   */
  static void _defaultTermination( ErrLog::Severity severity, 
				   int code );

  /**
   *  Tests the anticipated behavior of the default termination strategy.
   *  Available for use by implementations that need to know whether a
   *  termination will result before deciding on the disposition of a
   *  message.
   *
   *  @return 0 if termination will not occur, non-zero if it will.
   *          Negative if by signal (SIGABRT), positive if by exit(!=0).
   *
   *  @see QtrErrLog
   */
  static int _getDefaultTermination( ErrLog::Severity severity, 
				     int code );

//-----------------------------------------------------------------------
// Static private member functions --------------------------------------
//-----------------------------------------------------------------------

private:

  // These functions provide implementation details.

  static void _executeDefaultTermination( int value );


//-----------------------------------------------------------------------
// Static protected member data -----------------------------------------
//-----------------------------------------------------------------------

protected:

  // data members for the static front end and default implementation
  // These appear to be used in implementations.  A future cleanup pass
  // may be able to convert them all to private.
  static ErrStream* _myStreamCout;
  static ErrStream* _myStreamCerr;
  static ErrStream* _myDevnull;
  static Severity _minSeverity;


//-----------------------------------------------------------------------
// Static private member data -------------------------------------------
//-----------------------------------------------------------------------

private:

  // Singleton polymorphic implementation instance.
  static ErrLog* _implInstance;

  // data members for the static front end and default implementation
  static bool  _nestedWarnings;

private:

  bool _useStdio;

  // Contained by reference to avoid introducing header file dependencies:
  ErrOptRef<bool>* _useStdioOpt;
  ErrOptParser* _itsErrOptParser;

  // dis-allow copy construction and assignment
  ErrLog( const ErrLog& );
  ErrLog& operator=( const ErrLog& );

};


// Friends of ErrStream that are needed in implementing the "endmsg" feature.

void endmsg(ErrStream& es); // declare
std::ostream& operator<<(std::ostream& os, void (*fp)(ErrStream & es) );


// --------------------------------------------------------------------------
// Inline implementations ---------------------------------------------------
// --------------------------------------------------------------------------

inline bool 
ErrLog::logging( ErrLog::Severity severity, 
		 const char* facility, 
		 int code )
{
  return ( _implementation() != 0
	   ? _implementation()->doLogging( severity, facility, code )
	   : ErrLog::_getDefaultLogging( severity, facility, code )   );
}


inline void
ErrLog::_setImplementation( ErrLog* implementation )
{
  // "There can only be one" (or zero -- we can reset to the default).
  assert( implementation == 0 || _implementation() == 0 );

  // If the assertion is compiled away, this call is simply ignored.
  if ( implementation == 0 || _implementation() == 0 ) {
    _implInstance = implementation;
  }
}


inline bool 
ErrLog::_getDefaultLogging( ErrLog::Severity severity,
			    const char*,    // unused
			    int )           // unused
{
  return ( severity >= _minSeverity );
}

#endif // ERRLOG_HH