Skip to content
Snippets Groups Projects
ErrLog.cc 9.34 KiB
Newer Older
//--------------------------------------------------------------------------
//
// Description:
//
//  Error & logging abstract base class
//
// Environment:
//	Software developed for the BaBar Detector at the SLAC B-Factory.
//
// Author List:
//	Bob Jacobsen
//      $Id: ErrLog.cc,v 1.1.1.1 2005/03/29 17:04:19 steinke Exp $
//
// Copyright Information:
//	Copyright (C) 1998	Lawrence Berkeley Laboratory
//
//------------------------------------------------------------------------

//-----------------------
// This Class's Header --
//-----------------------
#include "ErrLogger/ErrLog.hh"

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

#include <iostream>
#include <sstream>
#include <string>
#include <cstdlib>

//-------------------------------
// Collaborating Class Headers --
//-------------------------------
#include "ErrLogger/ErrStreamWrapper.hh"
#include "ErrLogger/ErrOpt.hh"
#include "ErrLogger/ErrOptParser.hh"
using std::cerr;
using std::cout;
using std::ios;
using std::ostream;
using std::ostringstream;
using std::stringstream;


//		--------------------------------
// 		-- Local Variable Definitions --
//		--------------------------------
static ostringstream devnull;

//		---------------------------------
// 		-- Global Variable Definitions --
//		---------------------------------

//		---------------------------------
// 		-- Global Function Definitions --
//		---------------------------------


// static size_t
// strnlen( const char* string, size_t maximum )
// {
//   for ( size_t length = 0; length < maximum; length++ ) {
//     if (string[length] == '\0') return length;
//   }
//   return maximum;
// }



ErrLog::ErrLog( ErrLog::Severity minSeverity )
  : _useStdio(true)
  , _useStdioOpt( new ErrOptRef<bool>("stdio", ErrOpt::Optional, _useStdio) )
  , _itsErrOptParser( new ErrOptParser )
{
  // It is assumed that any subclass constructor calling this protected
  // constructor will be establishing itself as the active ErrLog instance.
  // So we allow it to set the default minimum severity in the static front
  // end to ErrLog.
  _minSeverity = minSeverity;
}


ErrLog::~ErrLog()
{
  delete _itsErrOptParser;
  delete _useStdioOpt;
}


ostream& 
ErrLog::msg( ErrLog::Severity severity, 
	     const char* facility, 
	     int code )
{
  if ( ErrStream::activeStream() != 0 
       && ErrStream::activeStream()->inUse() ) {
    // A new message is being started before the last one has been
    // completed with endmsg.

    // 1) Flush what has come of the original message so far.
    // 2) Optionally write a warning about this.
    // 3) Continue on to establish the new message.

    // 1) We complete the previous pending message.  This is important so
    //    that, if it's buffered, it will be written with its original
    //    severity and other parameters.
    ErrStream* prevStream = ErrStream::activeStream();

    if ( _nestedWarnings ) {
      static_cast<ostream&>(*prevStream) 
	<< " [interrupted by nested message from " << facility << "]";
    }
    endmsg( *prevStream );

    if ( _nestedWarnings ) {
      // 2) Warn about this usage.
      
      // Need to trap the facility and code, because they will be 
      // overwritten by the ErrMsg call itself.
      std::string prevFacility( prevStream->facility() );
      int prevCode = prevStream->code();

      ErrMsg(warning) << "\n *** ErrLogger Warning *** \n"
		      << "The ErrStream was previously used without\n"
		      << "endmsg being called to end the error message.\n"
		      << "Please see ErrLogger/ErrStream.hh for more\n"
		      << "information.\n\n"
		      << " Error Location/Facility: " << prevFacility
		      << " [code " << prevCode << "]\n"
		      << " *** ErrLogger Warning Finished ***\n"
		      << endmsg;
    }

    // 3) Now we can proceed.
  }

  // initialize result
  ErrStream* resultPtr = 0;

  if (_implementation() != 0) {
    resultPtr = & ( _implementation()->doMsg( severity, facility, code ) );
  }
  else {
    resultPtr = _getDefaultStream( severity, facility, code );
  } // end else

  // check that resultPtr is set
  assert( resultPtr!=0 );

  // Save the message parameters.
  _setParameters( *resultPtr, severity, facility, code );

  resultPtr->activate();

  // mark this stream as in use
  resultPtr->setInUse(true);
      
  return static_cast<ostream&>( *resultPtr );
}



ostream& 
ErrLog::msg( ErrLog::Severity severity, 
	     const char* facility, 
	     const char* code )
{
  // This implementation is provided for backward compatibility with the
  // old, misunderstanding-derived notion that the "const char* code"
  // form of msg() should be the primary one.  Now (June 2002) that we
  // have switched to the original intent, where "int code" is the 
  // primary interface, we still provide this interface, in a way which
  // does almost exactly what the old one did in practice in all the
  // existing ErrLog implementations: append "(" + code + ")" to the
  // facility.

  std::string newfac( facility );
  newfac += "(";
  newfac += code;
  newfac += ")";

  return ErrLog::msg( severity, newfac.c_str(), 0 );
}


