Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an option to print the logs in a machine-readable format #3073

Merged
merged 8 commits into from
Jun 11, 2020

Conversation

thufschmitt
Copy link
Member

Add a --log-format option which can be used to print the logs in json (pretty similar to the json logger used internally) so that external tools can easily consume it.

This required moving things around a bit because nix 2.0-style commands where overriding the logging mechanism to display the status bar (which made the --log-format option unusable). As a nice side effect, nix 1.0-style commands can now use --log-format bar to get the status bar

src/nix/main.cc Outdated
@@ -79,8 +78,8 @@ struct NixArgs : virtual MultiCommand, virtual MixCommonArgs
mkFlag()
.longName("print-build-logs")
.shortName('L')
.description("print full build logs on stderr")
.set(&printBuildLogs, true);
.description("print full build logs on stderr. DEPRECATED, use '--log-format bar-with-logs' instead")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This message implies that -L is also deprecated, which should not be the case.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. I've removed it altogether. Tell me if you still want a depreciation warning on --print-build-logs

logFormatJSON,
logFormatBar,
logFormatBarWithLogs,
} LogFormat;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move most of this to src/nix? That way src/libutil only contains the abstract Logger class and the implementations are in src/nix.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or alternatively to src/libmain.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've moved most of it to libmain. The implementation of SimpleLogger is still in that file, and JSONLogger is also still in libutil (because it's strongly tied to handleJSONLogMessage and that one is used in libstore). Does that look good?


nlohmann::json jsonActivityType(ActivityType type) override
{
switch (type) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's more maintainable to log the activity type and result type as a numerical value.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an annoying tradeoff to make because magic numbers are definitely not user-friendly, but I agree these custom-print functions will probably cause some issue at some point (unless there's a way to auto-generate it?).

I've removed the custom json logger in 1e893d4, so we're back to numerical values.

@thufschmitt
Copy link
Member Author

I'm having some second-though on that PR: The internal json isn't really nice to work with from outside of Nix, so it would be nicer to first settle on a nicer format before being tied to a non-so-nice representation

@zimbatm
Copy link
Member

zimbatm commented Sep 3, 2019

What makes it not so nice to use?

@thufschmitt
Copy link
Member Author

There's a number of small things which either make it non-intuitive or require some non-trivial logic to get some useful information from it. In particular I noted:

  • The format of the messages isn't really standardized between the different kind of messages (for instance most messages have a text field for storing some free-form message, except the msg message type which has a msg field for that. Also, most messages have a level field, but not all)
  • Some information is packed into the fields field which contains a list of free-form elements, and it's up to the user to guess what each element corresponds to
  • Amongst the most useful messages types are activityStart and activityStop which indicate when each phase of the internal pipeline (instantiation, substitution, hooks) start and stop. But the activityStop messages only contain an id which maps them to a previous activityStart, meaning that to make sense of it a client must keep internally a map of all the started activities to know which activity has ended, and retrieve from that the type of activity, its various parameters, etc…
  • The activity types are magic numerical constants (so quite hard to interpret), although this has been discussed in Add an option to print the logs in a machine-readable format #3073 (comment) and is likely to stay that way

(note that I'm not at all saying that it's a bad format per se, it seems to work quite well inside of Nix, but it's probably not a good format for an external consumption by a client which doesn't know the internals of Nix)

@thufschmitt
Copy link
Member Author

I'm lacking of time to work on this right now. @edolstra What do you think of renaming the json log-format to internal-json merge this as it-is? That way we already have an (explicitely unstable and not user-friendly) machine-readable log, and we keep the possibility to add a nicer json log-format later.

@edolstra
Copy link
Member

@regnat Yes, I think this looks good as-is.

logFormatRaw,
logFormatJSON,
logFormatBar,
logFormatBarWithLogs,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is bar-with-logs? Is it the same as nix --print-build-logs / -L?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

case logFormatBarWithLogs:
return createProgressBar(true);
default:
throw Error(format("Invalid log format '%i'") % logFormat);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be written as

        throw Error("invalid log format '%i'", logFormat);

Also note that exception messages are not capitalized.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 5bc2744

zimbatm added a commit to tweag/nix that referenced this pull request Sep 26, 2019
@zimbatm
Copy link
Member

zimbatm commented Sep 26, 2019

and now with documentation. @edolstra is there anything else you would like to see happening for this PR?

@thufschmitt
Copy link
Member Author

Just noticed that this somehow cause builtins:buildEnv to fail with "Bad file descriptor" when using the daemon unless we pass at least four -v flags to nix. I'll give it a look

@zimbatm
Copy link
Member

zimbatm commented Oct 9, 2019

@regnat I don't think the sandboxed builder should emit any logs, this is the real issue IMO.

@zimbatm
Copy link
Member

zimbatm commented Oct 9, 2019

stracing the nix-daemon:

