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
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;
}
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);
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);
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) {
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);
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);
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;
* 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) {
/* 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;
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) {
* 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);
}
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));
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,
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)
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:
return 0;
}
-ExecContext *unit_get_exec_context(Unit *u) {
+ExecContext *unit_get_exec_context(const Unit *u) {
size_t offset;
assert(u);
assert(u);
assert(where);
+ if (!unit_log_level_test(u, LOG_NOTICE))
+ return;
+
r = dir_is_empty(where);
if (r > 0 || r == -ENOTDIR)
return;
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) {
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;
}
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(
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) {
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];
}
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_;
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);
#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, ...) \
#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
--- /dev/null
+[Unit]
+Description=Silent successful service
+
+[Service]
+LogLevelMax=notice
+ExecStart=/bin/true
[[ $(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.