systemd 把日志格式中的 %u、%t 改成了用户名和运行目录
systemd unit 的 Environment 和 ExecStart 会处理百分号 specifier;需要交给应用的字面百分号写成双百分号,并分别验证加载配置与应用输出。
服务能启动,日志格式却突然出现 root、/run 或 shell 路径,而原本写进格式字符串的是 %u、%t、%s。如果同样的参数在终端正常,放进 systemd unit 的 Environment= 或 ExecStart= 后才变化,要检查百分号是否被 systemd 当成了 specifier。
这里处理的是 unit 中的字符串解析。应用日志格式中的百分号可能有自己的含义;必须先让它穿过 systemd 这一层,才能交给应用解释。具体 specifier 的值取决于管理器和服务配置,不要把所有服务的 %u 都认定为 root。
引号不能阻止百分号展开
Environment= 的值虽然不会做 shell 式的 $变量 展开,却会进行 systemd specifier 展开。给整个赋值加引号,可以保护其中的空格,不能使 %u 变成字面量。
例如应用原本需要收到以下格式:
%u %t
在 unit 中应写为:
[Service]
Environment="LOG_FORMAT=%%u %%t"
%% 经过 systemd 后成为一个 %。这不是让应用接受双百分号,而是让应用最终仍收到 %u %t。只对需要原样传入的百分号这样处理;原本有意使用的 %i 等实例 specifier 不能机械地全部替换。
这条规则针对支持 specifier 的 unit 设置,不是所有配置文件通用的转义规则。不要把应用自身的配置文件也批量改成双百分号。
保持整段格式为一个参数
如果应用通过命令行接收格式字符串,除了百分号,还需要确认参数边界。以下为示意片段,程序路径和 --format 必须替换为真实程序及其实际支持的选项:
[Service]
Environment="LOG_FORMAT=%%u %%t"
ExecStart=/opt/example-app/bin/server --format ${LOG_FORMAT}
${LOG_FORMAT} 会把环境值作为一个参数传入,包括内部空格。单独使用 $LOG_FORMAT 则可能按空白拆成多个参数。两者是另一个解析问题,不应靠增加百分号来处理。
也可以在支持 specifier 的 ExecStart= 参数中直接写字面格式,例如 --format "%%u %%t"。选择一种明确的配置方式即可,不需要为了转义百分号额外套一层 shell。若原命令已经调用 shell,还要分清 systemd 与 shell 各自的解析,不能只按交互终端的结果推断。
查看实际加载的是哪一份配置
以下使用系统服务,example-app.service 是占位名称。用户服务应在所属用户下使用对应的 systemctl --user 管理范围。
systemctl cat example-app.service
systemctl show example-app.service -p FragmentPath -p DropInPaths -p ExecStart
journalctl -u example-app.service -b -n 60 --no-pager
先找出格式字符串是在主 unit、drop-in 还是应用配置文件中设置的。cat 展示磁盘文件,show 展示管理器加载的属性;磁盘已改而进程仍使用旧值,是不同于转义写错的情况。日志和参数可能包含业务数据,不要公开整份环境或命令行输出。
修改前保存原配置。只需覆盖环境变量时,可以在已有 drop-in 中修改同名赋值,保留其他配置。如果还必须覆盖 ExecStart=,需要先用空的 ExecStart= 清除旧命令,再写完整的新命令;不能遗漏原来的数据目录、监听参数、用户和服务类型。
完成 unit 或 drop-in 修改后,重新加载管理器配置:
sudo systemctl daemon-reload
这不会让运行中的应用自动取得新参数。确认允许中断的时段后,再重启目标服务:
sudo systemctl restart example-app.service
sudo systemctl status example-app.service --no-pager -l
验证应用拿到的值,而不只看启动成功
通过应用已有的配置诊断或一条受控请求产生的日志,核对需要的字段是否恢复。例如应由应用解释的 %u、%t 不应提前变成 systemd 的用户名或运行目录。若出现字面的双百分号,应检查字符串是否实际经过了支持 specifier 的 unit 设置,而不是直接放到了应用配置中。
还要检查日志记录是否持续产生、字段数量和格式是否符合下游解析要求。服务为 active 只说明服务状态,不能证明日志格式正确。本文没有把任何应用特定格式推广为通用语法;应用仍报格式错误时,继续按该程序的格式规则定位。
回退时恢复本次修改前的 drop-in 或主 unit,再重新加载并在维护窗口重启。若本次只是改了现有文件的一个环境赋值,不要删除整个覆盖目录或清除其他运维配置。


