我有一段代码将CSV日志文件截断到指定的尾随时间段。每个CSV条目中的第一个字段是时间戳。
下面正确清除,但没有自动截断日志,导致file_get_contents&之间丢失日志条目丢失的可能性。 file_put_contents。由于新条目位于文件的底部,因此在此之前不存在破坏日志的风险。
我考虑过手动执行file_get_contents& file_put_contents,但PHP文档声称这些操作做了所有类型的超级有趣的巫毒优化,并且是我想要做的推荐方法(将所有文件内容作为字符串并使用字符串填充文件),所以我很好奇有一种方法可以使用这些功能而不会不安全。
$time = time();
$fp = @fopen( $file, 'r' );
if ( $fp !== false ) {
$truncate = false;
$offset = 0;
// find the first non-expired entry
while ( ( $fields = fgetcsv( $fp ) ) !== false ) {
if ( ! is_null( $fields ) && $time > ( $fields[0] + $purge_interval ) ) {
// we've reached the recent entries -- nothing beyond here will be removed
break;
}
$offset = @ftell( $fp );
if ( false === $offset ) {
break;
}
$truncate = true;
}
@fclose( $fp );
if ( $truncate ) {
// need the next two lines atomically performed...
$data = file_get_contents( $file, false, null, $offset );
file_put_contents( $file, $data, LOCK_EX );
}
}
答案 0 :(得分:2)
没有像这样的就地并发修改的防弹方法。该过程必须删除其中一个属性才能实现。
由于您还控制了日志编写器,因此一个简单而好的解决方案是删除绝对并发并使用flock
同步对日志的访问。日志编写者会定期打开要附加到它的日志,并且它们和截断过程也会在操作期间锁定日志文件。
例如,截断实用程序会执行
if (flock($fp, LOCK_EX)) {
$data = file_get_contents( $file, false, null, $offset );
file_put_contents( $file, $data, LOCK_EX );
flock($fp, LOCK_UN);
}
日志编写者也会在写入文件之前获取锁。一个兴趣点是编写者可能更喜欢尝试非阻塞锁,如果忙,则继续将日志存储在内存中,以免在未知的时间内阻塞进程;在这种情况下,将定期再次尝试该过程。
答案 1 :(得分:0)
我认为日志文件遵循"追加,只写和#34;原因是:很难同时使它们具有高性能和可编辑性。这就是为什么通常的日志文件通过cron作业在文件系统中原子旋转,以允许切断旧部件,可能压缩或最终删除它,同时允许新数据存储在新文件中。
因此,我尝试通过使用单独的文件来分隔日志条目的创建和处理它们。每天或每小时创建一个新的日志文件。新文件启动后处理旧文件。