blocxx

LogAppender.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2005, Quest Software, Inc. All rights reserved.
00003 * Copyright (C) 2006, Novell, Inc. All rights reserved.
00004 * 
00005 * Redistribution and use in source and binary forms, with or without
00006 * modification, are permitted provided that the following conditions are met:
00007 * 
00008 *     * Redistributions of source code must retain the above copyright notice,
00009 *       this list of conditions and the following disclaimer.
00010 *     * Redistributions in binary form must reproduce the above copyright
00011 *       notice, this list of conditions and the following disclaimer in the
00012 *       documentation and/or other materials provided with the distribution.
00013 *     * Neither the name of 
00014 *       Quest Software, Inc., 
00015 *       nor Novell, Inc., 
00016 *       nor the names of its contributors or employees may be used to 
00017 *       endorse or promote products derived from this software without 
00018 *       specific prior written permission.
00019 * 
00020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00024 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00025 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00026 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00027 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00028 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00029 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00030 * POSSIBILITY OF SUCH DAMAGE.
00031 *******************************************************************************/
00032 
00033 
00039 #include "blocxx/BLOCXX_config.h"
00040 #include "blocxx/LogAppender.hpp"
00041 #include "blocxx/String.hpp"
00042 #include "blocxx/Array.hpp"
00043 #include "blocxx/LogMessage.hpp"
00044 #include "blocxx/Logger.hpp"
00045 #include "blocxx/Assertion.hpp"
00046 #include "blocxx/StringBuffer.hpp"
00047 #include "blocxx/NullAppender.hpp"
00048 #ifndef BLOCXX_WIN32
00049 #include "blocxx/SyslogAppender.hpp"
00050 #endif
00051 #include "blocxx/CerrAppender.hpp"
00052 #include "blocxx/FileAppender.hpp"
00053 #include "blocxx/MultiProcessFileAppender.hpp"
00054 #include "blocxx/Format.hpp"
00055 #include "blocxx/SortedVectorMap.hpp"
00056 #include "blocxx/NonRecursiveMutex.hpp"
00057 #include "blocxx/NonRecursiveMutexLock.hpp"
00058 #include "blocxx/ThreadOnce.hpp"
00059 #include "blocxx/NullLogger.hpp"
00060 #include "blocxx/GlobalPtr.hpp"
00061 
00062 
00063 namespace BLOCXX_NAMESPACE
00064 {
00065 
00066 #ifdef BLOCXX_WIN32
00067 DWORD dwTlsIndex = 0;
00068 #endif
00069 
00070 char const LOG_1_LOCATION_opt[] = "log.%1.location";
00071 char const LOG_1_MAX_FILE_SIZE_opt[] = "log.%1.max_file_size";
00072 char const LOG_1_MAX_BACKUP_INDEX_opt[] = "log.%1.max_backup_index";
00073 char const LOG_1_FLUSH_opt[] = "log.%1.flush";
00074 char const LOG_1_SYSLOG_IDENTITY_opt[] = "log.%1.identity";
00075 char const LOG_1_SYSLOG_FACILITY_opt[] = "log.%1.facility";
00076 
00077 
00079 LogAppender::~LogAppender()
00080 {
00081 }
00082 
00084 // we're passing a pointer to this to pthreads, it has to have C linkage.
00085 extern "C" 
00086 {
00087 static void freeThreadLogAppender(void *ptr)
00088 {
00089    delete static_cast<LogAppenderRef *>(ptr);
00090 }
00091 } // end extern "C"
00092 
00094 namespace
00095 {
00096 
00097 OnceFlag g_onceGuard  = BLOCXX_ONCE_INIT;
00098 NonRecursiveMutex* g_mutexGuard = NULL;
00099 
00100 struct NullAppenderFactory
00101 {
00102    static LogAppenderRef* create()
00103    {
00104       return new LogAppenderRef(new NullAppender());
00105    }
00106 };
00107 ::BLOCXX_NAMESPACE::GlobalPtr<LogAppenderRef,NullAppenderFactory> g_defaultLogAppender = BLOCXX_GLOBAL_PTR_INIT;
00108 
00109 
00111 void initGuardAndKey()
00112 {
00113    g_mutexGuard = new NonRecursiveMutex();
00114 #ifdef BLOCXX_WIN32
00115    LPVOID thread_data = NULL;
00116    BOOL ret = TlsSetValue(dwTlsIndex, thread_data)
00117    BLOCXX_ASSERTMSG(ret, "failed create a thread specific key");
00118 #elif BLOCXX_NCR
00119    int ret = pthread_keycreate(&g_loggerKey, freeThreadLogAppender);
00120    BLOCXX_ASSERTMSG(ret == 0, "failed create a thread specific key");
00121 #else
00122    int ret = pthread_key_create(&g_loggerKey, freeThreadLogAppender);
00123    BLOCXX_ASSERTMSG(ret == 0, "failed create a thread specific key");
00124 #endif
00125 }
00126 
00127 
00128 } // end unnamed namespace
00129 
00131 // STATIC
00132 LogAppenderRef
00133 LogAppender::getCurrentLogAppender()
00134 {
00135    LogAppenderRef threadLogAppender = getThreadLogAppender();
00136    if(threadLogAppender)
00137    {
00138       return threadLogAppender;
00139    }
00140    else
00141    {
00142       return getDefaultLogAppender();
00143    }
00144 }
00145 
00147 // STATIC
00148 LogAppenderRef
00149 LogAppender::getDefaultLogAppender()
00150 {
00151    callOnce(g_onceGuard, initGuardAndKey);
00152    NonRecursiveMutexLock lock(*g_mutexGuard);
00153 
00154    // This looks unsafe, but the get() method (called indirectly by operator*),
00155    // if it has never been previously called, will allocate a new
00156    // LogAppenderRef wich will have a NullAppender inside it.
00157    return *g_defaultLogAppender;
00158 }
00159 
00160 
00162 // STATIC
00163 bool
00164 LogAppender::setDefaultLogAppender(const LogAppenderRef &ref)
00165 {
00166    if (ref)
00167    {
00168       callOnce(g_onceGuard, initGuardAndKey);
00169       NonRecursiveMutexLock lock(*g_mutexGuard);
00170 
00171       LogAppenderRef(ref).swap(*g_defaultLogAppender);
00172       return true;
00173    }
00174    return false;
00175 }
00176 
00177 
00179 // STATIC
00180 LogAppenderRef
00181 LogAppender::getThreadLogAppender()
00182 {
00183    callOnce(g_onceGuard, initGuardAndKey);
00184    LogAppenderRef *ptr = NULL;
00185 
00186 #ifdef BLOCXX_WIN32
00187    ptr = static_cast<LogAppenderRef *>(TlsGetValue(dwTlsIndex));
00188 #elif BLOCXX_NCR
00189    pthread_addr_t addr_ptr = NULL;
00190    int ret = pthread_getspecific(g_loggerKey, &addr_ptr);
00191    if (ret == 0)
00192    {
00193       ptr = static_cast<LogAppenderRef *>(addr_ptr);
00194    }
00195 #else
00196    ptr = static_cast<LogAppenderRef *>(pthread_getspecific(g_loggerKey));
00197 #endif
00198 
00199    if(ptr)
00200    {
00201       return *ptr;
00202    }
00203    else
00204    {
00205       return LogAppenderRef();
00206    }
00207 }
00208 
00210 // STATIC
00211 bool
00212 LogAppender::setThreadLogAppender(const LogAppenderRef &ref)
00213 {
00214    callOnce(g_onceGuard, initGuardAndKey);
00215    LogAppenderRef *ptr = 0;
00216    if (ref)
00217    {
00218       ptr = new LogAppenderRef(ref);
00219    }
00220 #ifdef BLOCXX_WIN32
00221    LogAppenderRef *ptr_old = static_cast<LogAppenderRef *>(TlsGetValue(dwTlsIndex));
00222    if (ptr_old)
00223    {
00224       delete ptr_old;
00225    }
00226    
00227    BOOL ret = FALSE;
00228    if (!(ret = TlsSetValue(dwTlsIndex, ptr)))
00229    {
00230       if (ptr)
00231       {
00232          delete ptr;
00233       }
00234    }
00235    BLOCXX_ASSERTMSG(ret, "failed to set a thread specific logger");
00236 #elif BLOCXX_NCR
00237    pthread_addr_t addr_ptr = NULL;
00238    pthread_getspecific(g_loggerKey, &addr_ptr);
00239    freeThreadLogAppender(addr_ptr);
00240    int ret = pthread_setspecific(g_loggerKey, ptr);
00241    BLOCXX_ASSERTMSG(ret == 0, "failed to set a thread specific logger");
00242 #else
00243    freeThreadLogAppender(pthread_getspecific(g_loggerKey));
00244 
00245    int ret = pthread_setspecific(g_loggerKey, ptr);
00246    if (ret != 0)
00247    {
00248       delete ptr;
00249    }
00250    BLOCXX_ASSERTMSG(ret == 0, "failed to set a thread specific logger");
00251 #endif
00252 
00253    return (ref != 0);
00254 }
00255 
00256 
00258 void
00259 LogAppender::logMessage(const LogMessage& message) const
00260 {
00261    if (componentAndCategoryAreEnabled(message.component, message.category))
00262    {
00263       StringBuffer buf;
00264       m_formatter.formatMessage(message, buf);
00265       doProcessLogMessage(buf.releaseString(), message);
00266    }
00267 }
00268 
00270 bool
00271 LogAppender::categoryIsEnabled(const String& category) const
00272 {
00273    return m_allCategories || m_categories.count(category) > 0;
00274 }
00275 
00277 bool
00278 LogAppender::componentAndCategoryAreEnabled(const String& component, const String& category) const
00279 {
00280    return (m_allComponents || m_components.count(component) > 0) &&
00281       categoryIsEnabled(category);
00282 }
00283 
00285 namespace
00286 {
00287    String
00288    getConfigItem(const LoggerConfigMap& configItems, const String &itemName, const String& defRetVal = "")
00289    {
00290       LoggerConfigMap::const_iterator i = configItems.find(itemName);
00291       if (i != configItems.end())
00292       {
00293          return i->second;
00294       }
00295       else
00296       {
00297          return defRetVal;
00298       }
00299    }
00300 }
00301 
00303 LogAppenderRef
00304 LogAppender::createLogAppender(
00305    const String& name,
00306    const StringArray& components,
00307    const StringArray& categories,
00308    const String& messageFormat,
00309    const String& type,
00310    const LoggerConfigMap& configItems)
00311 {
00312    LogAppenderRef appender;
00313    if (type.empty() || type.equalsIgnoreCase(TYPE_NULL))
00314    {
00315       appender = new NullAppender(components, categories, messageFormat);
00316    }
00317 #ifndef BLOCXX_WIN32
00318    else if ( type == TYPE_SYSLOG )
00319    {
00320       String identity = getConfigItem(configItems, Format(LogConfigOptions::LOG_1_SYSLOG_IDENTITY_opt, name), BLOCXX_DEFAULT_LOG_1_SYSLOG_IDENTITY);
00321       String facility = getConfigItem(configItems, Format(LogConfigOptions::LOG_1_SYSLOG_FACILITY_opt, name), BLOCXX_DEFAULT_LOG_1_SYSLOG_FACILITY);
00322 
00323       appender = new SyslogAppender(components, categories, messageFormat, identity, facility);
00324    }
00325 #endif
00326    else if (type == TYPE_STDERR || type == "cerr")
00327    {
00328       appender = new CerrAppender(components, categories, messageFormat);
00329    }
00330    else if (type == TYPE_FILE || type == TYPE_MPFILE)
00331    {
00332       String configItem = Format(LogConfigOptions::LOG_1_LOCATION_opt, name);
00333       String filename = getConfigItem(configItems, configItem);
00334       
00335       UInt64 maxFileSize(0);
00336       try
00337       {
00338          maxFileSize = getConfigItem(configItems, Format(LogConfigOptions::LOG_1_MAX_FILE_SIZE_opt, name), 
00339             BLOCXX_DEFAULT_LOG_1_MAX_FILE_SIZE).toUInt64();
00340       }
00341       catch (StringConversionException& e)
00342       {
00343          BLOCXX_THROW_ERR_SUBEX(LoggerException, 
00344             Format("%1: Invalid config value: %2", LogConfigOptions::LOG_1_MAX_FILE_SIZE_opt, e.getMessage()).c_str(), 
00345             Logger::E_INVALID_MAX_FILE_SIZE, e);
00346       }
00347       
00348       unsigned int maxBackupIndex(0);
00349       try
00350       {
00351          maxBackupIndex = getConfigItem(configItems, Format(LogConfigOptions::LOG_1_MAX_BACKUP_INDEX_opt, name), 
00352             BLOCXX_DEFAULT_LOG_1_MAX_BACKUP_INDEX).toUnsignedInt();
00353       }
00354       catch (StringConversionException& e)
00355       {
00356          BLOCXX_THROW_ERR_SUBEX(LoggerException, 
00357             Format("%1: Invalid config value: %2", LogConfigOptions::LOG_1_MAX_BACKUP_INDEX_opt, e.getMessage()).c_str(), 
00358             Logger::E_INVALID_MAX_BACKUP_INDEX, e);
00359       }
00360 
00361       if (type == TYPE_FILE)
00362       {
00363          bool flushLog =
00364             getConfigItem(
00365                configItems,
00366                Format(LogConfigOptions::LOG_1_FLUSH_opt, name),
00367                BLOCXX_DEFAULT_LOG_1_FLUSH
00368             ).equalsIgnoreCase("true");
00369          appender = new FileAppender(
00370             components, categories, filename.c_str(), messageFormat,
00371             maxFileSize, maxBackupIndex, flushLog
00372          );
00373       }
00374       else // type == TYPE_MPFILE
00375       {
00376          appender = new MultiProcessFileAppender(
00377             components, categories, filename, messageFormat,
00378             maxFileSize, maxBackupIndex
00379          );
00380       }
00381    }
00382    else
00383    {
00384       BLOCXX_THROW_ERR(LoggerException, Format("Unknown log type: %1", type).c_str(), Logger::E_UNKNOWN_LOG_APPENDER_TYPE);
00385    }
00386 
00387    return appender;
00388 }
00389 
00391 const GlobalStringArray LogAppender::ALL_COMPONENTS = BLOCXX_GLOBAL_STRING_INIT("*");
00392 const GlobalStringArray LogAppender::ALL_CATEGORIES = BLOCXX_GLOBAL_STRING_INIT("*");
00393 const GlobalString LogAppender::STR_TTCC_MESSAGE_FORMAT = BLOCXX_GLOBAL_STRING_INIT("%r [%t] %-5p %c - %m");
00394 const GlobalString LogAppender::TYPE_SYSLOG = BLOCXX_GLOBAL_STRING_INIT("syslog");
00395 const GlobalString LogAppender::TYPE_STDERR = BLOCXX_GLOBAL_STRING_INIT("stderr");
00396 const GlobalString LogAppender::TYPE_FILE = BLOCXX_GLOBAL_STRING_INIT("file");
00397 const GlobalString LogAppender::TYPE_MPFILE = BLOCXX_GLOBAL_STRING_INIT("mpfile");
00398 const GlobalString LogAppender::TYPE_NULL = BLOCXX_GLOBAL_STRING_INIT("null");
00399 
00401 LogAppender::LogAppender(const StringArray& components, const StringArray& categories, const String& pattern)
00402    : m_components(components.begin(), components.end())
00403    , m_categories(categories.begin(), categories.end())
00404    , m_formatter(pattern)
00405    , m_logLevel(E_NONE_LEVEL)
00406 {
00407    m_allComponents = m_components.count("*") > 0;
00408    m_allCategories = m_categories.count("*") > 0;
00409 
00410    // set up the log level
00411    size_t numCategories = m_categories.size();
00412    size_t debug3Count = m_categories.count(Logger::STR_DEBUG3_CATEGORY);
00413    size_t debug2Count = m_categories.count(Logger::STR_DEBUG2_CATEGORY);
00414    size_t debugCount = m_categories.count(Logger::STR_DEBUG_CATEGORY);
00415    size_t infoCount = m_categories.count(Logger::STR_INFO_CATEGORY);
00416    size_t warningCount = m_categories.count(Logger::STR_WARNING_CATEGORY);
00417    size_t errorCount = m_categories.count(Logger::STR_ERROR_CATEGORY);
00418    size_t fatalCount = m_categories.count(Logger::STR_FATAL_CATEGORY);
00419    int nonLevelCategoryCount = numCategories - debug3Count - debug2Count - debugCount - infoCount - warningCount - errorCount - fatalCount;
00420 
00421    if (numCategories == 0)
00422    {
00423       m_logLevel = E_NONE_LEVEL;
00424    }
00425    else if (m_allCategories || nonLevelCategoryCount > 0)
00426    {
00427       m_logLevel = E_ALL_LEVEL;
00428    }
00429    else if (debug3Count > 0)
00430    {
00431       m_logLevel = E_DEBUG3_LEVEL;
00432    }
00433    else if (debug2Count > 0)
00434    {
00435       m_logLevel = E_DEBUG2_LEVEL;
00436    }
00437    else if (debugCount > 0)
00438    {
00439       m_logLevel = E_DEBUG_LEVEL;
00440    }
00441    else if (infoCount > 0)
00442    {
00443       m_logLevel = E_INFO_LEVEL;
00444    }
00445    else if (warningCount > 0)
00446    {
00447       m_logLevel = E_WARNING_LEVEL;
00448    }
00449    else if (errorCount > 0)
00450    {
00451       m_logLevel = E_ERROR_LEVEL;
00452    }
00453    else if (fatalCount > 0)
00454    {
00455       m_logLevel = E_FATAL_ERROR_LEVEL;
00456    }
00457    else
00458    {
00459       BLOCXX_ASSERTMSG(0, "Internal error. LogAppender unable to determine log level!");
00460    }
00461 }
00462 
00463 
00464 
00465 } // end namespace BLOCXX_NAMESPACE
00466 
00467