systemd 脚本打不开 /dev/stderr,日志没进 journal
journal的stderr可为Unix套接字,路径存在仍无法重新打开。自己的shell日志用>&2,第三方输出保留诊断、业务内容和失败状态。
脚本在终端能把日志写入 /dev/stderr,作为 systemd 服务运行却报无法打开,journal 也没有预期记录。若服务的 stderr 接入 journal,并且脚本使用 > /dev/stderr 或程序把它作为输出文件重新打开,要检查 stderr 实际是否为 Unix 流套接字。
路径存在与能够重新打开是两件事。journal 接收的 stdout、stderr 可以是 AF_UNIX 流套接字,不能按普通文件或管道经 /proc/self/fd/2 再次打开。具体报错文字和错误码受调用方影响,不应只凭 No such device or address 就认定根因。
自己的 shell 日志直接写已有描述符
下面两种写法动作不同:
printf '%s\n' 'starting task' > /dev/stderr
它请求打开一个路径。若最终指向上述套接字,可能失败。自己的 shell 日志应复制已有的 stderr 描述符:
printf '%s\n' 'starting task' >&2
>&2 不要求重新打开 /dev/stderr。示例消息可以替换为不含秘密的诊断内容;不要为了检查日志而输出令牌、密码或完整业务数据。
先检查已加载的 StandardOutput=、StandardError= 和实际写日志进程的 FD 2。只看 unit 主进程还不够,包装脚本或子进程可能关闭或重新接入了描述符。若 stderr 已关闭、指向其他目标,或者程序写的是自己的日志文件,改这条 shell 重定向不能解决问题。
第三方程序的输出文件选项要单独处理
程序将 /dev/stderr 作为“输出文件名”时,shell 的 >&2 无法替它修改内部打开行为。应检查程序是否支持写已有描述符或标准输出,再在包装层按需要接入正确的诊断通道。
尤其不要把下载命令的标准输出全部重定向进 journal。标准输出可能承载二进制下载或响应正文;诊断信息、业务内容和状态信息需要保持各自的去向。改包装脚本时还要保留退出状态及信号处理,不要为了让日志出现就增加一个会掩盖前段失败的管道。
curl: (23) Failed writing received data to disk/application 只是写入失败的一类提示。磁盘空间、目标权限、接收端提前关闭等也需要检查;没有确认进程来源与 FD 类型时,不能把所有 Jenkins、容器或 curl 23 错误都归为 journal 套接字。
保留原来的日志与业务通道
这里不需要把服务改为 root,也不需要给 /dev/stderr 或日志目录设置 777。权限变化不能让一个不支持重新打开的套接字变成普通文件。
如果 unit 直接在 ExecStart= 中写了裸管道,却没有调用 shell,应先处理 ExecStart 管道没有执行。已经使用 shell 但两路输出去向不对时,则核对 Bash 重定向的先后顺序。这两种问题与重新打开 journal 描述符不同。
修正后,检查实际服务进程的新诊断消息进入预期 journal,业务输出仍保存在原来的目标,退出状态仍能反映任务失败。需要重启加载改动时,先保存配置并安排允许中断的窗口;不要清空已有日志来制造“成功”的验收结果。


