Налаштування логування (ELK Stack) для веб-застосунку

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.
Розробка та обслуговування будь-яких видів сайтів:
Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Налаштування логування (ELK Stack) для веб-застосунку
Складна
~5 робочих днів
Часті питання
Наші компетенції:
Етапи розробки
Останні роботи
  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Налаштування логування (ELK Stack) для вашого веб-застосунку

ELK (Elasticsearch + Logstash + Kibana) або сучасний варіант з Beats — це зріла платформа для централізованого логування. Вона справляється з обсягами від кількох гігабайтів на день до терабайтів, але потребує ретельного налаштування індексів, ресурсів та політики збереження. Без цього кластер швидко деградує.

Вибір схеми: ELK vs EFK

Класичний стек: Filebeat → Logstash → Elasticsearch → Kibana

Спрощений для малих обсягів: Filebeat → Elasticsearch (без Logstash, парсинг через ingest pipelines прямо в ES)

Для Kubernetes: Fluent Bit → Elasticsearch → Kibana (Fluent Bit легший ніж Filebeat, нативна інтеграція з k8s metadata)

Вибір залежить від складності трансформацій: якщо потрібні складні збагачення, grok-парсинг та маршрутизація до кількох місць призначення — Logstash незамінний. Якщо логи структуровані (JSON) і потрібна просто доставка — ingest pipelines достатньо.

Розгортування через Docker Compose

Для dev/staging середовищ:

# docker-compose.yml
version: '3.8'
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.13.0
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=true
      - xpack.security.http.ssl.enabled=false
      - ELASTIC_PASSWORD=changeme
      - "ES_JAVA_OPTS=-Xms2g -Xmx2g"
    volumes:
      - esdata:/usr/share/elasticsearch/data
    ports:
      - "9200:9200"
    ulimits:
      memlock:
        soft: -1
        hard: -1

  kibana:
    image: docker.elastic.co/kibana/kibana:8.13.0
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
      - ELASTICSEARCH_USERNAME=kibana_system
      - ELASTICSEARCH_PASSWORD=changeme
    ports:
      - "5601:5601"
    depends_on:
      - elasticsearch

  logstash:
    image: docker.elastic.co/logstash/logstash:8.13.0
    volumes:
      - ./logstash/pipeline:/usr/share/logstash/pipeline
      - ./logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml
    ports:
      - "5044:5044"   # Beats input
      - "5000:5000"   # TCP input
    depends_on:
      - elasticsearch

volumes:
  esdata:

Logstash Pipeline

Конфігурація для парсинга Nginx access логів та application JSON логів:

# logstash/pipeline/main.conf
input {
  beats {
    port => 5044
  }
  tcp {
    port => 5000
    codec => json_lines
  }
}

filter {
  if [fields][log_type] == "nginx_access" {
    grok {
      match => {
        "message" => '%{IPORHOST:client_ip} - %{DATA:user} \[%{HTTPDATE:timestamp}\] "%{WORD:method} %{DATA:request} HTTP/%{NUMBER:http_version}" %{NUMBER:status_code:int} %{NUMBER:bytes_sent:int} "%{DATA:referrer}" "%{DATA:user_agent}" %{NUMBER:request_time:float}'
      }
    }
    date {
      match => ["timestamp", "dd/MMM/yyyy:HH:mm:ss Z"]
      target => "@timestamp"
    }
    geoip {
      source => "client_ip"
      target => "geoip"
    }
    useragent {
      source => "user_agent"
      target => "ua"
    }
    mutate {
      remove_field => ["message", "timestamp"]
    }
  }

  if [fields][log_type] == "app_json" {
    json {
      source => "message"
      target => "app"
    }
    mutate {
      remove_field => ["message"]
    }
  }
}

output {
  if [fields][log_type] == "nginx_access" {
    elasticsearch {
      hosts => ["http://elasticsearch:9200"]
      user => "elastic"
      password => "changeme"
      index => "nginx-access-%{+YYYY.MM.dd}"
    }
  } else {
    elasticsearch {
      hosts => ["http://elasticsearch:9200"]
      user => "elastic"
      password => "changeme"
      index => "app-logs-%{+YYYY.MM.dd}"
    }
  }
}

Filebeat на серверах застосунків

# /etc/filebeat/filebeat.yml
filebeat.inputs:
  - type: log
    enabled: true
    paths:
      - /var/log/nginx/access.log
    fields:
      log_type: nginx_access
    fields_under_root: false
    multiline:
      # Nginx access log — однорядковий, multiline не потрібен

  - type: log
    enabled: true
    paths:
      - /var/www/app/storage/logs/laravel.log
    fields:
      log_type: app_json
    multiline:
      pattern: '^\['
      negate: true
      match: after
      max_lines: 50

output.logstash:
  hosts: ["logstash-server:5044"]
  ssl.enabled: false

# Metadata хоста
processors:
  - add_host_metadata:
      when.not.contains.tags: forwarded
  - add_fields:
      target: ''
      fields:
        environment: production
        service: web-app

