TransferStatistics 应用监控系统

HSK99

TransferStatistics

TransferStatistics 使用webman开发的一个应用监控系统,用于查看应用调用记录、请求量、调用耗时、调用分析等。

系统使用 UDP 接收上报数据;使用 Redis 存储、汇总数据

项目地址

https://github.com/hsk99/transfer-statistics

所需环境

PHP版本不低于7.3,并安装 Redis 拓展

安装

composer安装

创建项目

composer create-project hsk99/transfer-statistics

下载安装

1、下载 或 git clone https://github.com/hsk99/transfer-statistics

2、执行命令 composer install

配置

1、config/redis.php 设置 Redis

2、config/app.php 设置 登录用户名、密码

3、config/server.php 设置 WebServer

4、config/process.php 设置 采集服务

运行

执行命令 php start.php start

查看统计

浏览器访问 http://ip地址:8788

上报数据

使用类库:Client/StatisticClient.php

1、webman 中间件使用

config/app.php 增加 statisticAddress 项,设置 上报地址

<?php

namespace app\common\middleware;

use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;

class Statistic implements MiddlewareInterface
{
    public function process(Request $request, callable $next): Response
    {
        $ip         = $request->getRealIp($safe_mode = true);
        $controller = $request->controller;
        $action     = $request->action;
        $transfer   = $controller . '::' . $action;

        // 开始计时
        $unique = StatisticClient::tick('project', $ip, $transfer);

        $response = $next($request);

        $code    = $response->getStatusCode();
        $success = $code < 400;
        $details = [
            'ip'              => $request->getRealIp($safe_mode = true) ?? '',   // 请求客户端IP
            'url'             => $request->fullUrl() ?? '',                      // 请求URL
            'method'          => $request->method() ?? '',                       // 请求方法
            'request_param'   => $request->all() ?? [],                          // 请求参数
            'request_header'  => $request->header() ?? [],                       // 请求头
            'cookie'          => $request->cookie() ?? [],                       // 请求cookie
            'session'         => $request->session()->all() ?? [],               // 请求session
            'response_code'   => $response->getStatusCode() ?? '',               // 响应码
            'response_header' => $response->getHeaders() ?? [],                  // 响应头
            'response_body'   => $success ?: (string)$response->rawBody(),       // 响应数据(发生异常)
        ];
        // 数据上报
        StatisticClient::report($unique, 'project', $ip, $transfer, $success, $code, json_encode($details, 320));

        return $response;
    }
}

2、通用使用

<?php

require_once __DIR__ . '/StatisticClient.php';

// 设置上报地址
StatisticClient::$remoteAddress = 'udp://127.0.0.1:8789';

$project  = 'test';       // 应用
$ip       = '127.0.0.1';  // 客户端IP
$transfer = 'test';       // 调用

// 计时
$unique = StatisticClient::tick($project, $ip, $transfer);

// 模拟运行
usleep(mt_rand(5, 200));

// 上报
$code    = mt_rand(100, 600);           // 状态码
$success = $code < 400 ? true : false;  // 是否成功
$details = json_encode([
    'project'  => $project,
    'ip'       => $ip,
    'transfer' => $transfer,
    'code'     => $code,
    'success'  => $success
], 320);  // 详情(JSON格式)
StatisticClient::report($unique, $project, $ip, $transfer, $success, $code, $details);

3、SQL监控(ThinkORM 示例)

\think\facade\Db::listen(function ($sql, $runtime, $master) {
    switch (true) {
        case is_numeric($runtime):
            $transfer = $sql;
            $cost     = $runtime;
            break;
        case !is_numeric($runtime) && 'CONNECT' === substr($sql, 0, 7):
            @preg_match("/UseTime:([0-9]+(\\.[0-9]+)?|[0-9]+(\\.[0-9]+))/", $sql, $result);
            if (count($result) > 1) {
                $transfer = substr($sql, strpos($sql, "s ] ") + 4);
                $cost     = $result[1];
            } else {
                $transfer = $sql;;
                $cost     = 0;
            }
            break;
        default:
            $transfer = $sql;;
            $cost     = 0;
            break;
    }
    StatisticClient::report('', 'projectSql', '127.0.0.1', $transfer, true, 1, json_encode([
        'sql'     => $sql,
        'runtime' => $cost . 's',
        'master'  => $master,
    ], 320), $cost);
});

图片展示

















2968 21 4
21个评论

Tinywan

大赞!

  • 暂无评论
walkor

我去,你这个贼帅啊!!!

  • 暂无评论
