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 "threading/mutex_auto_lock.h"
33 #include "util/basic_macros.h"
34 #include "util/stream.h"
35 #include "irrlichttypes.h"
40 LL_NONE, // Special level that is always printed
43 LL_ACTION, // In-game actions
56 typedef u8 LogLevelMask;
57 #define LOGLEVEL_TO_MASKLEVEL(x) (1 << x)
61 void addOutput(ILogOutput *out);
62 void addOutput(ILogOutput *out, LogLevel lev);
63 void addOutputMasked(ILogOutput *out, LogLevelMask mask);
64 void addOutputMaxLevel(ILogOutput *out, LogLevel lev);
65 LogLevelMask removeOutput(ILogOutput *out);
66 void setLevelSilenced(LogLevel lev, bool silenced);
68 void registerThread(const std::string &name);
69 void deregisterThread();
71 void log(LogLevel lev, const std::string &text);
72 // Logs without a prefix
73 void logRaw(LogLevel lev, const std::string &text);
75 static LogLevel stringToLevel(const std::string &name);
76 static const std::string getLevelLabel(LogLevel lev);
78 bool hasOutput(LogLevel level) {
79 return m_has_outputs[level].load(std::memory_order_relaxed);
82 static LogColor color_mode;
85 void logToOutputsRaw(LogLevel, const std::string &line);
86 void logToOutputs(LogLevel, const std::string &combined,
87 const std::string &time, const std::string &thread_name,
88 const std::string &payload_text);
90 const std::string getThreadName();
92 std::vector<ILogOutput *> m_outputs[LL_MAX];
93 std::atomic<bool> m_has_outputs[LL_MAX];
95 // Should implement atomic loads and stores (even though it's only
96 // written to when one thread has access currently).
97 // Works on all known architectures (x86, ARM, MIPS).
98 volatile bool m_silenced_levels[LL_MAX];
99 std::map<std::thread::id, std::string> m_thread_names;
100 mutable std::mutex m_mutex;
105 virtual void logRaw(LogLevel, const std::string &line) = 0;
106 virtual void log(LogLevel, const std::string &combined,
107 const std::string &time, const std::string &thread_name,
108 const std::string &payload_text) = 0;
111 class ICombinedLogOutput : public ILogOutput {
113 void log(LogLevel lev, const std::string &combined,
114 const std::string &time, const std::string &thread_name,
115 const std::string &payload_text)
117 logRaw(lev, combined);
121 class StreamLogOutput : public ICombinedLogOutput {
123 StreamLogOutput(std::ostream &stream) :
127 is_tty = isatty(fileno(stdout));
133 void logRaw(LogLevel lev, const std::string &line);
136 std::ostream &m_stream;
140 class FileLogOutput : public ICombinedLogOutput {
142 void setFile(const std::string &filename, s64 file_size_max);
144 void logRaw(LogLevel lev, const std::string &line)
146 m_stream << line << std::endl;
150 std::ofstream m_stream;
153 class LogOutputBuffer : public ICombinedLogOutput {
155 LogOutputBuffer(Logger &logger) :
161 virtual ~LogOutputBuffer()
163 m_logger.removeOutput(this);
166 void updateLogLevel();
168 void logRaw(LogLevel lev, const std::string &line);
172 MutexAutoLock lock(m_buffer_mutex);
173 m_buffer = std::queue<std::string>();
178 MutexAutoLock lock(m_buffer_mutex);
179 return m_buffer.empty();
184 MutexAutoLock lock(m_buffer_mutex);
185 if (m_buffer.empty())
187 std::string s = std::move(m_buffer.front());
193 // g_logger serializes calls to logRaw() with a mutex, but that
194 // doesn't prevent get() / clear() from being called on top of it.
195 // This mutex prevents that.
196 mutable std::mutex m_buffer_mutex;
197 std::queue<std::string> m_buffer;
202 class AndroidLogOutput : public ICombinedLogOutput {
204 void logRaw(LogLevel lev, const std::string &line);
211 * This is the interface that sits between the LogStreams and the global logger.
212 * Primarily used to route streams to log levels, but could also enable other
218 // Must be thread-safe. These can be called from any thread.
219 virtual bool hasOutput() = 0;
220 virtual void log(const std::string &buf) = 0;
227 * An ostream-like object that can proxy to a real ostream or do nothing,
228 * depending on how it is configured. See LogStream below.
233 StreamProxy(std::ostream *os) : m_os(os) { }
236 StreamProxy& operator<<(T&& arg) {
238 *m_os << std::forward<T>(arg);
243 StreamProxy& operator<<(std::ostream& (*manip)(std::ostream&)) {
258 * The public interface for log streams (infostream, verbosestream, etc).
260 * LogStream minimizes the work done when a given stream is off. (meaning
261 * it has no output targets, so it goes to /dev/null)
263 * For example, consider:
265 * verbosestream << "hello world" << 123 << std::endl;
267 * The compiler evaluates this as:
269 * (((verbosestream << "hello world") << 123) << std::endl)
272 * If `verbosestream` is on, the innermost expression (marked by ^) will return
273 * a StreamProxy that forwards to a real ostream, that feeds into the logger.
274 * However, if `verbosestream` is off, it will return a StreamProxy that does
275 * nothing on all later operations. Specifically, CPU time won't be wasted
276 * writing "hello world" and 123 into a buffer, or formatting the log entry.
278 * It is also possible to directly check if the stream is on/off:
280 * if (verbosestream) {
281 * auto data = ComputeExpensiveDataForTheLog();
282 * verbosestream << data << endl;
289 LogStream() = delete;
290 DISABLE_CLASS_COPY(LogStream);
292 LogStream(LogTarget &target) :
294 m_buffer(std::bind(&LogStream::internalFlush, this, std::placeholders::_1)),
297 m_dummy_stream(&m_dummy_buffer),
299 m_dummy_proxy(nullptr) { }
302 StreamProxy& operator<<(T&& arg) {
303 StreamProxy& sp = m_target.hasOutput() ? m_proxy : m_dummy_proxy;
304 sp << std::forward<T>(arg);
308 StreamProxy& operator<<(std::ostream& (*manip)(std::ostream&)) {
309 StreamProxy& sp = m_target.hasOutput() ? m_proxy : m_dummy_proxy;
315 return m_target.hasOutput();
318 void internalFlush(const std::string &buf) {
322 operator std::ostream&() {
323 return m_target.hasOutput() ? m_stream : m_dummy_stream;
327 // 10 streams per thread x (256 + overhead) ~ 3K per thread
328 static const int BUFFER_LENGTH = 256;
330 StringStreamBuffer<BUFFER_LENGTH> m_buffer;
331 DummyStreamBuffer m_dummy_buffer;
332 std::ostream m_stream;
333 std::ostream m_dummy_stream;
335 StreamProxy m_dummy_proxy;
340 extern AndroidLogOutput stdout_output;
341 extern AndroidLogOutput stderr_output;
343 extern StreamLogOutput stdout_output;
344 extern StreamLogOutput stderr_output;
347 extern Logger g_logger;
350 * By making the streams thread_local, each thread has its own
351 * private buffer. Two or more threads can write to the same stream
352 * simultaneously (lock-free), and there won't be any interference.
354 * The finished lines are sent to a LogTarget which is a global (not thread-local)
355 * object, and from there relayed to g_logger. The final writes are serialized
356 * by the mutex in g_logger.
359 extern thread_local LogStream dstream;
360 extern thread_local LogStream rawstream; // Writes directly to all LL_NONE log outputs with no prefix.
361 extern thread_local LogStream errorstream;
362 extern thread_local LogStream warningstream;
363 extern thread_local LogStream actionstream;
364 extern thread_local LogStream infostream;
365 extern thread_local LogStream verbosestream;
366 extern thread_local LogStream tracestream;
367 // TODO: Search/replace these with verbose/tracestream
368 extern thread_local LogStream derr_con;
369 extern thread_local LogStream dout_con;
371 #define TRACESTREAM(x) do { \