unit: Make sure individual unit maximum log level always takes priority

Currently LogLevelMax= can only be used to decrease the maximum log level
for a unit but not to increase it. Let's make sure the latter works as
well, so LogLevelMax=debug can be used to enable debug logging for specific
units without enabling debug logging globally.
This commit is contained in:
Daan De Meyer
2025-04-07 20:06:54 +02:00
parent 6d47c16ce0
commit ba77798bba
2 changed files with 48 additions and 38 deletions

View File

@@ -3268,23 +3268,30 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
<varlistentry>
<term><varname>LogLevelMax=</varname></term>
<listitem><para>Configures filtering by log level of log messages generated by this unit. Takes a
<command>syslog</command> log level, one of <option>emerg</option> (lowest log level, only highest priority
messages), <option>alert</option>, <option>crit</option>, <option>err</option>, <option>warning</option>,
<option>notice</option>, <option>info</option>, <option>debug</option> (highest log level, also lowest priority
messages). See <citerefentry
<listitem><para>Sets the maximum log level for log messages generated by this unit. Takes a
<command>syslog</command> log level, one of <option>emerg</option> (lowest log level, only highest
priority messages), <option>alert</option>, <option>crit</option>, <option>err</option>,
<option>warning</option>, <option>notice</option>, <option>info</option>, <option>debug</option>
(highest log level, also lowest priority messages). See <citerefentry
project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
details. By default, no filtering is applied (i.e. the default maximum log level is <option>debug</option>). Use
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, 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
<citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry> might
prohibit messages of higher log levels to be stored on disk, even though the per-unit
details. By default, the maximum log level is not overridden.</para>
<para>This option can be used 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. Alternatively, this option can be used to enable extra logging about a
specific unit by the system or user manager processes without changing the global log level for the
system or user manager processes by setting <varname>LogLevelMax=</varname><option>debug</option>.
</para>
<para>Note that the configured level is applied to any log messages written by any of the processes
belonging to this unit, as well as any log messages written by the system or user manager processes
in reference to this unit, sent via any supported logging protocol. The override 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
<citerefentry><refentrytitle>journald.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
might prohibit messages of higher log levels to be stored on disk, even though the per-unit
<varname>LogLevelMax=</varname> permitted it to be processed.</para>
<xi:include href="version-info.xml" xpointer="v236"/></listitem>

View File

@@ -1018,10 +1018,22 @@ static inline bool unit_has_job_type(Unit *u, JobType type) {
return u && u->job && u->job->type == type;
}
static inline int unit_get_log_level_max(const Unit *u) {
if (u) {
if (u->debug_invocation)
return LOG_DEBUG;
ExecContext *ec = unit_get_exec_context(u);
if (ec && ec->log_level_max >= 0)
return ec->log_level_max;
}
return log_get_max_level();
}
static inline bool unit_log_level_test(const Unit *u, int level) {
assert(u);
ExecContext *ec = unit_get_exec_context(u);
return !ec || ec->log_level_max < 0 || ec->log_level_max >= LOG_PRI(level) || u->debug_invocation;
return LOG_PRI(level) <= unit_get_log_level_max(u);
}
/* unit_log_skip is for cases like ExecCondition= where a unit is considered "done"
@@ -1085,15 +1097,12 @@ OOMPolicy oom_policy_from_string(const char *s) _pure_;
({ \
const Unit *_u = (unit); \
const int _l = (level); \
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_SET_LOG_LEVEL(unit_get_log_level_max(_u)); \
const ExecContext *_c = _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__, unit_log_field(_u), _u->id, unit_invocation_log_field(_u), _u->invocation_id_string, ##__VA_ARGS__) : \
log_internal(_l, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \
_u ? log_object_internal(_l, error, PROJECT_FILE, __LINE__, __func__, unit_log_field(_u), _u->id, unit_invocation_log_field(_u), _u->invocation_id_string, ##__VA_ARGS__) : \
log_internal(_l, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \
})
#define log_unit_full_errno(unit, level, error, ...) \
@@ -1129,14 +1138,11 @@ OOMPolicy oom_policy_from_string(const char *s) _pure_;
({ \
const Unit *_u = (unit); \
const int _l = (level); \
bool _do_log = unit_log_level_test(_u, _l); \
const ExecContext *_c = _do_log && _u ? \
unit_get_exec_context(_u) : NULL; \
LOG_CONTEXT_SET_LOG_LEVEL(unit_get_log_level_max(_u)); \
const ExecContext *_c = _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); \
log_struct_errno(_l, error, __VA_ARGS__, LOG_UNIT_ID(_u)); \
})
#define log_unit_struct(unit, level, ...) log_unit_struct_errno(unit, level, 0, __VA_ARGS__)
@@ -1145,14 +1151,11 @@ OOMPolicy oom_policy_from_string(const char *s) _pure_;
({ \
const Unit *_u = (unit); \
const int _l = (level); \
bool _do_log = unit_log_level_test(_u, _l); \
const ExecContext *_c = _do_log && _u ? \
unit_get_exec_context(_u) : NULL; \
LOG_CONTEXT_SET_LOG_LEVEL(unit_get_log_level_max(_u)); \
const ExecContext *_c = _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); \
log_struct_iovec_errno(_l, error, iovec, n_iovec); \
})
#define log_unit_struct_iovec(unit, level, iovec, n_iovec) log_unit_struct_iovec_errno(unit, level, 0, iovec, n_iovec)
@@ -1222,7 +1225,7 @@ typedef struct UnitForEachDependencyData {
LOG_CONTEXT_PUSH_KEY_VALUE(unit_log_field(u), u->id); \
LOG_CONTEXT_PUSH_KEY_VALUE(unit_invocation_log_field(u), u->invocation_id_string); \
LOG_CONTEXT_PUSH_IOV(c ? c->log_extra_fields : NULL, c ? c->n_log_extra_fields : 0); \
LOG_CONTEXT_SET_LOG_LEVEL(c->log_level_max >= 0 ? c->log_level_max : log_get_max_level())
LOG_CONTEXT_SET_LOG_LEVEL(unit_get_log_level_max(u))
#define LOG_CONTEXT_PUSH_UNIT(unit) \
_LOG_CONTEXT_PUSH_UNIT(unit, UNIQ_T(u, UNIQ), UNIQ_T(c, UNIQ))