From c2503e359a3a03b73acce0a92d27b1b8f54ff92c Mon Sep 17 00:00:00 2001 From: Ryan Hendrickson Date: Fri, 30 Apr 2021 12:47:10 -0400 Subject: [PATCH] core: apply LogLevelMax to messages about units This commit applies the filtering imposed by LogLevelMax on a unit's processes to messages logged by PID1 about the unit as well. The target use case for this feature is a service that runs on a timer many times an hour, where the system administrator decides that writing a generic success message to the journal every few minutes or seconds adds no diagnostic value and isn't worth the clutter or disk I/O. --- man/systemd.exec.xml | 3 +- src/core/cgroup.c | 18 ++-- src/core/execute.c | 64 ++++++------- src/core/job.c | 77 +++++++-------- src/core/service.c | 11 +-- src/core/unit.c | 95 ++++++++++--------- src/core/unit.h | 37 +++++++- .../testsuite-04.units/silent-success.service | 6 ++ test/units/testsuite-04.sh | 5 + 9 files changed, 178 insertions(+), 138 deletions(-) create mode 100644 test/testsuite-04.units/silent-success.service diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 2aefb4eb255..b2fbb3b6441 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -2653,7 +2653,8 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy this option to configure the logging system to drop log messages of a specific service above the specified level. For example, set LogLevelMax= in order to turn off debug logging of a particularly chatty unit. Note that the configured level is applied to any log messages written by any - of the processes belonging to this unit, sent via any supported logging protocol. The filtering is applied + of the processes belonging to this unit, as well as any log messages written by the system manager process + (PID 1) in reference to this unit, sent via any supported logging protocol. The filtering is applied early in the logging pipeline, before any kind of further processing is done. Moreover, messages which pass through this filter successfully might still be dropped by filters applied at a later stage in the logging subsystem. For example, MaxLevelStore= configured in diff --git a/src/core/cgroup.c b/src/core/cgroup.c index d541c16f5fe..9fbf64b7bee 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -2923,11 +2923,10 @@ int unit_check_oomd_kill(Unit *u) { return 0; if (n > 0) - log_struct(LOG_NOTICE, - "MESSAGE_ID=" SD_MESSAGE_UNIT_OOMD_KILL_STR, - LOG_UNIT_ID(u), - LOG_UNIT_INVOCATION_ID(u), - LOG_UNIT_MESSAGE(u, "systemd-oomd killed %"PRIu64" process(es) in this unit.", n)); + log_unit_struct(u, LOG_NOTICE, + "MESSAGE_ID=" SD_MESSAGE_UNIT_OOMD_KILL_STR, + LOG_UNIT_INVOCATION_ID(u), + LOG_UNIT_MESSAGE(u, "systemd-oomd killed %"PRIu64" process(es) in this unit.", n)); return 1; } @@ -2955,11 +2954,10 @@ int unit_check_oom(Unit *u) { if (!increased) return 0; - log_struct(LOG_NOTICE, - "MESSAGE_ID=" SD_MESSAGE_UNIT_OUT_OF_MEMORY_STR, - LOG_UNIT_ID(u), - LOG_UNIT_INVOCATION_ID(u), - LOG_UNIT_MESSAGE(u, "A process of this unit has been killed by the OOM killer.")); + log_unit_struct(u, LOG_NOTICE, + "MESSAGE_ID=" SD_MESSAGE_UNIT_OUT_OF_MEMORY_STR, + LOG_UNIT_INVOCATION_ID(u), + LOG_UNIT_MESSAGE(u, "A process of this unit has been killed by the OOM killer.")); if (UNIT_VTABLE(u)->notify_cgroup_oom) UNIT_VTABLE(u)->notify_cgroup_oom(u); diff --git a/src/core/execute.c b/src/core/execute.c index 9f54ad424b1..482ae4875b1 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -4318,24 +4318,23 @@ static int exec_child( r = find_executable_full(command->path, false, &executable, &executable_fd); if (r < 0) { if (r != -ENOMEM && (command->flags & EXEC_COMMAND_IGNORE_FAILURE)) { - log_struct_errno(LOG_INFO, r, - "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR, - LOG_UNIT_ID(unit), - LOG_UNIT_INVOCATION_ID(unit), - LOG_UNIT_MESSAGE(unit, "Executable %s missing, skipping: %m", - command->path), - "EXECUTABLE=%s", command->path); + log_unit_struct_errno(unit, LOG_INFO, r, + "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR, + LOG_UNIT_INVOCATION_ID(unit), + LOG_UNIT_MESSAGE(unit, "Executable %s missing, skipping: %m", + command->path), + "EXECUTABLE=%s", command->path); return 0; } *exit_status = EXIT_EXEC; - return log_struct_errno(LOG_INFO, r, - "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR, - LOG_UNIT_ID(unit), - LOG_UNIT_INVOCATION_ID(unit), - LOG_UNIT_MESSAGE(unit, "Failed to locate executable %s: %m", - command->path), - "EXECUTABLE=%s", command->path); + + return log_unit_struct_errno(unit, LOG_INFO, r, + "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR, + LOG_UNIT_INVOCATION_ID(unit), + LOG_UNIT_MESSAGE(unit, "Failed to locate executable %s: %m", + command->path), + "EXECUTABLE=%s", command->path); } r = add_shifted_fd(keep_fds, ELEMENTSOF(keep_fds), &n_keep_fds, executable_fd, &executable_fd); @@ -4643,11 +4642,10 @@ static int exec_child( line = exec_command_line(final_argv); if (line) - log_struct(LOG_DEBUG, - "EXECUTABLE=%s", executable, - LOG_UNIT_MESSAGE(unit, "Executing: %s", line), - LOG_UNIT_ID(unit), - LOG_UNIT_INVOCATION_ID(unit)); + log_unit_struct(unit, LOG_DEBUG, + "EXECUTABLE=%s", executable, + LOG_UNIT_MESSAGE(unit, "Executing: %s", line), + LOG_UNIT_INVOCATION_ID(unit)); } if (exec_fd >= 0) { @@ -4739,14 +4737,13 @@ int exec_spawn(Unit *unit, and, until the next SELinux policy changes, we save further reloads in future children. */ mac_selinux_maybe_reload(); - log_struct(LOG_DEBUG, - LOG_UNIT_MESSAGE(unit, "About to execute %s", line), - "EXECUTABLE=%s", command->path, /* We won't know the real executable path until we create - the mount namespace in the child, but we want to log - from the parent, so we need to use the (possibly - inaccurate) path here. */ - LOG_UNIT_ID(unit), - LOG_UNIT_INVOCATION_ID(unit)); + log_unit_struct(unit, LOG_DEBUG, + LOG_UNIT_MESSAGE(unit, "About to execute %s", line), + "EXECUTABLE=%s", command->path, /* We won't know the real executable path until we create + the mount namespace in the child, but we want to log + from the parent, so we need to use the (possibly + inaccurate) path here. */ + LOG_UNIT_INVOCATION_ID(unit)); if (params->cgroup_path) { r = exec_parameters_get_cgroup_path(params, &subcgroup_path); @@ -4790,13 +4787,12 @@ int exec_spawn(Unit *unit, exit_status_to_string(exit_status, EXIT_STATUS_LIBC | EXIT_STATUS_SYSTEMD); - log_struct_errno(LOG_ERR, r, - "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR, - LOG_UNIT_ID(unit), - LOG_UNIT_INVOCATION_ID(unit), - LOG_UNIT_MESSAGE(unit, "Failed at step %s spawning %s: %m", - status, command->path), - "EXECUTABLE=%s", command->path); + log_unit_struct_errno(unit, LOG_ERR, r, + "MESSAGE_ID=" SD_MESSAGE_SPAWN_FAILED_STR, + LOG_UNIT_INVOCATION_ID(unit), + LOG_UNIT_MESSAGE(unit, "Failed at step %s spawning %s: %m", + status, command->path), + "EXECUTABLE=%s", command->path); } _exit(exit_status); diff --git a/src/core/job.c b/src/core/job.c index d313ebdb8ec..cfc12096159 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -557,6 +557,9 @@ static void job_log_begin_status_message(Unit *u, uint32_t job_id, JobType t) { if (!IN_SET(t, JOB_START, JOB_STOP, JOB_RELOAD)) return; + if (!unit_log_level_test(u, LOG_INFO)) + return; + if (log_on_console()) /* Skip this if it would only go on the console anyway */ return; @@ -578,13 +581,12 @@ static void job_log_begin_status_message(Unit *u, uint32_t job_id, JobType t) { * which is supposed the highest level, friendliest output * possible, which means we should avoid the low-level unit * name. */ - log_struct(LOG_INFO, - LOG_MESSAGE("%s", buf), - "JOB_ID=%" PRIu32, job_id, - "JOB_TYPE=%s", job_type_to_string(t), - LOG_UNIT_ID(u), - LOG_UNIT_INVOCATION_ID(u), - mid); + log_unit_struct(u, LOG_INFO, + LOG_MESSAGE("%s", buf), + "JOB_ID=%" PRIu32, job_id, + "JOB_TYPE=%s", job_type_to_string(t), + LOG_UNIT_INVOCATION_ID(u), + mid); } static void job_emit_begin_status_message(Unit *u, uint32_t job_id, JobType t) { @@ -879,18 +881,20 @@ static void job_log_done_status_message(Unit *u, uint32_t job_id, JobType t, Job /* Show condition check message if the job did not actually do anything due to failed condition. */ if (t == JOB_START && result == JOB_DONE && !u->condition_result) { - log_struct(LOG_INFO, - "MESSAGE=Condition check resulted in %s being skipped.", unit_status_string(u), - "JOB_ID=%" PRIu32, job_id, - "JOB_TYPE=%s", job_type_to_string(t), - "JOB_RESULT=%s", job_result_to_string(result), - LOG_UNIT_ID(u), - LOG_UNIT_INVOCATION_ID(u), - "MESSAGE_ID=" SD_MESSAGE_UNIT_STARTED_STR); + log_unit_struct(u, LOG_INFO, + "MESSAGE=Condition check resulted in %s being skipped.", unit_status_string(u), + "JOB_ID=%" PRIu32, job_id, + "JOB_TYPE=%s", job_type_to_string(t), + "JOB_RESULT=%s", job_result_to_string(result), + LOG_UNIT_INVOCATION_ID(u), + "MESSAGE_ID=" SD_MESSAGE_UNIT_STARTED_STR); return; } + if (!unit_log_level_test(u, job_result_log_level[result])) + return; + format = job_get_done_status_message_format(u, t, result); if (!format) return; @@ -922,24 +926,22 @@ static void job_log_done_status_message(Unit *u, uint32_t job_id, JobType t, Job break; default: - log_struct(job_result_log_level[result], - LOG_MESSAGE("%s", buf), - "JOB_ID=%" PRIu32, job_id, - "JOB_TYPE=%s", job_type_to_string(t), - "JOB_RESULT=%s", job_result_to_string(result), - LOG_UNIT_ID(u), - LOG_UNIT_INVOCATION_ID(u)); + log_unit_struct(u, job_result_log_level[result], + LOG_MESSAGE("%s", buf), + "JOB_ID=%" PRIu32, job_id, + "JOB_TYPE=%s", job_type_to_string(t), + "JOB_RESULT=%s", job_result_to_string(result), + LOG_UNIT_INVOCATION_ID(u)); return; } - log_struct(job_result_log_level[result], - LOG_MESSAGE("%s", buf), - "JOB_ID=%" PRIu32, job_id, - "JOB_TYPE=%s", job_type_to_string(t), - "JOB_RESULT=%s", job_result_to_string(result), - LOG_UNIT_ID(u), - LOG_UNIT_INVOCATION_ID(u), - mid); + log_unit_struct(u, job_result_log_level[result], + LOG_MESSAGE("%s", buf), + "JOB_ID=%" PRIu32, job_id, + "JOB_TYPE=%s", job_type_to_string(t), + "JOB_RESULT=%s", job_result_to_string(result), + LOG_UNIT_INVOCATION_ID(u), + mid); } static void job_emit_done_status_message(Unit *u, uint32_t job_id, JobType t, JobResult result) { @@ -1048,14 +1050,13 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr * this context. And JOB_FAILURE is already handled by the * unit itself. */ if (IN_SET(result, JOB_TIMEOUT, JOB_DEPENDENCY)) { - log_struct(LOG_NOTICE, - "JOB_TYPE=%s", job_type_to_string(t), - "JOB_RESULT=%s", job_result_to_string(result), - LOG_UNIT_ID(u), - LOG_UNIT_MESSAGE(u, "Job %s/%s failed with result '%s'.", - u->id, - job_type_to_string(t), - job_result_to_string(result))); + log_unit_struct(u, LOG_NOTICE, + "JOB_TYPE=%s", job_type_to_string(t), + "JOB_RESULT=%s", job_result_to_string(result), + LOG_UNIT_MESSAGE(u, "Job %s/%s failed with result '%s'.", + u->id, + job_type_to_string(t), + job_result_to_string(result))); unit_start_on_failure(u); } diff --git a/src/core/service.c b/src/core/service.c index ffa301980fb..1cce15eb766 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -2277,12 +2277,11 @@ static void service_enter_restart(Service *s) { s->n_restarts ++; s->flush_n_restarts = false; - log_struct(LOG_INFO, - "MESSAGE_ID=" SD_MESSAGE_UNIT_RESTART_SCHEDULED_STR, - LOG_UNIT_ID(UNIT(s)), - LOG_UNIT_INVOCATION_ID(UNIT(s)), - LOG_UNIT_MESSAGE(UNIT(s), "Scheduled restart job, restart counter is at %u.", s->n_restarts), - "N_RESTARTS=%u", s->n_restarts); + log_unit_struct(UNIT(s), LOG_INFO, + "MESSAGE_ID=" SD_MESSAGE_UNIT_RESTART_SCHEDULED_STR, + LOG_UNIT_INVOCATION_ID(UNIT(s)), + LOG_UNIT_MESSAGE(UNIT(s), "Scheduled restart job, restart counter is at %u.", s->n_restarts), + "N_RESTARTS=%u", s->n_restarts); /* Notify clients about changed restart counter */ unit_add_to_dbus_queue(UNIT(s)); diff --git a/src/core/unit.c b/src/core/unit.c index 6ef90eea0e4..67d811d7dd9 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -1463,6 +1463,9 @@ static int log_unit_internal(void *userdata, int level, int error, const char *f va_list ap; int r; + if (u && !unit_log_level_test(u, level)) + return -ERRNO_VALUE(error); + va_start(ap, format); if (u) r = log_object_internalv(level, error, file, line, func, @@ -2166,6 +2169,13 @@ static int unit_log_resources(Unit *u) { value > NOTICEWORTHY_IP_BYTES); } + /* This check is here because it is the earliest point following all possible log_level assignments. If + * log_level is assigned anywhere after this point, move this check. */ + if (!unit_log_level_test(u, log_level)) { + r = 0; + goto finish; + } + if (have_ip_accounting) { if (any_traffic) { if (igress) @@ -2220,7 +2230,7 @@ static int unit_log_resources(Unit *u) { t = strjoina(u->manager->invocation_log_field, u->invocation_id_string); iovec[n_iovec + 3] = IOVEC_MAKE_STRING(t); - log_struct_iovec(log_level, iovec, n_iovec + 4); + log_unit_struct_iovec(u, log_level, iovec, n_iovec + 4); r = 0; finish: @@ -3880,7 +3890,7 @@ int unit_patch_contexts(Unit *u) { return 0; } -ExecContext *unit_get_exec_context(Unit *u) { +ExecContext *unit_get_exec_context(const Unit *u) { size_t offset; assert(u); @@ -4492,6 +4502,9 @@ void unit_warn_if_dir_nonempty(Unit *u, const char* where) { assert(u); assert(where); + if (!unit_log_level_test(u, LOG_NOTICE)) + return; + r = dir_is_empty(where); if (r > 0 || r == -ENOTDIR) return; @@ -4500,12 +4513,11 @@ void unit_warn_if_dir_nonempty(Unit *u, const char* where) { return; } - log_struct(LOG_NOTICE, - "MESSAGE_ID=" SD_MESSAGE_OVERMOUNTING_STR, - LOG_UNIT_ID(u), - LOG_UNIT_INVOCATION_ID(u), - LOG_UNIT_MESSAGE(u, "Directory %s to mount over is not empty, mounting anyway.", where), - "WHERE=%s", where); + log_unit_struct(u, LOG_NOTICE, + "MESSAGE_ID=" SD_MESSAGE_OVERMOUNTING_STR, + LOG_UNIT_INVOCATION_ID(u), + LOG_UNIT_MESSAGE(u, "Directory %s to mount over is not empty, mounting anyway.", where), + "WHERE=%s", where); } int unit_fail_if_noncanonical(Unit *u, const char* where) { @@ -4526,12 +4538,11 @@ int unit_fail_if_noncanonical(Unit *u, const char* where) { return 0; /* No need to mention "." or "..", they would already have been rejected by unit_name_from_path() */ - log_struct(LOG_ERR, - "MESSAGE_ID=" SD_MESSAGE_OVERMOUNTING_STR, - LOG_UNIT_ID(u), - LOG_UNIT_INVOCATION_ID(u), - LOG_UNIT_MESSAGE(u, "Mount path %s is not canonical (contains a symlink).", where), - "WHERE=%s", where); + log_unit_struct(u, LOG_ERR, + "MESSAGE_ID=" SD_MESSAGE_OVERMOUNTING_STR, + LOG_UNIT_INVOCATION_ID(u), + LOG_UNIT_MESSAGE(u, "Mount path %s is not canonical (contains a symlink).", where), + "WHERE=%s", where); return -ELOOP; } @@ -5310,35 +5321,32 @@ int unit_pid_attachable(Unit *u, pid_t pid, sd_bus_error *error) { void unit_log_success(Unit *u) { assert(u); - log_struct(LOG_INFO, - "MESSAGE_ID=" SD_MESSAGE_UNIT_SUCCESS_STR, - LOG_UNIT_ID(u), - LOG_UNIT_INVOCATION_ID(u), - LOG_UNIT_MESSAGE(u, "Deactivated successfully.")); + log_unit_struct(u, LOG_INFO, + "MESSAGE_ID=" SD_MESSAGE_UNIT_SUCCESS_STR, + LOG_UNIT_INVOCATION_ID(u), + LOG_UNIT_MESSAGE(u, "Deactivated successfully.")); } void unit_log_failure(Unit *u, const char *result) { assert(u); assert(result); - log_struct(LOG_WARNING, - "MESSAGE_ID=" SD_MESSAGE_UNIT_FAILURE_RESULT_STR, - LOG_UNIT_ID(u), - LOG_UNIT_INVOCATION_ID(u), - LOG_UNIT_MESSAGE(u, "Failed with result '%s'.", result), - "UNIT_RESULT=%s", result); + log_unit_struct(u, LOG_WARNING, + "MESSAGE_ID=" SD_MESSAGE_UNIT_FAILURE_RESULT_STR, + LOG_UNIT_INVOCATION_ID(u), + LOG_UNIT_MESSAGE(u, "Failed with result '%s'.", result), + "UNIT_RESULT=%s", result); } void unit_log_skip(Unit *u, const char *result) { assert(u); assert(result); - log_struct(LOG_INFO, - "MESSAGE_ID=" SD_MESSAGE_UNIT_SKIPPED_STR, - LOG_UNIT_ID(u), - LOG_UNIT_INVOCATION_ID(u), - LOG_UNIT_MESSAGE(u, "Skipped due to '%s'.", result), - "UNIT_RESULT=%s", result); + log_unit_struct(u, LOG_INFO, + "MESSAGE_ID=" SD_MESSAGE_UNIT_SKIPPED_STR, + LOG_UNIT_INVOCATION_ID(u), + LOG_UNIT_MESSAGE(u, "Skipped due to '%s'.", result), + "UNIT_RESULT=%s", result); } void unit_log_process_exit( @@ -5365,19 +5373,18 @@ void unit_log_process_exit( else level = LOG_WARNING; - log_struct(level, - "MESSAGE_ID=" SD_MESSAGE_UNIT_PROCESS_EXIT_STR, - LOG_UNIT_MESSAGE(u, "%s exited, code=%s, status=%i/%s", - kind, - sigchld_code_to_string(code), status, - strna(code == CLD_EXITED - ? exit_status_to_string(status, EXIT_STATUS_FULL) - : signal_to_string(status))), - "EXIT_CODE=%s", sigchld_code_to_string(code), - "EXIT_STATUS=%i", status, - "COMMAND=%s", strna(command), - LOG_UNIT_ID(u), - LOG_UNIT_INVOCATION_ID(u)); + log_unit_struct(u, level, + "MESSAGE_ID=" SD_MESSAGE_UNIT_PROCESS_EXIT_STR, + LOG_UNIT_MESSAGE(u, "%s exited, code=%s, status=%i/%s", + kind, + sigchld_code_to_string(code), status, + strna(code == CLD_EXITED + ? exit_status_to_string(status, EXIT_STATUS_FULL) + : signal_to_string(status))), + "EXIT_CODE=%s", sigchld_code_to_string(code), + "EXIT_STATUS=%i", status, + "COMMAND=%s", strna(command), + LOG_UNIT_INVOCATION_ID(u)); } int unit_exit_status(Unit *u) { diff --git a/src/core/unit.h b/src/core/unit.h index 5bc1237ce07..abd8aecc1a0 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -658,7 +658,7 @@ typedef struct UnitVTable { extern const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX]; -static inline const UnitVTable* UNIT_VTABLE(Unit *u) { +static inline const UnitVTable* UNIT_VTABLE(const Unit *u) { return unit_vtable[u->type]; } @@ -821,7 +821,7 @@ void unit_ref_unset(UnitRef *ref); int unit_patch_contexts(Unit *u); -ExecContext *unit_get_exec_context(Unit *u) _pure_; +ExecContext *unit_get_exec_context(const Unit *u) _pure_; KillContext *unit_get_kill_context(Unit *u) _pure_; CGroupContext *unit_get_cgroup_context(Unit *u) _pure_; @@ -892,6 +892,11 @@ static inline bool unit_has_job_type(Unit *u, JobType type) { return u && u->job && u->job->type == type; } +static inline bool unit_log_level_test(const Unit *u, int level) { + ExecContext *ec = unit_get_exec_context(u); + return !ec || ec->log_level_max < 0 || ec->log_level_max >= LOG_PRI(level); +} + /* unit_log_skip is for cases like ExecCondition= where a unit is considered "done" * after some execution, rather than succeeded or failed. */ void unit_log_skip(Unit *u, const char *result); @@ -931,9 +936,10 @@ int unit_thaw_vtable_common(Unit *u); #define log_unit_full_errno_zerook(unit, level, error, ...) \ ({ \ const Unit *_u = (unit); \ - (log_get_max_level() < LOG_PRI(level)) ? -ERRNO_VALUE(error) : \ - _u ? log_object_internal(level, 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(level, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \ + const int _l = (level); \ + (log_get_max_level() < LOG_PRI(_l) || (_u && !unit_log_level_test(_u, _l))) ? -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__); \ }) #define log_unit_full_errno(unit, level, error, ...) \ @@ -957,6 +963,27 @@ int unit_thaw_vtable_common(Unit *u); #define log_unit_warning_errno(unit, error, ...) log_unit_full_errno(unit, LOG_WARNING, error, __VA_ARGS__) #define log_unit_error_errno(unit, error, ...) log_unit_full_errno(unit, LOG_ERR, error, __VA_ARGS__) +#define log_unit_struct_errno(unit, level, error, ...) \ + ({ \ + const Unit *_u = (unit); \ + const int _l = (level); \ + unit_log_level_test(_u, _l) ? \ + log_struct_errno(_l, error, __VA_ARGS__, LOG_UNIT_ID(_u)) : \ + -ERRNO_VALUE(error); \ + }) + +#define log_unit_struct(unit, level, ...) log_unit_struct_errno(unit, level, 0, __VA_ARGS__) + +#define log_unit_struct_iovec_errno(unit, level, error, iovec, n_iovec) \ + ({ \ + const int _l = (level); \ + unit_log_level_test(unit, _l) ? \ + log_struct_iovec_errno(_l, error, iovec, n_iovec) : \ + -ERRNO_VALUE(error); \ + }) + +#define log_unit_struct_iovec(unit, level, iovec, n_iovec) log_unit_struct_iovec_errno(unit, level, 0, iovec, n_iovec) + #define LOG_UNIT_MESSAGE(unit, fmt, ...) "MESSAGE=%s: " fmt, (unit)->id, ##__VA_ARGS__ #define LOG_UNIT_ID(unit) (unit)->manager->unit_log_format_string, (unit)->id #define LOG_UNIT_INVOCATION_ID(unit) (unit)->manager->invocation_log_format_string, (unit)->invocation_id_string diff --git a/test/testsuite-04.units/silent-success.service b/test/testsuite-04.units/silent-success.service new file mode 100644 index 00000000000..a9f713701ca --- /dev/null +++ b/test/testsuite-04.units/silent-success.service @@ -0,0 +1,6 @@ +[Unit] +Description=Silent successful service + +[Service] +LogLevelMax=notice +ExecStart=/bin/true diff --git a/test/units/testsuite-04.sh b/test/units/testsuite-04.sh index da5f9ee2af1..7a17e086114 100755 --- a/test/units/testsuite-04.sh +++ b/test/units/testsuite-04.sh @@ -98,6 +98,11 @@ cmp /expected /output [[ $(journalctl -b -o cat -t "$ID" --output-fields=_PID | sort -u | grep -c "^.*$") -eq 3 ]] [[ $(journalctl -b -o cat -t "$ID" --output-fields=MESSAGE | grep -Pc "^(This will|usually fail|and be truncated)$") -eq 3 ]] +# test that LogLevelMax can also suppress logging about services, not only by services +systemctl start silent-success +journalctl --sync +[[ -z `journalctl -b -q -u silent-success.service` ]] + # Add new tests before here, the journald restarts below # may make tests flappy. -- 2.39.2