evilk

666
太棒了
感谢大佬分享
但不知道能否监控到SQL耗时

  • HSK99 2021-12-24

    上报客户端已更新,SQL监控(ThinkORM 示例)文章已更新

10bang

6666,很赞啊

  • 暂无评论
yzh52521

666666 威武

  • 暂无评论
不败少龙

帅 帅

  • 暂无评论
HSK99

增加 SQL运行 监控

ThinkORM 示例

\think\facade\Db::listen(function ($sql, $runtime, $master) {
    switch (true) {
        case is_numeric($runtime):
            $transfer = $sql;
            $cost     = $runtime;
            break;
        case !is_numeric($runtime) && 'CONNECT' === substr($sql, 0, 7):
            @preg_match("/UseTime:([0-9]+(\\.[0-9]+)?|[0-9]+(\\.[0-9]+))/", $sql, $result);
            if (count($result) > 1) {
                $transfer = substr($sql, strpos($sql, "s ] ") + 4);
                $cost     = $result[1];
            } else {
                $transfer = $sql;;
                $cost     = 0;
            }
            break;
        default:
            $transfer = $sql;;
            $cost     = 0;
            break;
    }
    StatisticClient::report('', 'projectSql', '127.0.0.1', $transfer, true, 1, json_encode([
        'sql'     => $sql,
        'runtime' => $cost . 's',
        'master'  => $master,
    ], 320), $cost);
});

由于编码长度限制SQL过长时“调用入口”会被截断,可在详情里面查看

图片展示 已更新文章

  • 暂无评论
evilk

问一下
如果一直监控着
redis里面的数据,是不是会一直增长下去?

  • 暂无评论
小阳光

作者做的不错,其实还可以用tideways收集到每个函数的耗时,以及每条sql的耗时,绘制火焰图,一眼看出哪里比较慢

碎玉

题主辛苦,第一次接触应用监控项目,有一些不明白的地方,想请教下:
webman中间件:
1、使用类库:Client/StatisticClient.php:这个文件放到我的webman项目里吗?放到哪个位置?
2、config/app.php 增加 statisticAddress 项,设置 上报地址: 这个config/app.php是指的我的项目呢还是监控项目的?增加statisticAddress项怎么设置地址?
3、下面的示例代码写到哪里?怎么命名?
通用使用:
上报数据的示例代码应该放在哪里?是项目后端代码的开头还是末尾?
SQL监控:
示例代码是不是写到每一处需要监控的sql处?
新手上路,如有冒犯,请多见谅,谢谢。

  • xcsoft 2022-01-05

    1: 直接将Client/StatisticClient.php 粘贴在 你webman项目的中间件里 都可以. 2. 放在你的webman项目 地址为udp://监控服务器ip:port 3. 放在webman项目的中间件中(与1相关), 并在config/middleware.php 添加全局中间件

宁采陈

看了一下,很棒。就是layuiadmin不是压缩版,可能涉及版权问题

  • 暂无评论
qq285567309

windows下支持吗

  • 暂无评论
mlyykk

还在用在webman1.0版本,没想到社区现在都这么热闹了. 新版界面看起来也清爽多了.

  • 暂无评论
ab0029

666

  • 暂无评论
tanxinchong

很赞 学习了

  • 暂无评论
zjcyjj

win client 服务端可以运行 客户端发送过去 服务端收不到 linux还没试试

  • 暂无评论
owenzhang

我写了一篇使用实例 大家可以参考看看
TransferStatistics使用实例:应用和SQL监控系统workerman
https://my.oschina.net/owenzhang24/blog/5522317

  • 暂无评论
evilk

非常棒!
如果可以将请求和请求中的SQL绑定就好了

  • owenzhang 18天前

    那里可以在请求的Statistic类内$details-》response_body内填写你的sql即可啊

没茅台

能否说一下 加上自定义进程UDP进行传输有什么好处呢,跟直接执行redis有什么区别呢。

  • li914 9天前

    方便其他应用程序进行上报数据吧

  • ziyoren 8天前

    微服务,分布式,transfer-statistics是一个独立服务;其他应用只需要一个StatisticClient就可以把采集数据上报到这个服务中。直接执行Redis不就融合到某个应用一起了

  • 没茅台 6天前

    受教