[pid 12635] write(4, "gmlo\0\0\0\0P\0\0\0\0\0\0\0@nix {\"action\":\""..., 96) = -1 EBADF (Bad file descriptor)
[pid 12635] write(2, "error: writing to file: Bad file"..., 44) = 44
[pid 12630] read(10, "error: writing to file: Bad file"..., 4096) = 44
[pid 12630] write(4, "gmlo\0\0\0\0,\0\0\0\0\0\0\0error: writing t"..., 64) = 64
[pid 12630] write(4, "TLSR\0\0\0\0\t\0\0\0\37)\0\0e\0\0\0\0\0\0\0\1\0\0\0\0\0\0\0"..., 96) = 96
[pid 12630] select(11, [10], NULL, NULL, NULL <unfinished ...>
[pid 12635] +++ exited with 1 +++
(edited)

pid 12635 is a build sandbox

@zimbatm
Copy link
Member

zimbatm commented Oct 9, 2019

when the sandbox starts, it closes all the existing file descriptors except 1 and 2 (and 12 twice), so that's why writing to FD 4 is not possible.

[pid 12635] close(12)                   = 0
[pid 12635] close(3)                    = 0
[pid 12635] close(4)                    = 0
[pid 12635] close(5)                    = 0
[pid 12635] close(6)                    = 0
[pid 12635] close(7)                    = 0
[pid 12635] close(8)                    = 0
[pid 12635] close(9)                    = 0
[pid 12635] close(10)                   = 0
[pid 12635] close(11)                   = 0
[pid 12635] close(12)                   = -1 EBADF (Bad file descriptor)

@edolstra
Copy link
Member

edolstra commented Oct 9, 2019

This is probably caused by logger not being reset in the child process. startProcess() does this:

        if (!options.allowVfork)
            logger = makeDefaultLogger();

which means that the child has the wrong logger if allowVfork = true.

thufschmitt pushed a commit to tweag/nix that referenced this pull request Dec 17, 2019
@thufschmitt
Copy link
Member Author

@edolstra I've fixed the file descriptor issue (was apparently due to vfork indeed) and rebased on top of master − I've also found why the nix-env failure hadn't been caught in the tests.

@@ -153,7 +164,7 @@ class ProgressBar : public Logger
state->activitiesByType[type].its.emplace(act, i);

if (type == actBuild) {
auto name = storePathToName(getS(fields, 0));
auto name = getName(fields, 4);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This no longer abbreviates the name, i.e. the progress bar now looks like this:

[1/0/2 built, 16.7 MiB DL] building /nix/store/g29xgw3fy960ghskj45x4x1mjv6jv3l2-nix-tarball-2.4pre7196_d94cc07 (autoconfPhase): autoreconf: running: ...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, this was happening when using an older daemon (because the messages sent back by the daemon have been extended to include the (abbreviated) derivation name, but there's a fallback code for old daemons that don't do it, but it was missing a call to storePathToName).
Should be good now

thufschmitt added a commit to tweag/nix that referenced this pull request Dec 17, 2019
@@ -17,20 +16,32 @@ static std::string getS(const std::vector<Logger::Field> & fields, size_t n)
return fields[n].s;
}

static std::string storePathToName(std::string path)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use the original version? std::string_view is cheaper than std::string.

@Profpatsch
Copy link
Member

Might I ping this? :)

@thufschmitt thufschmitt force-pushed the machine-logs branch 2 times, most recently from 73851d6 to aa251ad Compare June 8, 2020 07:30
thufschmitt and others added 3 commits June 8, 2020 09:31
Add a new `--log-format` cli argument to change the format of the logs.
The possible values are
- raw (the default one for old-style commands)
- bar (the default one for new-style commands)
- bar-with-logs (equivalent to `--print-build-logs`)
- internal-json (the internal machine-readable json format)
Make the printing of the build logs systematically go through the
logger, and replicate the behavior of `no-build-output` by having two
different loggers (one that prints the build logs and one that doesn't)
@thufschmitt
Copy link
Member Author

I've been reminded of this last week, so I rebased this on top of the latest master and simplified it as much as I could (well, nearly rewritten it from scratch actually 🤔 ), let me know what you think

@@ -0,0 +1,51 @@
#include "loggers.hh"
#include "../nix/progress-bar.hh"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes libmain depend on nix, which is a layering violation. It means you can't link against libmain anymore.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mh that's annoying 🤔

I moved progress-bar to libmain to fix that, but it also required moving paths to libutil to prevent libmain from depending on libexpr. Not sure if that's a great idea

edolstra and others added 5 commits June 8, 2020 13:24
Needed so that we can include it as a logger in loggers.cc without
adding a dependency on nix

This also requires moving names.hh to libutil to prevent a circular
dependency between libmain and libexpr
@edolstra edolstra merged commit ac4d43a into NixOS:master Jun 11, 2020
@edolstra
Copy link
Member

Merged, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants