diff --git a/config/log.php b/config/log.php index 0d8cdde..895fff3 100644 --- a/config/log.php +++ b/config/log.php @@ -42,7 +42,23 @@ return [ 'realtime_write' => false, ], 'debug_mysql' => [ - 'type' => 'DebugMysql' + 'type' => 'DebugMysql', + // 服务器地址 + 'hostname' => Env::get('database.hostname', ''), + // 数据库名 + 'database' => Env::get('database.database', ''), + // 用户名 + 'username' => Env::get('database.username', ''), + // 密码 + 'password' => Env::get('database.password', ''), + // 端口 + 'hostport' => Env::get('database.hostport', '3306'), + // 数据库连接参数 + 'params' => [], + // 数据库编码默认采用utf8 + 'charset' => Env::get('database.charset', 'utf8'), + // 数据库表前缀 + 'prefix' => Env::get('database.prefix', 'ul_'), ] // 其它日志通道配置 ], diff --git a/database/migrations/20210724150134_create_table_debug_mysql.php b/database/migrations/20210724150134_create_table_debug_mysql.php index df6f361..98fe710 100644 --- a/database/migrations/20210724150134_create_table_debug_mysql.php +++ b/database/migrations/20210724150134_create_table_debug_mysql.php @@ -31,10 +31,14 @@ class CreateTableDebugMysql extends Migrator { $table = $this->table('debug_log') ->setComment('日志表') - ->addColumn(ColumnFormat::timestamp('create_time')) - ->addColumn(ColumnFormat::stringShort('create_time_title')) + ->addColumn(ColumnFormat::stringShort('uid')) ->addColumn(ColumnFormat::stringShort('level')) ->addColumn(ColumnFormat::stringLong('content')) + ->addColumn(ColumnFormat::stringShort('app_name')) + ->addColumn(ColumnFormat::stringShort('controller_name')) + ->addColumn(ColumnFormat::stringShort('action_name')) + ->addColumn(ColumnFormat::timestamp('create_time')) + ->addColumn(ColumnFormat::stringShort('create_time_title')) ->create(); } } diff --git a/extend/think/log/driver/DebugMysql.php b/extend/think/log/driver/DebugMysql.php index 935b396..2508d19 100644 --- a/extend/think/log/driver/DebugMysql.php +++ b/extend/think/log/driver/DebugMysql.php @@ -2,31 +2,170 @@ namespace think\log\driver; -use app\model\DebugLog; use think\contract\LogHandlerInterface; -use think\facade\Log; +use think\facade\App; +use PDO; -class DebugMysql implements LogHandlerInterface +class DebugMysql implements LogHandlerInterface { + protected $enableLog = true; + + protected $config = []; + + protected $pdo = null; + + protected $fileRescource = null; + + protected $tableName = ''; + + public function __construct(App $app, $config = []) + { + if (is_array($config)) { + $this->config = array_merge($this->config, $config); + } + + $dsn = $this->parseDsn($config); + + try { + + $pdo = $this->createPdo($dsn, $config['username'], $config['password'], $config['params']); + + $this->pdo = $pdo; + } catch (\Throwable $th) { + $this->pdo = null; + + $log_path = App::getRuntimePath() . 'log/' . date('ymd') . '.csv'; + + $dirname = dirname($log_path); + + if (!is_dir($dirname)) { + mkdir($log_path, 0777, true); + } + + $first_line = false; + if (!file_exists($log_path)) { + $first_line = true; + } + + $this->fileRescource = fopen($log_path, 'a'); + + if ($first_line) { + $fields = [ + 'level', + 'content', + 'create_time', + 'create_time_title', + 'uid', + 'app_name', + 'controller_name', + 'action_name', + ]; + fputcsv($this->fileRescource, $fields); + } + } + + + $this->tableName = $config['prefix'] . 'debug_log'; + } + public function save(array $log): bool { + + $app_name = app('http')->getName() ?: ''; + + $controller_name = request()->controller(); + $action_name = request()->action(); + + if (App::runningInConsole()) { + $app_name = 'cli'; + } + $create_time = time(); + $create_time_title = date('Y-m-d H:i:s', $create_time); + $log_key = uniqid(); + foreach ($log as $log_level => $log_list) { foreach ($log_list as $key => $log_item) { - DebugLog::create([ + + if (!is_string($log_item)) { + $log_item = json_encode($log_item, JSON_UNESCAPED_UNICODE); + } + $log_data = [ 'level' => $log_level, 'content' => $log_item, 'create_time' => $create_time, 'create_time_title' => $create_time_title, - ]); + 'uid' => $log_key, + 'app_name' => $app_name, + 'controller_name' => $controller_name, + 'action_name' => $action_name, + ]; + + if (!is_null($this->pdo)) { + + foreach ($log_data as $key => &$value) { + $value = str_replace('\'', '\\\'', $value); + } + + $data_keys = array_keys($log_data); + + $data_keys_in_sql = join(',', $data_keys); + + $data_values_in_sql = join('\',\'', $log_data); + + $sql = "INSERT INTO {$this->tableName} ($data_keys_in_sql) VALUES ('$data_values_in_sql');"; + + $this->pdo->exec($sql); + } else { + + fputcsv($this->fileRescource, $log_data); + } } } return true; } + + /** + * 解析pdo连接的dsn信息 + * @access protected + * @param array $config 连接信息 + * @return string + */ + protected function parseDsn(array $config): string + { + if (!empty($config['socket'])) { + $dsn = 'mysql:unix_socket=' . $config['socket']; + } elseif (!empty($config['hostport'])) { + $dsn = 'mysql:host=' . $config['hostname'] . ';port=' . $config['hostport']; + } else { + $dsn = 'mysql:host=' . $config['hostname']; + } + $dsn .= ';dbname=' . $config['database']; + + if (!empty($config['charset'])) { + $dsn .= ';charset=' . $config['charset']; + } + + return $dsn; + } + + protected function createPdo($dsn, $username, $password, $params) + { + return new PDO($dsn, $username, $password, $params); + } + + public function __destruct() + { + $this->pdo = null; + + if (!is_null($this->fileRescource)) { + fclose($this->fileRescource); + } + } }