PHP 怎么零代码埋点

wen PHP项目 1

本文目录导读:

PHP 怎么零代码埋点

  1. 方案一:使用 Nginx + Lua(OpenResty)【推荐】
  2. 方案二:PHP 扩展(C 扩展)拦截
  3. 方案三:PHP 框架中间件(最常用)
  4. 方案四:AOP 切面编程(面向切面)
  5. 方案五:自建日志系统 + 日志分析【间接埋点】
  6. 总结推荐

在 PHP 中实现“零代码埋点”通常是指不修改业务逻辑代码,通过外部手段(如框架中间件、代理层、扩展)来自动采集数据,以下是几种主流的实现方案:


使用 Nginx + Lua(OpenResty)【推荐】

这是最彻底的“零代码”方案,在 Web 服务器层拦截请求,完全不触碰 PHP 代码。

原理:Nginx 在处理请求时,通过 log_by_luaheader_filter_by_lua 钩子,在请求结束或响应发出时自动记录日志。

# nginx.conf 配置示例
http {
    lua_shared_dict my_cache 10m;
    server {
        listen 80;
        location /api/ {
            # 业务请求转给 PHP-FPM
            fastcgi_pass php_fpm;
            # 响应完成后,自动记录埋点数据(零 PHP 代码)
            log_by_lua_block {
                local cjson = require "cjson"
                local track = {}
                track.uri = ngx.var.request_uri
                track.method = ngx.var.request_method
                track.status = ngx.status
                track.latency = (ngx.now() - ngx.req.start_time()) * 1000
                track.client_ip = ngx.var.remote_addr
                track.user_agent = ngx.var.http_user_agent
                -- 发送到 Kafka 或日志系统
                local producer = require "resty.kafka.producer"
                local broker_list = {{host = "kafka-server", port = 9092}}
                local bp = producer:new(broker_list)
                bp:send("php_track_topic", cjson.encode(track))
            }
        }
    }
}

优点:完全不影响 PHP 代码,性能极高,支持高并发。
缺点:需要熟悉 OpenResty/Lua,且无法获取 PHP 内部变量(如数据库查询耗时)。


PHP 扩展(C 扩展)拦截

开发一个 PHP 扩展,利用 PHP 的 zend_execute_internal 钩子或 auto_prepend_file 机制,在脚本执行前后自动执行埋点。

实现方式(简化)

// PHP_MSHOT 扩展伪代码
void php_track_request_start(INTERNAL_FUNCTION_PARAMETERS) {
    // 记录开始时间
    track_begin_time = hrtime();
}
void php_track_request_end(INTERNAL_FUNCTION_PARAMETERS) {
    // 计算耗时
    // 通过 dlopen/curl 发送到采集服务器
    // 不修改任何业务代码
}

注册钩子(在 php.ini 中):

; 自动加载扩展,无需修改代码
extension=track_ext.so
; 每次请求开始和结束时自动执行
auto_prepend_file=/path/to/prepend.php
auto_append_file=/path/to/append.php

优点:能获取 PHP 内部信息。
缺点:需要 C 语言功底,维护成本高。


PHP 框架中间件(最常用)

如果你的项目使用 Laravel、ThinkPHP 等现代框架,可以通过全局中间件实现,业务代码零侵入(但需要配置中间件文件)。

Laravel 示例

<?php
// app/Http/Middleware/AutoTrack.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Redis;
class AutoTrack
{
    public function handle($request, Closure $next)
    {
        $start = microtime(true);
        $response = $next($request); // 业务代码执行
        // 业务完成后自动记录,无需在控制器写任何埋点代码
        $latency = (microtime(true) - $start) * 1000;
        $data = [
            'uri'       => $request->getRequestUri(),
            'method'    => $request->method(),
            'status'    => $response->status(),
            'latency'   => round($latency, 2),
            'timestamp' => date('Y-m-d H:i:s'),
        ];
        // 异步发送到日志系统
        Redis::pipeline(function ($pipe) use ($data) {
            $pipe->rPush('track_queue', json_encode($data));
            $pipe->expire('track_queue', 86400);
        });
        return $response;
    }
}

注册中间件(app/Http/Kernel.php):

protected $middleware = [
    \App\Http\Middleware\AutoTrack::class, // 全局注册,所有请求自动生效
];

优点:框架生态成熟,易维护,不影响业务逻辑。
缺点:需要找到项目的中间件核心,理解框架路由机制。


AOP 切面编程(面向切面)

使用 AOP 库(如 Go!AOPRay.Aop),通过配置文件定义切点,自动拦截方法调用。

<?php
// aspect_track.php
use Go\Aop\Aspect;
use Go\Aop\Intercept\MethodInvocation;
use Go\Lang\Annotation\Around;
class TrackAspect implements Aspect
{
    /**
     * @Around("execution(public App\Controller\**\*(*))")
     */
    public function aroundTracker(MethodInvocation $invocation)
    {
        $start = microtime(true);
        $result = $invocation->proceed(); // 执行业务代码
        $latency = microtime(true) - $start;
        file_put_contents('/tmp/track.log', 
            $invocation->getMethod()->getName() . ':' . $latency . "\n", 
            FILE_APPEND);
        return $result;
    }
}

注册切面(config.php):

return [
    'aspect' => [
        TrackAspect::class
    ],
    'include_paths' => [
        __DIR__ . '/../app/Controller', // 监控这些目录
    ]
];

优点:精确到方法级别,适合复杂业务监控。
缺点:需要引入新的库,对性能有一定影响。


自建日志系统 + 日志分析【间接埋点】

如果只是要获取请求日志(不需要业务数据),可以完全不写埋点代码。

  1. Nginx 访问日志:直接开启 $request_time$upstream_response_time

    log_format main '$remote_addr - $remote_user [$time_local] '
                 '"$request" $status $body_bytes_sent '
                 '$request_time $upstream_response_time';
    access_log /var/log/nginx/access.log main;
  2. PHP-FPM slowlog:记录慢请求,无需改代码。

    ; php-fpm.conf
    slowlog = log/$pool.log.slow
    request_slowlog_timeout = 5s  ; 超过5秒自动记录堆栈
  3. 监控系统:配合 telegraf + Prometheus,每秒主动抓取 PHP-FPM 状态页。

    # 开启 fpm 状态页
    pm.status_path = /status

总结推荐

场景 推荐方案 侵入性
团队会 Nginx/Lua 方案一(OpenResty) 零侵入,性能最好
用 Laravel/Tp 框架 方案三(中间件) 仅配置中间件
没有框架的裸 PHP 方案二(扩展)+ 方案五(Nginx日志) 零侵入但需 C 语言
精确到方法级别 方案四(AOP) 需引入库

最终建议:如果条件允许,优先选择 OpenResty + Lua,这是最干净的“零代码”方案;如果团队 PHP 为主且用框架,用全局中间件是最实际的解决方案。

抱歉,评论功能暂时关闭!