+typedef u8 LogLevelMask;
+#define LOGLEVEL_TO_MASKLEVEL(x) (1 << x)
+
+class Logger {
+public:
+ void addOutput(ILogOutput *out);
+ void addOutput(ILogOutput *out, LogLevel lev);
+ void addOutputMasked(ILogOutput *out, LogLevelMask mask);
+ void addOutputMaxLevel(ILogOutput *out, LogLevel lev);
+ LogLevelMask removeOutput(ILogOutput *out);
+ void setLevelSilenced(LogLevel lev, bool silenced);
+
+ void registerThread(const std::string &name);
+ void deregisterThread();
+
+ void log(LogLevel lev, const std::string &text);
+ // Logs without a prefix
+ void logRaw(LogLevel lev, const std::string &text);
+
+ void setTraceEnabled(bool enable) { m_trace_enabled = enable; }
+ bool getTraceEnabled() { return m_trace_enabled; }
+
+ static LogLevel stringToLevel(const std::string &name);
+ static const std::string getLevelLabel(LogLevel lev);
+
+private:
+ void logToOutputsRaw(LogLevel, const std::string &line);
+ void logToOutputs(LogLevel, const std::string &combined,
+ const std::string &time, const std::string &thread_name,
+ const std::string &payload_text);
+
+ const std::string getThreadName();
+
+ std::vector<ILogOutput *> m_outputs[LL_MAX];
+
+ // Should implement atomic loads and stores (even though it's only
+ // written to when one thread has access currently).
+ // Works on all known architectures (x86, ARM, MIPS).
+ volatile bool m_silenced_levels[LL_MAX];
+ std::map<threadid_t, std::string> m_thread_names;
+ mutable Mutex m_mutex;
+ bool m_trace_enabled;
+};
+
+class ILogOutput {
+public:
+ virtual void logRaw(LogLevel, const std::string &line) = 0;
+ virtual void log(LogLevel, const std::string &combined,
+ const std::string &time, const std::string &thread_name,
+ const std::string &payload_text) = 0;
+};
+
+class ICombinedLogOutput : public ILogOutput {
+public:
+ void log(LogLevel lev, const std::string &combined,
+ const std::string &time, const std::string &thread_name,
+ const std::string &payload_text)
+ {
+ logRaw(lev, combined);
+ }
+};