diff --git a/doc/man1/flux-job.rst b/doc/man1/flux-job.rst index 77334a7523a5..2d720734e427 100644 --- a/doc/man1/flux-job.rst +++ b/doc/man1/flux-job.rst @@ -23,6 +23,7 @@ SYNOPSIS | **flux** **job** **purge** [*-f*] [*--age-limit=FSD*] [*--num-limit=N*] [*ids...*] | **flux** **job** **info** [*--original*] [*--base*] *id* *key* | **flux** **job** **hostpids** [*OPTIONS*] *id* +| **flux** **job** **eventlog** [*-F*] [*-H*] [*-p PATH*] *id* DESCRIPTION @@ -502,6 +503,40 @@ Options: (a floating point value with optional suffix ``s`` for seconds, ``m`` for minutes, ``h`` for hours, or ``d`` for days). +eventlog +-------- + +.. program:: flux job eventlog + +:program:`flux job eventlog` prints a job eventlog. + +Options: + +.. option:: -f, --format=FORMAT + + Specify the output FORMAT (text or json). Default: text. + +.. option:: -T, --time-format=FORMAT + + Specify the timestamp FORMAT (raw, iso, or offset). Default: raw. + +.. option:: -H, --human + + Display human-readable output. + +.. option:: -L, --color=WHEN + + Colorize output when supported and WHEN condition is met + (always, never, or auto). Default: always. + +.. option:: -F, --follow + + Follow events until the job is inactive. + +.. option:: -p, --path=PATH + + Specify alternate eventlog name or path suffix, e.g. ``exec``, ``output``, + ``guest.exec.eventlog``. Default: ``eventlog``. RESOURCES ========= diff --git a/src/cmd/job/eventlog.c b/src/cmd/job/eventlog.c index 7819e9463e12..c8624f382ef8 100644 --- a/src/cmd/job/eventlog.c +++ b/src/cmd/job/eventlog.c @@ -23,6 +23,7 @@ #include "src/common/libeventlog/eventlog.h" #include "src/common/libeventlog/formatter.h" #include "ccan/str/str.h" +#include "ccan/array_size/array_size.h" #include "common.h" struct optparse_option eventlog_opts[] = { @@ -114,25 +115,31 @@ struct eventlog_ctx { struct path_shortname { const char *name; const char *path; + const char *end_event; }; -/* Set of shorthand names for common job eventlog paths: +/* Set of shorthand names and RFC-defined terminating event, + * if any, for common job eventlog paths: */ struct path_shortname eventlog_paths[] = { - { "exec", "guest.exec.eventlog" }, - { "output", "guest.output" }, - { "input", "guest.input" }, - { NULL, NULL }, + { "eventlog", "eventlog", "clean" }, + { "exec", "guest.exec.eventlog", "done" }, + { "output", "guest.output", NULL }, + { "input", "guest.input", NULL }, }; -const char *path_lookup (const char *name) +const char *path_lookup (const char *name, const char **end_event) { - const struct path_shortname *path = eventlog_paths; - while (path->name) { - if (streq (name, path->name)) - return path->path; - path++; + for (int i = 0; i < ARRAY_SIZE (eventlog_paths); i++) { + const struct path_shortname *entry = &eventlog_paths[i]; + if (streq (name, entry->name) || streq (name, entry->path)) { + if (end_event) + *end_event = entry->end_event; + return entry->path; + } } + if (end_event) + *end_event = NULL; return name; } @@ -194,7 +201,6 @@ int cmd_eventlog (optparse_t *p, int argc, char **argv) flux_t *h; int optindex = optparse_option_index (p); flux_future_t *f; - const char *topic = "job-info.lookup"; struct eventlog_ctx ctx = {0}; if (!(h = flux_open (NULL, 0))) @@ -210,7 +216,7 @@ int cmd_eventlog (optparse_t *p, int argc, char **argv) if (optparse_hasopt (p, "follow")) return wait_event_run (p, ctx.jobid, - "clean", + NULL, // will look up terminating event optparse_get_str (p, "path", "eventlog"), NULL, 1, @@ -220,14 +226,17 @@ int cmd_eventlog (optparse_t *p, int argc, char **argv) false); ctx.id = parse_jobid (ctx.jobid); - ctx.path = path_lookup (optparse_get_str (p, "path", "eventlog")); + ctx.path = path_lookup (optparse_get_str (p, "path", "eventlog"), NULL); ctx.p = p; if (!(ctx.evf = eventlog_formatter_create ())) log_err_exit ("eventlog_formatter_create"); formatter_parse_options (p, ctx.evf); - if (!(f = flux_rpc_pack (h, topic, FLUX_NODEID_ANY, 0, + if (!(f = flux_rpc_pack (h, + "job-info.lookup", + FLUX_NODEID_ANY, + 0, "{s:I s:[s] s:i}", "id", ctx.id, "keys", ctx.path, @@ -394,8 +403,9 @@ static int wait_event_run (optparse_t *p, ctx.p = p; ctx.jobid = jobid; ctx.id = parse_jobid (ctx.jobid); - ctx.wait_event = wait_event; - ctx.path = path_lookup (path); + ctx.path = path_lookup (path, &ctx.wait_event); + if (wait_event) + ctx.wait_event = wait_event; ctx.verbose = verbose; ctx.quiet = quiet; if ((ctx.count = count) <= 0) diff --git a/t/t2230-job-info-lookup.t b/t/t2230-job-info-lookup.t index ba35f49551b8..9181f9e93224 100755 --- a/t/t2230-job-info-lookup.t +++ b/t/t2230-job-info-lookup.t @@ -229,6 +229,18 @@ test_expect_success 'flux job eventlog -p fails on invalid path' ' jobid=$(submit_job) && test_must_fail flux job eventlog -p "foobar" $jobid ' +test_expect_success 'flux job eventlog -F -p eventlog terminates on clean' ' + jobid=$(submit_job) && + run_timeout 30 \ + flux job eventlog -p eventlog -F $jobid 2>follow_main.err && + test_cmp /dev/null follow_main.err +' +test_expect_success 'flux job eventlog -F -p exec terminates on done' ' + jobid=$(submit_job) && + run_timeout 30 \ + flux job eventlog -p exec -F $jobid 2>follow_exec.err && + test_cmp /dev/null follow_exec.err +' # submit job in separate test to avoid using the form: # jobid=$(flux submit ..) && flux job eventlog $jobid ... & # which will pick up the wrong $jobid since the pipeline is placed into