bool
ErrLog::logging( ErrLog::Severity severity, 
		 const char* facility, 
		 const char* code )
{
  // See msg( ErrLog::Severity severity, const char* facility, 
  //          const char* code ) above.  Note that these string
  // manipulations are a lot of work to do if you just want to check 
  // to see whether a particular class of message is being logged!

  std::string newfac( facility );
  newfac += "(";
  newfac += code;
  newfac += ")";

  return ErrLog::logging( severity, newfac.c_str(), 0 );
}



const char*
ErrLog::severityName( ErrLog::Severity severity )
{
  switch ( severity ) {
  case ErrLog::debugging:
    return "debugging";
  case ErrLog::trace:
    return "trace";
  case ErrLog::routine:
    return "routine";
  case ErrLog::warning:
    return "warning";
  case ErrLog::error:
    return "error";
  case ErrLog::severe:
    return "severe";
  case ErrLog::fatal:
    return "fatal";
  }

  return "?invalid-severity?";
}



char
ErrLog::severityLetter( ErrLog::Severity severity )
{
  switch ( severity ) {
  case ErrLog::debugging:
    return 'D';
  case ErrLog::trace:
    return 'T';
  case ErrLog::routine:
    return 'R';
  case ErrLog::warning:
    return 'W';
  case ErrLog::error:
    return 'E';
  case ErrLog::severe:
    return 'S';
  case ErrLog::fatal:
    return 'F';
  }

  return '?';
}



bool 
ErrLog::doLogging( ErrLog::Severity severity,
		   const char* facility,
		   int code )
{
  return ErrLog::_getDefaultLogging( severity, facility, code );
}


ErrStream* 
ErrLog::_getDefaultStream( ErrLog::Severity severity,
			   const char* facility,
			   int code )
{
  // initialize result
  ErrStream* result = 0;

  // here use the default implementation
  if ( _myStreamCerr==0 )
    _myStreamCerr = new ErrStreamWrapper(cerr);
  if ( _myStreamCout==0 )
    _myStreamCout = new ErrStreamWrapper(cout);
  if ( _myDevnull==0 )
    _myDevnull    = new ErrStreamWrapper(devnull);

  // select which stream to write to
  if ( logging( severity, facility, code ) ) {
    // pick the correct stream
    if ( severity>=warning )
      result = _myStreamCerr;
    else
      result = _myStreamCout;
  } // end if logging
  else { // not logging
    // send to local char stream after resetting to start over
    devnull.rdbuf()->pubseekoff(0, ios::beg,ios::out);
    result = _myDevnull;
  } // end if

  return result;
}


void
ErrLog::_setParameters( ErrStream& es,
			ErrLog::Severity severity,
			const char* facility,
			int code )
{
  es.setParameters( severity, facility, code );
}

void
ErrLog::_defaultTermination( ErrLog::Severity severity, 
			     int code )
{
  _executeDefaultTermination( _getDefaultTermination( severity, code ) );
}

int
ErrLog::_getDefaultTermination( ErrLog::Severity severity, 
				int code )
{
  int result = 0;
  if ( severity == ErrLog::severe ) {
    // Default behavior for this level is to exit with a non-zero status.
    // That status is determined based on the "code".  For a code of zero,
    // the default exit code is used.
    if ( code == 0 ) {
      result = DefaultExitCode;
    }
    else {
      // Otherwise we take the low-order seven bits of the "code", so
      // that we stay in the range shells understand as exit()-driven
      // process terminations.
      result = code & 0x7f;

      // Except we never allow that to be zero, either.  We assign a
      // special code as a warning.
      if ( result == 0 ) result = DefaultExitCode - 1;
    }
  }
  else if ( severity == ErrLog::fatal ) {
    // Return the value that indicates "abort".
    result = -1;
  }

  return result;
}

void
ErrLog::_executeDefaultTermination( int value )
{
  if ( value == -1 ) ::abort();
  else if ( value > 0 ) ::exit( value );
}

void
ErrLog::turnOffWarnings()
{
  _nestedWarnings = false;
}

bool
ErrLog::_configureOptions()
{
  bool ok = true;
  ok = ok && _errOptParser().registerNegatableOption( *_useStdioOpt );
  return ok;
}

bool 
ErrLog::parseOpts( const char* optarg ) 
{
  return _errOptParser().parseOpts(optarg);
}


// Protected static member initializers.

ErrStream*       ErrLog::_myStreamCerr = 0;
ErrStream*       ErrLog::_myStreamCout = 0;
ErrStream*       ErrLog::_myDevnull = 0;
ErrLog::Severity ErrLog::_minSeverity = ErrLog::debugging;


// Private static member initializers.

ErrLog*          ErrLog::_implInstance   = 0;
bool             ErrLog::_nestedWarnings = true;