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.
This commit is contained in:
Ryan Hendrickson 2021-04-30 12:47:10 -04:00 committed by Lennart Poettering
parent 16ecdf3c80
commit c2503e359a
9 changed files with 178 additions and 138 deletions

View file

@ -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 <varname>LogLevelMax=</varname><option>info</option> 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, <varname>MaxLevelStore=</varname> configured in

View file

@ -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);

View file

@ -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);

View file

@ -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);
}

View file

@ -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));

View file

@ -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) {

View file

@ -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

View file

@ -0,0 +1,6 @@
[Unit]
Description=Silent successful service
[Service]
LogLevelMax=notice
ExecStart=/bin/true

View file

@ -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.