Yii使用DbTarget實現日誌功能

huaweichenai發表於2020-07-21

一:在配置檔案的log元件中配置DbTarget

image.png

'log' => [
    'traceLevel' => YII_DEBUG ? 3 : 0,
    'targets' => [
        [
            'class' => 'yii\log\FileTarget',
            'levels' => ['error', 'warning'],
        ],
        'test' => [
            'class' => 'yii\log\DbTarget',//DaTarget類
            'logTable' => '{{%test_log}}',//日誌表
            'levels' => ['error', 'info', 'warning'],//日誌等級
        ],
    ],
],

二:生成日誌表

在專案目錄下執行如下命令生成日誌表

php yii migrate --migrationPath=@yii/log/migrations/

三:使用日誌

在需要使用日誌的地方使用

Yii::info()

四:自定義DbTarget日誌

1:首先建立一個自定義的日誌表

(1)在專案目錄下執行

php yii migrate/create create_test_log  

(2):在建立的migrate檔案下編寫建立資料庫的遷移指令碼

<?php
use yii\db\Migration;
/**
 * Class m200720_091126_create_test_log
 */
class m200720_091126_create_test_log extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function safeUp()
    {
        $this->createTable('{{%test_log}}', [
            'id' => $this->bigPrimaryKey(),
            'level' => $this->integer()->notNull()->comment('日誌等級'),
            'category' => $this->string(100)->notNull()->comment('分類名稱'),
            'prefix' => $this->text(),
            'route' => $this->string(100)->notNull()->comment('路由'),
            'method' => $this->string(20)->notNull()->comment('請求方式'),
            'app' => $this->string(20)->comment('請求應用'),
            'module' => $this->string(20)->comment('請求模組'),
            'request' => $this->text()->comment('請求引數'),
            'status' => $this->string(10)->notNull()->comment('狀態碼'),
            'message' => $this->text()->comment('日誌內容'),
            'request_at' => $this->double()->notNull()->comment('請求時間'),
            'ip' => $this->string(63)->comment('請求IP'),
        ], 'CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE=InnoDB COMMENT=\'請求日誌表\'');
        //增加索引
        $this->createIndex('idx_log_level', '{{%test_log}}', 'level');
        $this->createIndex('idx_log_category', '{{%test_log}}', 'category');
        $this->createIndex('idx_log_route', '{{%test_log}}', 'route');
        $this->createIndex('idx_log_method', '{{%test_log}}', 'method');
        $this->createIndex('idx_log_status', '{{%test_log}}', 'status');
    }
    /**
     * @inheritdoc
     */
    public function safeDown()
    {
        $this->dropTable('{{%test_log}}');
    }
}

(3):執行如下命令生成DbTarget日誌表

php yii migrate

2:編寫一個DbTarget類來繼承yiilogDbTarget類

<?php
namespace app\components;
use Yii;
use yii\helpers\VarDumper;
use yii\log\LogRuntimeException;
use yii\web\HttpException;
use yii\web\Request;
/**
 * DbTarget stores log messages in a database table.
 *
 * @see yii\log\DbTarget
 *
 * @author wangjian
 * @since 1.0
 */
class DbTarget extends \yii\log\DbTarget
{
    /**
     * @inheritdoc
     */
    public $categories = [
        'application',
        'yii\web\HttpException:*',
    ];
    /**
     * @inheritdoc
     */
    public $except = [
        // 'yii\web\HttpException:404',
    ];
    /**
     * @inheritdoc
     */
    public $logVars = ['_GET', '_POST'];
    /**
     * @var string 使用者元件ID
     */
    public $userComponentId = 'user';
    /**
     * @inheritdoc
     */
    public function collect($messages, $final)
    {
        $this->messages = array_merge($this->messages, static::filterMessages($messages, $this->getLevels(), $this->categories, $this->except));
        $count = count($this->messages);
        if ($count > 0 && ($final || $this->exportInterval > 0 && $count >= $this->exportInterval)) {
            $oldExportInterval = $this->exportInterval;
            $this->exportInterval = 0;
            $this->export();
            $this->exportInterval = $oldExportInterval;
            $this->messages = [];
        }
    }
    /**
     * @inheritdoc
     */
    public function getMessagePrefix($message)
    {
        if ($this->prefix !== null) {
            return call_user_func($this->prefix, $message);
        }
        if (Yii::$app === null) {
            return '';
        }
        $ip = $this->getIp();
        $ip = empty($ip) ? '-' : $ip;
        return "[$ip]";
    }
    /**
     * @inheritdoc
     */
    public function export()
    {
        if ($this->db->getTransaction()) {
            $this->db = clone $this->db;
        }
        $tableName = $this->db->quoteTableName($this->logTable);
        $sql = "INSERT INTO $tableName ([[level]], [[category]], [[prefix]], [[route]], [[method]], [[app]], [[module]], [[request]], [[status]], [[message]], [[request_at]], [[ip]])
                VALUES (:level, :category, :prefix, :route, :method, :app, :module, :request, :status, :message, :request_at, :ip)";
        $command = $this->db->createCommand($sql);
        $request = Yii::$app->getRequest();
        list($route, $params) = $request->resolve();
        $method = $request->getMethod();
        $module = Yii::$app->controller->module->id;
        $route = str_replace("{$module}/", '', $route);
        foreach ($this->messages as $message) {
            list($text, $level, $category, $timestamp) = $message;
            $statusCode = 200;
            if (!is_string($text)) {
                if ($text instanceof \Throwable || $text instanceof \Exception) {
                    $statusCode = $text instanceof HttpException ? $text->statusCode : 500;
                    $text = $text->getMessage();
                } else {
                    $text = VarDumper::export($text);
                }
            }
            if ($command->bindValues([
                    ':level' => $level,
                    ':category' => $category,
                    ':prefix' => $this->getMessagePrefix($message),
                    ':route' => $route,
                    ':method' => $method,
                    ':app' => Yii::$app->id,
                    ':module' => $module,
                    ':request' => $this->getContextMessage(),
                    ':status' => $statusCode,
                    ':message' => $text,
                    ':request_at' => $timestamp,
                    ':ip' => $this->getIp(),
                ])->execute() > 0) {
                continue;
            }
            throw new LogRuntimeException('Unable to export log through database!');
        }
    }
    /**
     * 獲取當前IP
     */
    protected function getIp()
    {
        $request = Yii::$app->getRequest();
        return $request instanceof Request ? $request->getUserIP() : '';
    }
}

3:在配置檔案中將yiilogDbTarget類改成我們自定義的類

'log' => [
    'traceLevel' => YII_DEBUG ? 3 : 0,
    'targets' => [
        [
            'class' => 'yii\log\FileTarget',
            'levels' => ['error', 'warning'],
        ],
        'test' => [
            'class' => 'app\components\DbTarget',
            'logTable' => '{{%test_log}}',
            'levels' => ['error', 'info', 'warning'],
        ],
    ],
],

4:使用DbTarget日誌

同樣的使用Yii::info來記錄日誌

相關文章