删除文件的一部分然后让您附加更多数据的过程几乎在任何系统上都不可用,即使它是可能的,但它不是一个人做的事情。它可以在内核级别完成并且非常有效,但我也从未见过。 (即内核将简单地从文件的开头取消链接 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,这可能奇怪。