ziyoren
ErrorException: Undefined array key 0 in /path/vendor/workerman/workerman/Protocols/Http/Response.php:396
Stack trace:
#0 /path/vendor/workerman/workerman/Protocols/Http/Response.php(396): {closure}(2, 'Undefined array...', '/Users/ziyoren/...', 396)
#1 /path/vendor/workerman/workerman/Protocols/Http.php(254): Workerman\Protocols\Http\Response->__toString()
#2 /path/vendor/workerman/workerman/Connection/TcpConnection.php(321): Workerman\Protocols\Http::encode(Object(Webman\Http\Response), Object(Workerman\Connection\TcpConnection))
#3 /path/vendor/workerman/webman-framework/src/App.php(396): Workerman\Connection\TcpConnection->send(Object(Webman\Http\Response))
#4 /path/vendor/workerman/webman-framework/src/App.php(166): Webman\App::send(Object(Workerman\Connection\TcpConnection), Object(Webman\Http\Response), Object(support\Request))
#5 /path/vendor/workerman/workerman/Connection/TcpConnection.php(638): Webman\App->onMessage(Object(Workerman\Connection\TcpConnection), Object(support\Request))
#6 /path/vendor/workerman/workerman/Events/Select.php(295): Workerman\Connection\TcpConnection->baseRead(Resource id #210)
#7 /path/vendor/workerman/workerman/Worker.php(2431): Workerman\Events\Select->loop()
#8 /path/vendor/workerman/workerman/Worker.php(1555): Workerman\Worker->run()
#9 /path/vendor/workerman/workerman/Worker.php(1397): Workerman\Worker::forkOneWorkerForLinux(Object(Workerman\Worker))
#10 /path/vendor/workerman/workerman/Worker.php(1371): Workerman\Worker::forkWorkersForLinux()
#11 /path/vendor/workerman/workerman/Worker.php(549): Workerman\Worker::forkWorkers()
#12 /path/start.php(108): Workerman\Worker::runAll()
#13 {main}
worker[wechatpay:52968] exit with status 64000

只要加上中间件,就报错。不知道为什么?请问如何排查?

<?php
namespace app\middleware;

use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
use app\services\StatisticClient; //客户端类放在这里

class Statistic implements MiddlewareInterface
{
    public function process(Request $request, callable $next) : Response
    {
        $ip         = $request->getRealIp();
        $controller = $request->controller;
        $action     = $request->action;
        $transfer   = $controller . '::' . $action;

        // 开始计时
        $unique = StatisticClient::tick('project', $ip, $transfer); //只要调用到这里就会报错

        $response = $next($request);

        $code    = $response->getStatusCode();
        $success = $code < 400;
        $details = [
            'ip'              => $ip ?? '',   // 请求客户端IP
            'url'             => $request->fullUrl() ?? '',                      // 请求URL
            'method'          => $request->method() ?? '',                       // 请求方法
            'request_param'   => $request->all() ?? [],                          // 请求参数
            'request_header'  => $request->header() ?? [],                       // 请求头
            'cookie'          => $request->cookie() ?? [],                       // 请求cookie
            'session'         => $request->session()->all() ?? [],               // 请求session
            'response_code'   => $response->getStatusCode() ?? '',               // 响应码
            'response_header' => $response->getHeaders() ?? [],                  // 响应头
            'response_body'   => $success ?: (string)$response->rawBody(),       // 响应数据(发生异常)
        ];
        // 数据上报
        StatisticClient::report($unique, 'project', $ip, $transfer, $success, $code, json_encode($details, 320));

        return $response;
    }

}
  • ziyoren 8天前

    Workerman version:4.0.31 PHP version:8.0.8
    Webman-framework v1.3.4

  • ziyoren 8天前

    原因:PHP8报错了,导致后面的程序无法按预期的运行。

    StatisticClient类的tick方法,第一个参数给了默认值,函数的参数带有默认值时,如果不再参数最后,PHP8会提示deprecated。

        public static function tick(string $project = '', string $ip, string $transfer = ''): string
        {
            $unique = uniqid();
    
            self::$timeMap[md5($project . $ip . $transfer . $unique)] = microtime(true);
    
            return $unique;
        }

    将上面的代码改一下

        public static function tick(string $project, string $ip, string $transfer = ''): string
        {
            $unique = uniqid();
    
            self::$timeMap[md5($project . $ip . $transfer . $unique)] = microtime(true);
    
            return $unique;
        }

    注意:其他几个方法也要修改。

    代码还是要规范啊!!!!

  • ziyoren 8天前

    @walkor 老大,这个php8的报错webman好像没捕获到啊

kinross

  • 暂无评论

HSK99

-15
积分
0
获赞数
0
粉丝数
2019-06-29 加入
🔝