#pragma once ///@file #include "types.hh" #include "error.hh" #include "config.hh" #include namespace nix { typedef enum { actUnknown = 0, actCopyPath = 100, actFileTransfer = 101, actRealise = 102, actCopyPaths = 103, actBuilds = 104, actBuild = 105, actOptimiseStore = 106, actVerifyPaths = 107, actSubstitute = 108, actQueryPathInfo = 109, actPostBuildHook = 110, actBuildWaiting = 111, } ActivityType; typedef enum { resFileLinked = 100, resBuildLogLine = 101, resUntrustedPath = 102, resCorruptedPath = 103, resSetPhase = 104, resProgress = 105, resSetExpected = 106, resPostBuildLogLine = 107, } ResultType; typedef uint64_t ActivityId; struct LoggerSettings : Config { Setting showTrace{ this, false, "show-trace", R"( Whether Nix should print out a stack trace in case of Nix expression evaluation errors. )"}; }; extern LoggerSettings loggerSettings; 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 stop() { }; virtual void pause() { }; virtual void resume() { }; // Whether the logger prints the whole build log virtual bool isVerbose() { return false; } virtual void log(Verbosity lvl, std::string_view s) = 0; void log(std::string_view s) { log(lvlInfo, s); } virtual void logEI(const ErrorInfo & ei) = 0; void logEI(Verbosity lvl, ErrorInfo ei) { ei.level = lvl; logEI(ei); } 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) { }; virtual void writeToStdout(std::string_view s); template inline void cout(const Args & ... args) { writeToStdout(fmt(args...)); } virtual std::optional ask(std::string_view s) { return {}; } virtual void setPrintBuildLogs(bool printBuildLogs) { } }; 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 * makeSimpleLogger(bool printBuildLogs = true); Logger * makeJSONLogger(Logger & prevLogger); std::optional parseJSONMessage(const std::string & msg); bool handleJSONLogMessage(nlohmann::json & json, const Activity & act, std::map & activities, bool trusted); bool handleJSONLogMessage(const std::string & msg, const Activity & act, std::map & activities, bool trusted); /** * suppress msgs > this */ extern Verbosity verbosity; /** * Print a message with the standard ErrorInfo format. * In general, use these 'log' macros for reporting problems that may require user * intervention or that need more explanation. Use the 'print' macros for more * lightweight status messages. */ #define logErrorInfo(level, errorInfo...) \ do { \ if ((level) <= nix::verbosity) { \ logger->logEI((level), errorInfo); \ } \ } while (0) #define logError(errorInfo...) logErrorInfo(lvlError, errorInfo) #define logWarning(errorInfo...) logErrorInfo(lvlWarn, errorInfo) /** * Print a string 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 printMsgUsing(loggerParam, level, args...) \ do { \ auto __lvl = level; \ if (__lvl <= nix::verbosity) { \ loggerParam->log(__lvl, fmt(args)); \ } \ } while (0) #define printMsg(level, args...) printMsgUsing(logger, level, args) #define printError(args...) printMsg(lvlError, args) #define notice(args...) printMsg(lvlNotice, 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) /** * if verbosity >= lvlWarn, print a message with a yellow 'warning:' prefix. */ template inline void warn(const std::string & fs, const Args & ... args) { boost::format f(fs); formatHelper(f, args...); logger->warn(f.str()); } #define warnOnce(haveWarned, args...) \ if (!haveWarned) { \ haveWarned = true; \ warn(args); \ } void writeToStderr(std::string_view s); }