< Previous by Date Date Index Next by Date >
< Previous in Thread Thread Index Next in Thread >

RE: [reSIProcate] external logger


Sounds like there is some interest, attached are the files I modified.
These files are intended to be a starting point.  Since I am a newbie I am
sure they may be a better/cleaner way of adding this.  

Thanks,

-Justin

-----Original Message-----
From: Thomas Gal [mailto:ThomasGal@xxxxxxxxxxxx] 
Sent: Thursday, January 20, 2005 10:08 AM
To: 'Justin Matthews'; resiprocate-devel@xxxxxxxxxxxxxxxxxxx
Subject: RE: [reSIProcate] external logger

I think that's great as no doubt everyone will likely be integrating this
into larger schemes and quite often have their own logging code.

-Tom

thomasgal@xxxxxxxxxxxx  

> -----Original Message-----
> From: resiprocate-devel-bounces@xxxxxxxxxxxxxxxxxxx 
> [mailto:resiprocate-devel-bounces@xxxxxxxxxxxxxxxxxxx] On 
> Behalf Of Justin Matthews
> Sent: Wednesday, January 19, 2005 7:15 PM
> To: resiprocate-devel@xxxxxxxxxxxxxxxxxxx
> Subject: [reSIProcate] external logger
> 
> Hello,
> 
> The application that I am working on that uses resip has its 
> own logging functionality.  The logger in our app ("myapp") 
> has some enhanced features similar to those found in log4j 
> (logging configurable using a config file, different outputs 
> including sockets, syslog, rolling file appenders...).
> What I would like is for myapp to be in total control of the 
> logging.  I have modified resip by adding a new 
> Log::initialize() function that takes a pointer to a function 
> as the only argument and modified Logger.hxx to forward all 
> log requests to this function if the external function 
> pointer "type" logger is enabled.  Is this, or a similar 
> solution, something that would be considered for addition 
> into resip?  Does anyone have any other suggestions for 
> adding this functionality?
> 
> Thanks,
> 
> Justin
> 
> _______________________________________________
> resiprocate-devel mailing list
> resiprocate-devel@xxxxxxxxxxxxxxxxxxx
> https://list.sipfoundry.org/mailman/listinfo/resiprocate-devel
> 

#if !defined(RESIP_LOGGER_HXX)
#define RESIP_LOGGER_HXX 

#include <iosfwd>
#include <sstream>

#include "resiprocate/os/Log.hxx"
#include "resiprocate/os/Lock.hxx"

#ifdef WIN32

#include "resiprocate/os/DataStream.hxx"

#include "resiprocate/os/Data.hxx"

#include <windows.h>

#endif 



/**
   Defines a set of logging macros, one for each level of logging.

   Example:
#include Logger.hxx
#define RESIPROCATE_SUBSYSTEM resip::Subsystem::SIP
   ...
   DebugLog(<< "hi there " << mix << 4 << types);  // note leading << and no 
endl
*/


#define DELIM 


