blocxx
|
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