diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 653aa0d792..4001123a96 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -2909,8 +2909,8 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX LogExtraFields= Configures additional log metadata fields to include in all log records generated by - processes associated with this unit. This setting takes one or more journal field assignments in the - format FIELD=VALUE separated by whitespace. See + processes associated with this unit, including systemd. This setting takes one or more journal field + assignments in the format FIELD=VALUE separated by whitespace. See systemd.journal-fields7 for details on the journal field concept. Even though the underlying journal implementation permits binary field values, this setting accepts only valid UTF-8 values. To include space characters in a diff --git a/src/basic/log.c b/src/basic/log.c index 40602d5dda..8b973f9e0a 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -76,6 +76,8 @@ typedef struct LogContext { /* Depending on which destructor is used (log_context_free() or log_context_detach()) the memory * referenced by this is freed or not */ char **fields; + struct iovec *input_iovec; + size_t n_input_iovec; bool owned; LIST_FIELDS(struct LogContext, ll); } LogContext; @@ -613,7 +615,7 @@ static void log_do_context(struct iovec *iovec, size_t iovec_len, size_t *n) { assert(iovec); assert(n); - LIST_FOREACH(ll, c, _log_context) + LIST_FOREACH(ll, c, _log_context) { STRV_FOREACH(s, c->fields) { if (*n + 2 >= iovec_len) return; @@ -621,6 +623,15 @@ static void log_do_context(struct iovec *iovec, size_t iovec_len, size_t *n) { iovec[(*n)++] = IOVEC_MAKE_STRING(*s); iovec[(*n)++] = IOVEC_MAKE_STRING("\n"); } + + for (size_t i = 0; i < c->n_input_iovec; i++) { + if (*n + 2 >= iovec_len) + return; + + iovec[(*n)++] = c->input_iovec[i]; + iovec[(*n)++] = IOVEC_MAKE_STRING("\n"); + } + } } static int write_to_journal( @@ -1555,6 +1566,7 @@ LogContext* log_context_attach(LogContext *c) { assert(c); _log_context_num_fields += strv_length(c->fields); + _log_context_num_fields += c->n_input_iovec; return LIST_PREPEND(ll, _log_context, c); } @@ -1563,8 +1575,9 @@ LogContext* log_context_detach(LogContext *c) { if (!c) return NULL; - assert(_log_context_num_fields >= strv_length(c->fields)); + assert(_log_context_num_fields >= strv_length(c->fields) + c->n_input_iovec); _log_context_num_fields -= strv_length(c->fields); + _log_context_num_fields -= c->n_input_iovec; LIST_REMOVE(ll, _log_context, c); return NULL; @@ -1583,14 +1596,33 @@ LogContext* log_context_new(char **fields, bool owned) { return log_context_attach(c); } +LogContext* log_context_newv(struct iovec *input_iovec, size_t n_input_iovec, bool owned) { + if (!input_iovec || n_input_iovec == 0) + return NULL; /* Nothing to do */ + + LogContext *c = new(LogContext, 1); + if (!c) + return NULL; + + *c = (LogContext) { + .input_iovec = input_iovec, + .n_input_iovec = n_input_iovec, + .owned = owned, + }; + + return log_context_attach(c); +} + LogContext* log_context_free(LogContext *c) { if (!c) return NULL; log_context_detach(c); - if (c->owned) + if (c->owned) { strv_free(c->fields); + iovec_array_free(c->input_iovec, c->n_input_iovec); + } return mfree(c); } @@ -1603,6 +1635,14 @@ LogContext* log_context_new_consume(char **fields) { return c; } +LogContext* log_context_new_consumev(struct iovec *input_iovec, size_t n_input_iovec) { + LogContext *c = log_context_newv(input_iovec, n_input_iovec, /*owned=*/ true); + if (!c) + iovec_array_free(input_iovec, n_input_iovec); + + return c; +} + size_t log_context_num_contexts(void) { size_t n = 0; diff --git a/src/basic/log.h b/src/basic/log.h index 0d4956e6b5..c4ac73e27b 100644 --- a/src/basic/log.h +++ b/src/basic/log.h @@ -461,10 +461,12 @@ LogContext* log_context_attach(LogContext *c); LogContext* log_context_detach(LogContext *c); LogContext* log_context_new(char **fields, bool owned); +LogContext* log_context_newv(struct iovec *input_iovec, size_t n_input_iovec, bool owned); LogContext* log_context_free(LogContext *c); -/* Same as log_context_new(), but frees the given fields strv on failure. */ +/* Same as log_context_new(), but frees the given fields strv/iovec on failure. */ LogContext* log_context_new_consume(char **fields); +LogContext* log_context_new_consumev(struct iovec *input_iovec, size_t n_input_iovec); /* Returns the number of attached log context objects. */ size_t log_context_num_contexts(void); @@ -486,8 +488,15 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(LogContext*, log_context_free); #define LOG_CONTEXT_PUSH_STRV(strv) \ _LOG_CONTEXT_PUSH_STRV(strv, UNIQ_T(c, UNIQ)) -/* LOG_CONTEXT_CONSUME_STR()/LOG_CONTEXT_CONSUME_STRV() are identical to - * LOG_CONTEXT_PUSH_STR()/LOG_CONTEXT_PUSH_STRV() except they take ownership of the given str/strv argument. +#define _LOG_CONTEXT_PUSH_IOV(input_iovec, n_input_iovec, c) \ + _unused_ _cleanup_(log_context_freep) LogContext *c = log_context_newv(input_iovec, n_input_iovec, /*owned=*/ false); + +#define LOG_CONTEXT_PUSH_IOV(input_iovec, n_input_iovec) \ + _LOG_CONTEXT_PUSH_IOV(input_iovec, n_input_iovec, UNIQ_T(c, UNIQ)) + +/* LOG_CONTEXT_CONSUME_STR()/LOG_CONTEXT_CONSUME_STRV()/LOG_CONTEXT_CONSUME_IOV() are identical to + * LOG_CONTEXT_PUSH_STR()/LOG_CONTEXT_PUSH_STRV()/LOG_CONTEXT_PUSH_IOV() except they take ownership of the + * given str/strv argument. */ #define _LOG_CONTEXT_CONSUME_STR(s, c, strv) \ @@ -504,3 +513,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(LogContext*, log_context_free); #define LOG_CONTEXT_CONSUME_STRV(strv) \ _LOG_CONTEXT_CONSUME_STRV(strv, UNIQ_T(c, UNIQ)) + +#define _LOG_CONTEXT_CONSUME_IOV(input_iovec, n_input_iovec, c) \ + _unused_ _cleanup_(log_context_freep) LogContext *c = log_context_new_consumev(input_iovec, n_input_iovec); + +#define LOG_CONTEXT_CONSUME_IOV(input_iovec, n_input_iovec) \ + _LOG_CONTEXT_CONSUME_IOV(input_iovec, n_input_iovec, UNIQ_T(c, UNIQ)) diff --git a/src/core/unit.h b/src/core/unit.h index 3f8377fbf6..d5a6d595e2 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -1078,7 +1078,13 @@ Condition *unit_find_failed_condition(Unit *u); ({ \ const Unit *_u = (unit); \ const int _l = (level); \ - (log_get_max_level() < LOG_PRI(_l) || (_u && !unit_log_level_test(_u, _l))) ? -ERRNO_VALUE(error) : \ + bool _do_log = !(log_get_max_level() < LOG_PRI(_l) || \ + (_u && !unit_log_level_test(_u, _l))); \ + const ExecContext *_c = _do_log && _u ? \ + unit_get_exec_context(_u) : NULL; \ + LOG_CONTEXT_PUSH_IOV(_c ? _c->log_extra_fields : NULL, \ + _c ? _c->n_log_extra_fields : 0); \ + !_do_log ? -ERRNO_VALUE(error) : \ _u ? log_object_internal(_l, error, PROJECT_FILE, __LINE__, __func__, _u->manager->unit_log_field, _u->id, _u->manager->invocation_log_field, _u->invocation_id_string, ##__VA_ARGS__) : \ log_internal(_l, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \ }) @@ -1116,7 +1122,12 @@ Condition *unit_find_failed_condition(Unit *u); ({ \ const Unit *_u = (unit); \ const int _l = (level); \ - unit_log_level_test(_u, _l) ? \ + bool _do_log = unit_log_level_test(_u, _l); \ + const ExecContext *_c = _do_log && _u ? \ + unit_get_exec_context(_u) : NULL; \ + LOG_CONTEXT_PUSH_IOV(_c ? _c->log_extra_fields : NULL, \ + _c ? _c->n_log_extra_fields : 0); \ + _do_log ? \ log_struct_errno(_l, error, __VA_ARGS__, LOG_UNIT_ID(_u)) : \ -ERRNO_VALUE(error); \ }) @@ -1125,8 +1136,14 @@ Condition *unit_find_failed_condition(Unit *u); #define log_unit_struct_iovec_errno(unit, level, error, iovec, n_iovec) \ ({ \ + const Unit *_u = (unit); \ const int _l = (level); \ - unit_log_level_test(unit, _l) ? \ + bool _do_log = unit_log_level_test(_u, _l); \ + const ExecContext *_c = _do_log && _u ? \ + unit_get_exec_context(_u) : NULL; \ + LOG_CONTEXT_PUSH_IOV(_c ? _c->log_extra_fields : NULL, \ + _c ? _c->n_log_extra_fields : 0); \ + _do_log ? \ log_struct_iovec_errno(_l, error, iovec, n_iovec) : \ -ERRNO_VALUE(error); \ }) diff --git a/src/test/test-log.c b/src/test/test-log.c index 30c536d5c7..68b5cb5092 100644 --- a/src/test/test-log.c +++ b/src/test/test-log.c @@ -4,6 +4,7 @@ #include #include "format-util.h" +#include "io-util.h" #include "log.h" #include "process-util.h" #include "string-util.h" @@ -135,6 +136,28 @@ static void test_log_context(void) { assert_se(log_context_num_fields() == 2); } + { + /* Test that everything still works with a mixed strv and iov. */ + struct iovec iov[] = { + IOVEC_MAKE_STRING("ABC=def"), + IOVEC_MAKE_STRING("GHI=jkl"), + }; + _cleanup_free_ struct iovec_wrapper *iovw = iovw_new(); + assert_se(iovw); + assert_se(iovw_consume(iovw, strdup("MNO=pqr"), STRLEN("MNO=pqr") + 1) == 0); + + LOG_CONTEXT_PUSH_IOV(iov, ELEMENTSOF(iov)); + LOG_CONTEXT_CONSUME_IOV(iovw->iovec, iovw->count); + LOG_CONTEXT_PUSH("STU=vwx"); + + assert_se(log_context_num_contexts() == 3); + assert_se(log_context_num_fields() == 4); + + test_log_struct(); + test_long_lines(); + test_log_syntax(); + } + assert_se(log_context_num_contexts() == 0); assert_se(log_context_num_fields() == 0); }