Пишем свой логер обращений к серверу

Язык: PHP
Framework: Laravel
Демо: http://laravel.jobtools.ru
Исходники:  https://bitbucket.org/gerz/laravel.jobtools.ru/src/master/ 

Поставим задачу: нам нужен простой логгер сохраняющий все обращения к нашему серверу. Для удобства просмотра через WEB интерфейс всех, кто нас посетил, пусть данные будут сохранятся в таблицу MySQL. И пусть будет возможность отключения логгирования в конфиг файле.

Для начала опишем миграцию для создания нашей таблицы логов, для этого в консоли набираем artisan команду:

php artisan make:migration create_logs_table

Редактируем создавшийся файл миграции, который, кстати, по умолчанию создается по следующему пути: \database\migrations\2019_06_04_133003_create_logs_table.php

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateLogTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('logs', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->dateTime('time');
            $table->integer('duration');
            $table->string('ip',100)->nullabe();
            $table->string('url')->nullabe();
            $table->string('method',10)->nullabe();
            $table->string('input')->nullabe();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('logs');
    }
}

Мы определили поля, необходимые для нашего логера:

  • time — время события;
  • duration — длительность;
  • ip — ip адрес зашедшего пользователя;
  • url — адрес, который запросил пользователь;
  • method — HTTP метод (GET, POST);
  • input — передаваемые параметры.

Создадим таблицу по нашей миграции:

php artisan migrate

Таблица создана, приступаем к созданию middleware — фильтр обработки HTTP-запросов. Советую ознакомится с документацией Что такое Middleware. На помощь нам опять придет artisan:

php artisan make:middleware DataLogger

Допишем в созданный по пути \app\Http\Middleware\DataLogger.php файл следующий код:

<?php

namespace App\Http\Middleware;

use Closure;
use App\Models\Log; //Прописываем нашу модель, для возможности доступа к ней

class DataLogger
{
    private $startTime;    //Сохраним в дальнейшем в этой переменной время начала запроса, для просчета общей длительности запроса
    /**
    * Handle an incoming request.
    *
    * @param  \Illuminate\Http\Request  $request
    * @param  \Closure  $next
    * @return mixed
    */
    public function handle($request, Closure $next)  //Функция в которую приходи наш запрос
    {
        $this->startTime = microtime(true);
        return $next($request);
    }

    public function terminate($request, $response)  //Функция, которая вызывается после посылки ответа пользователю
    {
    if ( env('API_DATALOGGER', true) ) {   //Если в env файле прописана опция API_DATALOGGER = true используем логирование
        if ( env('API_DATALOGGER_USE_DB', true) ) {       // Если в env файле прописана опция API_DATA_LOGGER_USE_DB=true, то для сохранения используем БД иначе пишем просто в файл
            $endTime = microtime(true);
            $log = new Log();
            $log->time = gmdate('Y-m-d H:i:s');
            $log->duration = number_format($endTime - LARAVEL_START, 3);
            $log->ip = $request->ip();
            $log->url = $request->fullUrl();
            $log->method = $request->method();
            $log->input = $request->getContent();
            $log->save();     //Сохранили в базу нашу запись
        }
        else              //Если опция записи в БД недоступна пишем в файл
        {
            $endTime = microtime(true);
            $filename = 'api_datalogger_' . date('d-m-y') . '.log';
            $dataToLog  = 'Time: '   . gmdate("F j, Y, g:i a") . "\n";
            $dataToLog .= 'Duration: ' . number_format($endTime - LARAVEL_START, 3) . "\n";
            $dataToLog .= 'IP Address: ' . $request->ip() . "\n";
            $dataToLog .= 'URL: '    . $request->fullUrl() . "\n";
            $dataToLog .= 'Method: ' . $request->method() . "\n";
            $dataToLog .= 'Input: '  . $request->getContent() . "\n";
            \File::append( storage_path('logs' . DIRECTORY_SEPARATOR . $filename), $dataToLog . "\n" . str_repeat("=", 20) . "\n\n");       //Записали в файл
        }
        }
    }
}

В приведенном выше примере, все достаточно просто и прозрачно. При поступлении запроса в наш фильтр (функция Handle) сохраняем время начала запроса. После ответа пользователю в браузер, вызывается ‘завершающая процедура’ terminate, которая в зависимости от параметров в лог файле записывает данный в БД или файл.

Если сейчас запустить сервер и открыть любую страницу нашего приложения, то ничего не произойдет, не будет ни ошибки, ни записи логов. Нужно завершить создание middleware, зарегистрировав его в app\Http\Kernel.php
Допишите в переменной $middleware наш класс (он последний в списке):

   protected $middleware = [
        \App\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \App\Http\Middleware\TrustProxies::class,
        \App\Http\Middleware\DataLogger::class,
    ];

Наш фильтр будет вызываться при каждом запросе к серверу.

В следующей части создадим контроллер и шаблон для просмотра наших логов, хранящихся в базе данных.

3 Replies to “Пишем свой логер обращений к серверу”

  1. В поле duration может возникнуть ошибка, т.к. число вероятнее будет дробным.
    $table->integer(‘duration’);

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *