< 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/>. * */