#pragma once #include "types.hh" namespace nix { typedef enum { lvlError = 0, lvlWarn, lvlInfo, lvlTalkative, lvlChatty, lvlDebug, lvlVomit } Verbosity; typedef enum { actUnknown = 0, actCopyPath = 100, actDownload = 101, actRealise = 102, actCopyPaths = 103, actBuilds = 104, actBuild = 105, actOptimiseStore = 106, actVerifyPaths = 107, actSubstitute = 108, actQueryPathInfo = 109, actPostBuildHook = 110, } ActivityType; typedef enum { resFileLinked = 100, resBuildLogLine = 101, resUntrustedPath = 102, resCorruptedPath = 103, resSetPhase = 104, resProgress = 105, resSetExpected = 106, resPostBuildLogLine = 107, } ResultType; typedef uint64_t ActivityId; class Logger { friend struct Activity; public: struct Field { // FIXME: use std::variant. enum { tInt = 0, tString = 1 } type; uint64_t i = 0; std::string s; Field(const std::string & s) : type(tString), s(s) { } Field(const char * s) : type(tString), s(s) { } Field(const uint64_t & i) : type(tInt), i(i) { } }; typedef std::vector Fields; virtual ~Logger() { } virtual void log(Verbosity lvl, const FormatOrString & fs) = 0; void log(const FormatOrString & fs) { log(lvlInfo, fs); } virtual void warn(const std::string & msg); virtual void startActivity(ActivityId act, Verbosity lvl, ActivityType type, const std::string & s, const Fields & fields, ActivityId parent) { }; virtual void stopActivity(ActivityId act) { }; virtual void result(ActivityId act, ResultType type, const Fields & fields) { }; }; ActivityId getCurActivity(); void setCurActivity(const ActivityId activityId); struct Activity { Logger & logger; const ActivityId id; Activity(Logger & logger, Verbosity lvl, ActivityType type, const std::string & s = "", const Logger::Fields & fields = {}, ActivityId parent = getCurActivity()); Activity(Logger & logger, ActivityType type, const Logger::Fields & fields = {}, ActivityId parent = getCurActivity()) : Activity(logger, lvlError, type, "", fields, parent) { }; Activity(const Activity & act) = delete; ~Activity(); void progress(uint64_t done = 0, uint64_t expected = 0, uint64_t running = 0, uint64_t failed = 0) const { result(resProgress, done, expected, running, failed); } void setExpected(ActivityType type2, uint64_t expected) const { result(resSetExpected, type2, expected); } template void result(ResultType type, const Args & ... args) const { Logger::Fields fields; nop{(fields.emplace_back(Logger::Field(args)), 1)...}; result(type, fields); } void result(ResultType type, const Logger::Fields & fields) const { logger.result(id, type, fields); } friend class Logger; }; struct PushActivity { const ActivityId prevAct; PushActivity(ActivityId act) : prevAct(getCurActivity()) { setCurActivity(act); } ~PushActivity() { setCurActivity(prevAct); } }; extern Logger * logger; Logger * makeDefaultLogger(); Logger * makeJSONLogger(Logger & prevLogger); bool handleJSONLogMessage(const std::string & msg, const Activity & act, std::map & activities, bool trusted); extern Verbosity verbosity; /* suppress msgs > this */ /* Print a message if the current log level is at least the specified level. Note that this has to be implemented as a macro to ensure that the arguments are evaluated lazily. */ #define printMsg(level, args...) \ do { \ if (level <= nix::verbosity) { \ logger->log(level, fmt(args)); \ } \ } while (0) #define printError(args...) printMsg(lvlError, args) #define printInfo(args...) printMsg(lvlInfo, args) #define printTalkative(args...) printMsg(lvlTalkative, args) #define debug(args...) printMsg(lvlDebug, args) #define vomit(args...) printMsg(lvlVomit, args) template inline void warn(const std::string & fs, Args... args) { boost::format f(fs); nop{boost::io::detail::feed(f, args)...}; logger->warn(f.str()); } void warnOnce(bool & haveWarned, const FormatOrString & fs); void writeToStderr(const string & s); }