在 mod_php 中,您可以使用该apache_note()
函数在 Apache 访问日志中记录变量:
<?php
apache_note('SCRIPT_TIME', '1234');
?>
使用 Apache 配置:
LogFormat "%h %l %u %t [%D/%{SCRIPT_TIME}n] \"%r\" %>s %b" inc_info
...
CustomLog /path/to/access_log inc_info
但apache_note()
PHP-FPM中没有该功能。
同样,您不能使用apache_setenv()
或setenv()
设置环境变量(通过 记录%{SCRIPT_TIME}e
)。
一种可能性是设置一个 Apache 可以使用“%{SCRIPT_TIME}i”记录的标头,但您需要小心不要让敏感信息发送到客户端(例如,了解登录脚本的准确处理时间可能是一个安全问题)。但更重要的是,如果内容已经发送到客户端,它就不起作用了,因为您无法再发送标头(如下面的完整示例所示)。
或者,PHP 可以编写自己的日志文件,但这将重复 Apache 日志已经执行的大量操作,有可能错过日志条目(例如,如果脚本有错误),并且将在 Apache 用户权限下创建(而不是以 root 身份写入)。
为了更详细,这是我用来记录脚本处理时间的代码:
<?php
define('SCRIPT_START', microtime(true));
function log_shutdown() {
if (!defined('SCRIPT_END')) {
define('SCRIPT_END', number_format(round((microtime(true) - SCRIPT_START), 4), 4));
}
if (function_exists('apache_note')) {
apache_note('SCRIPT_TIME', SCRIPT_END);
}
}
register_shutdown_function('log_shutdown');
?>
从时间角度来看,虽然 Apache 确实提供了“%D”来记录“处理请求所花费的时间”,但这在很大程度上依赖于用户的互联网连接。
这与我尝试在我的服务器上启用 HTTP/2 有关,配置如下:
Protocols h2 http/1.1
<FilesMatch \.php$>
CGIPassAuth on
SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
SetHandler "proxy:fcgi://127.0.0.1:9001"
</FilesMatch>
同样相关的是Nginx 版本
答案1
如果您仅记录处理时间并使用 nginx,则@MichaelHampton 的建议可以通过记录变量来起作用$upstream_response_time
。
但我想继续使用 Apache,并存储额外的详细信息(用户 ID、处理某些任务的时间等)...
我的解决方案是创建一个单独的日志文件,PHP 可以在任何时间写入(即使在发送标头之后),我将为每个请求创建一个唯一的参考代码(这样两个日志就可以合并)。
首先,让您的 PHP 脚本生成参考代码,并将其作为标题发送:
<?php
// A more compact uniqid - which uses hex encoding, and a full UNIX timestamp
$start = microtime(true);
$sec = floor($start);
$usec = round(($start - $sec) * 1000000); // Only accurate to 6 decimal places.
$sec -= strtotime('-' . date('w', $sec) . ' days, 00:00:00', $sec); // Time since Sunday 00:00, max value = 604800 (60*60*24*7)
$code = '';
foreach([$sec, $usec, rand(100000, 999999)] as $code_part) {
$a = dechex($code_part); // decbin returns a string
$a = hex2bin(((strlen($a) % 2) == 0 ? '' : '0') . $a);
$a = base64_encode_rfc4648($a);
$code .= str_pad($a, 4, '.', STR_PAD_LEFT); // 4 characters max = 999999 -> "0f423f" (hex) -> "D0I/" (base64)
}
define('REQUEST_START', $start);
define('REQUEST_CODE', $code);
header('X-Request-Code: ' . $code); // For Apache
?>
Apache 可以记录该参考代码(更多信息):
Header always note "X-Request-Code" "Request-Code"
Header always unset "X-Request-Code"
LogFormat "%h %l %u [%{%Y-%m-%d %H:%M:%S}t] [%{Request-Code}n] \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" inc_ref
CustomLog /path/to/access_log inc_ref
然后你可以添加一个register_shutdown_function()
执行以下操作:
<?php
$log_values = [
date('Y-m-d H:i:s'),
REQUEST_CODE,
$user_id,
$_SERVER['REMOTE_ADDR'],
$_SERVER['REQUEST_METHOD'],
$_SERVER['REQUEST_URI'],
http_response_code(),
round((microtime(true) - REQUEST_START), 4),
];
if (($fp = fopen($log_file, 'a')) !== false) {
fputcsv($fp, $log_values);
fclose($fp);
}
?>
出于懒惰的原因,我重复了访问日志中的信息(例如,grep 单个日志文件)。