1 /* 2 * Logging.cpp - facilities for flexible logging 3 4 Copyright (C) 2003 and beyond by Woody Zenfell, III 5 and the "Aleph One" developers. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 This license is contained in the file "COPYING", 18 which is included with this source code; it is available online at 19 http://www.gnu.org/licenses/gpl.html 20 21 22 Jan. 16, 2003 (Woody Zenfell): Created. 23 24 July 13, 2003 (Woody Zenfell): 25 Split out the repetitive bits to Logging_gruntwork.h; now generating that with a script 26 Now offering logWarningNMT3() and co. for use in non-main threads, for added safety 27 New Logging level 'summary' 28 */ 29 30 #ifndef LOGGING_H 31 #define LOGGING_H 32 33 #include <stdarg.h> 34 35 enum { 36 logFatalLevel = 0, // program must exit 37 logErrorLevel = 10, // can't do something significant 38 logWarningLevel = 20, // can continue but results could be really screwy 39 logAnomalyLevel = 30, // can continue, results could be off a little, but no big deal 40 logNoteLevel = 40, // something worth mentioning 41 logSummaryLevel = 45, // occasional dumps of aggregate statistics 42 logTraceLevel = 50, // details of actions and logic 43 logDumpLevel = 60 // values of data etc. 44 }; 45 46 47 class Logger { 48 public: 49 virtual void pushLogContext(const char* inFile, int inLine, const char* inContext, ...); 50 virtual void logMessage(const char* inDomain, int inLevel, const char* inFile, int inLine, const char* inMessage, ...); 51 virtual void logMessageNMT(const char* inDomain, int inLevel, const char* inFile, int inLine, const char* inMessage, ...); 52 53 virtual void pushLogContextV(const char* inFile, int inLine, const char* inContext, va_list inArgList) = 0; 54 virtual void popLogContext() = 0; 55 virtual void logMessageV(const char* inDomain, int inLevel, const char* inFile, int inLine, const char* inMessage, va_list inArgList) = 0; 56 virtual void flush() = 0; 57 58 virtual ~Logger(); 59 }; 60 61 62 // Primitive functions supporting the macros, classes, etc. 63 Logger* GetCurrentLogger(); 64 65 // Other functions 66 void setLoggingThreshhold(const char* inDomain, short inThreshhold); // message appears if its level < inThreshhold 67 void setShowLoggingLocations(const char* inDomain, bool inShowLocations); // show file and line? 68 void setFlushLoggingOutput(const char* inDomain, bool inFlushOutput); // flush output file after every log message? 69 70 71 class InfoTree; 72 void parse_mml_logging(const InfoTree& root); 73 void reset_mml_logging(); 74 75 // Log file name, for display in error messages 76 const char *loggingFileName(); 77 78 79 // Catch-all domain for use when nobody overrides us 80 extern const char* logDomain; 81 82 83 84 // (COMMENTS FOR logError() FAMILY) 85 // Ease-of-use macros wrap around logMessage. 86 #define logFatal(...) (GetCurrentLogger()->logMessage(logDomain, logFatalLevel, __FILE__, __LINE__, __VA_ARGS__)) 87 #define logError(...) (GetCurrentLogger()->logMessage(logDomain, logErrorLevel, __FILE__, __LINE__, __VA_ARGS__)) 88 #define logWarning(...) (GetCurrentLogger()->logMessage(logDomain, logWarningLevel, __FILE__, __LINE__, __VA_ARGS__)) 89 #define logAnomaly(...) (GetCurrentLogger()->logMessage(logDomain, logAnomalyLevel, __FILE__, __LINE__, __VA_ARGS__)) 90 #define logNote(...) (GetCurrentLogger()->logMessage(logDomain, logNoteLevel, __FILE__, __LINE__, __VA_ARGS__)) 91 #define logSummary(...) (GetCurrentLogger()->logMessage(logDomain, logSummaryLevel, __FILE__, __LINE__, __VA_ARGS__)) 92 #define logTrace(...) (GetCurrentLogger()->logMessage(logDomain, logTraceLevel, __FILE__, __LINE__, __VA_ARGS__)) 93 #define logDump(...) (GetCurrentLogger()->logMessage(logDomain, logDumpLevel, __FILE__, __LINE__, __VA_ARGS__)) 94 95 // NB! use logError() and co. only in the main thread. Elsewhere, use logErrorNMT() and co. 96 // (the NMT versions may have more complex - or missing - implementations to make them safer) 97 #define logFatalNMT(...) (GetCurrentLogger()->logMessageNMT(logDomain, logFatalLevel, __FILE__, __LINE__, __VA_ARGS__)) 98 #define logErrorNMT(...) (GetCurrentLogger()->logMessageNMT(logDomain, logErrorLevel, __FILE__, __LINE__, __VA_ARGS__)) 99 #define logWarningNMT(...) (GetCurrentLogger()->logMessageNMT(logDomain, logWarningLevel, __FILE__, __LINE__, __VA_ARGS__)) 100 #define logAnomalyNMT(...) (GetCurrentLogger()->logMessageNMT(logDomain, logAnomalyLevel, __FILE__, __LINE__, __VA_ARGS__)) 101 #define logNoteNMT(...) (GetCurrentLogger()->logMessageNMT(logDomain, logNoteLevel, __FILE__, __LINE__, __VA_ARGS__)) 102 #define logSummaryNMT(...) (GetCurrentLogger()->logMessageNMT(logDomain, logSummaryLevel, __FILE__, __LINE__, __VA_ARGS__)) 103 #define logTraceNMT(...) (GetCurrentLogger()->logMessageNMT(logDomain, logTraceLevel, __FILE__, __LINE__, __VA_ARGS__)) 104 #define logDumpNMT(...) (GetCurrentLogger()->logMessageNMT(logDomain, logDumpLevel, __FILE__, __LINE__, __VA_ARGS__)) 105 106 // (COMMENTS FOR logContext() FAMILY) 107 // Enter a (runtime) logging subcontext; expires at end of block. Use only within functions!! 108 // Use only as a statement, e.g. logContext("foo");, never in an expression. Useless in code like 109 // if(a) logContext("a is true"); else logContext("b is true");. For that sort of thing, use 110 // the class directly - declare an instance of it earlier and call enterContext() in your if(). 111 // Contexts should be phrased like "starting a new game". 112 // We need to use makeUniqueIdentifier so that __LINE__ gets expanded before concatenation. 113 #define makeUniqueIdentifier(a, b) a ## b 114 115 #define logContext(...) LogContext makeUniqueIdentifier(_theLogContext,__LINE__)(false, __FILE__, __LINE__, __VA_ARGS__) 116 #define logContextNMT(...) LogContext makeUniqueIdentifier(_theLogContext,__LINE__)(true, __FILE__, __LINE__, __VA_ARGS__) 117 118 119 // Class intended for stack-based use for entering/leaving a logging subcontext 120 class LogContext { 121 public: LogContext(bool inNonMainThread)122 LogContext(bool inNonMainThread) : contextSet(false), nonMainThread(inNonMainThread) {} 123 LogContext(const char * inFile,int inLine,const char * inContext,...)124 LogContext(const char* inFile, int inLine, const char* inContext, ...) : contextSet(false), nonMainThread(false) { 125 va_list theVarArgs; 126 va_start(theVarArgs, inContext); 127 enterContextV(inFile, inLine, inContext, theVarArgs); 128 va_end(theVarArgs); 129 } 130 LogContext(bool inNonMainThread,const char * inFile,int inLine,const char * inContext,...)131 LogContext(bool inNonMainThread, const char* inFile, int inLine, const char* inContext, ...) : contextSet(false), nonMainThread(inNonMainThread) { 132 va_list theVarArgs; 133 va_start(theVarArgs, inContext); 134 enterContextV(inFile, inLine, inContext, theVarArgs); 135 va_end(theVarArgs); 136 } 137 enterContext(const char * inFile,int inLine,const char * inContext,...)138 void enterContext(const char* inFile, int inLine, const char* inContext, ...) { 139 va_list theVarArgs; 140 va_start(theVarArgs, inContext); 141 enterContextV(inFile, inLine, inContext, theVarArgs); 142 va_end(theVarArgs); 143 } 144 enterContextV(const char * inFile,int inLine,const char * inContext,va_list inArgs)145 void enterContextV(const char* inFile, int inLine, const char* inContext, va_list inArgs) { 146 if(contextSet) 147 leaveContext(); 148 149 GetCurrentLogger()->pushLogContextV(inFile, inLine, inContext, inArgs); 150 151 contextSet = true; 152 } 153 leaveContext()154 void leaveContext() { 155 if(contextSet) 156 GetCurrentLogger()->popLogContext(); 157 158 contextSet = false; 159 } 160 ~LogContext()161 ~LogContext() { 162 leaveContext(); 163 } 164 165 protected: 166 bool contextSet; 167 bool nonMainThread; 168 }; 169 170 171 172 // Not yet complete - idea is let some set of logging operations be "held", 173 // then whether to log them normally or otherwise (perhaps with a "visibility boost") 174 // can be decided later. Intended for, like, if a sub-function tries a lot of different ways 175 // to succeed, its efforts can be logged in a SubLog. Typically any small failures etc. 176 // would not be interesting. But, if the function as a whole fails, it'd be nice to 177 // know (what was tried and what the results of each try were). The idea then is, 178 // much of the SubLog can be included in a regular Log. 179 /* 180 class SubLogger : public Logger { 181 public: 182 virtual void pushLogContext(const char* inFile, int inLine, const char* inContext, ...); 183 virtual void popLogContext(); 184 virtual void logMessage(const char* inDomain, int inLevel, const char* inFile, int inLine, const char* inMessage, ...); 185 protected: 186 struct LogAction { 187 enum { ePushContext, ePopContext, eMessage } mType; 188 string mDomain; 189 int mLevel; 190 string mMessage; 191 string mFile; 192 int mLine; 193 }; 194 195 vector<LogAction> mLogActions; 196 }; 197 */ 198 199 #endif // LOGGING_H 200