PHP写日志

需求:PHP在每次请求结束之前,要向一个文件写入数据。要克服环境因素,尽量保证写操作成功。

于是我第一版的代码是这样的:

<?php

function write_log($message) {
    file_put_contents('./logs/2015.log', $message);
}

write_log(123);

?>

这时候,我遇到了第一个问题,如果没有./log目录怎么办?因为不论是代码初次上线,或者是新开发clone完项目准备开发,都有可能遇到没有./logs这个目录的情况。


需求:需要加上路径判断。

于是就有了第二版:

<?php

function write_log($message) {
    if (!is_dir('./logs')) {
        mkdir('./logs');	
    }

    file_put_contents('./logs/2015.log', $message);
}

write_log(123);

?>

写到这里,我觉得我已经很好的完成了我作为程序员的工作,既实现了功能,又加了保护逻辑,谁知道吃翔的经历刚刚开始。有一天突然之间不停的有warning,是在mkdir()这一行,虽然对于程序员来说,warning == running,但还是上去看了看,最终定位到原因:./logs的目录权限被篡改成了0400。导致文件写入失败。


需求:写日志不能影响正常请求流程,在上述情况下,需要告警出来。

第三版:

<?php

function write_log($message) {
    if (!is_dir('./logs')) {
        mkdir('./logs');	
    }

    if (!is_writeable('./logs/2015.log')) { // 这样不管哪一级目录、文件被改成了不可写,都不会触发错误,只会告警出来。
        // 告警
        return false;
    }

    file_put_contents('./logs/2015.log', $message);
}

write_log(123);

?>

文章写到这里,我就没心情调侃了,直接说第四次遇到的问题。./logs目录权限没问题,但是./logs的父目录的权限被改成了0400,所以没办法mkdir(‘./logs’)。

ps:这里再补充说一下,如果不是!is_dir(‘./logs’)的话,也只会告警出来,不会主流程走不下去。


需求:上述问题,也只能告警出来,不能影响主流程。

第四版a:

<?php

function write_log($message) {
    if (!is_dir('./logs')) {
        try {
            mkdir('./logs');	
        } catch (Exception $e) {
            // 告警
            return false;
        }
    }

    if (!is_writeable('./logs/2015.log')) { // 这样不管哪一级目录、文件被改成了不可写,都不会触发错误,只会告警出来。
        // 告警
        return false;
    }

    file_put_contents('./logs/2015.log', $message);
}

write_log(123);

?>

可怜的人啊,如果你觉得这个没问题的话,你就被php坑了。

PHP对异常的奇葩处理,请参考这里,下面附上正确姿势:

第四版f:

<?php

function write_log($message) {
    if (!is_dir('./logs')) {
        $mkdir_res = @mkdir('./logs');	
        
        if (!$mkdir_res) {
            // 告警
            return false;
        }
    }

    if (!is_writeable('./logs/2015.log')) { // 这样不管哪一级目录、文件被改成了不可写,都不会触发错误,只会告警出来。
        // 告警
        return false;
    }

    file_put_contents('./logs/2015.log', $message);
}

write_log(123);

?>

写到这里,终于算是把这个问题告一段落了,最后总结一下遇到的问题:

  1. 没有目录
  2. 没有权限
  3. 异常捕获失败

最后,我推荐运维同学可以用旁路脚本结合crontab做检查,将风险降到最低。