PageRenderTime 74ms CodeModel.GetById 0ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llcommon/llerror.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1404 lines | 1061 code | 222 blank | 121 comment | 111 complexity | 146425eb39a72cc7f4baa2611b488086 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llerror.cpp
  3. * @date December 2006
  4. * @brief error message system
  5. *
  6. * $LicenseInfo:firstyear=2006&license=viewerlgpl$
  7. * Second Life Viewer Source Code
  8. * Copyright (C) 2010, Linden Research, Inc.
  9. *
  10. * This library is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU Lesser General Public
  12. * License as published by the Free Software Foundation;
  13. * version 2.1 of the License only.
  14. *
  15. * This library is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  18. * Lesser General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Lesser General Public
  21. * License along with this library; if not, write to the Free Software
  22. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  23. *
  24. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  25. * $/LicenseInfo$
  26. */
  27. #include "linden_common.h"
  28. #include "llerror.h"
  29. #include "llerrorcontrol.h"
  30. #include <cctype>
  31. #ifdef __GNUC__
  32. # include <cxxabi.h>
  33. #endif // __GNUC__
  34. #include <sstream>
  35. #if !LL_WINDOWS
  36. # include <syslog.h>
  37. # include <unistd.h>
  38. #endif // !LL_WINDOWS
  39. #include <vector>
  40. #include "llapp.h"
  41. #include "llapr.h"
  42. #include "llfile.h"
  43. #include "lllivefile.h"
  44. #include "llsd.h"
  45. #include "llsdserialize.h"
  46. #include "llstl.h"
  47. #include "lltimer.h"
  48. namespace {
  49. #if !LL_WINDOWS
  50. class RecordToSyslog : public LLError::Recorder
  51. {
  52. public:
  53. RecordToSyslog(const std::string& identity)
  54. : mIdentity(identity)
  55. {
  56. openlog(mIdentity.c_str(), LOG_CONS|LOG_PID, LOG_LOCAL0);
  57. // we need to set the string from a local copy of the string
  58. // since apparanetly openlog expects the const char* to remain
  59. // valid even after it returns (presumably until closelog)
  60. }
  61. ~RecordToSyslog()
  62. {
  63. closelog();
  64. }
  65. virtual void recordMessage(LLError::ELevel level,
  66. const std::string& message)
  67. {
  68. int syslogPriority = LOG_CRIT;
  69. switch (level) {
  70. case LLError::LEVEL_DEBUG: syslogPriority = LOG_DEBUG; break;
  71. case LLError::LEVEL_INFO: syslogPriority = LOG_INFO; break;
  72. case LLError::LEVEL_WARN: syslogPriority = LOG_WARNING; break;
  73. case LLError::LEVEL_ERROR: syslogPriority = LOG_CRIT; break;
  74. default: syslogPriority = LOG_CRIT;
  75. }
  76. syslog(syslogPriority, "%s", message.c_str());
  77. }
  78. private:
  79. std::string mIdentity;
  80. };
  81. #endif
  82. class RecordToFile : public LLError::Recorder
  83. {
  84. public:
  85. RecordToFile(const std::string& filename)
  86. {
  87. mFile.open(filename, llofstream::out | llofstream::app);
  88. if (!mFile)
  89. {
  90. llinfos << "Error setting log file to " << filename << llendl;
  91. }
  92. }
  93. ~RecordToFile()
  94. {
  95. mFile.close();
  96. }
  97. bool okay() { return mFile; }
  98. virtual bool wantsTime() { return true; }
  99. virtual void recordMessage(LLError::ELevel level,
  100. const std::string& message)
  101. {
  102. mFile << message << std::endl;
  103. // mFile.flush();
  104. // *FIX: should we do this?
  105. }
  106. private:
  107. llofstream mFile;
  108. };
  109. class RecordToStderr : public LLError::Recorder
  110. {
  111. public:
  112. RecordToStderr(bool timestamp) : mTimestamp(timestamp), mUseANSI(ANSI_PROBE) { }
  113. virtual bool wantsTime() { return mTimestamp; }
  114. virtual void recordMessage(LLError::ELevel level,
  115. const std::string& message)
  116. {
  117. if (ANSI_PROBE == mUseANSI)
  118. mUseANSI = (checkANSI() ? ANSI_YES : ANSI_NO);
  119. if (ANSI_YES == mUseANSI)
  120. {
  121. // Default all message levels to bold so we can distinguish our own messages from those dumped by subprocesses and libraries.
  122. colorANSI("1"); // bold
  123. switch (level) {
  124. case LLError::LEVEL_ERROR:
  125. colorANSI("31"); // red
  126. break;
  127. case LLError::LEVEL_WARN:
  128. colorANSI("34"); // blue
  129. break;
  130. case LLError::LEVEL_DEBUG:
  131. colorANSI("35"); // magenta
  132. break;
  133. default:
  134. break;
  135. }
  136. }
  137. fprintf(stderr, "%s\n", message.c_str());
  138. if (ANSI_YES == mUseANSI) colorANSI("0"); // reset
  139. }
  140. private:
  141. bool mTimestamp;
  142. enum ANSIState {ANSI_PROBE, ANSI_YES, ANSI_NO};
  143. ANSIState mUseANSI;
  144. void colorANSI(const std::string color)
  145. {
  146. // ANSI color code escape sequence
  147. fprintf(stderr, "\033[%sm", color.c_str() );
  148. };
  149. bool checkANSI(void)
  150. {
  151. #if LL_LINUX || LL_DARWIN
  152. // Check whether it's okay to use ANSI; if stderr is
  153. // a tty then we assume yes. Can be turned off with
  154. // the LL_NO_ANSI_COLOR env var.
  155. return (0 != isatty(2)) &&
  156. (NULL == getenv("LL_NO_ANSI_COLOR"));
  157. #endif // LL_LINUX
  158. return false;
  159. };
  160. };
  161. class RecordToFixedBuffer : public LLError::Recorder
  162. {
  163. public:
  164. RecordToFixedBuffer(LLLineBuffer* buffer) : mBuffer(buffer) { }
  165. virtual void recordMessage(LLError::ELevel level,
  166. const std::string& message)
  167. {
  168. mBuffer->addLine(message);
  169. }
  170. private:
  171. LLLineBuffer* mBuffer;
  172. };
  173. #if LL_WINDOWS
  174. class RecordToWinDebug: public LLError::Recorder
  175. {
  176. public:
  177. virtual void recordMessage(LLError::ELevel level,
  178. const std::string& message)
  179. {
  180. llutf16string utf16str =
  181. wstring_to_utf16str(utf8str_to_wstring(message));
  182. utf16str += '\n';
  183. OutputDebugString(utf16str.c_str());
  184. }
  185. };
  186. #endif
  187. }
  188. namespace
  189. {
  190. std::string className(const std::type_info& type)
  191. {
  192. #ifdef __GNUC__
  193. // GCC: type_info::name() returns a mangled class name, must demangle
  194. static size_t abi_name_len = 100;
  195. static char* abi_name_buf = (char*)malloc(abi_name_len);
  196. // warning: above is voodoo inferred from the GCC manual,
  197. // do NOT change
  198. int status;
  199. // We don't use status, and shouldn't have to pass apointer to it
  200. // but gcc 3.3 libstc++'s implementation of demangling is broken
  201. // and fails without.
  202. char* name = abi::__cxa_demangle(type.name(),
  203. abi_name_buf, &abi_name_len, &status);
  204. // this call can realloc the abi_name_buf pointer (!)
  205. return name ? name : type.name();
  206. #elif LL_WINDOWS
  207. // DevStudio: type_info::name() includes the text "class " at the start
  208. static const std::string class_prefix = "class ";
  209. std::string name = type.name();
  210. std::string::size_type p = name.find(class_prefix);
  211. if (p == std::string::npos)
  212. {
  213. return name;
  214. }
  215. return name.substr(p + class_prefix.size());
  216. #else
  217. return type.name();
  218. #endif
  219. }
  220. std::string functionName(const std::string& preprocessor_name)
  221. {
  222. #if LL_WINDOWS
  223. // DevStudio: the __FUNCTION__ macro string includes
  224. // the type and/or namespace prefixes
  225. std::string::size_type p = preprocessor_name.rfind(':');
  226. if (p == std::string::npos)
  227. {
  228. return preprocessor_name;
  229. }
  230. return preprocessor_name.substr(p + 1);
  231. #else
  232. return preprocessor_name;
  233. #endif
  234. }
  235. class LogControlFile : public LLLiveFile
  236. {
  237. LOG_CLASS(LogControlFile);
  238. public:
  239. static LogControlFile& fromDirectory(const std::string& dir);
  240. virtual bool loadFile();
  241. private:
  242. LogControlFile(const std::string &filename)
  243. : LLLiveFile(filename)
  244. { }
  245. };
  246. LogControlFile& LogControlFile::fromDirectory(const std::string& dir)
  247. {
  248. std::string dirBase = dir + "/";
  249. // NB: We have no abstraction in llcommon for the "proper"
  250. // delimiter but it turns out that "/" works on all three platforms
  251. std::string file = dirBase + "logcontrol-dev.xml";
  252. llstat stat_info;
  253. if (LLFile::stat(file, &stat_info)) {
  254. // NB: stat returns non-zero if it can't read the file, for example
  255. // if it doesn't exist. LLFile has no better abstraction for
  256. // testing for file existence.
  257. file = dirBase + "logcontrol.xml";
  258. }
  259. return * new LogControlFile(file);
  260. // NB: This instance is never freed
  261. }
  262. bool LogControlFile::loadFile()
  263. {
  264. LLSD configuration;
  265. {
  266. llifstream file(filename());
  267. if (file.is_open())
  268. {
  269. LLSDSerialize::fromXML(configuration, file);
  270. }
  271. if (configuration.isUndefined())
  272. {
  273. llwarns << filename() << " missing, ill-formed,"
  274. " or simply undefined; not changing configuration"
  275. << llendl;
  276. return false;
  277. }
  278. }
  279. LLError::configure(configuration);
  280. llinfos << "logging reconfigured from " << filename() << llendl;
  281. return true;
  282. }
  283. typedef std::map<std::string, LLError::ELevel> LevelMap;
  284. typedef std::vector<LLError::Recorder*> Recorders;
  285. typedef std::vector<LLError::CallSite*> CallSiteVector;
  286. class Globals
  287. {
  288. public:
  289. std::ostringstream messageStream;
  290. bool messageStreamInUse;
  291. void addCallSite(LLError::CallSite&);
  292. void invalidateCallSites();
  293. static Globals& get();
  294. // return the one instance of the globals
  295. private:
  296. CallSiteVector callSites;
  297. Globals()
  298. : messageStreamInUse(false)
  299. { }
  300. };
  301. void Globals::addCallSite(LLError::CallSite& site)
  302. {
  303. callSites.push_back(&site);
  304. }
  305. void Globals::invalidateCallSites()
  306. {
  307. for (CallSiteVector::const_iterator i = callSites.begin();
  308. i != callSites.end();
  309. ++i)
  310. {
  311. (*i)->invalidate();
  312. }
  313. callSites.clear();
  314. }
  315. Globals& Globals::get()
  316. {
  317. /* This pattern, of returning a reference to a static function
  318. variable, is to ensure that this global is constructed before
  319. it is used, no matter what the global initialization sequence
  320. is.
  321. See C++ FAQ Lite, sections 10.12 through 10.14
  322. */
  323. static Globals* globals = new Globals;
  324. return *globals;
  325. }
  326. }
  327. namespace LLError
  328. {
  329. class Settings
  330. {
  331. public:
  332. bool printLocation;
  333. LLError::ELevel defaultLevel;
  334. LevelMap functionLevelMap;
  335. LevelMap classLevelMap;
  336. LevelMap fileLevelMap;
  337. LevelMap tagLevelMap;
  338. std::map<std::string, unsigned int> uniqueLogMessages;
  339. LLError::FatalFunction crashFunction;
  340. LLError::TimeFunction timeFunction;
  341. Recorders recorders;
  342. Recorder* fileRecorder;
  343. Recorder* fixedBufferRecorder;
  344. std::string fileRecorderFileName;
  345. int shouldLogCallCounter;
  346. static Settings& get();
  347. static void reset();
  348. static Settings* saveAndReset();
  349. static void restore(Settings*);
  350. private:
  351. Settings()
  352. : printLocation(false),
  353. defaultLevel(LLError::LEVEL_DEBUG),
  354. crashFunction(),
  355. timeFunction(NULL),
  356. fileRecorder(NULL),
  357. fixedBufferRecorder(NULL),
  358. shouldLogCallCounter(0)
  359. { }
  360. ~Settings()
  361. {
  362. for_each(recorders.begin(), recorders.end(),
  363. DeletePointer());
  364. }
  365. static Settings*& getPtr();
  366. };
  367. Settings& Settings::get()
  368. {
  369. Settings* p = getPtr();
  370. if (!p)
  371. {
  372. reset();
  373. p = getPtr();
  374. }
  375. return *p;
  376. }
  377. void Settings::reset()
  378. {
  379. Globals::get().invalidateCallSites();
  380. Settings*& p = getPtr();
  381. delete p;
  382. p = new Settings();
  383. }
  384. Settings* Settings::saveAndReset()
  385. {
  386. Globals::get().invalidateCallSites();
  387. Settings*& p = getPtr();
  388. Settings* originalSettings = p;
  389. p = new Settings();
  390. return originalSettings;
  391. }
  392. void Settings::restore(Settings* originalSettings)
  393. {
  394. Globals::get().invalidateCallSites();
  395. Settings*& p = getPtr();
  396. delete p;
  397. p = originalSettings;
  398. }
  399. Settings*& Settings::getPtr()
  400. {
  401. static Settings* currentSettings = NULL;
  402. return currentSettings;
  403. }
  404. }
  405. namespace LLError
  406. {
  407. CallSite::CallSite(ELevel level,
  408. const char* file,
  409. int line,
  410. const std::type_info& class_info,
  411. const char* function,
  412. const char* broadTag,
  413. const char* narrowTag,
  414. bool printOnce)
  415. : mLevel(level), mFile(file), mLine(line),
  416. mClassInfo(class_info), mFunction(function),
  417. mCached(false), mShouldLog(false),
  418. mBroadTag(broadTag), mNarrowTag(narrowTag), mPrintOnce(printOnce)
  419. { }
  420. void CallSite::invalidate()
  421. { mCached = false; }
  422. }
  423. namespace
  424. {
  425. bool shouldLogToStderr()
  426. {
  427. #if LL_DARWIN
  428. // On Mac OS X, stderr from apps launched from the Finder goes to the
  429. // console log. It's generally considered bad form to spam too much
  430. // there.
  431. // If stdin is a tty, assume the user launched from the command line and
  432. // therefore wants to see stderr. Otherwise, assume we've been launched
  433. // from the finder and shouldn't spam stderr.
  434. return isatty(0);
  435. #else
  436. return true;
  437. #endif
  438. }
  439. bool stderrLogWantsTime()
  440. {
  441. #if LL_WINDOWS
  442. return false;
  443. #else
  444. return true;
  445. #endif
  446. }
  447. void commonInit(const std::string& dir)
  448. {
  449. LLError::Settings::reset();
  450. LLError::setDefaultLevel(LLError::LEVEL_INFO);
  451. LLError::setFatalFunction(LLError::crashAndLoop);
  452. LLError::setTimeFunction(LLError::utcTime);
  453. if (shouldLogToStderr())
  454. {
  455. LLError::addRecorder(new RecordToStderr(stderrLogWantsTime()));
  456. }
  457. #if LL_WINDOWS
  458. LLError::addRecorder(new RecordToWinDebug);
  459. #endif
  460. LogControlFile& e = LogControlFile::fromDirectory(dir);
  461. // NOTE: We want to explicitly load the file before we add it to the event timer
  462. // that checks for changes to the file. Else, we're not actually loading the file yet,
  463. // and most of the initialization happens without any attention being paid to the
  464. // log control file. Not to mention that when it finally gets checked later,
  465. // all log statements that have been evaluated already become dirty and need to be
  466. // evaluated for printing again. So, make sure to call checkAndReload()
  467. // before addToEventTimer().
  468. e.checkAndReload();
  469. e.addToEventTimer();
  470. }
  471. }
  472. namespace LLError
  473. {
  474. void initForServer(const std::string& identity)
  475. {
  476. std::string dir = "/opt/linden/etc";
  477. if (LLApp::instance())
  478. {
  479. dir = LLApp::instance()->getOption("configdir").asString();
  480. }
  481. commonInit(dir);
  482. #if !LL_WINDOWS
  483. addRecorder(new RecordToSyslog(identity));
  484. #endif
  485. }
  486. void initForApplication(const std::string& dir)
  487. {
  488. commonInit(dir);
  489. }
  490. void setPrintLocation(bool print)
  491. {
  492. Settings& s = Settings::get();
  493. s.printLocation = print;
  494. }
  495. void setFatalFunction(const FatalFunction& f)
  496. {
  497. Settings& s = Settings::get();
  498. s.crashFunction = f;
  499. }
  500. FatalFunction getFatalFunction()
  501. {
  502. Settings& s = Settings::get();
  503. return s.crashFunction;
  504. }
  505. void setTimeFunction(TimeFunction f)
  506. {
  507. Settings& s = Settings::get();
  508. s.timeFunction = f;
  509. }
  510. void setDefaultLevel(ELevel level)
  511. {
  512. Globals& g = Globals::get();
  513. Settings& s = Settings::get();
  514. g.invalidateCallSites();
  515. s.defaultLevel = level;
  516. }
  517. ELevel getDefaultLevel()
  518. {
  519. Settings& s = Settings::get();
  520. return s.defaultLevel;
  521. }
  522. void setFunctionLevel(const std::string& function_name, ELevel level)
  523. {
  524. Globals& g = Globals::get();
  525. Settings& s = Settings::get();
  526. g.invalidateCallSites();
  527. s.functionLevelMap[function_name] = level;
  528. }
  529. void setClassLevel(const std::string& class_name, ELevel level)
  530. {
  531. Globals& g = Globals::get();
  532. Settings& s = Settings::get();
  533. g.invalidateCallSites();
  534. s.classLevelMap[class_name] = level;
  535. }
  536. void setFileLevel(const std::string& file_name, ELevel level)
  537. {
  538. Globals& g = Globals::get();
  539. Settings& s = Settings::get();
  540. g.invalidateCallSites();
  541. s.fileLevelMap[file_name] = level;
  542. }
  543. void setTagLevel(const std::string& tag_name, ELevel level)
  544. {
  545. Globals& g = Globals::get();
  546. Settings& s = Settings::get();
  547. g.invalidateCallSites();
  548. s.tagLevelMap[tag_name] = level;
  549. }
  550. }
  551. namespace {
  552. LLError::ELevel decodeLevel(std::string name)
  553. {
  554. static LevelMap level_names;
  555. if (level_names.empty())
  556. {
  557. level_names["ALL"] = LLError::LEVEL_ALL;
  558. level_names["DEBUG"] = LLError::LEVEL_DEBUG;
  559. level_names["INFO"] = LLError::LEVEL_INFO;
  560. level_names["WARN"] = LLError::LEVEL_WARN;
  561. level_names["ERROR"] = LLError::LEVEL_ERROR;
  562. level_names["NONE"] = LLError::LEVEL_NONE;
  563. }
  564. std::transform(name.begin(), name.end(), name.begin(), toupper);
  565. LevelMap::const_iterator i = level_names.find(name);
  566. if (i == level_names.end())
  567. {
  568. llwarns << "unrecognized logging level: '" << name << "'" << llendl;
  569. return LLError::LEVEL_INFO;
  570. }
  571. return i->second;
  572. }
  573. void setLevels(LevelMap& map, const LLSD& list, LLError::ELevel level)
  574. {
  575. LLSD::array_const_iterator i, end;
  576. for (i = list.beginArray(), end = list.endArray(); i != end; ++i)
  577. {
  578. map[*i] = level;
  579. }
  580. }
  581. }
  582. namespace LLError
  583. {
  584. void configure(const LLSD& config)
  585. {
  586. Globals& g = Globals::get();
  587. Settings& s = Settings::get();
  588. g.invalidateCallSites();
  589. s.functionLevelMap.clear();
  590. s.classLevelMap.clear();
  591. s.fileLevelMap.clear();
  592. s.tagLevelMap.clear();
  593. s.uniqueLogMessages.clear();
  594. setPrintLocation(config["print-location"]);
  595. setDefaultLevel(decodeLevel(config["default-level"]));
  596. LLSD sets = config["settings"];
  597. LLSD::array_const_iterator a, end;
  598. for (a = sets.beginArray(), end = sets.endArray(); a != end; ++a)
  599. {
  600. const LLSD& entry = *a;
  601. ELevel level = decodeLevel(entry["level"]);
  602. setLevels(s.functionLevelMap, entry["functions"], level);
  603. setLevels(s.classLevelMap, entry["classes"], level);
  604. setLevels(s.fileLevelMap, entry["files"], level);
  605. setLevels(s.tagLevelMap, entry["tags"], level);
  606. }
  607. }
  608. }
  609. namespace LLError
  610. {
  611. Recorder::~Recorder()
  612. { }
  613. // virtual
  614. bool Recorder::wantsTime()
  615. { return false; }
  616. void addRecorder(Recorder* recorder)
  617. {
  618. if (recorder == NULL)
  619. {
  620. return;
  621. }
  622. Settings& s = Settings::get();
  623. s.recorders.push_back(recorder);
  624. }
  625. void removeRecorder(Recorder* recorder)
  626. {
  627. if (recorder == NULL)
  628. {
  629. return;
  630. }
  631. Settings& s = Settings::get();
  632. s.recorders.erase(
  633. std::remove(s.recorders.begin(), s.recorders.end(), recorder),
  634. s.recorders.end());
  635. }
  636. }
  637. namespace LLError
  638. {
  639. void logToFile(const std::string& file_name)
  640. {
  641. LLError::Settings& s = LLError::Settings::get();
  642. removeRecorder(s.fileRecorder);
  643. delete s.fileRecorder;
  644. s.fileRecorder = NULL;
  645. s.fileRecorderFileName.clear();
  646. if (file_name.empty())
  647. {
  648. return;
  649. }
  650. RecordToFile* f = new RecordToFile(file_name);
  651. if (!f->okay())
  652. {
  653. delete f;
  654. return;
  655. }
  656. s.fileRecorderFileName = file_name;
  657. s.fileRecorder = f;
  658. addRecorder(f);
  659. }
  660. void logToFixedBuffer(LLLineBuffer* fixedBuffer)
  661. {
  662. LLError::Settings& s = LLError::Settings::get();
  663. removeRecorder(s.fixedBufferRecorder);
  664. delete s.fixedBufferRecorder;
  665. s.fixedBufferRecorder = NULL;
  666. if (!fixedBuffer)
  667. {
  668. return;
  669. }
  670. s.fixedBufferRecorder = new RecordToFixedBuffer(fixedBuffer);
  671. addRecorder(s.fixedBufferRecorder);
  672. }
  673. std::string logFileName()
  674. {
  675. LLError::Settings& s = LLError::Settings::get();
  676. return s.fileRecorderFileName;
  677. }
  678. }
  679. namespace
  680. {
  681. void writeToRecorders(LLError::ELevel level, const std::string& message)
  682. {
  683. LLError::Settings& s = LLError::Settings::get();
  684. std::string messageWithTime;
  685. for (Recorders::const_iterator i = s.recorders.begin();
  686. i != s.recorders.end();
  687. ++i)
  688. {
  689. LLError::Recorder* r = *i;
  690. if (r->wantsTime() && s.timeFunction != NULL)
  691. {
  692. if (messageWithTime.empty())
  693. {
  694. messageWithTime = s.timeFunction() + " " + message;
  695. }
  696. r->recordMessage(level, messageWithTime);
  697. }
  698. else
  699. {
  700. r->recordMessage(level, message);
  701. }
  702. }
  703. }
  704. }
  705. /*
  706. Recorder formats:
  707. $type = "ERROR" | "WARNING" | "ALERT" | "INFO" | "DEBUG"
  708. $loc = "$file($line)"
  709. $msg = "$loc : " if FATAL or printing loc
  710. "" otherwise
  711. $msg += "$type: "
  712. $msg += contents of stringstream
  713. $time = "%Y-%m-%dT%H:%M:%SZ" if UTC
  714. or "%Y-%m-%dT%H:%M:%S %Z" if local
  715. syslog: "$msg"
  716. file: "$time $msg\n"
  717. stderr: "$time $msg\n" except on windows, "$msg\n"
  718. fixedbuf: "$msg"
  719. winddebug: "$msg\n"
  720. Note: if FATAL, an additional line gets logged first, with $msg set to
  721. "$loc : error"
  722. You get:
  723. llfoo.cpp(42) : error
  724. llfoo.cpp(42) : ERROR: something
  725. */
  726. namespace {
  727. bool checkLevelMap(const LevelMap& map, const std::string& key,
  728. LLError::ELevel& level)
  729. {
  730. LevelMap::const_iterator i = map.find(key);
  731. if (i == map.end())
  732. {
  733. return false;
  734. }
  735. level = i->second;
  736. return true;
  737. }
  738. class LogLock
  739. {
  740. public:
  741. LogLock();
  742. ~LogLock();
  743. bool ok() const { return mOK; }
  744. private:
  745. bool mLocked;
  746. bool mOK;
  747. };
  748. LogLock::LogLock()
  749. : mLocked(false), mOK(false)
  750. {
  751. if (!gLogMutexp)
  752. {
  753. mOK = true;
  754. return;
  755. }
  756. const int MAX_RETRIES = 5;
  757. for (int attempts = 0; attempts < MAX_RETRIES; ++attempts)
  758. {
  759. apr_status_t s = apr_thread_mutex_trylock(gLogMutexp);
  760. if (!APR_STATUS_IS_EBUSY(s))
  761. {
  762. mLocked = true;
  763. mOK = true;
  764. return;
  765. }
  766. ms_sleep(1);
  767. //apr_thread_yield();
  768. // Just yielding won't necessarily work, I had problems with
  769. // this on Linux - doug 12/02/04
  770. }
  771. // We're hosed, we can't get the mutex. Blah.
  772. std::cerr << "LogLock::LogLock: failed to get mutex for log"
  773. << std::endl;
  774. }
  775. LogLock::~LogLock()
  776. {
  777. if (mLocked)
  778. {
  779. apr_thread_mutex_unlock(gLogMutexp);
  780. }
  781. }
  782. }
  783. namespace LLError
  784. {
  785. bool Log::shouldLog(CallSite& site)
  786. {
  787. LogLock lock;
  788. if (!lock.ok())
  789. {
  790. return false;
  791. }
  792. Globals& g = Globals::get();
  793. Settings& s = Settings::get();
  794. s.shouldLogCallCounter += 1;
  795. std::string class_name = className(site.mClassInfo);
  796. std::string function_name = functionName(site.mFunction);
  797. #if LL_LINUX
  798. // gross, but typeid comparison seems to always fail here with gcc4.1
  799. if (0 != strcmp(site.mClassInfo.name(), typeid(NoClassInfo).name()))
  800. #else
  801. if (site.mClassInfo != typeid(NoClassInfo))
  802. #endif // LL_LINUX
  803. {
  804. function_name = class_name + "::" + function_name;
  805. }
  806. ELevel compareLevel = s.defaultLevel;
  807. // The most specific match found will be used as the log level,
  808. // since the computation short circuits.
  809. // So, in increasing order of importance:
  810. // Default < Broad Tag < File < Class < Function < Narrow Tag
  811. ((site.mNarrowTag != NULL) ? checkLevelMap(s.tagLevelMap, site.mNarrowTag, compareLevel) : false)
  812. || checkLevelMap(s.functionLevelMap, function_name, compareLevel)
  813. || checkLevelMap(s.classLevelMap, class_name, compareLevel)
  814. || checkLevelMap(s.fileLevelMap, abbreviateFile(site.mFile), compareLevel)
  815. || ((site.mBroadTag != NULL) ? checkLevelMap(s.tagLevelMap, site.mBroadTag, compareLevel) : false);
  816. site.mCached = true;
  817. g.addCallSite(site);
  818. return site.mShouldLog = site.mLevel >= compareLevel;
  819. }
  820. std::ostringstream* Log::out()
  821. {
  822. LogLock lock;
  823. if (lock.ok())
  824. {
  825. Globals& g = Globals::get();
  826. if (!g.messageStreamInUse)
  827. {
  828. g.messageStreamInUse = true;
  829. return &g.messageStream;
  830. }
  831. }
  832. return new std::ostringstream;
  833. }
  834. void Log::flush(std::ostringstream* out, char* message)
  835. {
  836. LogLock lock;
  837. if (!lock.ok())
  838. {
  839. return;
  840. }
  841. if(strlen(out->str().c_str()) < 128)
  842. {
  843. strcpy(message, out->str().c_str());
  844. }
  845. else
  846. {
  847. strncpy(message, out->str().c_str(), 127);
  848. message[127] = '\0' ;
  849. }
  850. Globals& g = Globals::get();
  851. if (out == &g.messageStream)
  852. {
  853. g.messageStream.clear();
  854. g.messageStream.str("");
  855. g.messageStreamInUse = false;
  856. }
  857. else
  858. {
  859. delete out;
  860. }
  861. return ;
  862. }
  863. void Log::flush(std::ostringstream* out, const CallSite& site)
  864. {
  865. LogLock lock;
  866. if (!lock.ok())
  867. {
  868. return;
  869. }
  870. Globals& g = Globals::get();
  871. Settings& s = Settings::get();
  872. std::string message = out->str();
  873. if (out == &g.messageStream)
  874. {
  875. g.messageStream.clear();
  876. g.messageStream.str("");
  877. g.messageStreamInUse = false;
  878. }
  879. else
  880. {
  881. delete out;
  882. }
  883. if (site.mLevel == LEVEL_ERROR)
  884. {
  885. std::ostringstream fatalMessage;
  886. fatalMessage << abbreviateFile(site.mFile)
  887. << "(" << site.mLine << ") : error";
  888. writeToRecorders(site.mLevel, fatalMessage.str());
  889. }
  890. std::ostringstream prefix;
  891. switch (site.mLevel)
  892. {
  893. case LEVEL_DEBUG: prefix << "DEBUG: "; break;
  894. case LEVEL_INFO: prefix << "INFO: "; break;
  895. case LEVEL_WARN: prefix << "WARNING: "; break;
  896. case LEVEL_ERROR: prefix << "ERROR: "; break;
  897. default: prefix << "XXX: "; break;
  898. };
  899. if (s.printLocation)
  900. {
  901. prefix << abbreviateFile(site.mFile)
  902. << "(" << site.mLine << ") : ";
  903. }
  904. #if LL_WINDOWS
  905. // DevStudio: __FUNCTION__ already includes the full class name
  906. #else
  907. #if LL_LINUX
  908. // gross, but typeid comparison seems to always fail here with gcc4.1
  909. if (0 != strcmp(site.mClassInfo.name(), typeid(NoClassInfo).name()))
  910. #else
  911. if (site.mClassInfo != typeid(NoClassInfo))
  912. #endif // LL_LINUX
  913. {
  914. prefix << className(site.mClassInfo) << "::";
  915. }
  916. #endif
  917. prefix << site.mFunction << ": ";
  918. if (site.mPrintOnce)
  919. {
  920. std::map<std::string, unsigned int>::iterator messageIter = s.uniqueLogMessages.find(message);
  921. if (messageIter != s.uniqueLogMessages.end())
  922. {
  923. messageIter->second++;
  924. unsigned int num_messages = messageIter->second;
  925. if (num_messages == 10 || num_messages == 50 || (num_messages % 100) == 0)
  926. {
  927. prefix << "ONCE (" << num_messages << "th time seen): ";
  928. }
  929. else
  930. {
  931. return;
  932. }
  933. }
  934. else
  935. {
  936. prefix << "ONCE: ";
  937. s.uniqueLogMessages[message] = 1;
  938. }
  939. }
  940. prefix << message;
  941. message = prefix.str();
  942. writeToRecorders(site.mLevel, message);
  943. if (site.mLevel == LEVEL_ERROR && s.crashFunction)
  944. {
  945. s.crashFunction(message);
  946. }
  947. }
  948. }
  949. namespace LLError
  950. {
  951. Settings* saveAndResetSettings()
  952. {
  953. return Settings::saveAndReset();
  954. }
  955. void restoreSettings(Settings* s)
  956. {
  957. return Settings::restore(s);
  958. }
  959. std::string removePrefix(std::string& s, const std::string& p)
  960. {
  961. std::string::size_type where = s.find(p);
  962. if (where == std::string::npos)
  963. {
  964. return s;
  965. }
  966. return std::string(s, where + p.size());
  967. }
  968. void replaceChar(std::string& s, char old, char replacement)
  969. {
  970. std::string::size_type i = 0;
  971. std::string::size_type len = s.length();
  972. for ( ; i < len; i++ )
  973. {
  974. if (s[i] == old)
  975. {
  976. s[i] = replacement;
  977. }
  978. }
  979. }
  980. std::string abbreviateFile(const std::string& filePath)
  981. {
  982. std::string f = filePath;
  983. #if LL_WINDOWS
  984. replaceChar(f, '\\', '/');
  985. #endif
  986. static std::string indra_prefix = "indra/";
  987. f = removePrefix(f, indra_prefix);
  988. #if LL_DARWIN
  989. static std::string newview_prefix = "newview/../";
  990. f = removePrefix(f, newview_prefix);
  991. #endif
  992. return f;
  993. }
  994. int shouldLogCallCount()
  995. {
  996. Settings& s = Settings::get();
  997. return s.shouldLogCallCounter;
  998. }
  999. #if LL_WINDOWS
  1000. // VC80 was optimizing the error away.
  1001. #pragma optimize("", off)
  1002. #endif
  1003. void crashAndLoop(const std::string& message)
  1004. {
  1005. // Now, we go kaboom!
  1006. int* make_me_crash = NULL;
  1007. *make_me_crash = 0;
  1008. while(true)
  1009. {
  1010. // Loop forever, in case the crash didn't work?
  1011. }
  1012. // this is an attempt to let Coverity and other semantic scanners know that this function won't be returning ever.
  1013. exit(EXIT_FAILURE);
  1014. }
  1015. #if LL_WINDOWS
  1016. #pragma optimize("", on)
  1017. #endif
  1018. std::string utcTime()
  1019. {
  1020. time_t now = time(NULL);
  1021. const size_t BUF_SIZE = 64;
  1022. char time_str[BUF_SIZE]; /* Flawfinder: ignore */
  1023. int chars = strftime(time_str, BUF_SIZE,
  1024. "%Y-%m-%dT%H:%M:%SZ",
  1025. gmtime(&now));
  1026. return chars ? time_str : "time error";
  1027. }
  1028. }
  1029. namespace LLError
  1030. {
  1031. char** LLCallStacks::sBuffer = NULL ;
  1032. S32 LLCallStacks::sIndex = 0 ;
  1033. #define SINGLE_THREADED 1
  1034. class CallStacksLogLock
  1035. {
  1036. public:
  1037. CallStacksLogLock();
  1038. ~CallStacksLogLock();
  1039. #if SINGLE_THREADED
  1040. bool ok() const { return true; }
  1041. #else
  1042. bool ok() const { return mOK; }
  1043. private:
  1044. bool mLocked;
  1045. bool mOK;
  1046. #endif
  1047. };
  1048. #if SINGLE_THREADED
  1049. CallStacksLogLock::CallStacksLogLock()
  1050. {
  1051. }
  1052. CallStacksLogLock::~CallStacksLogLock()
  1053. {
  1054. }
  1055. #else
  1056. CallStacksLogLock::CallStacksLogLock()
  1057. : mLocked(false), mOK(false)
  1058. {
  1059. if (!gCallStacksLogMutexp)
  1060. {
  1061. mOK = true;
  1062. return;
  1063. }
  1064. const int MAX_RETRIES = 5;
  1065. for (int attempts = 0; attempts < MAX_RETRIES; ++attempts)
  1066. {
  1067. apr_status_t s = apr_thread_mutex_trylock(gCallStacksLogMutexp);
  1068. if (!APR_STATUS_IS_EBUSY(s))
  1069. {
  1070. mLocked = true;
  1071. mOK = true;
  1072. return;
  1073. }
  1074. ms_sleep(1);
  1075. }
  1076. // We're hosed, we can't get the mutex. Blah.
  1077. std::cerr << "CallStacksLogLock::CallStacksLogLock: failed to get mutex for log"
  1078. << std::endl;
  1079. }
  1080. CallStacksLogLock::~CallStacksLogLock()
  1081. {
  1082. if (mLocked)
  1083. {
  1084. apr_thread_mutex_unlock(gCallStacksLogMutexp);
  1085. }
  1086. }
  1087. #endif
  1088. //static
  1089. void LLCallStacks::push(const char* function, const int line)
  1090. {
  1091. CallStacksLogLock lock;
  1092. if (!lock.ok())
  1093. {
  1094. return;
  1095. }
  1096. if(!sBuffer)
  1097. {
  1098. sBuffer = new char*[512] ;
  1099. sBuffer[0] = new char[512 * 128] ;
  1100. for(S32 i = 1 ; i < 512 ; i++)
  1101. {
  1102. sBuffer[i] = sBuffer[i-1] + 128 ;
  1103. }
  1104. sIndex = 0 ;
  1105. }
  1106. if(sIndex > 511)
  1107. {
  1108. clear() ;
  1109. }
  1110. strcpy(sBuffer[sIndex], function) ;
  1111. sprintf(sBuffer[sIndex] + strlen(function), " line: %d ", line) ;
  1112. sIndex++ ;
  1113. return ;
  1114. }
  1115. //static
  1116. std::ostringstream* LLCallStacks::insert(const char* function, const int line)
  1117. {
  1118. std::ostringstream* _out = LLError::Log::out();
  1119. *_out << function << " line " << line << " " ;
  1120. return _out ;
  1121. }
  1122. //static
  1123. void LLCallStacks::end(std::ostringstream* _out)
  1124. {
  1125. CallStacksLogLock lock;
  1126. if (!lock.ok())
  1127. {
  1128. return;
  1129. }
  1130. if(!sBuffer)
  1131. {
  1132. sBuffer = new char*[512] ;
  1133. sBuffer[0] = new char[512 * 128] ;
  1134. for(S32 i = 1 ; i < 512 ; i++)
  1135. {
  1136. sBuffer[i] = sBuffer[i-1] + 128 ;
  1137. }
  1138. sIndex = 0 ;
  1139. }
  1140. if(sIndex > 511)
  1141. {
  1142. clear() ;
  1143. }
  1144. LLError::Log::flush(_out, sBuffer[sIndex++]) ;
  1145. }
  1146. //static
  1147. void LLCallStacks::print()
  1148. {
  1149. CallStacksLogLock lock;
  1150. if (!lock.ok())
  1151. {
  1152. return;
  1153. }
  1154. if(sIndex > 0)
  1155. {
  1156. llinfos << " ************* PRINT OUT LL CALL STACKS ************* " << llendl ;
  1157. while(sIndex > 0)
  1158. {
  1159. sIndex-- ;
  1160. llinfos << sBuffer[sIndex] << llendl ;
  1161. }
  1162. llinfos << " *************** END OF LL CALL STACKS *************** " << llendl ;
  1163. }
  1164. if(sBuffer)
  1165. {
  1166. delete[] sBuffer[0] ;
  1167. delete[] sBuffer ;
  1168. sBuffer = NULL ;
  1169. }
  1170. }
  1171. //static
  1172. void LLCallStacks::clear()
  1173. {
  1174. sIndex = 0 ;
  1175. }
  1176. }