Отправка логів застосунку безпосередньо

Для Laravel — через користувацький Monolog handler:

// config/logging.php
'channels' => [
    'logstash' => [
        'driver' => 'custom',
        'via' => App\Logging\LogstashLogger::class,
        'host' => env('LOGSTASH_HOST', 'logstash'),
        'port' => env('LOGSTASH_PORT', 5000),
        'level' => 'debug',
    ],
    'stack' => [
        'driver' => 'stack',
        'channels' => ['daily', 'logstash'],
    ],
],
// app/Logging/LogstashLogger.php
namespace App\Logging;

use Monolog\Logger;
use Monolog\Handler\SocketHandler;
use Monolog\Formatter\JsonFormatter;

class LogstashLogger
{
    public function __invoke(array $config): Logger
    {
        $handler = new SocketHandler(
            "tcp://{$config['host']}:{$config['port']}"
        );
        $handler->setFormatter(new JsonFormatter());

        return new Logger('app', [$handler]);
    }
}

Тепер кожен Log::error(...) відправляє структурований JSON безпосередньо в Logstash.

Index Lifecycle Management (ILM)

Без ILM індекси зростають безконтрольно і заповнюють диск. Політика для логів застосунків:

PUT _ilm/policy/app-logs-policy
{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_size": "5gb",
            "max_age": "1d"
          },
          "set_priority": { "priority": 100 }
        }
      },
      "warm": {
        "min_age": "3d",
        "actions": {
          "shrink": { "number_of_shards": 1 },
          "forcemerge": { "max_num_segments": 1 },
          "set_priority": { "priority": 50 }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "freeze": {},
          "set_priority": { "priority": 0 }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": { "delete": {} }
      }
    }
  }
}

Index template пов'язує політику з паттерном:

PUT _index_template/app-logs-template
{
  "index_patterns": ["app-logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 2,
      "number_of_replicas": 1,
      "index.lifecycle.name": "app-logs-policy",
      "index.lifecycle.rollover_alias": "app-logs"
    }
  }
}

Kibana: базові налаштування

Після першого запуску:

  1. Stack Management → Index Patterns → створіть паттерн app-logs-* з полем @timestamp
  2. Discover → виберіть паттерн → переконайтесь що дані надходять
  3. Dashboards → створіть дашборд з віджетами:
    • Top error messages (Terms aggregation по app.message.keyword)
    • HTTP status distribution (Pie chart по status_code)
    • Request rate (Date histogram по @timestamp)
    • Error rate by service (Bar chart з фільтром app.level: error)

Збережені пошуки для швидкого доступу:

  • app.level: error AND environment: production
  • status_code >= 500
  • request_time > 3

Продуктивність Elasticsearch

Критичні налаштування для production:

# /etc/elasticsearch/elasticsearch.yml
cluster.name: app-logging
node.name: es-node-1

# Запобігання своупу
bootstrap.memory_lock: true

# Heap — не більше 50% RAM, не більше 31g (JVM compressed oops limit)
# Встановіть через ES_JAVA_OPTS або jvm.options

# Slow log для налагодження повільних запитів
index.search.slowlog.threshold.query.warn: 2s
index.indexing.slowlog.threshold.index.warn: 1s

# Thread pool для bulk indexing
thread_pool.write.queue_size: 200

Оптимальна кількість шардів: 1 шард ≈ 20-40 GB. Занадто багато мілких шардів (oversharding) — часта причина деградації кластера.

Алерти через Kibana Alerting

// Watcher для алертингу на 5xx помилки
PUT _watcher/watch/high-error-rate
{
  "trigger": { "schedule": { "interval": "5m" } },
  "input": {
    "search": {
      "request": {
        "indices": ["nginx-access-*"],
        "body": {
          "query": {
            "bool": {
              "filter": [
                { "range": { "@timestamp": { "gte": "now-5m" } } },
                { "range": { "status_code": { "gte": 500 } } }
              ]
            }
          }
        }
      }
    }
  },
  "condition": {
    "compare": { "ctx.payload.hits.total.value": { "gt": 50 } }
  },
  "actions": {
    "notify_telegram": {
      "webhook": {
        "method": "POST",
        "url": "https://api.telegram.org/bot<TOKEN>/sendMessage",
        "body": "{\"chat_id\": \"<CHAT_ID>\", \"text\": \"5xx error spike: {{ctx.payload.hits.total.value}} errors in 5m\"}"
      }
    }
  }
}

Розклад

Розгортування ELK через Docker Compose, налаштування Filebeat на 3-5 серверах, базові pipelines для Nginx та застосунку, ILM-політика, початкові дашборди в Kibana: 2-3 робочі дні.

Повноцінне налаштування з парсингом усіх типів логів, алертингом, налаштуванням безпеки (TLS, RBAC), production-конфігурацією кластера з репліакцією: 5-7 днів.

Масштабування існуючого кластера або міграція з іншої системи логування оцінюється окремо після аудиту.