增加测试机制;完善DebugMysql日志逻辑;更新debug_log表字段格式;发布新版本

This commit is contained in:
2024-01-03 16:22:01 +08:00
parent 58bb8166c9
commit e6779f4921
14 changed files with 767 additions and 76 deletions

View File

@@ -2,6 +2,7 @@
namespace think;
use app\common\command\Test;
use app\common\event\AdminLoginSuccess\LogEvent;
use app\common\event\AdminLoginType\DemoEvent;
use app\common\provider\ExceptionHandle;
@@ -10,9 +11,9 @@ use app\common\provider\View;
use think\app\Service as AppService;
use think\captcha\CaptchaService;
use think\facade\App;
use think\migration\Service;
use think\migration\Service as MigrateService;
class UlthonAdminService extends \think\Service
class UlthonAdminService extends Service
{
public function boot()
{
@@ -40,10 +41,14 @@ class UlthonAdminService extends \think\Service
$this->app->register(AppService::class);
// 注册数据库迁移服务
$this->app->register(Service::class);
$this->app->register(MigrateService::class);
// 绑定命令行
$this->commands([
Test::class,
]);
// 绑定标识容器
$provider_default = [
'think\Request' => Request::class,
'think\exception\Handle' => ExceptionHandle::class,
@@ -68,7 +73,7 @@ class UlthonAdminService extends \think\Service
// 多语言加载
// \think\middleware\LoadLangPack::class,
// Session初始化
100 => \think\middleware\SessionInit::class,
100 => middleware\SessionInit::class,
];
$this->app->middleware->import($middleware);

View File

@@ -2,77 +2,90 @@
namespace think\log\driver;
use PDO;
use think\contract\LogHandlerInterface;
use think\facade\App;
use PDO;
class DebugMysql implements LogHandlerInterface
class DebugMysql implements LogHandlerInterface
{
protected $enableLog = true;
protected $config = [];
/**
* @var PDO
*/
protected $pdo = null;
protected $file = null;
protected $fileRescource = null;
protected $tableName = '';
protected $reConnectTimes = 0;
protected $fileLogTimes = 0;
public $devMode = true;
/**
* 服务器断线标识字符.
*
* @var array
*/
protected $breakMatchStr = [
'server has gone away',
'no connection to the server',
'Lost connection',
'is dead or not enabled',
'Error while sending',
'decryption failed or bad record mac',
'server closed the connection unexpectedly',
'SSL connection has been closed unexpectedly',
'Error writing data to the connection',
'Resource deadlock avoided',
'failed with errno',
'child connection forced to terminate due to client_idle_limit',
'query_wait_timeout',
'reset by peer',
'Physical connection is not usable',
'TCP Provider: Error code 0x68',
'ORA-03114',
'Packets out of order. Expected',
'Adaptive Server connection failed',
'Communication link failure',
'connection is no longer usable',
'Login timeout expired',
'SQLSTATE[HY000] [2002] Connection refused',
'running with the --read-only option so it cannot execute this statement',
'The connection is broken and recovery is not possible. The connection is marked by the client driver as unrecoverable. No attempt was made to restore the connection.',
'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Try again',
'SQLSTATE[HY000] [2002] php_network_getaddresses: getaddrinfo failed: Name or service not known',
'SQLSTATE[HY000]: General error: 7 SSL SYSCALL error: EOF detected',
'SQLSTATE[HY000] [2002] Connection timed out',
'SSL: Connection timed out',
'SQLSTATE[HY000]: General error: 1105 The last transaction was aborted due to Seamless Scaling. Please retry.',
];
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;
$this->initConnect();
} 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->initFile();
}
$this->tableName = $config['prefix'] . 'debug_log';
}
public function save(array $log): bool
{
$app_name = app('http')->getName() ?: '';
$controller_name = '';
@@ -81,7 +94,6 @@ class DebugMysql implements LogHandlerInterface
if (App::runningInConsole()) {
$app_name = 'cli';
} else {
$controller_name = request()->controller();
$action_name = request()->action();
}
@@ -100,7 +112,10 @@ class DebugMysql implements LogHandlerInterface
foreach ($log as $log_level => $log_list) {
foreach ($log_list as $key => $log_item) {
if (!is_string($log_item)) {
$log_item = print_r($log_item, true);
}
$log_data = [
'level' => $log_level,
'content' => $log_item,
@@ -109,30 +124,17 @@ class DebugMysql implements LogHandlerInterface
'uid' => $log_key,
'app_name' => $app_name,
'controller_name' => $controller_name,
'action_name' => $action_name
'action_name' => $action_name,
];
if (!is_null($this->pdo)) {
$prepare_name = [];
foreach ($log_data as $key => $value) {
$prepare_name[] = ':' . $key;
try {
if (!is_null($this->pdo)) {
$this->saveByConnect($log_data);
} else {
$this->saveByFile($log_data);
}
$data_keys = array_keys($log_data);
$data_keys_in_sql = join(',', $data_keys);
$prepare_name_in_sql = join(',', $prepare_name);
$sql = "INSERT INTO {$this->tableName} ($data_keys_in_sql) VALUES ($prepare_name_in_sql);";
$stmt = $this->pdo->prepare($sql);
$stmt->execute($log_data);
} else {
fputcsv($this->fileRescource, $log_data);
} catch (\Throwable $th) {
$this->saveByFile($log_data);
}
}
}
@@ -140,9 +142,156 @@ class DebugMysql implements LogHandlerInterface
return true;
}
protected function saveByConnect($log_data)
{
if (is_null($this->pdo)) {
$this->saveByFile($log_data);
return;
}
$this->devLog('save by connect');
$prepare_name = [];
foreach ($log_data as $key => $value) {
$prepare_name[] = ':' . $key;
}
$data_keys = array_keys($log_data);
$data_keys_in_sql = implode(',', $data_keys);
$prepare_name_in_sql = implode(',', $prepare_name);
$sql = "INSERT INTO {$this->tableName} ($data_keys_in_sql) VALUES ($prepare_name_in_sql);";
try {
$stmt = $this->pdo->prepare($sql);
$stmt->execute($log_data);
} catch (\Exception $th) {
if ($this->isBreak($th)) {
if ($this->reConnectTimes > 3) {
$this->initFile();
throw $th;
}
$this->initConnect();
$this->reConnectTimes++;
$this->devLog('reconnect ' . $this->reConnectTimes);
$this->saveByConnect($log_data);
} else {
dump($th);
$this->saveByFile($log_data);
}
}
}
protected function saveByFile($log_data)
{
$this->devLog('save by file');
// 如果文件日志超过100条尝试重新通过数据库连接
if ($this->fileLogTimes > 10) {
$this->fileLogTimes = 0;
$this->initConnect();
$this->saveByConnect($log_data);
return;
}
try {
fputcsv($this->fileRescource, $log_data);
$this->fileLogTimes++;
} catch (\Throwable $th) {
$this->initFile();
$this->fileLogTimes++;
$this->saveByFile($log_data);
}
}
protected function initConnect()
{
$this->devLog('init connect');
if (!is_null($this->pdo)) {
$this->pdo = null;
}
$this->reConnectTimes = 0;
$config = $this->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;
}
return $this;
}
protected function initFile()
{
$this->devLog('init file');
if (!is_null($this->fileRescource)) {
return $this;
}
$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);
}
return $this;
}
/**
* 解析pdo连接的dsn信息
* @access protected
* 是否断线
*
* @param \PDOException|\Exception $e 异常对象
*
* @return bool
*/
protected function isBreak($e): bool
{
$error = $e->getMessage();
foreach ($this->breakMatchStr as $msg) {
if (false !== stripos($error, $msg)) {
return true;
}
}
return false;
}
/**
* 解析pdo连接的dsn信息.
* @param array $config 连接信息
* @return string
*/
@@ -177,4 +326,11 @@ class DebugMysql implements LogHandlerInterface
fclose($this->fileRescource);
}
}
protected function devLog($content)
{
if ($this->devMode) {
dump($content);
}
}
}

View File

@@ -27,6 +27,7 @@ class Service extends \think\Service
public function boot()
{
$this->app->bind(FakerGenerator::class, function () {
return FakerFactory::create($this->app->config->get('app.faker_locale', 'zh_CN'));
});
@@ -47,5 +48,6 @@ class Service extends \think\Service
SeedRun::class,
FactoryCreate::class,
]);
}
}