Vault
4.1
|
00001 /* 00002 Copyright c1997-2014 Trygve Isaacson. All rights reserved. 00003 This file is part of the Code Vault version 4.1 00004 http://www.bombaydigital.com/ 00005 License: MIT. See LICENSE.md in the Vault top level directory. 00006 */ 00007 00010 #include "vlogger.h" 00011 00012 #include "vthread.h" 00013 #include "vmutexlocker.h" 00014 #include "vsettings.h" 00015 #include "vbento.h" 00016 #include "vchar.h" 00017 00018 static const VNamedLoggerPtr NULL_NAMED_LOGGER_PTR; 00019 static const VLogAppenderPtr NULL_LOG_APPENDER_PTR; 00020 00021 volatile int VLogger::gMaxActiveLevel = 0; 00022 VNamedLoggerPtr VLogger::gDefaultLogger = NULL_NAMED_LOGGER_PTR; 00023 VLogAppenderPtr VLogger::gDefaultAppender = NULL_LOG_APPENDER_PTR; 00024 VFSNode VLogger::gBaseLogDirectory("."); 00025 00026 // VNamedLogger --------------------------------------------------------------- 00027 00028 VNamedLogger::VNamedLogger(const VString& name, int level, const VStringVector& appenderNames, VLogAppenderPtr specificAppender) 00029 : VEnableSharedFromThis<VNamedLogger>() 00030 , mName(name) 00031 , mLevel(level) 00032 , mAppendersMutex(VSTRING_FORMAT("VNamedLogger[%s]", name.chars()), true/*suppress logging*/) 00033 , mAppenderNames(appenderNames) 00034 , mSpecificAppender(specificAppender) 00035 , mRepetitionFilter() 00036 , mPrintStackConfig() { 00037 if (appenderNames.empty() && (specificAppender == NULL_LOG_APPENDER_PTR)) { 00038 mAppenderNames.push_back(VString::EMPTY()); 00039 } 00040 } 00041 00042 VNamedLogger::~VNamedLogger() { 00043 } 00044 00045 void VNamedLogger::clearAppenders() { 00046 mAppenderNames.clear(); 00047 } 00048 00049 void VNamedLogger::setAppender(const VString& appenderName) { 00050 mAppenderNames.clear(); 00051 mAppenderNames.push_back(appenderName); 00052 } 00053 00054 void VNamedLogger::addAppender(const VString& appenderName) { 00055 mAppenderNames.push_back(appenderName); 00056 } 00057 00058 void VNamedLogger::log(int level, const char* file, int line, const VString& message, const VString& specifiedLoggerName) { 00059 if (level > mLevel) { 00060 return; 00061 } 00062 00063 VNamedLogger::_breakpointLocationForLog(); 00064 00065 if (mRepetitionFilter.isEnabled()) { // avoid mutex if no need to check filter 00066 VMutexLocker timeoutLocker(&mAppendersMutex, "VNamedLogger::log() checkTimeout"); 00067 mRepetitionFilter.checkTimeout(*this); 00068 } 00069 00070 VMutexLocker locker(&mAppendersMutex, "VNamedLogger::log"); 00071 if (mRepetitionFilter.checkMessage(*this, level, file, line, message, specifiedLoggerName, mName)) { 00072 this->_emitToAppenders(level, file, line, true, message, specifiedLoggerName, false, VString::EMPTY()); 00073 if (mPrintStackConfig.shouldPrintStack(level, *this)) { 00074 locker.unlock(); // avoid recursive deadlock, we're done with our data until we recur 00075 VThread::logStackCrawl(message, VNamedLoggerPtr(shared_from_this()), false); 00076 } 00077 } 00078 } 00079 00080 void VNamedLogger::addInfo(VBentoNode& infoNode) const { 00081 infoNode.addString("name", mName); 00082 infoNode.addInt("level", mLevel); 00083 00084 if (this->isDefaultLogger()) { 00085 infoNode.addBool("is-default-logger", true); 00086 } 00087 00088 if (mAppenderNames.size() == 1) { 00089 infoNode.addString("appender", mAppenderNames[0]); 00090 } else if (mAppenderNames.size() > 1) { 00091 (void) infoNode.addStringArray("appenders", mAppenderNames); 00092 } 00093 00094 infoNode.addBool("repetition-filter-enabled", mRepetitionFilter.isEnabled()); 00095 infoNode.addInt("print-stack-level", mPrintStackConfig.getLevel()); 00096 } 00097 00098 void VNamedLogger::log(int level, const VString& message) { 00099 this->log(level, NULL, 0, message); 00100 } 00101 00102 void VNamedLogger::logHexDump(int level, const VString& message, const VString& specifiedLoggerName, const Vu8* buffer, Vs64 length) { 00103 if (level > mLevel) { 00104 return; 00105 } 00106 00107 VNamedLogger::_breakpointLocationForLog(); 00108 00109 // Try to be efficient here: 00110 // Form the hex dump only if the length is > 0. 00111 // But do it once ahead of time, then interate over the appenders, writing to each one. 00112 00113 VString hexString; 00114 if (length > 0) { 00115 VMemoryStream tempBuffer; 00116 VTextIOStream stream(tempBuffer); 00117 VHex hex(&stream); 00118 hex.printHex(buffer, length); 00119 hexString.copyFromBuffer((const char*)tempBuffer.getBuffer(), 0, (int) tempBuffer.getEOFOffset()); 00120 } 00121 00122 VMutexLocker locker(&mAppendersMutex, "VNamedLogger::logHexDump"); 00123 this->_emitToAppenders(level, NULL, 0, true, message, specifiedLoggerName, true, hexString); 00124 } 00125 00126 void VNamedLogger::emitStackCrawlLine(const VString& message) { 00127 VMutexLocker locker(&mAppendersMutex, "VNamedLogger::emitStackCrawlLine"); 00128 this->_emitToAppenders(VLoggerLevel::TRACE /* not used for raw line emit */, NULL, 0, false, VString::EMPTY(), VString::EMPTY(), true, message); 00129 } 00130 00131 void VNamedLogger::setLevel(int level) { 00132 int oldLevel = mLevel; 00133 mLevel = level; 00134 VLogger::checkMaxActiveLogLevelForChangedLogger(oldLevel, level); 00135 } 00136 00137 bool VNamedLogger::isDefaultLogger() const { 00138 return VLogger::gDefaultLogger.get() == this; 00139 } 00140 00141 void VNamedLogger::_emitToAppenders(int level, const char* file, int line, bool emitMessage, const VString& message, const VString& specifiedLoggerName, bool emitRawLine, const VString& rawLine) { 00142 if (mSpecificAppender != NULL_LOG_APPENDER_PTR) { 00143 mSpecificAppender->emit(level, file, line, emitMessage, message, specifiedLoggerName, mName, emitRawLine, rawLine); 00144 } 00145 00146 for (VStringVector::const_iterator i = mAppenderNames.begin(); i != mAppenderNames.end(); ++i) { 00147 VLogAppenderPtr appender = (*i).isEmpty() ? VLogger::getDefaultAppender() : VLogger::getAppender(*i); 00148 appender->emit(level, file, line, emitMessage, message, specifiedLoggerName, mName, emitRawLine, rawLine); 00149 } 00150 00151 VLogger::emitToGlobalAppenders(level, file, line, emitMessage, message, specifiedLoggerName, mName, emitRawLine, rawLine); 00152 } 00153 00154 VString VNamedLogger::_toString() const { 00155 VString s(VSTRING_ARGS("VNamedLogger '%s' (%d) ->", mName.chars(), mLevel)); 00156 00157 VMutexLocker locker(&mAppendersMutex, "VNamedLogger::_toString"); 00158 for (VStringVector::const_iterator i = mAppenderNames.begin(); i != mAppenderNames.end(); ++i) { 00159 s += VSTRING_FORMAT(" '%s'", (*i).chars()); 00160 } 00161 00162 return s; 00163 } 00164 00165 // static 00166 void VNamedLogger::_breakpointLocationForLog() { 00167 // Put a breakpoint here if you want to break on every message that is logged, 00168 // once basic level filtering has been passed. 00169 } 00170 00171 // Provided factories --------------------------------------------------------- 00172 00173 class VCoutLogAppenderFactory : public VLogAppenderFactory { 00174 public: 00175 VCoutLogAppenderFactory() : VLogAppenderFactory() {} 00176 virtual ~VCoutLogAppenderFactory() {} 00177 00178 virtual VLogAppenderPtr instantiateLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults) const 00179 { return VLogAppenderPtr(new VCoutLogAppender(settings, defaults)); } 00180 virtual void addInfo(VBentoNode& infoNode) const 00181 { infoNode.addString("type", "VCoutLogAppenderFactory"); } 00182 }; 00183 00184 class VFileLogAppenderFactory : public VLogAppenderFactory { 00185 public: 00186 VFileLogAppenderFactory() : VLogAppenderFactory() {} 00187 virtual ~VFileLogAppenderFactory() {} 00188 00189 virtual VLogAppenderPtr instantiateLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults) const 00190 { return VLogAppenderPtr(new VFileLogAppender(settings, defaults)); } 00191 virtual void addInfo(VBentoNode& infoNode) const 00192 { infoNode.addString("type", "VFileLogAppenderFactory"); } 00193 }; 00194 00195 class VRollingFileLogAppenderFactory : public VLogAppenderFactory { 00196 public: 00197 VRollingFileLogAppenderFactory() : VLogAppenderFactory() {} 00198 virtual ~VRollingFileLogAppenderFactory() {} 00199 00200 virtual VLogAppenderPtr instantiateLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults) const 00201 { return VLogAppenderPtr(new VRollingFileLogAppender(settings, defaults)); } 00202 virtual void addInfo(VBentoNode& infoNode) const 00203 { infoNode.addString("type", "VRollingFileLogAppenderFactory"); } 00204 }; 00205 00206 class VSilentLogAppenderFactory : public VLogAppenderFactory { 00207 public: 00208 VSilentLogAppenderFactory() : VLogAppenderFactory() {} 00209 virtual ~VSilentLogAppenderFactory() {} 00210 00211 virtual VLogAppenderPtr instantiateLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults) const 00212 { return VLogAppenderPtr(new VSilentLogAppender(settings, defaults)); } 00213 virtual void addInfo(VBentoNode& infoNode) const 00214 { infoNode.addString("type", "VSilentLogAppenderFactory"); } 00215 }; 00216 00217 class VStringLogAppenderFactory : public VLogAppenderFactory { 00218 public: 00219 VStringLogAppenderFactory() : VLogAppenderFactory() {} 00220 virtual ~VStringLogAppenderFactory() {} 00221 00222 virtual VLogAppenderPtr instantiateLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults) const 00223 { return VLogAppenderPtr(new VStringLogAppender(settings, defaults)); } 00224 virtual void addInfo(VBentoNode& infoNode) const 00225 { infoNode.addString("type", "VStringLogAppenderFactory"); } 00226 }; 00227 00228 class VStringVectorLogAppenderFactory : public VLogAppenderFactory { 00229 public: 00230 VStringVectorLogAppenderFactory() : VLogAppenderFactory() {} 00231 virtual ~VStringVectorLogAppenderFactory() {} 00232 00233 virtual VLogAppenderPtr instantiateLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults) const 00234 { return VLogAppenderPtr(new VStringVectorLogAppender(settings, defaults)); } 00235 virtual void addInfo(VBentoNode& infoNode) const 00236 { infoNode.addString("type", "VStringVectorLogAppenderFactory"); } 00237 }; 00238 00239 // VLogger ------------------------------------------------------------------- 00240 00241 // This style of static mutex declaration and access ensures correct 00242 // initialization if accessed during the static initialization phase. 00243 static VMutex* _mutexInstance() { 00244 static VMutex* gVLoggerMutex = new VMutex("gVLoggerMutex", true/*suppress logging of the logger mutex*/); 00245 return gVLoggerMutex; 00246 } 00247 00248 // _mutexInstance() must be used internally whenever referencing these static accessors: 00249 00250 typedef std::map<VString, VNamedLoggerPtr> VNamedLoggerMap; 00251 static VNamedLoggerMap& _getLoggerMap() { 00252 static VNamedLoggerMap* gLoggerMap = new VNamedLoggerMap(); 00253 return *gLoggerMap; 00254 }; 00255 00256 typedef std::map<VString, VLogAppenderPtr> VLogAppendersMap; 00257 static VLogAppendersMap& _getAppendersMap() { 00258 static VLogAppendersMap* gAppendersMap = new VLogAppendersMap(); 00259 return *gAppendersMap; 00260 } 00261 static VLogAppendersMap& _getGlobalAppendersMap() { 00262 static VLogAppendersMap* gGlobalAppendersMap = new VLogAppendersMap(); 00263 return *gGlobalAppendersMap; 00264 } 00265 00266 typedef std::map<VString, VLogAppenderFactoryPtr> VLogAppenderFactoriesMap; 00267 static VLogAppenderFactoriesMap& _getAppenderFactoriesMap() { 00268 static VLogAppenderFactoriesMap* gAppenderFactoriesMap = new VLogAppenderFactoriesMap(); 00269 return *gAppenderFactoriesMap; 00270 } 00271 00272 // static 00273 VString VLoggerLevel::getName(int level) { 00274 // We make all strings 5 characters long for clean layout in the log output. 00275 00276 if (level == OFF) 00277 return VSTRING_COPY("OFF "); // Of course, OFF will never appear in output because there will be no such output! 00278 else if (level == FATAL) 00279 return VSTRING_COPY("FATAL"); 00280 else if (level == ERROR) 00281 return VSTRING_COPY("ERROR"); 00282 else if (level == WARN) 00283 return VSTRING_COPY("WARN "); 00284 else if (level == INFO) 00285 return VSTRING_COPY("INFO "); 00286 else if (level == DEBUG) 00287 return VSTRING_COPY("DEBUG"); 00288 else if (level == TRACE) 00289 return VSTRING_COPY("TRACE"); 00290 else if (level > DEBUG) 00291 return VSTRING_FORMAT("DBG%02d", level); 00292 else if (level > INFO) 00293 return VSTRING_FORMAT("INF%02d", level); 00294 else if (level > WARN) 00295 return VSTRING_FORMAT("WRN%02d", level); 00296 else if (level > ERROR) 00297 return VSTRING_FORMAT("ERR%02d", level); 00298 else 00299 return VSTRING_FORMAT("%05d", level); 00300 } 00301 00302 // static 00303 int VLoggerLevel::fromString(const VString& value) { 00304 if (value.equalsIgnoreCase("OFF")) { 00305 return VLoggerLevel::OFF; 00306 } else if (value.equalsIgnoreCase("FATAL")) { 00307 return VLoggerLevel::FATAL; 00308 } else if (value.equalsIgnoreCase("ERROR")) { 00309 return VLoggerLevel::ERROR; 00310 } else if (value.equalsIgnoreCase("WARN")) { 00311 return VLoggerLevel::WARN; 00312 } else if (value.equalsIgnoreCase("INFO")) { 00313 return VLoggerLevel::INFO; 00314 } else if (value.equalsIgnoreCase("DEBUG")) { 00315 return VLoggerLevel::DEBUG; 00316 } else if (value.equalsIgnoreCase("TRACE")) { 00317 return VLoggerLevel::TRACE; 00318 } else if (value.equalsIgnoreCase("ALL")) { 00319 return VLoggerLevel::ALL; 00320 } 00321 00322 return value.parseInt(); // Will throw VRangeException if value is not an integer. 00323 } 00324 00325 // static 00326 void VLogger::installNewLogAppender(const VSettingsNode& appenderSettings, const VSettingsNode& appenderDefaults) { 00327 VMutexLocker locker(_mutexInstance(), "VLogger::installNewLogAppender"); 00328 VLogAppenderFactoriesMap::const_iterator pos = _getAppenderFactoriesMap().find(appenderSettings.getString("kind")); 00329 if (pos != _getAppenderFactoriesMap().end()) { 00330 VLogAppenderPtr appender = pos->second->instantiateLogAppender(appenderSettings, appenderDefaults); 00331 00332 locker.unlock(); 00333 VLogger::registerLogAppender(appender); 00334 } 00335 } 00336 00337 // static 00338 void VLogger::installNewNamedLogger(const VSettingsNode& loggerSettings) { 00339 VString name = loggerSettings.getString("name"); 00340 VString levelText = loggerSettings.getString("level", "INFO"); 00341 int level = VLoggerLevel::fromString(levelText); 00342 VStringVector appenderNames; 00343 VString appenderName = loggerSettings.getString("appender", VString::EMPTY()); 00344 if (appenderName.isNotEmpty()) { 00345 appenderNames.push_back(appenderName); 00346 } 00347 00348 const int numAppenders = loggerSettings.countNamedChildren("appender"); 00349 for (int i = 0; i < numAppenders; ++i) { 00350 const VSettingsNode* appenderNode = loggerSettings.getNamedChild("appender", i); 00351 appenderName = appenderNode->getString("name", VString::EMPTY()); 00352 if (appenderName.isNotEmpty()) { 00353 appenderNames.push_back(appenderName); 00354 } 00355 } 00356 00357 VNamedLoggerPtr logger(new VNamedLogger(name, level, appenderNames)); 00358 int printStackLevel = loggerSettings.getInt("print-stack-level", VLoggerLevel::OFF); 00359 if (printStackLevel != VLoggerLevel::OFF) { 00360 int maxNumOccurrences = loggerSettings.getInt("print-stack-count", -1); 00361 VDuration timeLimit = loggerSettings.getDuration("print-stack-duration", VDuration::POSITIVE_INFINITY()); 00362 logger->setPrintStackInfo(printStackLevel, maxNumOccurrences, timeLimit); 00363 } 00364 00365 VMutexLocker locker(_mutexInstance(), "VLogger::installNewNamedLogger"); 00366 VLogger::_registerLogger(logger, false); 00367 } 00368 00369 // static 00370 void VLogger::installNewNamedLogger(const VString& name, int level, const VStringVector& appenderNames) { 00371 VNamedLoggerPtr logger(new VNamedLogger(name, level, appenderNames)); 00372 00373 VMutexLocker locker(_mutexInstance(), "VLogger::installNewNamedLogger"); 00374 VLogger::_registerLogger(logger, false); 00375 } 00376 00377 // static 00378 void VLogger::installNewNamedLogger(const VString& name, int level, const VString& appenderName) { 00379 VStringVector appenderNames; 00380 appenderNames.push_back(appenderName); 00381 VLogger::installNewNamedLogger(name, level, appenderNames); 00382 } 00383 00384 // static 00385 void VLogger::registerLogAppenderFactory(const VString& appenderKind, VLogAppenderFactoryPtr factory) { 00386 VMutexLocker locker(_mutexInstance(), "VLogger::registerLogAppenderFactory"); 00387 _getAppenderFactoriesMap()[appenderKind] = factory; 00388 } 00389 00390 // static 00391 void VLogger::configure(const VFSNode& baseLogDirectory, const VSettingsNode& loggingSettings) { 00392 if (baseLogDirectory.getPath().isNotEmpty()) 00393 gBaseLogDirectory = baseLogDirectory; 00394 00395 // The caller is welcome to register other appender factory types before calling configure where we register the built-in types. 00396 VLogger::registerLogAppenderFactory("cout", VLogAppenderFactoryPtr(new VCoutLogAppenderFactory())); 00397 VLogger::registerLogAppenderFactory("file", VLogAppenderFactoryPtr(new VFileLogAppenderFactory())); 00398 VLogger::registerLogAppenderFactory("rolling-file", VLogAppenderFactoryPtr(new VRollingFileLogAppenderFactory())); 00399 VLogger::registerLogAppenderFactory("silent", VLogAppenderFactoryPtr(new VSilentLogAppenderFactory())); 00400 VLogger::registerLogAppenderFactory("string", VLogAppenderFactoryPtr(new VStringLogAppenderFactory())); 00401 VLogger::registerLogAppenderFactory("string-vector", VLogAppenderFactoryPtr(new VStringVectorLogAppenderFactory())); 00402 00403 // Stash any per-appender defaults in a map while we configure, so we can pass them to the factories we call. 00404 std::map<VString, const VSettingsNode*> defaultsForAppenders; 00405 VSettings emptyDefaults; 00406 const int numAppenderDefaults = loggingSettings.countNamedChildren("appender-defaults"); 00407 for (int i = 0; i < numAppenderDefaults; ++i) { 00408 const VSettingsNode* appenderDefaultsNode = loggingSettings.getNamedChild("appender-defaults", i); 00409 defaultsForAppenders[appenderDefaultsNode->getString("kind", VString::EMPTY())] = appenderDefaultsNode; 00410 } 00411 00412 const int numAppenders = loggingSettings.countNamedChildren("appender"); 00413 for (int i = 0; i < numAppenders; ++i) { 00414 const VSettingsNode* appenderNode = loggingSettings.getNamedChild("appender", i); 00415 const VSettingsNode* appenderDefaults = defaultsForAppenders[appenderNode->getString("kind")]; 00416 if (appenderDefaults == NULL) { 00417 appenderDefaults = &emptyDefaults; 00418 } 00419 00420 VLogger::installNewLogAppender(*appenderNode, *appenderDefaults); 00421 } 00422 00423 const int numLoggers = loggingSettings.countNamedChildren("logger"); 00424 for (int i = 0; i < numLoggers; ++i) { 00425 const VSettingsNode* loggerNode = loggingSettings.getNamedChild("logger", i); 00426 VLogger::installNewNamedLogger(*loggerNode); 00427 } 00428 } 00429 00430 // static 00431 void VLogger::shutdown() { 00432 VMutexLocker locker(_mutexInstance(), "VLogger::shutdown"); 00433 00434 // Clear all shared_ptr references. This will allow all referenced objects to be deleted (unless someone outside retains a reference). 00435 gDefaultLogger.reset(); 00436 gDefaultAppender.reset(); 00437 _getLoggerMap().clear(); 00438 _getAppendersMap().clear(); 00439 _getAppenderFactoriesMap().clear(); 00440 00441 gMaxActiveLevel = 0; 00442 } 00443 00444 // static 00445 void VLogger::registerLogAppender(VLogAppenderPtr appender, bool asDefaultAppender) { 00446 VMutexLocker locker(_mutexInstance(), "VLogger::registerLogAppender"); 00447 VLogger::_registerAppender(appender, asDefaultAppender, false); 00448 } 00449 00450 // static 00451 void VLogger::registerGlobalAppender(VLogAppenderPtr appender, bool asDefaultAppender) { 00452 VMutexLocker locker(_mutexInstance(), "VLogger::registerLogAppender"); 00453 VLogger::_registerAppender(appender, asDefaultAppender, true); 00454 } 00455 00456 // static 00457 void VLogger::registerLogger(VNamedLoggerPtr namedLogger, bool asDefaultLogger) { 00458 VMutexLocker locker(_mutexInstance(), "VLogger::registerLogger"); 00459 VLogger::_registerLogger(namedLogger, asDefaultLogger); 00460 } 00461 00462 // static 00463 void VLogger::deregisterLogAppender(VLogAppenderPtr appender) { 00464 VMutexLocker locker(_mutexInstance(), "VLogger::deregisterLogAppender"); 00465 00466 if (gDefaultAppender == appender) { 00467 gDefaultAppender.reset(); 00468 } 00469 00470 VLogAppendersMap::iterator pos = _getAppendersMap().find(appender->getName()); 00471 if (pos != _getAppendersMap().end()) { 00472 _getAppendersMap().erase(pos); 00473 } 00474 00475 pos = _getGlobalAppendersMap().find(appender->getName()); 00476 if (pos != _getGlobalAppendersMap().end()) { 00477 _getGlobalAppendersMap().erase(pos); 00478 } 00479 00480 } 00481 00482 // static 00483 void VLogger::deregisterLogAppender(const VString& name) { 00484 VLogAppenderPtr appender = VLogger::findAppender(name); 00485 if (appender != nullptr) { 00486 VLogger::deregisterLogAppender(appender); 00487 } 00488 } 00489 00490 // static 00491 void VLogger::deregisterLogger(VNamedLoggerPtr namedLogger) { 00492 VMutexLocker locker(_mutexInstance(), "VLogger::deregisterLogger"); 00493 00494 if (gDefaultLogger == namedLogger) { 00495 gDefaultLogger.reset(); 00496 } 00497 00498 VNamedLoggerMap::iterator pos = _getLoggerMap().find(namedLogger->getName()); 00499 if (pos != _getLoggerMap().end()) { 00500 _getLoggerMap().erase(pos); 00501 } 00502 00503 VLogger::_checkMaxActiveLogLevelForRemovedLogger(namedLogger->getLevel()); 00504 } 00505 00506 // static 00507 void VLogger::deregisterLogger(const VString& name) { 00508 VNamedLoggerPtr logger = VLogger::findNamedLogger(name); 00509 if (logger != nullptr) { 00510 VLogger::deregisterLogger(logger); 00511 } 00512 } 00513 00514 // static 00515 bool VLogger::isDefaultLogLevelActive(int level) { 00516 return VLogger::isLogLevelActive(level) && VLogger::getDefaultLogger()->isEnabledFor(level); 00517 } 00518 00519 // static 00520 bool VLogger::isLogLevelActive(int level) { 00521 // No locking necessary to simply read this volatile value. 00522 return (level <= gMaxActiveLevel); 00523 } 00524 00525 // static 00526 VNamedLoggerPtr VLogger::getDefaultLogger() { 00527 VMutexLocker locker(_mutexInstance(), "VLogger::getDefaultLogger"); 00528 00529 if (gDefaultLogger == nullptr) { 00530 VLogger::_registerLogger(VNamedLoggerPtr(new VNamedLogger("auto-default-logger", VLoggerLevel::INFO, VStringVector())), true); 00531 } 00532 00533 return gDefaultLogger; 00534 } 00535 00536 #ifdef VLOGGER_INTERNAL_DEBUGGING 00537 // static 00538 void VLogger::_reportLoggerChange(bool before, const VString& label, const VNamedLoggerPtr& was, const VNamedLoggerPtr& is) { 00539 VString wasName = (was == nullptr) ? "null" : was->getName(); 00540 VString isName = is->getName(); 00541 VString msg(VSTRING_ARGS("_reportLoggerChange %s %s: was '%s', is '%s'", (before ? "before" : "after"), label.chars(), wasName.chars(), isName.chars())); 00542 std::cout << msg << std::endl; 00543 } 00544 #endif /* VLOGGER_INTERNAL_DEBUGGING */ 00545 00546 // static 00547 void VLogger::setDefaultLogger(VNamedLoggerPtr namedLogger) { 00548 VMutexLocker locker(_mutexInstance(), "VLogger::getDefaultLogger"); 00549 VLogger::_reportLoggerChange(true, "setDefaultLogger", gDefaultLogger, namedLogger); 00550 gDefaultLogger = namedLogger; 00551 VLogger::_reportLoggerChange(false, "setDefaultLogger", gDefaultLogger, namedLogger); 00552 } 00553 00554 // static 00555 VNamedLoggerPtr VLogger::getLogger(const VString& name) { 00556 VNamedLoggerPtr logger = findNamedLogger(name); 00557 00558 if (logger == nullptr) { 00559 logger = VLogger::getDefaultLogger(); 00560 } 00561 00562 return logger; 00563 } 00564 00565 // static 00566 VNamedLoggerPtr VLogger::findDefaultLogger() { 00567 VMutexLocker locker(_mutexInstance(), "VLogger::findDefaultLogger"); 00568 return gDefaultLogger; 00569 } 00570 00571 // static 00572 VNamedLoggerPtr VLogger::findDefaultLoggerForLevel(int level) { 00573 VMutexLocker locker(_mutexInstance(), "VLogger::findDefaultLoggerForLevel"); 00574 00575 if (gDefaultLogger == nullptr) { 00576 VLogger::_registerLogger(VNamedLoggerPtr(new VNamedLogger("default", VLoggerLevel::INFO, VStringVector())), true); 00577 } 00578 00579 return gDefaultLogger->isEnabledFor(level) ? gDefaultLogger : NULL_NAMED_LOGGER_PTR; 00580 } 00581 00582 // static 00583 VNamedLoggerPtr VLogger::findNamedLogger(const VString& name) { 00584 VMutexLocker locker(_mutexInstance(), "VLogger::findNamedLogger"); 00585 return VLogger::_findNamedLoggerFromPathName(name); 00586 } 00587 00588 // static 00589 VNamedLoggerPtr VLogger::findNamedLoggerForLevel(const VString& name, int level) { 00590 // Fast as possible short-circuit: If no logger is enabled at the level (global int test), further searching is not necessary. 00591 if (! VLogger::isLogLevelActive(level)) { 00592 return NULL_NAMED_LOGGER_PTR; 00593 } 00594 00595 VNamedLoggerPtr logger = VLogger::findNamedLogger(name); 00596 00597 // If found but level is too high, return null so it won't log. 00598 if ((logger != nullptr) && (logger->getLevel() < level)) { 00599 return NULL_NAMED_LOGGER_PTR; 00600 } 00601 00602 // If not found, get the default logger with a level check (returns null if level is too high). 00603 if (logger == nullptr) { 00604 logger = VLogger::findDefaultLoggerForLevel(level); 00605 } 00606 00607 return logger; 00608 } 00609 00610 #ifdef VLOGGER_INTERNAL_DEBUGGING 00611 // static 00612 void VLogger::_reportAppenderChange(bool before, const VString& label, const VLogAppenderPtr& was, const VLogAppenderPtr& is) { 00613 VString wasName = (was == nullptr) ? "null" : was->getName(); 00614 VString isName = is->getName(); 00615 VString msg(VSTRING_ARGS("_reportAppenderChange %s %s: was '%s', is '%s'", (before ? "before" : "after"), label.chars(), wasName.chars(), isName.chars())); 00616 std::cout << msg << std::endl; 00617 } 00618 #endif /* VLOGGER_INTERNAL_DEBUGGING */ 00619 00620 // static 00621 VLogAppenderPtr VLogger::getDefaultAppender() { 00622 VMutexLocker locker(_mutexInstance(), "VLogger::getDefaultAppender"); 00623 00624 if (gDefaultAppender == nullptr) { 00625 VLogger::_registerAppender(VLogAppenderPtr(new VCoutLogAppender("auto-default-cout-appender", VLogAppender::DO_FORMAT_OUTPUT, VString::EMPTY(), VString::EMPTY())), true); 00626 } 00627 00628 return gDefaultAppender; 00629 } 00630 00631 // static 00632 VLogAppenderPtr VLogger::getAppender(const VString& appenderName) { 00633 /* locker scope */ { 00634 VMutexLocker locker(_mutexInstance(), "VLogger::getAppender"); 00635 VLogAppendersMap::const_iterator pos = _getAppendersMap().find(appenderName); 00636 if (pos != _getAppendersMap().end()) { 00637 return pos->second; 00638 } 00639 } 00640 00641 // Doesn't exist. Return default appender. 00642 return VLogger::getDefaultAppender(); 00643 } 00644 00645 // static 00646 VLogAppenderPtrList VLogger::getAllAppenders() { 00647 VLogAppenderPtrList result; 00648 00649 /* locker scope */ { 00650 VMutexLocker locker(_mutexInstance(), "VLogger::getAllAppenders"); 00651 00652 for (VLogAppendersMap::const_iterator i = _getAppendersMap().begin(); i != _getAppendersMap().end(); ++i) { 00653 result.push_back((*i).second); 00654 } 00655 00656 for (VLogAppendersMap::const_iterator i = _getGlobalAppendersMap().begin(); i != _getGlobalAppendersMap().end(); ++i) { 00657 result.push_back((*i).second); 00658 } 00659 } 00660 00661 return result; 00662 } 00663 00664 // static 00665 VLogAppenderPtr VLogger::findDefaultAppender() { 00666 VMutexLocker locker(_mutexInstance(), "VLogger::findDefaultAppender"); 00667 return gDefaultAppender; 00668 } 00669 00670 // static 00671 VLogAppenderPtr VLogger::findAppender(const VString& name) { 00672 VLogAppendersMap::iterator pos = _getAppendersMap().find(name); 00673 if (pos != _getAppendersMap().end()) { 00674 return pos->second; 00675 } 00676 00677 return NULL_LOG_APPENDER_PTR; 00678 } 00679 00680 // static 00681 const VFSNode& VLogger::getBaseLogDirectory() { 00682 return gBaseLogDirectory; 00683 } 00684 00685 // static 00686 VBentoNode* VLogger::commandGetInfo() { 00687 VMutexLocker locker(_mutexInstance(), "VLogger::commandGetInfo"); 00688 return VLogger::_commandGetInfo(); 00689 } 00690 00691 // static 00692 VBentoNode* VLogger::_commandGetInfo() { 00693 VBentoNode* rootNode = new VBentoNode("logger-info"); 00694 VBentoNode* factoriesNode = rootNode->addNewChildNode("factories"); 00695 VBentoNode* appendersNode = rootNode->addNewChildNode("appenders"); 00696 VBentoNode* loggersNode = rootNode->addNewChildNode("loggers"); 00697 00698 rootNode->addInt("max-active-log-level", gMaxActiveLevel); 00699 00700 const VLogAppenderFactoriesMap& factories = _getAppenderFactoriesMap(); 00701 for (VLogAppenderFactoriesMap::const_iterator i = factories.begin(); i != factories.end(); ++i) { 00702 VBentoNode* factoryNode = factoriesNode->addNewChildNode("factory"); 00703 (*i).second->addInfo(*factoryNode); 00704 } 00705 00706 const VLogAppendersMap& appenders = _getAppendersMap(); 00707 for (VLogAppendersMap::const_iterator i = appenders.begin(); i != appenders.end(); ++i) { 00708 VBentoNode* appenderNode = appendersNode->addNewChildNode("appender"); 00709 (*i).second->addInfo(*appenderNode); 00710 } 00711 00712 const VNamedLoggerMap& loggers = _getLoggerMap(); 00713 for (VNamedLoggerMap::const_iterator i = loggers.begin(); i != loggers.end(); ++i) { 00714 VBentoNode* loggerNode = loggersNode->addNewChildNode("logger"); 00715 (*i).second->addInfo(*loggerNode); 00716 } 00717 00718 return rootNode; 00719 } 00720 00721 // static 00722 VString VLogger::commandGetInfoString() { 00723 VMutexLocker locker(_mutexInstance(), "VLogger::commandGetInfoString"); 00724 return VLogger::_commandGetInfoString(); 00725 } 00726 00727 // static 00728 VString VLogger::_commandGetInfoString() { 00729 VUniquePtr<VBentoNode> bento(VLogger::_commandGetInfo()); 00730 VString s; 00731 bento->writeToBentoTextString(s, true); 00732 return s; 00733 } 00734 00735 // static 00736 void VLogger::commandSetLogLevel(const VString& loggerName, int level) { 00737 00738 std::vector<VNamedLoggerPtr> targetLoggers; 00739 00740 // First, get all the desired loggers, with required locking in place. 00741 /* locker scope */ { 00742 VMutexLocker locker(_mutexInstance(), "VLogger::commandSetLogLevel()"); 00743 const VNamedLoggerMap& loggers = _getLoggerMap(); 00744 for (VNamedLoggerMap::const_iterator i = loggers.begin(); i != loggers.end(); ++i) { 00745 VNamedLoggerPtr logger = (*i).second; 00746 if (loggerName.isEmpty() || (logger->getName() == loggerName)) { 00747 targetLoggers.push_back(logger); 00748 } 00749 } 00750 } 00751 00752 // Now, set each logger's level with the public API. It locks each time. 00753 for (std::vector<VNamedLoggerPtr>::const_iterator i = targetLoggers.begin(); i != targetLoggers.end(); ++i) { 00754 (*i)->setLevel(level); 00755 } 00756 } 00757 00758 // static 00759 void VLogger::commandSetPrintStackLevel(const VString& loggerName, int printStackLevel, int count, const VDuration& timeLimit) { 00760 VMutexLocker locker(_mutexInstance(), "VLogger::commandSetLogLevel()"); 00761 const VNamedLoggerMap& loggers = _getLoggerMap(); 00762 for (VNamedLoggerMap::const_iterator i = loggers.begin(); i != loggers.end(); ++i) { 00763 VNamedLoggerPtr logger = (*i).second; 00764 if (loggerName.isEmpty() || (logger->getName() == loggerName)) { 00765 logger->setPrintStackInfo(printStackLevel, count, timeLimit); 00766 } 00767 } 00768 } 00769 00770 // static 00771 void VLogger::emitToGlobalAppenders(int level, const char* file, int line, bool emitMessage, const VString& message, const VString& specifiedLoggerName, const VString& actualLoggerName, bool emitRawLine, const VString& rawLine) { 00772 VMutexLocker locker(_mutexInstance(), "VLogger::emitToGlobalAppenders"); 00773 for (VLogAppendersMap::const_iterator i = _getGlobalAppendersMap().begin(); i != _getGlobalAppendersMap().end(); ++i) { 00774 VLogAppenderPtr appender = (*i).second; 00775 appender->emit(level, file, line, emitMessage, message, specifiedLoggerName, actualLoggerName, emitRawLine, rawLine); 00776 } 00777 } 00778 00779 // static 00780 VString VLogger::getCleansedLoggerName(const VString& s) { 00781 VString cleansed(s); 00782 cleansed.replace(VCodePoint('.'), VCodePoint('-')); 00783 return cleansed; 00784 } 00785 00786 // static 00787 void VLogger::_registerAppender(VLogAppenderPtr appender, bool asDefaultAppender, bool asGlobalAppender) { 00788 // ASSUMES CALLER HOLDS _mutexInstance(). 00789 00790 VLogger::_reportAppenderChange(true, "_registerAppender", gDefaultAppender, appender); 00791 00792 if (asDefaultAppender || (gDefaultAppender == nullptr)) { 00793 gDefaultAppender = appender; 00794 } 00795 00796 _getAppendersMap()[appender->getName()] = appender; 00797 00798 if (asGlobalAppender) { 00799 _getGlobalAppendersMap()[appender->getName()] = appender; 00800 } 00801 00802 VLogger::_reportAppenderChange(false, "_registerAppender", gDefaultAppender, appender); 00803 } 00804 00805 00806 // static 00807 void VLogger::_registerLogger(VNamedLoggerPtr namedLogger, bool asDefaultLogger) { 00808 // ASSUMES CALLER HOLDS _mutexInstance(). 00809 00810 VLogger::_reportLoggerChange(true, "_registerLogger", gDefaultLogger, namedLogger); 00811 00812 if (asDefaultLogger || (gDefaultLogger == nullptr)) { 00813 gDefaultLogger = namedLogger; 00814 } 00815 00816 _getLoggerMap()[namedLogger->getName()] = namedLogger; 00817 00818 VLogger::_checkMaxActiveLogLevelForNewLogger(namedLogger->getLevel()); 00819 00820 VLogger::_reportLoggerChange(false, "_registerLogger", gDefaultLogger, namedLogger); 00821 } 00822 00823 // static 00824 void VLogger::_checkMaxActiveLogLevelForNewLogger(int newActiveLevel) { 00825 // ASSUMES CALLER HOLDS _mutexInstance(). 00826 00827 // If the logger has a higher level, then its level is the new max. 00828 if (newActiveLevel > gMaxActiveLevel) { 00829 gMaxActiveLevel = newActiveLevel; 00830 } 00831 } 00832 00833 // static 00834 void VLogger::checkMaxActiveLogLevelForRemovedLogger(int removedActiveLevel) { 00835 VMutexLocker locker(_mutexInstance(), "checkMaxActiveLogLevelForRemovedLogger"); 00836 _checkMaxActiveLogLevelForRemovedLogger(removedActiveLevel); 00837 } 00838 00839 // static 00840 void VLogger::_checkMaxActiveLogLevelForRemovedLogger(int removedActiveLevel) { 00841 // ASSUMES CALLER HOLDS _mutexInstance(). 00842 00843 // If the logger had the highest level, we need to search to find the new max. 00844 if (removedActiveLevel >= gMaxActiveLevel) { 00845 VLogger::_recalculateMaxActiveLogLevel(); 00846 } 00847 } 00848 00849 // static 00850 void VLogger::checkMaxActiveLogLevelForChangedLogger(int oldActiveLevel, int newActiveLevel) { 00851 VMutexLocker locker(_mutexInstance(), "checkMaxActiveLogLevelForChangedLogger"); 00852 _checkMaxActiveLogLevelForChangedLogger(oldActiveLevel, newActiveLevel); 00853 } 00854 00855 // static 00856 void VLogger::_checkMaxActiveLogLevelForChangedLogger(int oldActiveLevel, int newActiveLevel) { 00857 // ASSUMES CALLER HOLDS _mutexInstance(). 00858 00859 // If the logger's new level is higher than current max, then its level is the new max. 00860 // Otherwise, if the old level was the max, and the new level is lower than it, we need to search to find the new max. 00861 if (newActiveLevel > gMaxActiveLevel) { 00862 gMaxActiveLevel = newActiveLevel; 00863 } else if ((oldActiveLevel >= gMaxActiveLevel) && (newActiveLevel < gMaxActiveLevel)) { 00864 VLogger::_recalculateMaxActiveLogLevel(); 00865 } 00866 } 00867 00868 // static 00869 void VLogger::_recalculateMaxActiveLogLevel() { 00870 // ASSUMES CALLER HOLDS _mutexInstance(). 00871 00872 // This value is less than previous max. Scan all loggers to see what the new max is. 00873 int newMax = 0; 00874 for (VNamedLoggerMap::const_iterator i = _getLoggerMap().begin(); i != _getLoggerMap().end(); ++i) { 00875 newMax = V_MAX(newMax, (*i).second->getLevel()); 00876 } 00877 00878 gMaxActiveLevel = newMax; 00879 } 00880 00881 // static 00882 VNamedLoggerPtr VLogger::_findNamedLoggerFromExactName(const VString& name) { 00883 VNamedLoggerMap::const_iterator pos = _getLoggerMap().find(name); 00884 if (pos == _getLoggerMap().end()) { 00885 return NULL_NAMED_LOGGER_PTR; 00886 } 00887 00888 return pos->second; 00889 } 00890 00891 // static 00892 VNamedLoggerPtr VLogger::_findNamedLoggerFromPathName(const VString& pathName) { 00893 VString nextNameToSearch(pathName); 00894 00895 while (nextNameToSearch.contains('.')) { 00896 VNamedLoggerPtr foundLogger = VLogger::_findNamedLoggerFromExactName(nextNameToSearch); 00897 if (foundLogger != nullptr) { 00898 return foundLogger; 00899 } 00900 00901 nextNameToSearch.substringInPlace(0, nextNameToSearch.lastIndexOf('.')); 00902 } 00903 00904 return VLogger::_findNamedLoggerFromExactName(nextNameToSearch); 00905 } 00906 00907 // VLogAppender ------------------------------------------------------ 00908 00909 //static const VString DEFAULT_APPENDER_FORMAT_SPEC("$localtime $level | $thread | $specifiedlogger=>$actuallogger | $location$message"); // <- useful for debugging the named logger routing 00910 static const VString DEFAULT_APPENDER_FORMAT_SPEC("$localtime $level | $thread | $location$message"); 00911 static const VString DEFAULT_TIME_FORMAT("y-MM-dd HH:mm:ss.SSS"); 00912 00913 VLogAppender::VLogAppender(const VString& name, bool formatOutput, const VString& formatSpec, const VString& timeFormat) 00914 : mMutex(VSTRING_FORMAT("VLogAppender(%s)", name.chars()), true/*this mutex itself must not log*/) 00915 , mName(name) 00916 , mFormatOutput(formatOutput) 00917 , mFormatSpec(formatSpec.isEmpty() ? DEFAULT_APPENDER_FORMAT_SPEC : formatSpec) 00918 , mTimeFormatter(timeFormat.isEmpty() ? DEFAULT_TIME_FORMAT : timeFormat) 00919 , mFormatUsesLocalTime(mFormatSpec.contains("$localtime")) 00920 , mFormatUsesUTCTime(mFormatSpec.contains("$utctime")) 00921 , mFormatUsesLevel(mFormatSpec.contains("$level")) 00922 , mFormatUsesThread(mFormatSpec.contains("$thread")) 00923 , mFormatUsesLocation(mFormatSpec.contains("$location")) 00924 , mFormatUsesSpecifiedLoggerName(mFormatSpec.contains("$specifiedlogger")) 00925 , mFormatUsesActualLoggerName(mFormatSpec.contains("$actuallogger")) 00926 { 00927 } 00928 00929 VLogAppender::VLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults) 00930 : mMutex(VSTRING_FORMAT("VLogAppender(%s)", settings.getString("name").chars()), true/*this mutex itself must not log*/) 00931 , mName(settings.getString("name")) 00932 , mFormatOutput(VLogAppender::_getBooleanInitSetting("format-output", settings, defaults, DO_FORMAT_OUTPUT)) 00933 , mFormatSpec(VLogAppender::_getStringInitSetting("format-spec", settings, defaults, DEFAULT_APPENDER_FORMAT_SPEC)) 00934 , mTimeFormatter(VLogAppender::_getStringInitSetting("time-format", settings, defaults, DEFAULT_TIME_FORMAT)) 00935 , mFormatUsesLocalTime(mFormatSpec.contains("$localtime")) 00936 , mFormatUsesUTCTime(mFormatSpec.contains("$utctime")) 00937 , mFormatUsesLevel(mFormatSpec.contains("$level")) 00938 , mFormatUsesThread(mFormatSpec.contains("$thread")) 00939 , mFormatUsesLocation(mFormatSpec.contains("$location")) 00940 , mFormatUsesSpecifiedLoggerName(mFormatSpec.contains("$specifiedlogger")) 00941 , mFormatUsesActualLoggerName(mFormatSpec.contains("$actuallogger")) 00942 { 00943 } 00944 00945 VLogAppender::~VLogAppender() { 00946 } 00947 00948 void VLogAppender::addInfo(VBentoNode& infoNode) const { 00949 infoNode.addString("name", mName); 00950 00951 if (this->isDefaultAppender()) { 00952 infoNode.addBool("is-default-appender", true); 00953 } 00954 00955 if (!mFormatOutput) { 00956 infoNode.addBool("format-output", DONT_FORMAT_OUTPUT); 00957 } 00958 00959 infoNode.addString("format-spec", mFormatSpec); 00960 infoNode.addString("time-format", mTimeFormatter.getFormatSpecifier()); 00961 } 00962 00963 void VLogAppender::emit(int level, const char* file, int line, bool emitMessage, const VString& message, const VString& specifiedLoggerName, const VString& actualLoggerName, bool emitRawLine, const VString& rawLine) { 00964 VLogAppender::_breakpointLocationForEmit(); 00965 00966 VMutexLocker locker(&mMutex, "emit"); // ensure multiple threads don't intertwine lines of a single emission 00967 00968 if (emitMessage) { 00969 this->_emitMessage(level, file, line, message, specifiedLoggerName, actualLoggerName); 00970 } 00971 00972 if (emitRawLine) { 00973 this->_emitRawLine(rawLine); 00974 } 00975 } 00976 00977 void VLogAppender::emitRaw(const VString& message) { 00978 this->emit(VLoggerLevel::TRACE, NULL, 0, false, VString::EMPTY(), VString::EMPTY(), VString::EMPTY(), true, message); 00979 } 00980 00981 bool VLogAppender::isDefaultAppender() const { 00982 return VLogger::gDefaultAppender.get() == this; 00983 } 00984 00985 // static 00986 bool VLogAppender::_getBooleanInitSetting(const VString& attributePath, const VSettingsNode& settings, const VSettingsNode& defaults, bool defaultValue) { 00987 return settings.getBoolean(attributePath, defaults.getBoolean(attributePath, defaultValue)); 00988 } 00989 00990 // static 00991 int VLogAppender::_getIntInitSetting(const VString& attributePath, const VSettingsNode& settings, const VSettingsNode& defaults, int defaultValue) { 00992 return settings.getInt(attributePath, defaults.getInt(attributePath, defaultValue)); 00993 } 00994 00995 // static 00996 VString VLogAppender::_getStringInitSetting(const VString& attributePath, const VSettingsNode& settings, const VSettingsNode& defaults, const VString& defaultValue) { 00997 return settings.getString(attributePath, defaults.getString(attributePath, defaultValue)); 00998 } 00999 01000 void VLogAppender::_emitMessage(int level, const char* file, int line, const VString& message, const VString& specifiedLoggerName, const VString& actualLoggerName) { 01001 if (mFormatOutput) { 01002 VString formattedMessage = this->_formatMessage(level, file, line, message, specifiedLoggerName, actualLoggerName); 01003 this->_emitRawLine(formattedMessage); 01004 } else { 01005 this->_emitRawLine(message); // directly, without applying formatting 01006 } 01007 } 01008 01009 VString VLogAppender::_formatMessage(int level, const char* file, int line, const VString& message, const VString& specifiedLoggerName, const VString& actualLoggerName) { 01010 VInstant now; 01011 VInstant trueNow(now); // copy constructor avoids another call to read the clock 01012 01013 // If we are running in simulated time, display both the current and simulated time. 01014 bool prependTrueTime = (mFormatUsesLocalTime || mFormatUsesUTCTime) && ((VInstant::getSimulatedClockOffset() != VDuration::ZERO()) || VInstant::isTimeFrozen()); 01015 if (prependTrueTime) { 01016 trueNow.setTrueNow(); 01017 } 01018 01019 VString formattedMessage = mFormatSpec; 01020 01021 // I am do replacement in this order (mainly with $message last) to be sure that 01022 // none of the replacements put a $ specifier into the string. 01023 if (mFormatUsesLocalTime) { 01024 VString localTimeStampString; 01025 if (prependTrueTime) { 01026 localTimeStampString = trueNow.getLocalString(mTimeFormatter) + " "; 01027 } 01028 01029 localTimeStampString += now.getLocalString(mTimeFormatter); 01030 formattedMessage.replace("$localtime", localTimeStampString); 01031 } 01032 01033 if (mFormatUsesUTCTime) { 01034 VString utcTimeStampString; 01035 if (prependTrueTime) { 01036 utcTimeStampString = trueNow.getUTCString(mTimeFormatter) + " "; 01037 } 01038 utcTimeStampString += now.getUTCString(mTimeFormatter); 01039 formattedMessage.replace("$utctime", utcTimeStampString); 01040 } 01041 01042 if (mFormatUsesLevel) { 01043 formattedMessage.replace("$level", VLoggerLevel::getName(level)); 01044 } 01045 01046 if (mFormatUsesLocation) { 01047 if (file == NULL) { 01048 formattedMessage.replace("$location", VString::EMPTY()); 01049 } else { 01050 formattedMessage.replace("$location", VSTRING_FORMAT("@ %s:%d: ", file, line)); 01051 } 01052 } 01053 01054 if (mFormatUsesThread) { 01055 formattedMessage.replace("$thread", VThread::getCurrentThreadName()); 01056 } 01057 01058 if (mFormatUsesSpecifiedLoggerName) { 01059 formattedMessage.replace("$specifiedlogger", specifiedLoggerName); 01060 } 01061 01062 if (mFormatUsesActualLoggerName) { 01063 formattedMessage.replace("$actuallogger", actualLoggerName); 01064 } 01065 01066 formattedMessage.replace("$message", message); 01067 01068 return formattedMessage; 01069 } 01070 01071 VString VLogAppender::_toString() const { 01072 return VSTRING_FORMAT("VLogAppender '%s'", mName.chars()); 01073 } 01074 01075 // static 01076 void VLogAppender::_breakpointLocationForEmit() { 01077 // Put a breakpoint here if you want to break on every message that is actually 01078 // emitted to an appender after the log level filtering and routing has passed. 01079 } 01080 01081 // VCoutLogAppender ---------------------------------------------------------- 01082 01083 VCoutLogAppender::VCoutLogAppender(const VString& name, bool formatOutput, const VString& formatSpec, const VString& timeFormat) 01084 : VLogAppender(name, formatOutput, formatSpec, timeFormat) 01085 { 01086 std::cout << std::endl; 01087 } 01088 01089 VCoutLogAppender::VCoutLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults) 01090 : VLogAppender(settings, defaults) 01091 { 01092 std::cout << std::endl; 01093 } 01094 01095 void VCoutLogAppender::addInfo(VBentoNode& infoNode) const { 01096 VLogAppender::addInfo(infoNode); 01097 infoNode.addString("type", "VCoutLogAppender"); 01098 } 01099 01100 void VCoutLogAppender::_emitRawLine(const VString& line) { 01101 std::cout << line.chars() << std::endl; 01102 (void) ::fflush(stdout); 01103 } 01104 01105 // VFileLogAppender ---------------------------------------------------------- 01106 01107 VFileLogAppender::VFileLogAppender(const VString& name, bool formatOutput, const VString& formatSpec, const VString& timeFormat, const VString& filePath) 01108 : VLogAppender(name, formatOutput, formatSpec, timeFormat) 01109 , mFileStream(VFSNode(filePath)) 01110 , mOutputStream(mFileStream) 01111 { 01112 this->_openFile(); 01113 } 01114 01115 VFileLogAppender::VFileLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults) 01116 : VLogAppender(settings, defaults) 01117 , mFileStream() 01118 , mOutputStream(mFileStream) 01119 { 01120 // If no path is specified, we'll use "<appendername>.log" in the base log directory, simply 01121 // using the appender's "name" property as our base file name. 01122 VString defaultPath; 01123 VLogger::getBaseLogDirectory().getChildPath(settings.getString("name") + ".log", defaultPath); 01124 mFileStream.setNode(VFSNode(_getStringInitSetting("path", settings, defaults, defaultPath))); 01125 01126 this->_openFile(); 01127 } 01128 01129 void VFileLogAppender::_openFile() { 01130 VFSNode newLogFileDir; 01131 mFileStream.getNode().getParentNode(newLogFileDir); 01132 newLogFileDir.mkdirs(); 01133 01134 mFileStream.openReadWrite(); 01135 mFileStream.seek(CONST_S64(0), SEEK_END); 01136 01137 mOutputStream.writeLineEnd(); 01138 } 01139 01140 void VFileLogAppender::addInfo(VBentoNode& infoNode) const { 01141 VLogAppender::addInfo(infoNode); 01142 infoNode.addString("type", "VFileLogAppender"); 01143 infoNode.addString("file", mFileStream.getNode().getPath()); 01144 } 01145 01146 void VFileLogAppender::_emitRawLine(const VString& line) { 01147 mOutputStream.writeLine(line); 01148 mOutputStream.flush(); 01149 } 01150 01151 // VRollingFileLogAppender --------------------------------------------------- 01152 01153 VRollingFileLogAppender::VRollingFileLogAppender(const VString& name, bool formatOutput, const VString& formatSpec, const VString& timeFormat, const VString& /*dirPath*/, const VString& /*fileNamePrefix*/, int /*maxNumLines*/) 01154 : VLogAppender(name, formatOutput, formatSpec, timeFormat) 01155 { 01156 } 01157 01158 VRollingFileLogAppender::VRollingFileLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults) 01159 : VLogAppender(settings, defaults) 01160 { 01161 /* todo: use these settings properties to construct, something like: 01162 mDirPath = VLogAppender::_getStringInitSetting("dir", settings, defaults, VLogger::getBaseLogDirectory().getPath()); 01163 mPrefix = VLogAppender::_getStringInitSetting("prefix", settings, defaults, settings.getString("name")); 01164 mMaxLines = VLogAppender::_getIntInitSetting("max-lines", settings, defaults, 10000); 01165 */ 01166 } 01167 01168 void VRollingFileLogAppender::addInfo(VBentoNode& infoNode) const { 01169 VLogAppender::addInfo(infoNode); 01170 infoNode.addString("type", "VRollingFileLogAppender"); 01171 // TODO: current file etc. 01172 } 01173 01174 void VRollingFileLogAppender::_emitRawLine(const VString& /*line*/) { 01175 // TODO! 01176 } 01177 01178 // VSilentLogAppender ---------------------------------------------------------- 01179 01180 void VSilentLogAppender::addInfo(VBentoNode& infoNode) const { 01181 VLogAppender::addInfo(infoNode); 01182 infoNode.addString("type", "VSilentLogAppender"); 01183 } 01184 01185 // VStringLogAppender ---------------------------------------------------------- 01186 01187 VStringLogAppender::VStringLogAppender(const VString& name, bool formatOutput, const VString& formatSpec, const VString& timeFormat) 01188 : VLogAppender(name, formatOutput, formatSpec, timeFormat) 01189 , mLines() 01190 { 01191 } 01192 01193 VStringLogAppender::VStringLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults) 01194 : VLogAppender(settings, defaults) 01195 , mLines() 01196 { 01197 } 01198 01199 void VStringLogAppender::addInfo(VBentoNode& infoNode) const { 01200 VLogAppender::addInfo(infoNode); 01201 infoNode.addString("type", "VStringLogAppender"); 01202 } 01203 01204 void VStringLogAppender::_emitRawLine(const VString& line) { 01205 mLines += line; 01206 mLines += VString::NATIVE_LINE_ENDING(); 01207 } 01208 01209 // VStringVectorLogAppender --------------------------------------------------- 01210 01211 VStringVectorLogAppender::VStringVectorLogAppender(const VString& name, bool formatOutput, const VString& formatSpec, const VString& timeFormat, /*@Nullable*/VStringVector* storage) 01212 : VLogAppender(name, formatOutput, formatSpec, timeFormat) 01213 , mStorage(storage) 01214 , mLines() 01215 { 01216 if (mStorage == NULL) { // If not supplied, use our internal lines vector storage. 01217 mStorage = &mLines; 01218 } 01219 } 01220 01221 VStringVectorLogAppender::VStringVectorLogAppender(const VSettingsNode& settings, const VSettingsNode& defaults) 01222 : VLogAppender(settings, defaults) 01223 , mStorage(NULL) 01224 , mLines() 01225 { 01226 mStorage = &mLines; 01227 } 01228 01229 VStringVectorLogAppender::~VStringVectorLogAppender() { 01230 mStorage = NULL; // We don't own the pointer. 01231 } 01232 01233 void VStringVectorLogAppender::addInfo(VBentoNode& infoNode) const { 01234 VLogAppender::addInfo(infoNode); 01235 infoNode.addString("type", "VStringVectorLogAppender"); 01236 infoNode.addBool("external-storage", mStorage != NULL); 01237 } 01238 01239 void VStringVectorLogAppender::_emitRawLine(const VString& line) { 01240 mStorage->push_back(line); 01241 } 01242 01243 // VStringLogger ------------------------------------------------------------- 01244 01245 VStringLogger::VStringLogger(const VString& name, int level, bool formatOutput, const VString& formatSpec, const VString& timeFormat) 01246 : VNamedLogger(name, level, VStringVector()) 01247 , mAppender(name + ".appender", formatOutput, formatSpec, timeFormat) 01248 { 01249 } 01250 01251 void VStringLogger::addInfo(VBentoNode& infoNode) const { 01252 VNamedLogger::addInfo(infoNode); 01253 infoNode.addString("type", "VStringLogger"); 01254 } 01255 01256 void VStringLogger::_emitToAppenders(int level, const char* file, int line, bool emitMessage, const VString& message, const VString& specifiedLoggerName, bool emitRawLine, const VString& rawLine) { 01257 mAppender.emit(level, file, line, emitMessage, message, specifiedLoggerName, this->getName(), emitRawLine, rawLine); 01258 } 01259 01260 // VStringVectorLogger ------------------------------------------------------------- 01261 01262 VStringVectorLogger::VStringVectorLogger(const VString& name, int level, /*@Nullable*/VStringVector* storage, bool formatOutput, const VString& formatSpec, const VString& timeFormat) 01263 : VNamedLogger(name, level, VStringVector()) 01264 , mAppender(name + ".appender", formatOutput, formatSpec, timeFormat, storage) 01265 { 01266 } 01267 01268 void VStringVectorLogger::addInfo(VBentoNode& infoNode) const { 01269 VNamedLogger::addInfo(infoNode); 01270 infoNode.addString("type", "VStringVectorLogger"); 01271 } 01272 01273 void VStringVectorLogger::_emitToAppenders(int level, const char* file, int line, bool emitMessage, const VString& message, const VString& specifiedLoggerName, bool emitRawLine, const VString& rawLine) { 01274 mAppender.emit(level, file, line, emitMessage, message, specifiedLoggerName, this->getName(), emitRawLine, rawLine); 01275 } 01276 01277 // VLoggerRepetitionFilter --------------------------------------------------- 01278 01279 VLoggerRepetitionFilter::VLoggerRepetitionFilter() 01280 : mEnabled(true) 01281 , mHasSavedMessage(false) 01282 , mNumSuppressedOccurrences(0) 01283 , mTimeOfLastOccurrence(VInstant::NEVER_OCCURRED()) 01284 , mLevel(0) 01285 , mFile(NULL) 01286 , mLine(0) 01287 , mMessage() 01288 , mSpecifiedLoggerName() 01289 , mActualLoggerName() 01290 { 01291 } 01292 01293 void VLoggerRepetitionFilter::reset() { 01294 mHasSavedMessage = false; 01295 mNumSuppressedOccurrences = 0; 01296 mTimeOfLastOccurrence = VInstant::NEVER_OCCURRED(); 01297 mLevel = 0; 01298 mFile = NULL; 01299 mLine = 0; 01300 mMessage = VString::EMPTY(); 01301 mSpecifiedLoggerName = VString::EMPTY(); 01302 mActualLoggerName = VString::EMPTY(); 01303 } 01304 01305 bool VLoggerRepetitionFilter::checkMessage(VNamedLogger& logger, int level, const char* file, int line, const VString& message, const VString& specifiedLoggerName, const VString& actualLoggerName) { 01306 if (!mEnabled) { 01307 return true; 01308 } 01309 01310 bool isRepeatMessage = mHasSavedMessage && 01311 (level == mLevel) && 01312 (file == mFile) && 01313 (line == mLine) && 01314 (message == mMessage); 01315 01316 if (isRepeatMessage) { 01317 // This is a repeat message. Update our info and return false to indicate that 01318 // this message should not yet be emitted. 01319 ++mNumSuppressedOccurrences; 01320 mTimeOfLastOccurrence.setNow(); 01321 } else { 01322 // This is not a repeat message. Emit any pending saved recurring message, 01323 // then reset to store this message, and return true to indicate that 01324 // this message should be emitted (the first occurrence of a message is 01325 // always emitted). 01326 01327 // Emit pending saved message. 01328 if (mHasSavedMessage && (mNumSuppressedOccurrences > 0)) { 01329 this->_emitSuppressedMessages(logger); 01330 } 01331 01332 // Reset and store this new message. 01333 mHasSavedMessage = true; 01334 mNumSuppressedOccurrences = 0; 01335 mTimeOfLastOccurrence.setNow(); 01336 mLevel = level; 01337 mFile = file; 01338 mLine = line; 01339 mMessage = message; 01340 mSpecifiedLoggerName = specifiedLoggerName; 01341 mActualLoggerName = actualLoggerName; 01342 } 01343 01344 return ! isRepeatMessage; 01345 } 01346 01347 void VLoggerRepetitionFilter::checkTimeout(VNamedLogger& logger) { 01348 if (!mEnabled) { 01349 return; 01350 } 01351 01352 if (mHasSavedMessage && (mNumSuppressedOccurrences > 0)) { 01353 VInstant now; 01354 if ((now - mTimeOfLastOccurrence) > VDuration::MINUTE()) { 01355 this->_emitSuppressedMessages(logger); 01356 } 01357 } 01358 } 01359 01360 void VLoggerRepetitionFilter::_emitSuppressedMessages(VNamedLogger& logger) { 01361 // If there was only 1 suppressed message, no need to mark it. 01362 if (mNumSuppressedOccurrences > 1) { 01363 VString tweakedMessage(VSTRING_ARGS("[%dx] %s", mNumSuppressedOccurrences, mMessage.chars())); 01364 logger._emitToAppenders(mLevel, mFile, mLine, true, tweakedMessage, mSpecifiedLoggerName, false, VString::EMPTY()); 01365 } else { 01366 logger._emitToAppenders(mLevel, mFile, mLine, true, mMessage, mSpecifiedLoggerName, false, VString::EMPTY()); 01367 } 01368 01369 mHasSavedMessage = false; 01370 mNumSuppressedOccurrences = 0; 01371 } 01372 01373 // VLoggerPrintStackConfig ---------------------------------------------------- 01374 01375 VLoggerPrintStackConfig::VLoggerPrintStackConfig() 01376 : mLevel(VLoggerLevel::OFF) 01377 , mMaxCount(-1) 01378 , mDuration(VDuration::POSITIVE_INFINITY()) 01379 , mCountdown(-1) 01380 , mExpiration(VInstant::INFINITE_FUTURE()) 01381 { 01382 } 01383 01384 void VLoggerPrintStackConfig::configure(int level, int maxNumOccurrences, const VDuration& timeLimit) { 01385 mLevel = level; 01386 mMaxCount = (maxNumOccurrences > 0) ? maxNumOccurrences : -1; // 0 really means off (-1) 01387 mDuration = timeLimit; 01388 mCountdown = mMaxCount; 01389 01390 if (timeLimit.isSpecific()) { 01391 mExpiration = VInstant(/*now*/) + timeLimit; 01392 } else { 01393 mExpiration = VInstant::INFINITE_FUTURE(); 01394 } 01395 } 01396 01397 bool VLoggerPrintStackConfig::shouldPrintStack(int level, VNamedLogger& logger) { 01398 if (level > mLevel) { 01399 return false; 01400 } 01401 01402 // mCountdown of -1 means no count limit, just a timeout limit, then turn off 01403 // mExpiration of infinite means no time limit, just a count limit, then turn off 01404 // if both are set, it means count down to zero, then suppress until timeout is reached 01405 // if neither is set, we always print the stack crawl for this level 01406 01407 bool resetCountdown = false; 01408 bool turnOff = false; 01409 bool printStack = false; 01410 VInstant now; 01411 01412 if (mCountdown == -1) { 01413 if (mDuration == VDuration::POSITIVE_INFINITY()) { 01414 // no count limit, no time limit: always print 01415 printStack = true; 01416 } else { 01417 // no count limit, but time limit defined: print if not expired, turn off if expired 01418 printStack = (now < mExpiration); 01419 turnOff = !printStack; 01420 } 01421 } else if (mCountdown == 0) { 01422 if (mDuration == VDuration::POSITIVE_INFINITY()) { 01423 // count limit reached, no time limit: turn off printing completely 01424 turnOff = true; 01425 } else { 01426 // count limit reached, time limit defined: check time limit and reset if expired 01427 resetCountdown = (now >= mExpiration); 01428 printStack = resetCountdown; 01429 } 01430 } else { // mCountdown > 0 01431 // count limit exists but not yet reached, so print 01432 printStack = true; 01433 01434 if (mDuration == VDuration::POSITIVE_INFINITY()) { 01435 // no time limit: nothing to do 01436 } else { 01437 // time limit defined: reset if expired 01438 resetCountdown = (now >= mExpiration); 01439 } 01440 } 01441 01442 if (resetCountdown) { 01443 mExpiration = (now + mDuration); // easier than looping increment from previous to future; good enough 01444 01445 if (mMaxCount > 0) { 01446 mCountdown = mMaxCount; 01447 } 01448 } 01449 01450 if (printStack) { 01451 if (mCountdown > 0) { 01452 --mCountdown; 01453 } 01454 } 01455 01456 if (turnOff) { 01457 mLevel = VLoggerLevel::OFF; 01458 mMaxCount = -1; 01459 mDuration = VDuration::POSITIVE_INFINITY(); 01460 mCountdown = -1; 01461 mExpiration = VInstant::INFINITE_FUTURE(); 01462 logger._emitToAppenders(VLoggerLevel::INFO, NULL, 0, true, "Print stack crawl for this logger is auto-disabling now.", VString::EMPTY(), false, VString::EMPTY()); 01463 } 01464 01465 return printStack; 01466 } 01467