// unconditionally output to cerr -- easily change back and forth
#define CerrLog(args_)                                                  \
do                                                                      \
{                                                                       \
    if( resip::Log::_type == resip::Log::externalFunc )                 \
    {                                                                   \
        std::ostringstream oss;                                         \
        oss args_;                                                      \
        resip::Log::externalLoggerFunction_(resip::Log::Err,"not 
implemented",__FILE__,__LINE__,oss.str().c_str()); \
    }                                                                   \
    else                                                                \
    {                                                                   \
        resip::Log::tags(resip::Log::StdErr, RESIPROCATE_SUBSYSTEM,     \
                   __FILE__, __LINE__, std::cerr) << ' ' << '|' << ' '  \
          args_ << std::endl;                                           \
    }                                                                   \
} while(0)

#define StackLog(args_)                                                         
\
GenericLog(RESIPROCATE_SUBSYSTEM, resip::Log::Stack, args_)

#define DebugLog(args_) \
GenericLog(RESIPROCATE_SUBSYSTEM, resip::Log::Debug, args_)

#define InfoLog(args_) \
GenericLog(RESIPROCATE_SUBSYSTEM, resip::Log::Info, args_)

#define WarningLog(args_) \
GenericLog(RESIPROCATE_SUBSYSTEM, resip::Log::Warning, args_)

#define ErrLog(args_) \
GenericLog(RESIPROCATE_SUBSYSTEM, resip::Log::Err, args_)

#define CritLog(args_) \
GenericLog(RESIPROCATE_SUBSYSTEM, resip::Log::Crit, args_)

#define CHECK_RECURSIVE_LOG
class AssertOnRecursiveLock
{
   public:
      AssertOnRecursiveLock();
      void set();
      ~AssertOnRecursiveLock();
   private:
      // no object semantics
      AssertOnRecursiveLock(const AssertOnRecursiveLock &);
      const AssertOnRecursiveLock & operator=(const AssertOnRecursiveLock &);
};


// do/while allows a {} block in an expression
#define GenericLog(system_, level_, args_)                                      
\
do                                                                              
\
{                                                                               
\
if( resip::Log::_type == resip::Log::externalFunc )                             
\
{                                                                               
\
    std::ostringstream oss;                                                     
\
    oss args_;                                                                  
\
    resip::Log::externalLoggerFunction_(level_,"not 
implemented",__FILE__,__LINE__,oss.str().c_str()); \
}                                                                               
\
else                                                                            
\
{                                                                               
\
   const resip::Log::ThreadSetting* setting = resip::Log::getThreadSetting();   
\
   if (setting)                                                                 
\
   {                                                                            
\
      if (level_ <= setting->level)                                             
\
      {                                                                         
\
         AssertOnRecursiveLock check;                                           
\
         resip::Lock lock(resip::Log::_mutex);                                  
\
         check.set();                                                           
\
         resip::Log::tags(level_, system_, __FILE__, __LINE__,                  
\
                          resip::GenericLogImpl::Instance()) << " | "           
\
                          args_ << std::endl;                                   
\
         if (resip::Log::_type == resip::Log::VSDebugWindow)                    
\
         {                                                                      
\
            resip::GenericLogImpl::OutputToWin32DebugWindow();                  
\
         }                                                                      
\
      }                                                                         
\
   }                                                                            
\
   else                                                                         
\
   {                                                                            
\
      if (resip::GenericLogImpl::isLogging(level_))                             
\
      {                                                                         
\
         AssertOnRecursiveLock check;                                           
\
         resip::Lock lock(resip::Log::_mutex);                                  
\
         check.set();                                                           
\
         if (resip::GenericLogImpl::isLogging(level_))                          
\
         {                                                                      
\
            resip::Log::tags(level_, system_, __FILE__, __LINE__,               
\
                             resip::GenericLogImpl::Instance()) << " | "        
\
                             args_ << std::endl;                                
\
            if (resip::Log::_type == resip::Log::VSDebugWindow)                 
\
            {                                                                   
\
               resip::GenericLogImpl::OutputToWin32DebugWindow();               
\
            }                                                                   
\
         }                                                                      
\
      }                                                                         
\
   }                                                                            
\
}                                                                               
\
} while (0) 

#ifdef NO_DEBUG
// Suppress debug logging at compile time
#undef DebugLog
#define DebugLog(args_)
#undef StackLog(args_)
#define StackLog(args_)
#endif

namespace resip
{

class GenericLogImpl :  public Log 
{
   public:
      static std::ostream& Instance();
      static bool isLogging(Log::Level level) ;
      static unsigned int MaxLineCount;
      static void OutputToWin32DebugWindow(); //xkd-2004-11-8


   private:
      static std::ostream* mLogger;
      static unsigned int mLineCount;
#ifdef WIN32
      static Data *mWin32DebugData;
      static DataStream *mWin32DebugStream;
#endif

};
 
}

#endif

/* ====================================================================
 * The Vovida Software License, Version 1.0 
 * 
 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
 *    and "Vovida Open Communication Application Library (VOCAL)" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact vocal@xxxxxxxxxxx
 *
 * 4. Products derived from this software may not be called "VOCAL", nor
 *    may "VOCAL" appear in their name, without prior written
 *    permission of Vovida Networks, Inc.
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * 
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by Vovida
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
 * Inc.  For more information on Vovida Networks, Inc., please see
 * <http://www.vovida.org/>.
 *
 */
#include "resiprocate/os/Socket.hxx"

#include <cassert>
#include <iostream>
#include <stdio.h>
#include "resiprocate/os/Data.hxx"

#ifndef WIN32
#include <sys/time.h>
#endif

#include <sys/types.h>
#include <time.h>

#include "resiprocate/os/Log.hxx"
#include "resiprocate/os/Lock.hxx"
#include "resiprocate/os/WinLeakCheck.hxx"

#define DELIM " | "

using namespace resip;
using namespace std;

Log::Level Log::_level = Log::Debug;
Log::Type Log::_type = Cout;
ExternalLoggerFunction Log::externalLoggerFunction_=NULL;
Data Log::_appName;
Data Log::_hostname;
Data Log::_logFileName;

#ifdef WIN32
        int Log::_pid=0;
#else 
        pid_t Log::_pid=0;
#endif


volatile short Log::touchCount = 0;

#ifndef WIN32
pthread_key_t Log::_levelKey;
HashMap<pthread_t, std::pair<Log::ThreadSetting, bool> > Log::_threadToLevel;
HashMap<int, std::set<pthread_t> > Log::_serviceToThreads;
#endif

HashMap<int, Log::Level> Log::_serviceToLevel;

const char
Log::_descriptions[][32] = {"NONE", "EMERG", "ALERT", "CRIT", "ERR", "WARNING", 
"NOTICE", "INFO", "DEBUG", "STACK", "CERR", ""}; 

Mutex Log::_mutex;

extern "C"
{
   void freeThreadSetting(void* setting)
   {
      delete static_cast<Log::ThreadSetting*>(setting);
   }
}

void
Log::initialize(const char* typed, const char* leveld, const char* appName, 
const char *logFileName)
{
   Log::initialize(Data(typed), Data(leveld), Data(appName), logFileName);
}

void
Log::initialize(const Data& typed, const Data& leveld, const Data& appName, 
const char *logFileName)
{
   Type type = Log::Cout;
   if (isEqualNoCase(typed, "cout")) type = Log::Cout;
   else if (isEqualNoCase(typed, "cerr")) type = Log::Cerr;
   else if (isEqualNoCase(typed, "file")) type = Log::File;
   else type = Log::Syslog;
   
   Level level = Log::Info;
   level = toLevel(leveld);

   Log::initialize(type, level, appName, logFileName);
}

void
Log::initialize(ExternalLoggerFunction func)
{
    externalLoggerFunction_ = func;
    _type = externalFunc;
}

void 
Log::initialize(Type type, Level level, const Data& appName, const char * 
logFileName)
{
   string copy(appName.c_str());
   
   _type = type;
   _level = level;

    if (logFileName)
    {
           _logFileName = logFileName;
    }

   string::size_type pos = copy.find_last_of('/');
   if ( pos == string::npos || pos == copy.size())
   {
      _appName = appName;
   }
   else
   {
      _appName = Data(copy.substr(pos+1).c_str());
   }
 
   char buffer[1024];  
   gethostname(buffer, sizeof(buffer));
   _hostname = buffer;
#ifdef WIN32 
   _pid = (int)GetCurrentProcess();
#else
   _pid = getpid();
#endif
   
#ifndef WIN32
   pthread_key_create(&Log::_levelKey, freeThreadSetting);
#endif
}

void
Log::setLevel(Level level)
{
   Lock lock(_mutex);
   _level = level; 
}

Data
Log::toString(Level l)
{
   return Data("LOG_") + _descriptions[l+1];
}

Log::Level
Log::toLevel(const Data& l)
{
   string pri = l.c_str();
   if (pri.find("LOG_", 0) == 0)
   {
      pri.erase(0, 4);
   }
   
   int i=0;
   while (string(_descriptions[i]).size())
   {
      if (pri == string(_descriptions[i])) 
      {
         return Level(i-1);
      }
      i++;
   }

   cerr << "Choosing Debug level since string was not understood: " << l << 
endl;
   return Log::Debug;
}

Log::Type
Log::toType(const Data& arg)
{
   if (arg == "cout" || arg == "COUT")
   {
      return Log::Cout;
   }
   else if (arg == "cerr" || arg == "CERR")
   {
      return Log::Cerr;
   }
   else if (arg == "file" || arg == "FILE")
   {
      return Log::File;
   }
   else
   {
      return Log::Syslog;
   }
}

ostream&
Log::tags(Log::Level level, 
          const Subsystem& subsystem, 
          const char* pfile,
          int line,
          ostream& strm) 
{
#if defined( __APPLE__ )
   strm << _descriptions[level+1] << DELIM 
        << time(0) << DELIM 
        << _appName << DELIM
        << subsystem << DELIM
        << pfile << ":" << line;
#else   
   char buffer[256];
   Data tstamp(Data::Borrow, buffer, sizeof(buffer));
#if defined( WIN32 )
   const char* file = pfile + strlen(pfile);
   while (file != pfile &&
          *file != '\\')
   {
      --file;
   }
   if (file != pfile)
   {
      ++file;
   }
   strm << _descriptions[level+1] << DELIM
        << timestamp(tstamp) << DELIM  
        << _appName << DELIM
        << subsystem << DELIM 
        << GetCurrentThreadId() << DELIM
        << file << ":" << line;
#else
   strm << _descriptions[level+1] << DELIM
        << timestamp() << DELIM  
        << _hostname << DELIM  
        << _appName << DELIM
        << subsystem << DELIM 
        << _pid << DELIM
        << pthread_self() << DELIM
        << pfile << ":" << line;
#endif // #if defined( WIN32 ) 
#endif // #if defined( __APPLE__ )
  return strm;
}

Data
Log::timestamp()
{
   char buffer[256];
   Data result(Data::Borrow, buffer, sizeof(buffer));
   return timestamp(result);
}

Data&
Log::timestamp(Data& res) 
{
   char* datebuf = const_cast<char*>(res.data());
   const unsigned int datebufSize = 256;
   res.clear();
   
#ifdef WIN32 
   int result = 1; 
   SYSTEMTIME systemTime;
   struct { int tv_sec; int tv_usec; } tv = {0,0};
   time((time_t *)(&tv.tv_sec));
   GetLocalTime(&systemTime);
   tv.tv_usec = systemTime.wMilliseconds * 1000; 
#else 
   struct timeval tv; 
   int result = gettimeofday (&tv, NULL);
#endif   

   if (result == -1)
   {
      /* If we can't get the time of day, don't print a timestamp.
         Under Unix, this will never happen:  gettimeofday can fail only
         if the timezone is invalid which it can't be, since it is
         uninitialized]or if tv or tz are invalid pointers. */
      datebuf [0] = 0;
   }
   else
   {
      /* The tv_sec field represents the number of seconds passed since
         the Epoch, which is exactly the argument gettimeofday needs. */
      const time_t timeInSeconds = (time_t) tv.tv_sec;
      strftime (datebuf,
                datebufSize,
                "%Y%m%d-%H%M%S", /* guaranteed to fit in 256 chars,
                                    hence don't check return code */
                localtime (&timeInSeconds));
   }
   
   char msbuf[5];
   /* Dividing (without remainder) by 1000 rounds the microseconds
      measure to the nearest millisecond. */
   sprintf(msbuf, ".%3.3ld", long(tv.tv_usec / 1000));

   int datebufCharsRemaining = datebufSize - strlen (datebuf);
   strncat (datebuf, msbuf, datebufCharsRemaining - 1);

   datebuf[datebufSize - 1] = '\0'; /* Just in case strncat truncated msbuf,
                                        thereby leaving its last character at
                                        the end, instead of a null terminator */

   // ugh, resize the Data
   res.at(strlen(datebuf)-1);
   return res;
}

Log::Level 
Log::getServiceLevel(int service)
{
   Lock lock(_mutex);
#ifdef WIN32
        assert(0);
        return Bogus;
#else
   HashMap<int, Level>::iterator res = Log::_serviceToLevel.find(service);
   if(res == Log::_serviceToLevel.end())
   {
      //!dcm! -- should perhaps throw an exception here, instead of setting a
      //default level of LOG_ERROR, but nobody uses this yet
      Log::_serviceToLevel[service] = Err;
      return Err;
   }
   return res->second;
#endif
}
   
const Log::ThreadSetting*
Log::getThreadSetting()
{
#ifdef WIN32
   return 0;
#else
   ThreadSetting* setting = 
static_cast<ThreadSetting*>(pthread_getspecific(Log::_levelKey));
   if (setting == 0)
   {
      return 0;
   }
   if (Log::touchCount > 0)
   {
      Lock lock(_mutex);
      pthread_t thread = pthread_self();
      HashMap<pthread_t, pair<ThreadSetting, bool> >::iterator res = 
Log::_threadToLevel.find(thread);
      assert(res != Log::_threadToLevel.end());
      if (res->second.second)
      {
         setting->level = res->second.first.level;
         res->second.second = false;
         touchCount--;
//         cerr << "**Log::getThreadSetting:touchCount: " << Log::touchCount << 
"**" << endl;

         //cerr << "touchcount decremented" << endl;
      }
   }
   return setting;
#endif
}

void 
Log::setThreadSetting(int serv)
{
   Log::setThreadSetting(ThreadSetting(serv, getServiceLevel(serv)));
}

void 
Log::setThreadSetting(int serv, Log::Level l)
{
   Log::setThreadSetting(ThreadSetting(serv, l));
}

void 
Log::setThreadSetting(ThreadSetting info)
{
#ifdef WIN32
        assert(0);
#else
   //cerr << "Log::setThreadSetting: " << "service: " << info.service << " 
level " << toString(info.level) << " for " << pthread_self() << endl;
   pthread_t thread = pthread_self();
   pthread_setspecific(_levelKey, (void *) new ThreadSetting(info));
   Lock lock(_mutex);

   if (Log::_threadToLevel.find(thread) != Log::_threadToLevel.end())
   {
      if (Log::_threadToLevel[thread].second == true)
      {
         touchCount--;
      }
   }
   Log::_threadToLevel[thread].first = info;
   Log::_threadToLevel[thread].second = false;
   Log::_serviceToThreads[info.service].insert(thread);
#endif
}
   
void 
Log::setServiceLevel(int service, Level l)
{
   Lock lock(_mutex);
   Log::_serviceToLevel[service] = l;
#ifdef WIN32
        assert(0);
#else
   set<pthread_t>& threads = Log::_serviceToThreads[service];
   for (set<pthread_t>::iterator i = threads.begin(); i != threads.end(); i++)
   {
      Log::_threadToLevel[*i].first.level = l;
      Log::_threadToLevel[*i].second = true;
   }
   Log::touchCount += threads.size();
#endif
//   cerr << "**Log::setServiceLevel:touchCount: " << Log::touchCount << "**" 
<< endl;
}

   
/* ====================================================================
 * The Vovida Software License, Version 1.0 
 * 
 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
 *    and "Vovida Open Communication Application Library (VOCAL)" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact vocal@xxxxxxxxxxx
 *
 * 4. Products derived from this software may not be called "VOCAL", nor
 *    may "VOCAL" appear in their name, without prior written
 *    permission of Vovida Networks, Inc.
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * 
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by Vovida
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
 * Inc.  For more information on Vovida Networks, Inc., please see
 * <http://www.vovida.org/>.
 *
 */
#if !defined(RESIP_LOG_HXX)
#define RESIP_LOG_HXX 

#include "resiprocate/os/Data.hxx"

#ifndef WIN32
#include <syslog.h>
#include <unistd.h>
#include <pthread.h>
#endif

#include <set>

#include "resiprocate/os/Subsystem.hxx"
#include "resiprocate/os/Mutex.hxx"
#include "resiprocate/os/HashMap.hxx"
#include <iostream>

/** The external logging function pointer is used with a corresponding call to 
Log::initialize.
  * This function is intended to be supplied by the user of resip to handle all 
logging events and
  * bypass the internal logging provided by the resip library.
  * @author justin.matthews@xxxxxxx
  * @revision 1/19/2005
  */
typedef void (*ExternalLoggerFunction)(int level, const char *subsystem, const 
char *file, int line, const char *message);

namespace resip
{

class Log
{
   public:
      typedef enum 
      {
         Cout = 0,
         Syslog, 
         File, 
         Cerr,
         VSDebugWindow,   // Use only for Visual Studio Debug Window logging - 
WIN32 must be defined
         externalFunc,
      } Type;
      
      typedef enum 
      {
         None = -1,
#ifdef WIN32
         Crit = 2,
         Err = 3,
         Warning = 4,
         Info = 6,
         Debug = 7,
#else
         Crit = LOG_CRIT,
// #ifdef ERR // ncurses defines a macro called ERR 
//          SIP2_ERR = LOG_ERR,
// #else
//          ERR = LOG_ERR,
// #endif
         Err,
         Warning = LOG_WARNING,
         Info = LOG_INFO,
         Debug = LOG_DEBUG,
#endif
         Stack = 8,
         StdErr = 9,
         Bogus = 666
      } Level;

      class ThreadSetting
      {
         public:
            ThreadSetting()
               : service(-1),
               level(Err)
            {}

            ThreadSetting(int serv, Level l)
               : service(serv),
                 level(l)
            {}

            ThreadSetting(const ThreadSetting& rhs)
               : service(rhs.service),
                 level(rhs.level)
            {}
            
            int service;
            Level level;
      };

      /// Return the loglevel, hostname, appname, pid, tid, subsystem
      static std::ostream& tags(Log::Level level, 
                                const Subsystem& subsystem, 
                                const char* file,
                                int line,
                                std::ostream& strm);
      static Data& timestamp(Data& result);
      static Data timestamp();
      static void initialize(Type type,
                             Level level,
                             const Data& appName,
                             const char * logFileName = 0);
      static void initialize(const Data& type,
                             const Data& level,
                             const Data& appName,
                             const char * logFileName = 0);
      static void initialize(const char* type,
                             const char* level,
                             const char* appName,
                             const char * logFileName = 0);

      /** Add this init to allow the user of this library to
        * provide there own logging service.
        * @author justin.matthews@xxxxxxx
        * @revision 1/19/2005
        */
      static void initialize(ExternalLoggerFunction);

      static void setLevel(Level level);
      static Level level() { return _level; }
      static Level toLevel(const Data& l);
      static Type toType(const Data& t);
      static Data toString(Level l);
      static Mutex _mutex;

      static void setServiceLevel(int service, Level l);
      static Level getServiceLevel(int service);

      static const ThreadSetting* getThreadSetting();
      static void setThreadSetting(ThreadSetting info);
      static void setThreadSetting(int serv, Level l);
      static void setThreadSetting(int serv);
      static volatile short touchCount;
      static Type _type;
      static ExternalLoggerFunction externalLoggerFunction_;
   protected:
      static Level _level;
      static Data _appName;
      static Data _hostname;
      static Data _logFileName;
      
#ifndef WIN32
      static pid_t _pid;
#else   
      static int _pid;
#endif
      static const char _descriptions[][32];
      static HashMap<int, Level> _serviceToLevel;

#ifndef WIN32
      static HashMap<pthread_t, std::pair<ThreadSetting, bool> > _threadToLevel;
      static HashMap<int, std::set<pthread_t> > _serviceToThreads;
      static pthread_key_t _levelKey;
#endif
};

}

#endif

/* ====================================================================
 * The Vovida Software License, Version 1.0 
 * 
 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
 *    and "Vovida Open Communication Application Library (VOCAL)" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact vocal@xxxxxxxxxxx
 *
 * 4. Products derived from this software may not be called "VOCAL", nor
 *    may "VOCAL" appear in their name, without prior written
 *    permission of Vovida Networks, Inc.
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * 
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by Vovida
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
 * Inc.  For more information on Vovida Networks, Inc., please see
 * <http://www.vovida.org/>.
 *
 */