Fix --color command line parameter ignorance (#7173)
[oweals/minetest.git] / src / log.h
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #pragma once
21
22 #include <map>
23 #include <queue>
24 #include <string>
25 #include <fstream>
26 #include <thread>
27 #include <mutex>
28 #if !defined(_WIN32)  // POSIX
29         #include <unistd.h>
30 #endif
31 #include "irrlichttypes.h"
32
33 class ILogOutput;
34
35 enum LogLevel {
36         LL_NONE, // Special level that is always printed
37         LL_ERROR,
38         LL_WARNING,
39         LL_ACTION,  // In-game actions
40         LL_INFO,
41         LL_VERBOSE,
42         LL_MAX,
43 };
44
45 enum LogColor {
46         LOG_COLOR_NEVER,
47         LOG_COLOR_ALWAYS,
48         LOG_COLOR_AUTO,
49 };
50
51 typedef u8 LogLevelMask;
52 #define LOGLEVEL_TO_MASKLEVEL(x) (1 << x)
53
54 class Logger {
55 public:
56         void addOutput(ILogOutput *out);
57         void addOutput(ILogOutput *out, LogLevel lev);
58         void addOutputMasked(ILogOutput *out, LogLevelMask mask);
59         void addOutputMaxLevel(ILogOutput *out, LogLevel lev);
60         LogLevelMask removeOutput(ILogOutput *out);
61         void setLevelSilenced(LogLevel lev, bool silenced);
62
63         void registerThread(const std::string &name);
64         void deregisterThread();
65
66         void log(LogLevel lev, const std::string &text);
67         // Logs without a prefix
68         void logRaw(LogLevel lev, const std::string &text);
69
70         void setTraceEnabled(bool enable) { m_trace_enabled = enable; }
71         bool getTraceEnabled() { return m_trace_enabled; }
72
73         static LogLevel stringToLevel(const std::string &name);
74         static const std::string getLevelLabel(LogLevel lev);
75
76         static LogColor color_mode;
77
78 private:
79         void logToOutputsRaw(LogLevel, const std::string &line);
80         void logToOutputs(LogLevel, const std::string &combined,
81                 const std::string &time, const std::string &thread_name,
82                 const std::string &payload_text);
83
84         const std::string getThreadName();
85
86         std::vector<ILogOutput *> m_outputs[LL_MAX];
87
88         // Should implement atomic loads and stores (even though it's only
89         // written to when one thread has access currently).
90         // Works on all known architectures (x86, ARM, MIPS).
91         volatile bool m_silenced_levels[LL_MAX];
92         std::map<std::thread::id, std::string> m_thread_names;
93         mutable std::mutex m_mutex;
94         bool m_trace_enabled;
95 };
96
97 class ILogOutput {
98 public:
99         virtual void logRaw(LogLevel, const std::string &line) = 0;
100         virtual void log(LogLevel, const std::string &combined,
101                 const std::string &time, const std::string &thread_name,
102                 const std::string &payload_text) = 0;
103 };
104
105 class ICombinedLogOutput : public ILogOutput {
106 public:
107         void log(LogLevel lev, const std::string &combined,
108                 const std::string &time, const std::string &thread_name,
109                 const std::string &payload_text)
110         {
111                 logRaw(lev, combined);
112         }
113 };
114
115 class StreamLogOutput : public ICombinedLogOutput {
116 public:
117         StreamLogOutput(std::ostream &stream) :
118                 m_stream(stream)
119         {
120 #if !defined(_WIN32)
121                 is_tty = isatty(fileno(stdout));
122 #else
123                 is_tty = false;
124 #endif
125         }
126
127         void logRaw(LogLevel lev, const std::string &line)
128         {
129                 bool colored_message = (Logger::color_mode == LOG_COLOR_ALWAYS) ||
130                         (Logger::color_mode == LOG_COLOR_AUTO && is_tty);
131                 if (colored_message)
132                         switch (lev) {
133                         case LL_ERROR:
134                                 // error is red
135                                 m_stream << "\033[91m";
136                                 break;
137                         case LL_WARNING:
138                                 // warning is yellow
139                                 m_stream << "\033[93m";
140                                 break;
141                         case LL_INFO:
142                                 // info is a bit dark
143                                 m_stream << "\033[37m";
144                                 break;
145                         case LL_VERBOSE:
146                                 // verbose is darker than info
147                                 m_stream << "\033[2m";
148                                 break;
149                         default:
150                                 // action is white
151                                 colored_message = false;
152                         }
153
154                 m_stream << line << std::endl;
155
156                 if (colored_message)
157                         // reset to white color
158                         m_stream << "\033[0m";
159         }
160
161 private:
162         std::ostream &m_stream;
163         bool is_tty;
164 };
165
166 class FileLogOutput : public ICombinedLogOutput {
167 public:
168         void open(const std::string &filename);
169
170         void logRaw(LogLevel lev, const std::string &line)
171         {
172                 m_stream << line << std::endl;
173         }
174
175 private:
176         std::ofstream m_stream;
177 };
178
179 class LogOutputBuffer : public ICombinedLogOutput {
180 public:
181         LogOutputBuffer(Logger &logger, LogLevel lev) :
182                 m_logger(logger)
183         {
184                 m_logger.addOutput(this, lev);
185         }
186
187         ~LogOutputBuffer()
188         {
189                 m_logger.removeOutput(this);
190         }
191
192         void logRaw(LogLevel lev, const std::string &line)
193         {
194                 m_buffer.push(line);
195         }
196
197         bool empty()
198         {
199                 return m_buffer.empty();
200         }
201
202         std::string get()
203         {
204                 if (empty())
205                         return "";
206                 std::string s = m_buffer.front();
207                 m_buffer.pop();
208                 return s;
209         }
210
211 private:
212         std::queue<std::string> m_buffer;
213         Logger &m_logger;
214 };
215
216
217 extern StreamLogOutput stdout_output;
218 extern StreamLogOutput stderr_output;
219 extern std::ostream null_stream;
220
221 extern std::ostream *dout_con_ptr;
222 extern std::ostream *derr_con_ptr;
223 extern std::ostream *dout_server_ptr;
224 extern std::ostream *derr_server_ptr;
225
226 #ifndef SERVER
227 extern std::ostream *dout_client_ptr;
228 extern std::ostream *derr_client_ptr;
229 #endif
230
231 extern Logger g_logger;
232
233 // Writes directly to all LL_NONE log outputs for g_logger with no prefix.
234 extern std::ostream rawstream;
235
236 extern std::ostream errorstream;
237 extern std::ostream warningstream;
238 extern std::ostream actionstream;
239 extern std::ostream infostream;
240 extern std::ostream verbosestream;
241 extern std::ostream dstream;
242
243 #define TRACEDO(x) do {               \
244         if (g_logger.getTraceEnabled()) { \
245                 x;                            \
246         }                                 \
247 } while (0)
248
249 #define TRACESTREAM(x) TRACEDO(verbosestream x)
250
251 #define dout_con (*dout_con_ptr)
252 #define derr_con (*derr_con_ptr)
253 #define dout_server (*dout_server_ptr)
254
255 #ifndef SERVER
256         #define dout_client (*dout_client_ptr)
257 #endif