Log/Log.h

158 lines
3.8 KiB
C++

#pragma once
#include <atomic>
#include <functional> // std::function
#include <sstream> // std::stringstream (buffer for log entries)
#include <string>
#include <vector>
#ifndef LOG_USEMUTEX
#define LOG_USEMUTEX 0
#endif
#ifndef LOG_ENABLEQT
#define LOG_ENABLEQT 0
#endif
#if LOG_ENABLEQT == 1
#include <QMessageLogContext>
#include <QDebug>
#endif
#if LOG_ENABLEQT == 1
template<typename T>
QDebug operator<<(QDebug dbg, const T& str) {
std::ostringstream stream;
stream << str;
QString conv = QString::fromStdString(stream.str());
QDebugStateSaver saver(dbg);
dbg << conv;
return dbg;
}
/*
// string specialisation, not allowed, because multiple definitions of the same operator are not allowed
template<>
QDebug operator<<(QDebug dbg, const std::string& str) {
QDebugStateSaver saver(dbg);
QString conv = QString::fromStdString(str);
dbg << conv;
return dbg;
}
*/
#endif
namespace Log {
enum class Level { off = 0, fatal, error, warn, note, info, debug, trace };
#if LOG_ENABLEQT == 1
void qtMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message);
#endif
// set up the logger with a ConsoleOutput
void init();
// close all output streams
void stop();
void addLogfile(const std::string& filename, Level max, bool truncate = false);
void addLogfile(const std::string& filename, Level min, Level max, bool truncate = false);
void setConsoleLogLevel(Level lvl);
void setColoredOutput(bool enabled);
// Log entry that can be formed with various mixed data types
// by concatenation with the << operator
// Inspired from https://stackoverflow.com/a/8337882
class Entry {
private:
std::stringstream ss;
const Level lvl;
#if LOG_ENABLEQT == 1
const QMessageLogContext* context = nullptr;
Entry(Level lvl, const QMessageLogContext&);
friend void qtMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message);
#endif
void addMetadataHeader();
public:
explicit Entry(Level lvl);
Entry(const Entry&) = delete;
Entry& operator=(const Entry&) = delete;
Entry(Entry&&) = default;
Entry& operator=(Entry&&) = default;
~Entry();
Level getLevel() const {
return lvl;
}
#if LOG_ENABLEQT == 1
constexpr const QMessageLogContext* getContext() const {
return context;
}
#endif
using MetaFunction = std::function<std::ostream&(std::ostream&, const Entry&)>;
template <typename T> Entry& operator<<(T&& msg) {
ss << std::forward<T>(msg);
return *this;
}
};
// class to automatically stop the logger on destruction
class Deleter {
public:
Deleter();
~Deleter();
private:
static std::atomic<uint32_t> refCount;
};
extern std::vector<Entry::MetaFunction> entryMetaFunctions;
std::ostream& defaultEntryMetaTime(std::ostream&, const Entry&);
std::ostream& defaultEntryMetaLevel(std::ostream&, const Entry&);
// custom io-manip
// FileSize is a iomanip to convert a filesize (fs) into a human readable format
class FileSize {
public:
explicit FileSize(uint64_t fs) : fs(fs) {}
FileSize(const FileSize&) = delete;
FileSize& operator=(const FileSize&) = delete;
operator std::string() const;
private:
uint64_t fs;
friend std::ostream& operator<<(std::ostream& str, const FileSize& fs);
};
struct PrintErrno {};
extern const PrintErrno err;
std::ostream& operator<<(std::ostream& str, const PrintErrno&);
std::ostream& operator<<(std::ostream& str, const FileSize& fs);
// copy Level values in Log namespace
constexpr Level off = Level::off;
constexpr Level fatal = Level::fatal;
constexpr Level error = Level::error;
constexpr Level warn = Level::warn;
constexpr Level note = Level::note;
constexpr Level info = Level::info;
constexpr Level debug = Level::debug;
constexpr Level trace = Level::trace;
// create Log lines (Entry objects) on using operator<< on Level values
template <typename T>
Entry operator<<(const Level lvl, T&& msg) {
Entry e{lvl};
e << std::forward<T>(msg);
return e;
}
} // namespace Log