3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
29 #if !defined(_WIN32) // POSIX
32 #include "util/basic_macros.h"
33 #include "util/stream.h"
34 #include "irrlichttypes.h"
39 LL_NONE, // Special level that is always printed
42 LL_ACTION, // In-game actions
55 typedef u8 LogLevelMask;
56 #define LOGLEVEL_TO_MASKLEVEL(x) (1 << x)
60 void addOutput(ILogOutput *out);
61 void addOutput(ILogOutput *out, LogLevel lev);
62 void addOutputMasked(ILogOutput *out, LogLevelMask mask);
63 void addOutputMaxLevel(ILogOutput *out, LogLevel lev);
64 LogLevelMask removeOutput(ILogOutput *out);
65 void setLevelSilenced(LogLevel lev, bool silenced);
67 void registerThread(const std::string &name);
68 void deregisterThread();
70 void log(LogLevel lev, const std::string &text);
71 // Logs without a prefix
72 void logRaw(LogLevel lev, const std::string &text);
74 static LogLevel stringToLevel(const std::string &name);
75 static const std::string getLevelLabel(LogLevel lev);
77 bool hasOutput(LogLevel level) {
78 return m_has_outputs[level].load(std::memory_order_relaxed);
81 static LogColor color_mode;
84 void logToOutputsRaw(LogLevel, const std::string &line);
85 void logToOutputs(LogLevel, const std::string &combined,
86 const std::string &time, const std::string &thread_name,
87 const std::string &payload_text);
89 const std::string getThreadName();
91 std::vector<ILogOutput *> m_outputs[LL_MAX];
92 std::atomic<bool> m_has_outputs[LL_MAX];
94 // Should implement atomic loads and stores (even though it's only
95 // written to when one thread has access currently).
96 // Works on all known architectures (x86, ARM, MIPS).
97 volatile bool m_silenced_levels[LL_MAX];
98 std::map<std::thread::id, std::string> m_thread_names;
99 mutable std::mutex m_mutex;
104 virtual void logRaw(LogLevel, const std::string &line) = 0;
105 virtual void log(LogLevel, const std::string &combined,
106 const std::string &time, const std::string &thread_name,
107 const std::string &payload_text) = 0;
110 class ICombinedLogOutput : public ILogOutput {
112 void log(LogLevel lev, const std::string &combined,
113 const std::string &time, const std::string &thread_name,
114 const std::string &payload_text)
116 logRaw(lev, combined);
120 class StreamLogOutput : public ICombinedLogOutput {
122 StreamLogOutput(std::ostream &stream) :
126 is_tty = isatty(fileno(stdout));
132 void logRaw(LogLevel lev, const std::string &line);
135 std::ostream &m_stream;
139 class FileLogOutput : public ICombinedLogOutput {
141 void setFile(const std::string &filename, s64 file_size_max);
143 void logRaw(LogLevel lev, const std::string &line)
145 m_stream << line << std::endl;
149 std::ofstream m_stream;
152 class LogOutputBuffer : public ICombinedLogOutput {
154 LogOutputBuffer(Logger &logger) :
160 virtual ~LogOutputBuffer()
162 m_logger.removeOutput(this);
165 void updateLogLevel();
167 void logRaw(LogLevel lev, const std::string &line);
171 m_buffer = std::queue<std::string>();
176 return m_buffer.empty();
183 std::string s = m_buffer.front();
189 std::queue<std::string> m_buffer;
194 class AndroidLogOutput : public ICombinedLogOutput {
196 void logRaw(LogLevel lev, const std::string &line);
203 * This is the interface that sits between the LogStreams and the global logger.
204 * Primarily used to route streams to log levels, but could also enable other
210 // Must be thread-safe. These can be called from any thread.
211 virtual bool hasOutput() = 0;
212 virtual void log(const std::string &buf) = 0;
219 * An ostream-like object that can proxy to a real ostream or do nothing,
220 * depending on how it is configured. See LogStream below.
225 StreamProxy(std::ostream *os) : m_os(os) { }
228 StreamProxy& operator<<(T&& arg) {
230 *m_os << std::forward<T>(arg);
235 StreamProxy& operator<<(std::ostream& (*manip)(std::ostream&)) {
250 * The public interface for log streams (infostream, verbosestream, etc).
252 * LogStream minimizes the work done when a given stream is off. (meaning
253 * it has no output targets, so it goes to /dev/null)
255 * For example, consider:
257 * verbosestream << "hello world" << 123 << std::endl;
259 * The compiler evaluates this as:
261 * (((verbosestream << "hello world") << 123) << std::endl)
264 * If `verbosestream` is on, the innermost expression (marked by ^) will return
265 * a StreamProxy that forwards to a real ostream, that feeds into the logger.
266 * However, if `verbosestream` is off, it will return a StreamProxy that does
267 * nothing on all later operations. Specifically, CPU time won't be wasted
268 * writing "hello world" and 123 into a buffer, or formatting the log entry.
270 * It is also possible to directly check if the stream is on/off:
272 * if (verbosestream) {
273 * auto data = ComputeExpensiveDataForTheLog();
274 * verbosestream << data << endl;
281 LogStream() = delete;
282 DISABLE_CLASS_COPY(LogStream);
284 LogStream(LogTarget &target) :
286 m_buffer(std::bind(&LogStream::internalFlush, this, std::placeholders::_1)),
289 m_dummy_stream(&m_dummy_buffer),
291 m_dummy_proxy(nullptr) { }
294 StreamProxy& operator<<(T&& arg) {
295 StreamProxy& sp = m_target.hasOutput() ? m_proxy : m_dummy_proxy;
296 sp << std::forward<T>(arg);
300 StreamProxy& operator<<(std::ostream& (*manip)(std::ostream&)) {
301 StreamProxy& sp = m_target.hasOutput() ? m_proxy : m_dummy_proxy;
307 return m_target.hasOutput();
310 void internalFlush(const std::string &buf) {
314 operator std::ostream&() {
315 return m_target.hasOutput() ? m_stream : m_dummy_stream;
319 // 10 streams per thread x (256 + overhead) ~ 3K per thread
320 static const int BUFFER_LENGTH = 256;
322 StringStreamBuffer<BUFFER_LENGTH> m_buffer;
323 DummyStreamBuffer m_dummy_buffer;
324 std::ostream m_stream;
325 std::ostream m_dummy_stream;
327 StreamProxy m_dummy_proxy;
332 extern AndroidLogOutput stdout_output;
333 extern AndroidLogOutput stderr_output;
335 extern StreamLogOutput stdout_output;
336 extern StreamLogOutput stderr_output;
339 extern Logger g_logger;
342 * By making the streams thread_local, each thread has its own
343 * private buffer. Two or more threads can write to the same stream
344 * simultaneously (lock-free), and there won't be any interference.
346 * The finished lines are sent to a LogTarget which is a global (not thread-local)
347 * object, and from there relayed to g_logger. The final writes are serialized
348 * by the mutex in g_logger.
351 extern thread_local LogStream dstream;
352 extern thread_local LogStream rawstream; // Writes directly to all LL_NONE log outputs with no prefix.
353 extern thread_local LogStream errorstream;
354 extern thread_local LogStream warningstream;
355 extern thread_local LogStream actionstream;
356 extern thread_local LogStream infostream;
357 extern thread_local LogStream verbosestream;
358 extern thread_local LogStream tracestream;
359 // TODO: Search/replace these with verbose/tracestream
360 extern thread_local LogStream derr_con;
361 extern thread_local LogStream dout_con;
363 #define TRACESTREAM(x) do { \