前言
之前用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);
}
}