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