在PHP中访问日志记录

Hal*_*yon 13 php apache logging

我想记录对文件/files夹中任何文件的访问,因此我可以使用PHP处理它以生成一些统计信息.

我不想编写一个名为via的自定义PHP处理程序,RewriteRule因为我不想处理状态代码,MIME类型和缓存头以及文件锁定问题.

我没有访问服务器配置,所以我无法使用CustomLog(我有权访问.htacess).

我无法使用,X-Sendfile因为它没有启用.

我没有访问权限access.log.


寻找一个明确的答案.

Bor*_*lid 12

你放在那里的限制很多.

您可以使用通过PHP安装的自定义处理程序执行此操作,该处理程序include位于每个适用的(或使用__FILE__解析,不适用)脚本的顶部.您必须有一个脚本,该脚本在每个文件被命中时运行,并且您已经排除了对服务器配置的更改(包括,我相信,.htaccess当您说RewriteRule不够好时),这意味着您通过基于脚本的网守.您不能拥有满足约束条件的解决方案,并且用户可以首先访问文件而无需使用PHP(或其他服务器端动态语言).可以通过用户重定向到实际文件而不是通过PHP运行静态内容来保留缓存.

您可以将日志信息存储在数据库中,也可以将文件存储在服务器可写入的位置(如果使用文件,请注意争用 - 附加模式很棘手).

编辑:quickshiftin指出了两种方法可以调用PHP,而无需include手动添加调用.


qui*_*tin 5

创建一个auto_prepend_file并定义一个函数来记录你想要的w/e.您需要访问.htaccess才能设置这些(并且webhost将需要像vhost中的AllowOverride一样),或者使用PHP 5.3,您可以使用每个目录的INI功能.

的.htaccess

php_value auto_prepend_file /path/to/file.php

每个目录php.ini(PHP 5.3 CGI /快速CGI SAPI)

user_ini.auto_prepend_file = /path/to/file.php

然后为你的文件/path/to/file.php(更确定的东西,我敢肯定;))

file_put_contents(
    LOG_FILE,
    implode(PHP_EOL . PHP_EOL, array(
                'SERVER: ' . PHP_EOL . print_r($_SERVER, true),
                'REQUEST: ' . PHP_EOL . print_r($_REQUEST, true)
            )),
    FILE_APPEND
);
Run Code Online (Sandbox Code Playgroud)

这种方法的优点是你可能能够逃脱它,你只需要在一个地方定义/包含日志记录代码.

编辑:

在回顾时,我发现你希望这适用于任意类型的文件...是的,这将是相当粗糙的.我能想到的最好的选择是将这些文件标记为.php或在.htaccess中定义自定义mime类型.想法是通过PHP解释器运行文件,从而执行auto_prepend_file,因为文件中没有PHP标记,内容直接发送到客户端.甚至可能在每个内容文件的顶部设置ContentType标头的一小部分PHP .我甚至不确定它会起作用但它可能会起作用.


Hal*_*yon 1

我已经尝试了很多事情,但似乎没有简单的解决方案。

我的解决方案使用Location@yes123 提出的标头技巧,但我对其进行了调整以符合我的偏好。

文件的链接保持不变,所以它仍然是:/files/path/to/my/file.abc 我有一个RewriteRule

RewriteRule ^files/(.*) path/to/tracker.php?path=/$1

然后在文件中,我Location通过添加?track=no到 URL 和之前的例外来发出标头RewriteRule

RewriteCond %{QUERY_STRING} !(&|^)track=no(&|$)

我又添加了一项优化。我已启用 E-Tags,因此如果客户端发送 E-Tag 标头,请查看它是否与文件匹配并返回 a304 Not Modified而不是Location.

$fs = stat($document_root . $path);
$apache_etag = calculate_apache_etag($fs);
if ((isset($_SERVER["HTTP_IF_MATCH"]) && etag_within_range($_SERVER["HTTP_IF_MATCH"], $apache_etag))
    || (isset($_SERVER["HTTP_IF_NONE_MATCH"]) && etag_within_range($_SERVER["HTTP_IF_NONE_MATCH"], $apache_etag))
) {
    header("ETag: " . $apache_etag, true, 304);
    exit;
}

function etag_within_range($etag1, $etag2) {
    list($size1, $mtime1) = explode("-", $etag1);
    list($size2, $mtime2) = explode("-", $etag2);
    $mtime1 = floor(hexdec($mtime1) / 1000000);
    $mtime2 = floor(hexdec($mtime2) / 1000000);
    return $mtime1 === $mtime2 && $size1 === $size2;
}
Run Code Online (Sandbox Code Playgroud)

calculate_apache_etag可以在这里找到实现: How do you make an etag that matches Apache?

etag_withing_rangemtime解决了与Apache 中更高精度进行比较的问题。


无效解决方案的注释

virtual

测试脚本:

var_dump(apache_response_headers());
virtual("/path/to/image.jpg");
var_dump(apache_response_headers());
Run Code Online (Sandbox Code Playgroud)

输出:

array(1) { ["X-Powered-By"]=> string(10) "PHP/5.2.11" }
[[binary junk]]
array(5) { ["X-Powered-By"]=> string(10) "PHP/5.2.11" ["Keep-Alive"]=> string(18) "timeout=5, max=100" ["Connection"]=> string(10) "Keep-Alive" ["Transfer-Encoding"]=> string(7) "chunked" ["Content-Type"]=> string(9) "text/html" }
Run Code Online (Sandbox Code Playgroud)

Content-Type: text/html 真的吗?:(

也许PHP5.3的header_remove功能可以解决这个问题?我没试过。