| < Previous by Date | Date Index | Next by Date > |
| < Previous in Thread | Thread Index | Next in Thread > |
|
My proposed fix is attached: Log.cxx and Log.hxx (including the isEqualNoCase thing), changes based off of resiprocate 1.9.6. -John Gregg On 05/14/2014 01:33 PM, slgodin@xxxxxxxxx wrote:
|
#include "rutil/Socket.hxx"
#include <cassert>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include "rutil/Data.hxx"
#ifndef WIN32
#include <sys/time.h>
#endif
#include <sys/types.h>
#include <time.h>
#include "rutil/Log.hxx"
#include "rutil/Logger.hxx"
#include "rutil/ParseBuffer.hxx"
#include "rutil/ThreadIf.hxx"
#include "rutil/Subsystem.hxx"
#include "rutil/SysLogStream.hxx"
#include "rutil/WinLeakCheck.hxx"
using namespace resip;
using namespace std;
const Data Log::delim(" | ");
Log::ThreadData Log::mDefaultLoggerData(0, Log::Cout, Log::Info, NULL, NULL);
Data Log::mAppName;
Data Log::mHostname;
unsigned int Log::MaxLineCount = 0; // no limit by default
unsigned int Log::MaxByteCount = 0; // no limit by default
#ifdef WIN32
int Log::mPid=0;
#else
pid_t Log::mPid=0;
#endif
volatile short Log::touchCount = 0;
/// DEPRECATED! Left for backward compatibility - use localLoggers instead
#ifdef LOG_ENABLE_THREAD_SETTING
#if defined(__APPLE__) || defined(__CYGWIN__)
HashValueImp(ThreadIf::Id, (size_t)data);
#endif
HashMap<ThreadIf::Id, std::pair<Log::ThreadSetting, bool> > Log::mThreadToLevel;
HashMap<int, std::set<ThreadIf::Id> > Log::mServiceToThreads;
ThreadIf::TlsKey* Log::mLevelKey;
#endif
HashMap<int, Log::Level> Log::mServiceToLevel;
Log::LocalLoggerMap Log::mLocalLoggerMap;
ThreadIf::TlsKey* Log::mLocalLoggerKey;
const struct Log::descriptionEntry Log::mDescriptions[] =
{
{ None, "NONE" },
{ Crit, "CRIT" },
{ Crit, "CRITICAL" },
{ Err, "ERR" },
{ Err, "ERROR" },
{ Warning, "WARNING" },
{ Warning, "WARN" },
{ Info, "INFO" },
{ Debug, "DEBUG" },
{ Stack, "STACK" },
{ StdErr, "CERR" },
{ Bogus, NULL }
};
Mutex Log::_mutex;
extern "C"
{
void freeThreadSetting(void* setting)
{
delete static_cast<Log::ThreadSetting*>(setting);
}
void freeLocalLogger(void* pThreadData)
{
if (pThreadData)
{
// There was some local logger installed. Decrease its use count before we
// continue.
Log::mLocalLoggerMap.decreaseUseCount((static_cast<Log::ThreadData*>(pThreadData))->id());
}
}
}
unsigned int LogStaticInitializer::mInstanceCounter=0;
LogStaticInitializer::LogStaticInitializer()
{
if (mInstanceCounter++ == 0)
{
#ifdef LOG_ENABLE_THREAD_SETTING
Log::mLevelKey = new ThreadIf::TlsKey;
ThreadIf::tlsKeyCreate(*Log::mLevelKey, freeThreadSetting);
#endif
Log::mLocalLoggerKey = new ThreadIf::TlsKey;
ThreadIf::tlsKeyCreate(*Log::mLocalLoggerKey, freeLocalLogger);
}
}
LogStaticInitializer::~LogStaticInitializer()
{
if (--mInstanceCounter == 0)
{
#ifdef LOG_ENABLE_THREAD_SETTING
ThreadIf::tlsKeyDelete(*Log::mLevelKey);
delete Log::mLevelKey;
#endif
ThreadIf::tlsKeyDelete(*Log::mLocalLoggerKey);
delete Log::mLocalLoggerKey;
}
}
void
Log::initialize(const char* typed, const char* leveld, const char* appName, const char *logFileName, ExternalLogger* externalLogger)
{
Log::initialize(Data(typed), Data(leveld), Data(appName), logFileName, externalLogger);
}
void
Log::initialize(const Data& typed, const Data& leveld, const Data& appName,
const char *logFileName, ExternalLogger* externalLogger)
{
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, externalLogger);
}
void
Log::initialize(Type type, Level level, const Data& appName,
const char * logFileName,
ExternalLogger* externalLogger)
{
Lock lock(_mutex);
mDefaultLoggerData.reset();
mDefaultLoggerData.set(type, level, logFileName, externalLogger);
ParseBuffer pb(appName);
pb.skipToEnd();
#ifdef _WIN32
pb.skipBackToChar('\\');
#else
pb.skipBackToChar('/');
#endif
mAppName = pb.position();
char buffer[1024];
gethostname(buffer, sizeof(buffer));
mHostname = buffer;
#ifdef WIN32
mPid = (int)GetCurrentProcess();
#else
mPid = getpid();
#endif
}
void
Log::initialize(Type type,
Level level,
const Data& appName,
ExternalLogger& logger)
{
initialize(type, level, appName, 0, &logger);
}
void
Log::setLevel(Level level)
{
Lock lock(_mutex);
getLoggerData().mLevel = level;
}
void
Log::setLevel(Level level, Subsystem& s)
{
Lock lock(_mutex);
s.setLevel(level);
}
void
Log::setLevel(Level level, Log::LocalLoggerId loggerId)
{
if (loggerId)
{
ThreadData *pData = mLocalLoggerMap.getData(loggerId);
if (pData)
{
// Local logger found. Set logging level.
pData->mLevel = level;
// We don't need local logger instance anymore.
mLocalLoggerMap.decreaseUseCount(loggerId);
pData = NULL;
}
}
else
{
Lock lock(_mutex);
mDefaultLoggerData.mLevel = level;
}
}
Log::Level
Log::level(Log::LocalLoggerId loggerId)
{
Level level;
ThreadData *pData;
if (loggerId && (pData = mLocalLoggerMap.getData(loggerId)))
{
// Local logger found. Set logging level.
level = pData->mLevel;
// We don't need local logger instance anymore.
mLocalLoggerMap.decreaseUseCount(loggerId);
pData = NULL;
}
else
{
Lock lock(_mutex);
level = mDefaultLoggerData.mLevel;
}
return level;
}
void
Log::setMaxLineCount(unsigned int maxLineCount)
{
Lock lock(_mutex);
getLoggerData().mMaxLineCount = maxLineCount;
}
void
Log::setMaxLineCount(unsigned int maxLineCount, Log::LocalLoggerId loggerId)
{
if (loggerId)
{
ThreadData *pData = mLocalLoggerMap.getData(loggerId);
if (pData)
{
// Local logger found. Set logging level.
pData->mMaxLineCount = maxLineCount;
// We don't need local logger instance anymore.
mLocalLoggerMap.decreaseUseCount(loggerId);
pData = NULL;
}
}
else
{
Lock lock(_mutex);
mDefaultLoggerData.mMaxLineCount = maxLineCount;
}
}
void
Log::setMaxByteCount(unsigned int maxByteCount)
{
Lock lock(_mutex);
getLoggerData().mMaxByteCount = maxByteCount;
}
void
Log::setMaxByteCount(unsigned int maxByteCount, Log::LocalLoggerId loggerId)
{
if (loggerId)
{
ThreadData *pData = mLocalLoggerMap.getData(loggerId);
if (pData)
{
// Local logger found. Set logging level.
pData->mMaxByteCount = maxByteCount;
// We don't need local logger instance anymore.
mLocalLoggerMap.decreaseUseCount(loggerId);
pData = NULL;
}
}
else
{
Lock lock(_mutex);
mDefaultLoggerData.mMaxByteCount = maxByteCount;
}
}
const static Data log_("LOG_");
Data
Log::toString(Level l)
{
int i;
for (i = 0; mDescriptions[i].description; i++)
{
if (mDescriptions[i].level == l)
{
return log_ + mDescriptions[i].description;
}
}
return log_ + "INVALID";
}
Log::Level
Log::toLevel(const Data& l)
{
Data pri( l.prefix("LOG_") ? l.substr(4) : l);
int i = 0;
for (i = 0; mDescriptions[i].description; i++)
{
if (isEqualNoCase(pri, mDescriptions[i].description))
{
return mDescriptions[i].level;
}
}
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;
}
}
EncodeStream &
Log::tags(Log::Level level,
const Subsystem& subsystem,
const char* pfile,
int line,
EncodeStream& strm)
{
char buffer[256];
Data ts(Data::Borrow, buffer, sizeof(buffer));
#if defined( __APPLE__ )
strm << toString(level) << Log::delim
<< timestamp(ts) << Log::delim
<< mAppName << Log::delim
<< subsystem << Log::delim
<< pthread_self() << Log::delim
<< pfile << ":" << line;
#elif defined( WIN32 )
const char* file = pfile + strlen(pfile);
while (file != pfile &&
*file != '\\')
{
--file;
}
if (file != pfile)
{
++file;
}
strm << toString(level) << Log::delim
<< timestamp(ts) << Log::delim
<< mAppName << Log::delim
<< subsystem << Log::delim
<< GetCurrentThreadId() << Log::delim
<< file << ":" << line;
#else // #if defined( WIN32 ) || defined( __APPLE__ )
if(resip::Log::getLoggerData().type() == Syslog)
{
strm << toString(level) << Log::delim
<< timestamp(ts) << Log::delim
// << mHostname << Log::delim
<< mAppName << Log::delim
<< subsystem << Log::delim
<< mPid << Log::delim
<< pthread_self() << Log::delim
<< pfile << ":" << line;
}
else
strm << toString(level) << Log::delim
<< timestamp(ts) << Log::delim
// << mHostname << Log::delim
<< mAppName << Log::delim
<< subsystem << Log::delim
// << mPid << Log::delim
<< pthread_self() << Log::delim
<< pfile << ":" << line;
#endif
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 { time_t tv_sec; int tv_usec; } tv = {0,0};
time(&tv.tv_sec);
GetLocalTime(&systemTime);
tv.tv_usec = systemTime.wMilliseconds * 1000;
#else
struct tm localTimeResult;
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 */
#ifdef WIN32
localtime (&timeInSeconds)); // Thread safe call on Windows
#else
localtime_r (&timeInSeconds, &localTimeResult)); // Thread safe version of localtime on linux
#endif
}
char msbuf[5];
/* Dividing (without remainder) by 1000 rounds the microseconds
measure to the nearest millisecond. */
snprintf(msbuf, 5, ".%3.3ld", long(tv.tv_usec / 1000));
int datebufCharsRemaining = datebufSize - (int)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);
HashMap<int, Level>::iterator res = Log::mServiceToLevel.find(service);
if(res == Log::mServiceToLevel.end())
{
//!dcm! -- should perhaps throw an exception here, instead of setting a
//default level of LOG_ERROR, but nobody uses this yet
Log::mServiceToLevel[service] = Err;
return Err;
}
return res->second;
}
const Log::ThreadSetting*
Log::getThreadSetting()
{
#ifndef LOG_ENABLE_THREAD_SETTING
return 0;
#else
ThreadSetting* setting = static_cast<ThreadSetting*>(ThreadIf::tlsGetValue(*Log::mLevelKey));
if (setting == 0)
{
return 0;
}
if (Log::touchCount > 0)
{
Lock lock(_mutex);
ThreadIf::Id thread = ThreadIf::selfId();
HashMap<ThreadIf::Id, pair<ThreadSetting, bool> >::iterator res = Log::mThreadToLevel.find(thread);
assert(res != Log::mThreadToLevel.end());
if (res->second.second)
{
setting->mLevel = res->second.first.mLevel;
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)
{
#ifndef LOG_ENABLE_THREAD_SETTING
assert(0);
#else
//cerr << "Log::setThreadSetting: " << "service: " << info.service << " level " << toString(info.level) << " for " << pthread_self() << endl;
ThreadIf::Id thread = ThreadIf::selfId();
ThreadIf::tlsSetValue(*mLevelKey, (void *) new ThreadSetting(info));
Lock lock(_mutex);
if (Log::mThreadToLevel.find(thread) != Log::mThreadToLevel.end())
{
if (Log::mThreadToLevel[thread].second == true)
{
touchCount--;
}
}
Log::mThreadToLevel[thread].first = info;
Log::mThreadToLevel[thread].second = false;
Log::mServiceToThreads[info.mService].insert(thread);
#endif
}
void
Log::setServiceLevel(int service, Level l)
{
Lock lock(_mutex);
Log::mServiceToLevel[service] = l;
#ifndef LOG_ENABLE_THREAD_SETTING
assert(0);
#else
set<ThreadIf::Id>& threads = Log::mServiceToThreads[service];
for (set<ThreadIf::Id>::iterator i = threads.begin(); i != threads.end(); i++)
{
Log::mThreadToLevel[*i].first.mLevel = l;
Log::mThreadToLevel[*i].second = true;
}
Log::touchCount += (short)threads.size();
#endif
// cerr << "**Log::setServiceLevel:touchCount: " << Log::touchCount << "**" << endl;
}
Log::LocalLoggerId Log::localLoggerCreate(Log::Type type,
Log::Level level,
const char * logFileName,
ExternalLogger* externalLogger)
{
return mLocalLoggerMap.create(type, level, logFileName, externalLogger);
}
int Log::localLoggerReinitialize(Log::LocalLoggerId loggerId,
Log::Type type,
Log::Level level,
const char * logFileName,
ExternalLogger* externalLogger)
{
return mLocalLoggerMap.reinitialize(loggerId, type, level, logFileName, externalLogger);
}
int Log::localLoggerRemove(Log::LocalLoggerId loggerId)
{
return mLocalLoggerMap.remove(loggerId);
}
int Log::setThreadLocalLogger(Log::LocalLoggerId loggerId)
{
ThreadData* pData = static_cast<ThreadData*>(ThreadIf::tlsGetValue(*Log::mLocalLoggerKey));
if (pData)
{
// There was some local logger installed. Decrease its use count before we
// continue.
mLocalLoggerMap.decreaseUseCount(pData->id());
pData = NULL;
}
if (loggerId)
{
pData = mLocalLoggerMap.getData(loggerId);
}
ThreadIf::tlsSetValue(*mLocalLoggerKey, (void *) pData);
return (loggerId == 0) || (pData != NULL)?0:1;
}
std::ostream&
Log::Instance(unsigned int bytesToWrite)
{
return getLoggerData().Instance(bytesToWrite);
}
void
Log::reset()
{
getLoggerData().reset();
}
#ifndef WIN32
void
Log::droppingPrivileges(uid_t uid, pid_t pid)
{
getLoggerData().droppingPrivileges(uid, pid);
}
#endif
bool
Log::isLogging(Log::Level level, const resip::Subsystem& sub)
{
if (sub.getLevel() != Log::None)
{
return level <= sub.getLevel();
}
else
{
return (level <= Log::getLoggerData().mLevel);
}
}
void
Log::OutputToWin32DebugWindow(const Data& result)
{
#ifdef WIN32
const char *text = result.c_str();
#ifdef UNDER_CE
LPWSTR lpwstrText = resip::ToWString(text);
OutputDebugStringW(lpwstrText);
FreeWString(lpwstrText);
#else
OutputDebugStringA(text);
#endif
#endif
}
Log::LocalLoggerId Log::LocalLoggerMap::create(Log::Type type,
Log::Level level,
const char * logFileName,
ExternalLogger* externalLogger)
{
Lock lock(mLoggerInstancesMapMutex);
Log::LocalLoggerId id = ++mLastLocalLoggerId;
Log::ThreadData *pNewData = new Log::ThreadData(id, type, level, logFileName,
externalLogger);
mLoggerInstancesMap[id].first = pNewData;
mLoggerInstancesMap[id].second = 0;
return id;
}
int Log::LocalLoggerMap::reinitialize(Log::LocalLoggerId loggerId,
Log::Type type,
Log::Level level,
const char * logFileName,
ExternalLogger* externalLogger)
{
Lock lock(mLoggerInstancesMapMutex);
LoggerInstanceMap::iterator it = mLoggerInstancesMap.find(loggerId);
if (it == mLoggerInstancesMap.end())
{
// No such logger ID
std::cerr << "Log::LocalLoggerMap::remove(): Unknown local logger id=" << loggerId << std::endl;
return 1;
}
it->second.first->reset();
it->second.first->set(type, level, logFileName, externalLogger);
return 0;
}
int Log::LocalLoggerMap::remove(Log::LocalLoggerId loggerId)
{
Lock lock(mLoggerInstancesMapMutex);
LoggerInstanceMap::iterator it = mLoggerInstancesMap.find(loggerId);
if (it == mLoggerInstancesMap.end())
{
// No such logger ID
std::cerr << "Log::LocalLoggerMap::remove(): Unknown local logger id=" << loggerId << std::endl;
return 1;
}
if (it->second.second > 0)
{
// Non-zero use-count.
std::cerr << "Log::LocalLoggerMap::remove(): Use count is non-zero (" << it->second.second << ")!" << std::endl;
return 2;
}
delete it->second.first; // delete ThreadData
mLoggerInstancesMap.erase(it);
return 0;
}
Log::ThreadData *Log::LocalLoggerMap::getData(Log::LocalLoggerId loggerId)
{
Lock lock(mLoggerInstancesMapMutex);
LoggerInstanceMap::iterator it = mLoggerInstancesMap.find(loggerId);
if (it == mLoggerInstancesMap.end())
{
// No such logger ID
return NULL;
}
it->second.second++;
return it->second.first;
}
void Log::LocalLoggerMap::decreaseUseCount(Log::LocalLoggerId loggerId)
{
Lock lock(mLoggerInstancesMapMutex);
LoggerInstanceMap::iterator it = mLoggerInstancesMap.find(loggerId);
if (it != mLoggerInstancesMap.end())
{
it->second.second--;
assert(it->second.second >= 0);
}
}
Log::Guard::Guard(resip::Log::Level level,
const resip::Subsystem& subsystem,
const char* file,
int line) :
mLevel(level),
mSubsystem(subsystem),
mFile(file),
mLine(line),
mData(Data::Borrow, mBuffer, sizeof(mBuffer)),
mStream(mData.clear())
{
if (resip::Log::getLoggerData().mType != resip::Log::OnlyExternalNoHeaders)
{
Log::tags(mLevel, mSubsystem, mFile, mLine, mStream);
mStream << resip::Log::delim;
mStream.flush();
mHeaderLength = mData.size();
}
else
{
mHeaderLength = 0;
}
}
Log::Guard::~Guard()
{
mStream.flush();
if (resip::Log::getExternal())
{
const resip::Data rest(resip::Data::Share,
mData.data() + mHeaderLength,
(int)mData.size() - mHeaderLength);
if (!(*resip::Log::getExternal())(mLevel,
mSubsystem,
resip::Log::getAppName(),
mFile,
mLine,
rest,
mData))
{
return;
}
}
Type logType = resip::Log::getLoggerData().mType;
if(logType == resip::Log::OnlyExternal ||
logType == resip::Log::OnlyExternalNoHeaders)
{
return;
}
resip::Lock lock(resip::Log::_mutex);
// !dlb! implement VSDebugWindow as an external logger
if (logType == resip::Log::VSDebugWindow)
{
mData += "\r\n";
OutputToWin32DebugWindow(mData);
}
else
{
// endl is magic in syslog -- so put it here
Instance((int)mData.size()+2) << mData << std::endl;
}
}
std::ostream&
Log::ThreadData::Instance(unsigned int bytesToWrite)
{
// std::cerr << "Log::ThreadData::Instance() id=" << mId << " type=" << mType << std::endl;
switch (mType)
{
case Log::Syslog:
if (mLogger == 0)
{
std::cerr << "Creating a syslog stream" << std::endl;
mLogger = new SysLogStream;
}
return *mLogger;
case Log::Cerr:
return std::cerr;
case Log::Cout:
return std::cout;
case Log::File:
if (mLogger == 0 ||
(maxLineCount() && mLineCount >= maxLineCount()) ||
(maxByteCount() && ((unsigned int)mLogger->tellp()+bytesToWrite) >= maxByteCount()))
{
std::cerr << "Creating a logger for file \"" << mLogFileName.c_str() << "\"" << std::endl;
Data logFileName(mLogFileName != "" ? mLogFileName : "resiprocate.log");
if (mLogger)
{
Data oldLogFileName(logFileName + ".old");
delete mLogger;
// Keep one backup file: Delete .old file, Rename log file to .old
// Could be expanded in the future to keep X backup log files
remove(oldLogFileName.c_str());
rename(logFileName.c_str(), oldLogFileName.c_str());
}
mLogger = new std::ofstream(logFileName.c_str(), std::ios_base::out | std::ios_base::app);
mLineCount = 0;
}
mLineCount++;
return *mLogger;
default:
assert(0);
return std::cout;
}
}
void
Log::ThreadData::reset()
{
delete mLogger;
mLogger = NULL;
}
#ifndef WIN32
void
Log::ThreadData::droppingPrivileges(uid_t uid, pid_t pid)
{
if(mType == Log::File)
{
Data logFileName(mLogFileName != "" ? mLogFileName : "resiprocate.log");
if(chown(logFileName.c_str(), uid, pid) < 0)
{
// Some error occurred
std::cerr << "ERROR: chown failed on " << logFileName << std::endl;
}
}
}
#endif
/* ====================================================================
* The Vovida Software License, Version 1.0
*
* Copyright (c) 2000-2005
*
* 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@xxxxxxxxxx.
*
* 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/>.
*
*/
#ifndef RESIP_Log_hxx
#define RESIP_Log_hxx
#include "rutil/Data.hxx"
#ifndef WIN32
#include <syslog.h>
#include <unistd.h>
#endif
#include <set>
#include "rutil/Mutex.hxx"
#include "rutil/Lock.hxx"
#include "rutil/HashMap.hxx"
#include "rutil/ThreadIf.hxx"
#include <iostream>
// !ipse! I think that this remark is no more valid with recent changes,
// but I don't have MacOs X to test with. Someone should take this duty.
//
// NOTE: disabling thread setting code for native mac os applications.
// since some logging takes place during static initialization we can't
// be sure all the pthread stuff is ready to go. this eventually causes
// crashes in the Mac OS native API.
#if !defined(TARGET_OS_MAC)
#define LOG_ENABLE_THREAD_SETTING
// defining hash function in mac os (non-sdk api) and cygwin because
// ThreadIf::Id is a pointer, (this assumes it's always the same pointer)
#if defined(__APPLE__) || defined(__CYGWIN__)
HashValue(resip::ThreadIf::Id);
#endif
#endif
extern "C"
{
// Forward declaration to make it friend of Log class.
void freeLocalLogger(void* pThreadData);
};
namespace resip
{
class ExternalLogger;
class Subsystem;
/**
@brief Singleton that handles logging calls.
@see Logger for usage details
*/
class Log
{
public:
enum Type
{
Cout = 0,
Syslog,
File,
Cerr,
VSDebugWindow, ///< Use only for Visual Studio Debug Window logging - WIN32 must be defined
OnlyExternal, ///< log messages are only written to external logger
OnlyExternalNoHeaders ///< same as OnlyExternal, only the messageWithHeaders param of the ExternalLogger
///< will be empty. This parameter usually contains a pre-formatted log entry.
};
enum Level
{
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
};
struct descriptionEntry
{
Level level;
const char * description;
};
/// Thread Local logger ID type.
typedef int LocalLoggerId;
/**
@brief Implementation for logging macros.
Log::Guard(Log::Info, Subsystem::TEST, __FILE__, __LINE__) << ... ;
*/
class Guard
{
public:
/** Remember the logging values and be a a stream to receive
the log contents. */
Guard(Level level,
const Subsystem& system,
const char* file,
int line);
/** Commit logging */
~Guard();
EncodeStream& asStream() {return mStream;}
operator EncodeStream&() {return mStream;}
private:
resip::Log::Level mLevel;
const resip::Subsystem& mSubsystem;
resip::Data::size_type mHeaderLength;
const char* mFile;
int mLine;
char mBuffer[128];
Data mData;
oDataStream mStream;
Guard& operator=(const Guard&);
};
class ThreadSetting
{
public:
ThreadSetting()
: mService(-1),
mLevel(Err)
{}
ThreadSetting(int serv, Level level)
: mService(serv),
mLevel(level)
{
}
int mService;
Level mLevel;
};
/// output the loglevel, hostname, appname, pid, tid, subsystem
static EncodeStream& tags(Log::Level level,
const Subsystem& subsystem,
const char* file,
int line,
EncodeStream& strm);
static Data& timestamp(Data& result);
static Data timestamp();
static ExternalLogger* getExternal()
{
return getLoggerData().mExternalLogger;
}
static Data getAppName()
{
return mAppName;
}
static void initialize(Type type,
Level level,
const Data& appName,
const char * logFileName = 0,
ExternalLogger* externalLogger = 0);
static void initialize(const Data& type,
const Data& level,
const Data& appName,
const char * logFileName = 0,
ExternalLogger* externalLogger = 0);
static void initialize(const char* type,
const char* level,
const char* appName,
const char * logFileName = 0,
ExternalLogger* externalLogger = 0);
static void initialize(Type type,
Level level,
const Data& appName,
ExternalLogger& logger);
/** @brief Set logging level for current thread.
* If thread has no local logger attached, then set global logging level.
*/
static void setLevel(Level level);
/** @brief Set logging level for given subsystem. */
static void setLevel(Level level, Subsystem& s);
/** Set logging level for given local logger. Use 0 to set global logging level. */
static void setLevel(Level level, LocalLoggerId loggerId);
/** @brief Return logging level for current thread.
* If thread has no local logger attached, then return global logging level.
*/
static Level level() { Lock lock(_mutex); return getLoggerData().mLevel; }
/** Return logging level for given local logger. Use 0 to set global logging level. */
static Level level(LocalLoggerId loggerId);
static LocalLoggerId id() { Lock lock(_mutex); return getLoggerData().id(); }
static void setMaxLineCount(unsigned int maxLineCount);
static void setMaxLineCount(unsigned int maxLineCount, LocalLoggerId loggerId);
static void setMaxByteCount(unsigned int maxByteCount);
static void setMaxByteCount(unsigned int maxByteCount, LocalLoggerId loggerId);
static Level toLevel(const Data& l);
static Type toType(const Data& t);
static Data toString(Level l);
/// DEPRECATED! Left for backward compatibility - use localLoggers instead
static void setServiceLevel(int service, Level l);
static Level getServiceLevel(int service);
/// DEPRECATED! Left for backward compatibility - use localLoggers instead
static const ThreadSetting* getThreadSetting();
static void setThreadSetting(ThreadSetting info);
static void setThreadSetting(int serv, Level l);
static void setThreadSetting(int serv);
/// Create new logger instance and return its ID (zero on error)
static LocalLoggerId localLoggerCreate(Type type,
Level level,
const char * logFileName = NULL,
ExternalLogger* externalLogger = NULL);
/** Reinitialize all new setting for a local logger instance
* @retval 0 on success
* @retval 1 if logger does not exist
*/
static int localLoggerReinitialize(LocalLoggerId loggerId,
Type type,
Level level,
const char * logFileName = NULL,
ExternalLogger* externalLogger = NULL);
/** Destroy existing logger instance.
* @retval 0 on success
* @retval 1 if logger does not exist
* @retval 2 if logger is still in use
* @retval >2 on other failures
*/
static int localLoggerRemove(LocalLoggerId loggerId);
/** Set logger instance with given ID as a thread local logger.
* Pass zero \p loggerId to remove thread local logger.
* @retval 0 on success
* @retval 1 if logger does not exist
* @retval >1 on other failures
*/
static int setThreadLocalLogger(LocalLoggerId loggerId);
static std::ostream& Instance(unsigned int bytesToWrite);
static bool isLogging(Log::Level level, const Subsystem&);
static void OutputToWin32DebugWindow(const Data& result);
static void reset(); ///< Frees logger stream
#ifndef WIN32
static void droppingPrivileges(uid_t uid, pid_t pid);
#endif
public:
static unsigned int MaxLineCount;
static unsigned int MaxByteCount;
protected:
static Mutex _mutex;
static volatile short touchCount;
static const Data delim;
class ThreadData
{
public:
ThreadData(LocalLoggerId id, Type type=Cout, Level level=Info,
const char *logFileName=NULL,
ExternalLogger *pExternalLogger=NULL)
: mLevel(level),
mMaxLineCount(0),
mMaxByteCount(0),
mExternalLogger(pExternalLogger),
mId(id),
mType(type),
mLogger(NULL),
mLineCount(0)
{
if (logFileName)
{
mLogFileName = logFileName;
}
}
~ThreadData() { reset(); }
void set(Type type=Cout, Level level=Info,
const char *logFileName=NULL,
ExternalLogger *pExternalLogger=NULL)
{
mType = type;
mLevel = level;
if (logFileName)
{
mLogFileName = logFileName;
}
mExternalLogger = pExternalLogger;
}
LocalLoggerId id() const {return mId;}
unsigned int maxLineCount() { return mMaxLineCount ? mMaxLineCount : MaxLineCount; } // return local max, if not set use global max
unsigned int maxByteCount() { return mMaxByteCount ? mMaxByteCount : MaxByteCount; } // return local max, if not set use global max
Type type() const {return mType;}
std::ostream& Instance(unsigned int bytesToWrite); ///< Return logger stream instance, creating it if needed.
void reset(); ///< Frees logger stream
#ifndef WIN32
void droppingPrivileges(uid_t uid, pid_t pid);
#endif
volatile Level mLevel;
volatile unsigned int mMaxLineCount;
volatile unsigned int mMaxByteCount;
ExternalLogger* mExternalLogger;
protected:
friend class Guard;
const LocalLoggerId mId;
Type mType;
Data mLogFileName;
std::ostream* mLogger;
unsigned int mLineCount;
};
static ThreadData mDefaultLoggerData; ///< Default logger settings.
static Data mAppName;
static Data mHostname;
#ifndef WIN32
static pid_t mPid;
#else
static int mPid;
#endif
static const descriptionEntry mDescriptions[];
static ThreadData &getLoggerData()
{
ThreadData* pData = static_cast<ThreadData*>(ThreadIf::tlsGetValue(*Log::mLocalLoggerKey));
return pData?*pData:mDefaultLoggerData;
}
/// Thread Local logger settings storage
class LocalLoggerMap
{
public:
LocalLoggerMap()
: mLastLocalLoggerId(0) {};
/// Create new logger instance and return its ID (zero on error)
LocalLoggerId create(Type type,
Level level,
const char * logFileName = NULL,
ExternalLogger* externalLogger = NULL);
/** Reinitialize all new setting for a local logger instance
* @retval 0 on success
* @retval 1 if logger does not exist
*/
int reinitialize(LocalLoggerId loggerId,
Type type,
Level level,
const char * logFileName = NULL,
ExternalLogger* externalLogger = NULL);
/** Remove existing logger instance from map and destroy.
* @retval 0 on success
* @retval 1 if logger does not exist
* @retval 2 if logger is still in use
* @retval >2 on other failures
*/
int remove(LocalLoggerId loggerId);
/** Get pointer to ThreadData for given ID and increase use counter.
* @returns NULL if ID does not exist. */
ThreadData *getData(LocalLoggerId loggerId);
/// Decrease use counter for given loggerId.
void decreaseUseCount(LocalLoggerId loggerId);
protected:
/// Storage for Thread Local loggers and their use-counts.
typedef HashMap<LocalLoggerId, std::pair<ThreadData*, int> > LoggerInstanceMap;
LoggerInstanceMap mLoggerInstancesMap;
/// Last used LocalLoggerId
LocalLoggerId mLastLocalLoggerId;
/// Mutex to synchronize access to Thread Local logger settings storage
Mutex mLoggerInstancesMapMutex;
};
friend void ::freeLocalLogger(void* pThreadData);
friend class LogStaticInitializer;
static LocalLoggerMap mLocalLoggerMap;
static ThreadIf::TlsKey* mLocalLoggerKey;
/// DEPRECATED! Left for backward compatibility - use localLoggers instead
#ifdef LOG_ENABLE_THREAD_SETTING
static HashMap<ThreadIf::Id, std::pair<ThreadSetting, bool> > mThreadToLevel;
static HashMap<int, std::set<ThreadIf::Id> > mServiceToThreads;
static ThreadIf::TlsKey* mLevelKey;
#endif
static HashMap<int, Level> mServiceToLevel;
};
/** @brief Interface functor for external logging.
*/
class ExternalLogger
{
public:
virtual ~ExternalLogger() {};
/** return true to also do default logging, false to suppress default logging. */
virtual bool operator()(Log::Level level,
const Subsystem& subsystem,
const Data& appName,
const char* file,
int line,
const Data& message,
const Data& messageWithHeaders) = 0;
};
/// Class to initialize Log class static variables.
class LogStaticInitializer {
public:
LogStaticInitializer();
~LogStaticInitializer();
protected:
static unsigned int mInstanceCounter;
#ifdef WIN32
// LogStaticInitializer calls ThreadIf::tlsKeyCreate which
// relies on the static TlsDestructorInitializer having set
// up mTlsDestructorsMutex which is used to Lock TLS access
// Since the order of static initialization is not reliable,
// we must make sure that TlsDestructorInitializer is initialized
// before LogStaticInitializer is inizialized:
TlsDestructorInitializer tlsDestructorInitializer;
#endif
};
static LogStaticInitializer _staticLogInit;
}
#endif
/* ====================================================================
* The Vovida Software License, Version 1.0
*
* Copyright (c) 2000-2005.
*
* 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@xxxxxxxxxx.
*
* 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/>.
*
*/