Thinkphp5日志驱动Mongodb版

前言

之前用tp的日志主要是以文件日志为主,在调试和应用过程中发现使用起来并不是很方便,在tp高级群里问了下有没有可以解析tp日志文件的composer组件也没人应,就自己写了个日志文件解析的类TPLogService。但是感觉依然不够理想,所以就写了个Mongodb版的日志驱动。


TPLogService介绍

TPLogService代码地址

https://github.com/AxiosCros/tpr-composer/blob/master/src/service/TPLogService.php

TPLogService主要特点

1.能解析file形式的日志文件。
2.日志解析是遍历日志文件夹下的所有日志文件,以目录为单位生成一个数组形式的一大堆数据,这些数据比较难统计分析。
3.支持只解析单日志文件,只需传入该文件路径即可。
4.因为不能根据条件查询,所以当一次性获取的日志数量特别多时,可能会非常非常慢。


Mongodb版日志驱动介绍

Mongodb版日志驱动的特点

1.支持查询,方便分析统计,比如统计某个方法的请求次数,查看处理最慢的是哪个方法等等。
2.性能更好。较比于日志文件解析,通过查询方式获取日志信息性能肯定是更好的。
3.方便获取。只要可以连接Mongodb数据库即可
4.不易丢失。文件形式虽然也是持久化的,但是一旦项目因为变动或其他原因,丢失了runtime目录,极易造成日志的丢失。而放置mongodb中,可以在一定程度上保证日志能够稳定存在。

代码文件地址

已提交pr,未合并,待正式合并后代码可能会有变动,也有可能最后转为composer组件的形式

framework: /library/think/log/driver/Mongodb.php

日志配置示例

config.php

'log'    => [
        // 日志记录方式,内置 file socket 支持扩展
        'type'  => 'Mongodb',
        // 日志保存目录
        'path'  => LOG_PATH,
        // 日志记录级别
        'level' => ['error','debug','info'],
        //MongoDB的连接配置
        'connection'=>'default',
        //日志数据库名称
        'database'=>'log',
        //日志时间日期格式
        'time_format'=>"Y-m-d H:i:s",
        //独立记录的日志级别
        'apart_level' => [],
    ],

mongodb连接配置示例

mongodb扩展配置:/config/extra/mongo.php

return [
    'default'=>[
        "type"              => '\think\mongo\Connection',
        "hostname"          => \think\Env::get('mongo.hostname'),
        "database"          => \think\Env::get('mongo.database'),
        "username"          => \think\Env::get('mongo.username'),
        "password"          => \think\Env::get('mongo.password'),
        "hostport"          => \think\Env::get('mongo.hostport'),
        "dsn"               => \think\Env::get('mongo.dsn'),
        "params"            => [],
        "charset"           => "utf8",
        "pk"                => "_id",
        "pk_type"           => "ObjectID",
        "prefix"            => "",
        "debug"             => false,
        "deploy"            => 0,
        "rw_separate"       => false,
        "master_num"        => 1,
        "slave_no"          => "",
        "fields_strict"     => true,
        "resultset_type"    => "array",
        "auto_timestamp"    => false,
        "datetime_format"   => "Y-m-d H:i:s",
        "sql_explain"       => false,
        "pk_convert_id"     => false,
        "type_map" => [
            "root" =>"array",
            "document"=>"array",
            "query" =>"\\think\\mongo\\Query"
        ]
    ]
];

具体代码

namespace think\log\driver;
use think\App;
use think\Config;
use think\Db;

class Mongodb{
    protected $config = [
        // 日志保存目录
        'path'  => LOG_PATH,
        //MongoDB的连接配置
        'connection'=>'default',
        //日志数据库名称
        'database'=>'log',
        //日志时间日期格式
        'time_format'=>"Y-m-d H:i:s",
        //独立记录的日志级别
        'apart_level' => [],
    ];

    protected $mongo_config;

    protected $database;

    public function __construct($config = [])
    {
        if (is_array($config)) {
            $this->config = array_merge($this->config, $config);
        }
        $this->mongo_config = Config::get("mongo.".$this->config['connection']);
        if(empty($this->mongo_config)){
            throw new \InvalidArgumentException('mongodb config not exits');
        }
        $this->database     = $this->config['database'];
    }

    /**
     * 日志写入接口
     * @access public
     * @param array $log 日志信息
     * @return bool
     */
    public function save(array $log = []){
        $insert = [];
        $timestamp = time();
        $datetime = isset($this->config['time_format'])?date($this->config['time_format']):date("Y-m-d H:i:s");

        if (App::$debug ) {
            if(isset($_SERVER['HTTP_HOST'])){
                $insert['current_url'] =  $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
            } else {
                $insert['current_url'] = "cmd:" . implode(' ', $_SERVER['argv']);
            }

            $runtime    = round(microtime(true) - THINK_START_TIME, 10);
            $qps        = $runtime > 0 ? number_format(1 / $runtime, 2). 'req/s]' : '∞'. 'req/s]';
            $runtime_str=  number_format($runtime, 6) . 's';
            $memory_use = number_format((memory_get_usage() - THINK_START_MEM) / 1024, 2);
            $file_load  = count(get_included_files());
            $server     = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : '0.0.0.0';
            $remote     = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0';
            $method     = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'CLI';
            $insert     = [
                'timestamp'=>$timestamp,
                'datetime'=>$datetime,
                'method'=>$method,
                'runtime'=>$runtime_str,
                'qps'=>$qps,
                'memory_use'=>$memory_use . 'kb',
                'file_load'=>$file_load,
                'server'=>$server,
                'remote'=>$remote
            ];
        }

        $content=[];
        foreach ($log as $type => $val){
            if(isset($content[$type])){
                $n = count($val);
            }else{
                $n=0;
            }
            foreach ($val as $msg) {
                if (!is_string($msg)) {
                    $msg = var_export($msg, true);
                }
                $content[$type][$n] = $msg;
                $n++;
            }
            if(in_array($type, $this->config['apart_level'])){
                $this->log($content,$type);
            }
        }
        $insert['log'] = $content;
        $this->log($insert);
        return true;
    }

    protected function log($insert=[],$database=''){
        if(empty($database)){
            $database = $this->database;
        }
        $mongo = Db::connect($this->mongo_config)->name($database);
        if(empty($mongo)){
            throw new \InvalidArgumentException('mongodb connection fail');
        }
        return $mongo->insert($insert);
    }
}

相关文章

添加新评论