【问题标题】:How to set max size for a specific file in linux如何在linux中设置特定文件的最大大小
【发布时间】:2018-05-30 22:56:21
【问题描述】:

我有一个应用程序,它将写入特定的日志文件,直到用户会话正在进行。 我正在寻找的是对日志文件的大小设置一个最大上限,这样它就不会超过特定的大小,有两个有用的场景是

  1. 任何密切关注日志文件的实用程序,一旦达到最大大小,就会从头开始截断文件内容,以便应用程序可以继续在末尾追加内容。

  2. 在创建文件时,我可以指定该文件的最大大小,当文件达到该最大大小时,它不应增长超过该点。

我不想要的是

  1. 设置 cron 作业或脚本,在特定时间间隔(例如 1 小时)后监控文件大小,然后在该时间删除其内容。

【问题讨论】:

  • man logrotate
  • @NiallCosgrove 如何设置文件大小上限,使其不会超过特定大小?
  • @Cyrus Logrotate perfoms 通过 cron 作业每小时检查一次文件大小,如果文件大小超过分配的大小,我想要立即采取行动。
  • 我认为这个问题在这里是题外话。 Server Fault 可能是一个更好的提问地点,但 man logrotate 是您最有可能从任何人那里得到的答案。
  • @NiallCosgrove 当然我会在 serverfault 中提出这个问题,我使用了 logrotate 但在这种情况下,如果我等待 logrotate 在一个小时后检查文件大小,正在写入的应用程序可能会写入在短时间内大量数据,并且可能会在此之前填满磁盘。

标签: linux bash logrotate


【解决方案1】:

作为一个shellscript:

file=file_to_watch
maxsize=98765
truncsice=8765
while : ; do
    inotifywait -e modify "$file"
    filesize=$(du "$file")
    if  [ $filesize -gt $maxsize ] ; then
        tail -c $truncsize "$file" > /tmp/truncatedfile.$$
        mv /tmp/truncatedfile.$$ "$file"
    fi
done

请注意,您可能会遇到一些可能导致丢失日志数据的竞争条件。

【讨论】:

    【解决方案2】:

    truncate -s 10M log.txt 怎么样?

    查看man truncate了解更多详情

    【讨论】:

    • 像这样的硬编码大小的“唯一”问题是它很可能在任何地方剪切一条线,而不是删除直到某一行末尾的所有内容......也许@ 987654323@
    【解决方案3】:

    删除文件的一部分然后让您附加更多数据的过程几乎在任何系统上都不可用,即使它是可能的,但它不是一个人做的事情。它可以在内核级别完成并且非常有效,但我也从未见过。 (即内核将简单地从文件的开头取消链接 inode,并在文件的第一个 inode 中为字节功能提供偏移量 - 与页面功能相反。)

    在 Unix 系统上,您可以为此目的使用 mmap() 和 unmap()。所以当你的应用程序。确定文件大小超过一定数量,它必须从文件开头读取,确定例如第 10,000 行日志的位置,然后memmove() 其余部分从头开始。最后,它会截断文件并以附加模式重新打开它。这最后一步是非常重要的一步……

    // WARNING: code without any error checking
    // if multiple processes may run in parallel, make sure to use a lock as well
    int fd = open("/var/log/mylog.log", O_RDWR);
    ssize_t size = lseek(fd.get(), 0, SEEK_END);
    lseek(fd.get(), 0, SEEK_SET);
    char * start = (char *)mmap(nullptr,
                                size,
                                PROT_READ | PROT_WRITE, MAP_SHARED,
                                fd,
                                0);
    char * end = start + size;
    char * l10000 = start; // search line 10,000
    for(int line(0); line < 10000; ++line)
    {
        for(; l10000 < end && *l10000 != '\n'; ++l10000);
        if(*l10000 == '\n')
        {
            ++l10000; // skip the '\n'
        }
    }
    ssize_t new_size = end - l10000;
    memmove(start, l10000, new_size);
    truncate(fd, new_size);
    close(fd);
    

    (在 GitHub 上的 sendmail::dequeue() 中找到示例,其中包括此处未找到的所有错误检查。)

    重要提示:memmove() 调用会缓慢,尤其是在相当大的日志文件上。

    请注意,大多数情况下,当一个进程打开一个日志文件时,它会保持它处于打开状态,这意味着更改它脚下的文件不会有什么好处。实际上,在此处的 mmap() 示例中,如果您不确定关闭并重新打开日志(未显示在代码)。

    因此,它在代码中是可行的(但在 C++ 中,您可以轻松地在 C 中编译它。)但是,如果您只想使用 bash,logrotate 肯定是您最好的选择。但是,默认情况下,至少在 Ubuntu 上,logrotate 每天只运行一次。您可以专门为使用您的应用程序或系统范围的用户更改它。

    至少您可以通过移动或复制logrotate 脚本来每小时运行一次,如下所示:

    sudo cp /etc/cron.daily/logrotate /etc/cron.hourly/logrotate
    

    您还可以设置每分钟运行该脚本的 CRON 文件。编辑根crontab 文件:

    sudo crontab -u root -e
    

    然后添加一行:

    * * * * *   root    /etc/cron.daily/logrotate
    

    确保测试并查看它是否按预期工作。如果添加此类,您还可以从那里删除 /etc/cron.daily/logrotate 脚本,这样它就不会尝试运行两次(一次在“每日”运行中,一次在每分钟运行中。)

    请注意,CRON 中存在一个挥之不去的错误,如我对 Ubuntu 的bug report 所示。过多使用 CRON(如每分钟一次)会导致内存问题。

    此外,如前面的代码示例所述,您必须重新打开日志文件。除非应用程序在每次要写入日志文件时重新打开日志文件,或者告诉它旋转(即关闭旧文件并打开新文件),否则仅旋转对您没有任何好处。应用程序将继续将数据附加到旧文件中,不管它的名称是什么。 Unix 记住,因为它在打开文件后使用 inode 而不是文件名。 (在 MS-Windows 下,如果不先关闭对文件的所有访问权限,您将无法重命名……这很烦人!)

    在许多情况下,您要么重新启动整个应用程序。 (因为不知道如何重新打开日志太笨了),您发送应用程序。一个信号,以便它重新打开日志文件或应用程序。知道文件更改了,不知何故...

    如果应用程序。没有能力或不知道,重新启动将是您唯一的选择。如果用户有 UI,这可能奇怪。

    【讨论】:

      猜你喜欢
      • 2013-08-13
      • 2011-04-20
      • 1970-01-01
      • 2016-09-29
      • 2016-09-05
      • 2021-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多