1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-07-01 04:18:00 +02:00

Allow separate JSON logging

If the NIX_LOG_FILE environment variable is set, Nix will write JSON
log messages to that file in addition to the regular logger (e.g. the
progress bar).
This commit is contained in:
Eelco Dolstra 2025-02-17 16:36:02 +01:00
parent 8ef94c1114
commit 1f702cdb01
5 changed files with 131 additions and 0 deletions

View file

@ -267,6 +267,24 @@ Logger * makeJSONLogger(Descriptor fd)
return new JSONLogger(fd); return new JSONLogger(fd);
} }
Logger * makeJSONLogger(const std::filesystem::path & path)
{
struct JSONFileLogger : JSONLogger {
AutoCloseFD fd;
JSONFileLogger(AutoCloseFD && fd)
: JSONLogger(fd.get())
, fd(std::move(fd))
{ }
};
auto fd{toDescriptor(open(path.c_str(), O_CREAT | O_APPEND | O_WRONLY, 0644))};
if (!fd)
throw SysError("opening log file '%1%'", path);
return new JSONFileLogger(std::move(fd));
}
static Logger::Fields getFields(nlohmann::json & json) static Logger::Fields getFields(nlohmann::json & json)
{ {
Logger::Fields fields; Logger::Fields fields;

View file

@ -5,6 +5,8 @@
#include "config.hh" #include "config.hh"
#include "file-descriptor.hh" #include "file-descriptor.hh"
#include <filesystem>
#include <nlohmann/json_fwd.hpp> #include <nlohmann/json_fwd.hpp>
namespace nix { namespace nix {
@ -185,8 +187,12 @@ extern Logger * logger;
Logger * makeSimpleLogger(bool printBuildLogs = true); Logger * makeSimpleLogger(bool printBuildLogs = true);
Logger * makeTeeLogger(std::vector<Logger *> loggers);
Logger * makeJSONLogger(Descriptor fd); Logger * makeJSONLogger(Descriptor fd);
Logger * makeJSONLogger(const std::filesystem::path & path);
/** /**
* @param source A noun phrase describing the source of the message, e.g. "the builder". * @param source A noun phrase describing the source of the message, e.g. "the builder".
*/ */

View file

@ -158,6 +158,7 @@ sources = files(
'strings.cc', 'strings.cc',
'suggestions.cc', 'suggestions.cc',
'tarfile.cc', 'tarfile.cc',
'tee-logger.cc',
'terminal.cc', 'terminal.cc',
'thread-pool.cc', 'thread-pool.cc',
'unix-domain-socket.cc', 'unix-domain-socket.cc',

102
src/libutil/tee-logger.cc Normal file
View file

@ -0,0 +1,102 @@
#include "logging.hh"
namespace nix {
struct TeeLogger : Logger
{
std::vector<Logger *> loggers;
TeeLogger(std::vector<Logger *> loggers)
: loggers(std::move(loggers))
{
}
void stop() override
{
for (auto & logger : loggers)
logger->stop();
};
void pause() override
{
for (auto & logger : loggers)
logger->pause();
};
void resume() override
{
for (auto & logger : loggers)
logger->resume();
};
void log(Verbosity lvl, std::string_view s) override
{
for (auto & logger : loggers)
logger->log(lvl, s);
}
void logEI(const ErrorInfo & ei) override
{
for (auto & logger : loggers)
logger->logEI(ei);
}
void startActivity(
ActivityId act,
Verbosity lvl,
ActivityType type,
const std::string & s,
const Fields & fields,
ActivityId parent) override
{
for (auto & logger : loggers)
logger->startActivity(act, lvl, type, s, fields, parent);
}
void stopActivity(ActivityId act) override
{
for (auto & logger : loggers)
logger->stopActivity(act);
}
void result(ActivityId act, ResultType type, const Fields & fields) override
{
for (auto & logger : loggers)
logger->result(act, type, fields);
}
void writeToStdout(std::string_view s) override
{
for (auto & logger : loggers) {
/* Let only the first logger write to stdout to avoid
duplication. This means that the first logger needs to
be the one managing stdout/stderr
(e.g. `ProgressBar`). */
logger->writeToStdout(s);
break;
}
}
std::optional<char> ask(std::string_view s) override
{
for (auto & logger : loggers) {
auto c = logger->ask(s);
if (c)
return c;
}
return std::nullopt;
}
void setPrintBuildLogs(bool printBuildLogs) override
{
for (auto & logger : loggers)
logger->setPrintBuildLogs(printBuildLogs);
}
};
Logger * makeTeeLogger(std::vector<Logger *> loggers)
{
return new TeeLogger(std::move(loggers));
}
}

View file

@ -485,6 +485,10 @@ void mainWrapped(int argc, char * * argv)
if (!args.helpRequested && !args.completions) throw; if (!args.helpRequested && !args.completions) throw;
} }
if (auto logFile = getEnv("NIX_LOG_FILE")) {
logger = makeTeeLogger({logger, makeJSONLogger(*logFile)});
}
if (args.helpRequested) { if (args.helpRequested) {
std::vector<std::string> subcommand; std::vector<std::string> subcommand;
MultiCommand * command = &args; MultiCommand * command = &args;