Пишем логи Laravel в Elasticsearch

Пишем логи Laravel в Elasticsearch

Функции ведения логов Laravel позволяют нам регистрировать различные события, которые могут оказаться полезными при анализе. Но может возникнуть проблема, когда у вас есть большой файл логов с огромным количеством данных.

Помочь в этом может Elasticsearch - мощный инструмент для большого спектра задач, но его также можно использовать для хранения, мониторинга и анализа логов.

Отправлять данные можно через Logstash - инструмент, который умеет принимать и парсить логи из многих источников, но для небольшого проекта мы можем сохранять их в базу данных Elasticsearch напрямую.

Установка пакета

Для начала нужно установить пакет elasticsearch:

composer require elasticsearch/elasticsearch

Добавление провайдера

В директории /app/Providers/ добавляем новый провайдер, создаем файл ElkServiceProvider.php

<?php

namespace App\Providers;

use Elasticsearch\Client;
use Elasticsearch\ClientBuilder;
use Illuminate\Support\ServiceProvider;
use Monolog\Formatter\ElasticsearchFormatter;

/**
 * Class ElkServiceProvider
 *
 * @package App\Providers
 */
class ElkServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton(Client::class, function ($app) {
            $url = $this->getHost();

            return ClientBuilder::create()->setHosts([$url])->build();
        });

        $this->app->bind(ElasticsearchFormatter::class, function ($app) {
            return new ElasticsearchFormatter($this->getIndexName(), $this->getIndexType());
        });
    }

    /**
     * Текущее окружение
     *
     * @return string
     */
    protected function getEnvironmentName(): string
    {
        return env('APP_ENV', 'production');
    }

    /**
     * Тип индекса
     *
     * @return string
     */
    public function getIndexType(): string
    {
        $env = $this->getEnvironmentName();

        return config(sprintf('elk.%s.type', $env));
    }

    /**
     * Имя индекса
     *
     * @return string
     */
    public function getIndexName(): string
    {
        $env = $this->getEnvironmentName();

        return config(sprintf('elk.%s.index', $env));
    }

    /**
     * Хост
     *
     * @return string
     */
    public function getHost(): string
    {
        $env = $this->getEnvironmentName();

        $schema = config(sprintf('elk.%s.schema', $env));
        $domain = config(sprintf('elk.%s.domain', $env));
        $port   = config(sprintf('elk.%s.port', $env));

        return $this->buildUrl($schema, $domain, $port);
    }

    /**
     * Сформировать путь к elk
     *
     * @param string $schema
     * @param string $domain
     * @param string $port
     *
     * @return string
     */
    protected function buildUrl(string $schema, string $domain, string $port): string
    {
        return sprintf('%s://%s:%s', $schema, $domain, $port);
    }
}

И регистрируем его в /config/app.php, в секции providers:

<?php

return [

    ...

    'providers' => [
        /*
         * Application Service Providers...
         */
        App\Providers\ElkServiceProvider::class,
    ]

    ...

]

Настройка

Далее создаем конфигурационный файл с настройками /config/elk.php

Для примера у нас будет 2 индекса: один для production версии, второй для локальной. Индексы делаем с ротацией каждый день.
Также прописываем протокол, IP адрес сервера и порт.

Логи будут сохраняться в нужный индекс, в зависимости от переменной APP_ENV, которая устанавливается в .env файле.

<?php

return [
    'production' => [
        'schema' => 'http',
        'domain' => '123.456.78.90',
        'port'   => '9201',
        'index'  => 'laravel-production-' . date('Y.m.d'),
        'type'   => '_doc'
    ],
    'local'      => [
        'schema' => 'http',
        'domain' => '123.456.78.90',
        'port'   => '9201',
        'index'  => 'laravel-local-' . date('Y.m.d'),
        'type'   => '_doc'
    ]
];

Использование

Теперь вы можете сохранять логи в Elasticsearch, указав канал custom.

Например:

<?php

use Illuminate\Support\Facades\Log;

Log::channel('custom')->error('Не удалось отправить уведомление', $data);
Копирование материалов сайта возможно только с указанием ссылки на первоисточник.