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