![ASCII 日志文件开头的空字符](https://linux22.com/image/193620/ASCII%20%E6%97%A5%E5%BF%97%E6%96%87%E4%BB%B6%E5%BC%80%E5%A4%B4%E7%9A%84%E7%A9%BA%E5%AD%97%E7%AC%A6.png)
我们有一个 java 应用程序,它使用 log4j2 生成日志文件,并有脚本在使用另一个脚本重新启动之前停止该进程。每天午夜停止和重新启动之间有 5 分钟的暂停时间。在启动脚本中,我们使用“mv”命令将日志文件重命名为timstamp作为扩展名。问题是其中一个日志文件在文件开头包含空字符(几 MB),并且日志文件变成二进制文件。一些观察笔记可为该问题提供更多背景信息: 1.- 在具有相同版本的 java 应用程序的其他主机中使用了相同的启动脚本。根本不存在这个问题。 2.- 偶尔发生。即,一周所有 5 个日志文件都损坏,另一周日志文件正常。 3.- 无法在类似的开发者Linux主机上重现;仅在生产 Linux 主机中。 4.- 日志文件的大小通常约为每天 4 - 6 GB。 5.- 应用程序将在每天午夜停止 + 5 分钟暂停 + 通过脚本启动。 6.- 使用 hexdump 来查看二进制日志文件的内容。它的开头有几 MB 的空字符,然后是正常的典型 ASCII 内容。
任何建议将不胜感激。谢谢!
答案1
当您查看“二进制”日志文件的大小并将ls -l
其与您将获得的大小进行比较时du -k
,您可能会注意到一些有趣的事情:该文件似乎比它在磁盘上占用的空间大!
您可能已经运行了该 java 应用程序进程的第二个副本,或者生产应用程序有时需要 5 分钟以上才能完成关闭。
因此,当出现空字符时,仍然有一个应用程序进程在运行,它会记住之前写入日志文件的位置。它打开要写入的文件,seek()
写入该位置,并写入其日志消息。就像平常一样。
但是,如果该文件之前不存在(因为它已mv
被删除),那么这正是您生成稀疏文件。这是一个非常古老的 Unix 文件系统功能。
稀疏文件本质上是数据压缩的最简单原型:仅包含空字节的整个磁盘块实际上并不作为数据存储在磁盘上,但告诉系统文件块所在位置的文件系统元数据只会得到一个特殊标记,有效地表示“在此文件位置插入 X 个空字节块”。
通过打开一个文件进行写入来创建一个稀疏文件,然后寻找超出当前文件结尾的内容而不实际写入任何内容然后写点东西。大多数 Unix 风格的文件系统会自动将旧的 EOF 和新写入的数据之间的块添加为稀疏块,而不是显式地将空值的中间写入块写入磁盘。当您读取文件时,文件系统驱动程序将自动将稀疏块填充为空字节块,因此应用程序根本不必意识到这一点。
您可能需要向应用程序启动脚本添加一个测试,用于fuser
查看日志文件是否已打开,如果打开则停止并显示错误消息。像这样的东西:
LOGFILE=/some/where/log4j2.log
if fuser -s $LOGFILE; then
echo "ERROR: $LOGFILE is still in use. Maybe the app is still running. Make it stop." >&2
exit 1
fi
# add here your commands to rotate the logs and start the application.
log4j2
实际上有自己的日志文件轮换设施:最好的解决方案可能是使用这些设施而不是使用外部脚本。