如何在没有块缓冲的情况下压缩标准输出

如何在没有块缓冲的情况下压缩标准输出

我试过这个

myprogram | gzip > /var/log/mylog.log

根据我的理解,只有在生成整个 gzip 块(我认为是 32kB)时,才会写入日志文件。这会导致写入磁盘时出现相当大的延迟。如果系统崩溃,日志的最后一部分将会丢失。

有没有 gzip 的替代方案可以在字节到达后立即压缩和写入字节?我认为有一些压缩算法不是基于块,而是基于字节流,但我无法找到这样的工具是否存在。

答案1

根据我的理解,只有在生成整个 gzip 块(我认为是 32kB)时,才会写入日志文件。

嗯,压缩器的工作就是获取许多位并生成更少的位。因此,一些按块操作基本上是固有的——最后,任何与 Lempel-Ziv 类似的东西(比如 gzip,但也像许多甚至大多数其他压缩器一样)都有在密码本中查找符号的工作,并且在与密码本的匹配完成之前,您无法知道是否已找到符号。

与码本的定期重置相结合导致相对固定的块长度。 (例如:gzip 不能让您选择块大小。块大小选择是 gzip 压缩方法所固有的。)

  • gzip你可以使用比;稍微不那么古老的东西。也许xz--flush-timeout就是您真正要寻找的。

  • 如果 32 kB 看起来“很多日志”,那么也许您根本不需要压缩?事实上,像 logrotate 这样的机制正是遵循这条路线:每隔几兆字节(对于现代计算机来说数据不多)切换到一个新日志,并且只压缩旧文件。

  • 此外,您的平均文件系统和块设备层缓冲区远大于 32 kB。你需要让你的文件系统处于 fsync 挂载模式,并且不要忘记,即便如此,你的存储设备的物理块可能是 4 kB(或倍数);如果实际上总是将其刷新到存储介质,则进行更小的写入会严重影响性能。

答案2

有很多解决方案,其中大多数需要您编写自己的压缩工具(您将其设置为更频繁地写入 - 基于 Lempel-Ziv 的工具可以写入流,但仅限于只能写入完整字节,LZ流通常具有不同的位长度标记,因此事物不在 8 位边界上,但它仍然可以随时写出已压缩的所有内容,除了末尾的部分仍然是部分字节)。然而,这是一种相当痛苦的方法。

一个更简单的方法,但它需要 2 个“部分”:

myprogram | split -u -l 1M -d /var/log/myprogram.log

这将创建一系列始终最新的日志文件,且永远不会大于 1 MB。

然后,您可以使用程序监视新的日志文件不再打开,然后压缩它,然后删除它。

最新的日志信息将始终立即可用,并且日志文件永远不会大于 1 MB。 gzip 始终会在删除“旧”文件之前完成压缩它们,因此您永远不会丢失任何日志信息。

#!/bin/sh

while true; do
  for file in /var/log/myprogram.log*[^z]; do
    proc_count=$(lsof -t "$file" | wc -l)
    if (( $proc_count == 0 )); then
      gzip "$file"
    fi
  done
  sleep 1
done

最好的解决方案是一个能够同时完成这两件事的程序;我可以写,因为我找到了你的帖子,因为我想要同样的东西。

相关内容