1 /***************************************************************************
2  *   Copyright (C) 2005-2019 by the FIFE team                              *
3  *   http://www.fifengine.net                                              *
4  *   This file is part of FIFE.                                            *
5  *                                                                         *
6  *   FIFE is free software; you can redistribute it and/or                 *
7  *   modify it under the terms of the GNU Lesser General Public            *
8  *   License as published by the Free Software Foundation; either          *
9  *   version 2.1 of the License, or (at your option) any later version.    *
10  *                                                                         *
11  *   This library is distributed in the hope that it will be useful,       *
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
14  *   Lesser General Public License for more details.                       *
15  *                                                                         *
16  *   You should have received a copy of the GNU Lesser General Public      *
17  *   License along with this library; if not, write to the                 *
18  *   Free Software Foundation, Inc.,                                       *
19  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
20  ***************************************************************************/
21 
22 #ifndef FIFE_LOGGER_H
23 #define FIFE_LOGGER_H
24 
25 // Standard C++ library includes
26 #include <iomanip>
27 #include <iostream>
28 #include <list>
29 #include <sstream>
30 #include <string>
31 #include <vector>
32 #include <map>
33 #include <set>
34 
35 // 3rd party library includes
36 
37 // FIFE includes
38 // These includes are split up in two parts, separated by one empty line
39 // First block: files included from the FIFE root src directory
40 // Second block: files included from the same folder
41 #include "util/base/fife_stdint.h"
42 
43 #include "modules.h"
44 
45 #ifdef LOG_ENABLED
46 
47 /** Logs given message with log level "debug" using given logger instance
48  */
49 #define FL_DBG(logger, msg) do { if (FIFE::LogManager::instance()->isVisible(logger.getModule())) logger.log(FIFE::LogManager::LEVEL_DEBUG, msg); } while(0)
50 
51 /** Logs given message with log level "log" using given logger instance
52  */
53 #define FL_LOG(logger, msg) do { if (FIFE::LogManager::instance()->isVisible(logger.getModule())) logger.log(FIFE::LogManager::LEVEL_LOG, msg); } while(0)
54 
55 /** Logs given message with log level "warning" using given logger instance
56  */
57 #define FL_WARN(logger, msg) do { if (FIFE::LogManager::instance()->isVisible(logger.getModule())) logger.log(FIFE::LogManager::LEVEL_WARN, msg); } while(0)
58 
59 /** Logs given message with log level "error" using given logger instance
60  */
61 #define FL_ERR(logger, msg) do { if (FIFE::LogManager::instance()->isVisible(logger.getModule())) logger.log(FIFE::LogManager::LEVEL_ERROR, msg); } while(0)
62 
63 /** Logs given message with log level "pacic" using given logger instance.
64  * Causes also program to abort
65  */
66 #define FL_PANIC(logger, msg) do { if (FIFE::LogManager::instance()->isVisible(logger.getModule())) logger.log(FIFE::LogManager::LEVEL_PANIC, msg); } while(0)
67 
68 #else
69 // empty definitions in case logs are turned off for speed
70 #define FL_DBG(logger, msg)
71 #define FL_LOG(logger, msg)
72 #define FL_WARN(logger, msg)
73 #define FL_ERR(logger, msg)
74 #define FL_PANIC(logger, msg)
75 #endif
76 
77 namespace FIFE {
78 
79 	/** Helper class to create log strings out from separate parts
80 	 * Usage: LMsg("some text") << variable << ", " << other variable
81 	 */
82 	class LMsg {
83 	public:
str(msg)84 		LMsg(const std::string& msg=""): str(msg) {}
~LMsg()85 		~LMsg() {}
86 
87 		template <typename T> LMsg& operator<<(const T& t) {
88 			std::ostringstream stream;
89 			stream << t;
90 			str += stream.str();
91 			return *this;
92 		}
93 
94 		std::string str;
95 	};
96 
97 	/** Logmanager takes care of log filtering and output direction
98 	 */
99 	class LogManager {
100 	public:
101 		/** Loglevel is used to set a treshold for output messages + related filter
102 		 * E.g. in case log message has LEVEL_WARN, but the filter treshold is LEVEL_ERROR,
103 		 * log message is not outputted
104 		 */
105 		enum LogLevel {
106 			LEVEL_DEBUG = 0,
107 			LEVEL_LOG   = 1,
108 			LEVEL_WARN  = 2,
109    			LEVEL_ERROR = 3,
110    			LEVEL_PANIC = 4
111 		};
112 
113 		/** Returns instance to log manager. Log manager is a singleton class
114 		 */
115 		static LogManager* instance();
116 
117 		/** Destructor
118 		 */
119 		~LogManager();
120 
121 		/** Logs given message
122 		 * @param level level of this log (e.g. warning)
123 		 * @param module module where this log message is coming from. Modules are defined in modules.h-file
124 		 * @param msg message to log
125 		 * @note do not use this method directly, instead use FL_WARN (or any other FL_XXX) macro
126 		 */
127 		void log(LogLevel level, logmodule_t module, const std::string& msg);
128 
129 		/** Sets currently used level filter.
130 		 * For usage, @see LogManager::LogLevel
131 		 */
132 		void setLevelFilter(LogLevel level);
133 
134 		/** Gets currently used level filter.
135 		 * @see LogManager::LogLevel
136 		 */
137 		LogLevel getLevelFilter();
138 
139 		/** Adds visible module into logmanager
140 		 * Module corresponds some module in the engine. Modules may contain other modules.
141 		 * Modules and their structure is defined in file modules.h.
142 		 * In case module is not visible, LogManager filters corresponding log messages
143 		 * from output. In case some lower-level module is set visible, it also sets
144 		 * all upper level modules visible
145 		 * @param module module to set visible
146 		 */
147 		void addVisibleModule(logmodule_t module);
148 
149 		/** Removes visible module, @see addVisibleModule
150 		 */
151 		void removeVisibleModule(logmodule_t module);
152 
153 		/** Removes all visible modules, @see addVisibleModule
154 		 */
155 		void clearVisibleModules();
156 
157 		/** Tells if given module is visible
158 		 */
159 		bool isVisible(logmodule_t module);
160 
161 		/** Sets LogManager to log to prompt
162 		 */
163 		void setLogToPrompt(bool logtoprompt);
164 
165 		/** Returns if LogManager is set to log to prompt
166 		 */
167 		bool isLogToPrompt();
168 
169 		/** Sets LogManager to log to a file
170 		 */
171 		void setLogToFile(bool logtofile);
172 
173 		/** Returns if LogManager is set to log to a file
174 		 */
175 		bool isLogToFile();
176 
177 		/** Gets display name for given module id
178 		 * E.g. LM_AUDIO -> "Audio"
179 		 */
180 		std::string getModuleName(logmodule_t module);
181 
182 	private:
183 		void validateModule(logmodule_t m);
184 
185 		// hidden constructor for singleton
186 		LogManager();
187 		// validates if definitions in module.h are valid
188 		void validateModuleDescription(logmodule_t module);
189 
190 		// singleton instance
191 		static LogManager* m_instance;
192 		// current filter level
193 		LogLevel m_level;
194 		// visibility array for modules
195 		bool m_modules[LM_MODULE_MAX];
196 		// used during module description validation to check cycles in hierarchy
197 		std::vector<logmodule_t> module_check_stack;
198 
199 		bool m_logtofile;
200 		bool m_logtoprompt;
201 
202 		std::ofstream* m_logfile;
203 	};
204 
205 	/** Create a Logger instance to communicate with LogManager
206 	 * Logger stores information about the current module thus reducing
207 	 * the typing needed for individual traces
208 	 * Common way of doing things is to instantiate a static Logger on
209 	 * top of .cpp file and then use that in .cpp-file's methods
210 	 */
211 	class Logger {
212 	public:
213 		/** Creates new logger and associates it with given module
214 		 */
215 		Logger(logmodule_t module);
216 
217 		/** Destructor
218 		 */
219 		~Logger();
220 
221 		/** logs given message with given log level
222 		 */
223 		void log(LogManager::LogLevel level, const std::string& msg);
224 
225 		/** logs given message with given log level.
226 		 * Message is wrapped into LMsg instance for easy formatting
227 		 */
228 		void log(LogManager::LogLevel level, const LMsg& msg);
229 
230 		/** gets module where this logger is associated to
231 		 */
getModule()232 		inline logmodule_t getModule() const { return m_module; }
233 
234 	private:
235 		logmodule_t m_module;
236 	};
237 
238 	/** Helper for printing a pointer
239 	 *
240 	 * This is a helper structure that allows printing any kind of pointer
241 	 * on (hopefully) any platform in hex, kind of like the %p format
242 	 * string of printf.
243 	 *
244 	 * The mechanism is used by calling something like:
245 	 *  somestream << pprint(ptr);
246 	 **/
247 	struct pprint {
248 		void* p;
pprintpprint249 		pprint( void* _p ) : p(_p) {}
250 	};
251 }
252 
253 namespace std {
254 	/** Print a pprint object to an ostream.
255 	 *
256 	 * This is pure Stroustrup, overloading the ostream operator<< to print
257 	 * a formatted pointer from a pprint object to an ostream.
258 	 *
259 	 * \param s output stream
260 	 * \param p pointer to print
261 	 * \return reference to the modified stream
262 	 * */
263 	template <class Ch, class Tr>
264 	basic_ostream<Ch,Tr>& operator<<( basic_ostream<Ch,Tr>& s, const FIFE::pprint& p ) {
265 		s << "0x"
266 			<< hex << setw( 2*sizeof(void*) ) << setfill('0')
267 			<< reinterpret_cast<uint64_t>( p.p );
268 
269 		return s;
270 	}
271 }
272 
273 
274 #endif
275