]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
unit: Make sure individual unit maximum log level always takes priority
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 7 Apr 2025 18:06:54 +0000 (20:06 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Wed, 23 Apr 2025 12:46:12 +0000 (14:46 +0200)
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.

man/systemd.exec.xml
src/core/unit.h

index bf4f223a431c0553da266b4a1dac147ee5162be4..ce76fb793b21f357bcea25668259e30bae238f72 100644 (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>
index 5bf52f5f6d21d02b137362ea2f5bfcfb854ed2d8..d1a0e20a435ffd3698d8577cf6b8371e5858f10a 100644 (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))