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