// DevOps

Как недорого организовать сбор логов с удалённых клиентов по HTTP

Опубликовано 22.09.2026

Если приложение работает на сотнях клиентских устройств или у вас парк датчиков с телеметрией, рано или поздно понадобится видеть, что происходит на местах. Коммерческие системы вроде Splunk или Datadog для этого избыточны и дороги, а если клиенты умеют отправлять HTTP-запросы, большая часть задачи уже решена: HTTPS проходит почти через любой межсетевой экран, и нужен только сервер, который будет принимать логи.

Ниже — три недорогих способа организовать сбор логов по HTTP: от простого скрипта до бессерверного варианта. Общие принципы централизованного логирования разобраны в статье «Централизованное логирование: Часть 1 — Зачем собирать логи в одном месте».


Почему HTTP

  • Универсальность — HTTP-клиент есть в любом языке программирования и на любой платформе.
  • Простота — POST-запрос с JSON-телом легко сформировать и отладить.
  • Доступность — порт 443 открыт почти везде, в отличие от портов syslog или GELF.

Используйте только HTTPS. Бесплатный сертификат Let’s Encrypt настраивается за несколько минут и защищает логи от перехвата; о бесплатных центрах сертификации см. статью «За пределами Let’s Encrypt».


Вариант 1. Скрипт на VPS

Самый простой способ, который разворачивается быстрее всего. Подходит для небольших проектов и небольшого числа клиентов.

Что потребуется

  • Недорогой VPS у любого провайдера.
  • Nginx и небольшое приложение на любом языке (Python/Flask, Node.js/Express, PHP).

Как это работает

  1. Nginx принимает HTTPS-запросы и передаёт их приложению.

  2. Приложение (около двадцати строк кода):

    • принимает POST-запросы на /log;
    • проверяет секретный ключ в заголовке X-API-Key;
    • дописывает JSON в файл журнала.

Пример на Python (Flask)

python
from flask import Flask, request, abort
import os
import json

app = Flask(__name__)

API_KEY = os.environ["LOG_API_KEY"]

@app.route('/log', methods=['POST'])
def receive_log():
    if request.headers.get('X-API-Key') != API_KEY:
        abort(401)

    data = request.get_json(silent=True)
    if not data:
        abort(400)

    try:
        with open("/var/log/my-app/events.log", "a") as f:
            f.write(json.dumps(data) + "\n")
    except Exception as e:
        print(f"Failed to write log: {e}")
        abort(500)

    return "OK", 200

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=5000)

Ключ берётся из переменной окружения LOG_API_KEY без значения по умолчанию: если переменная не задана, приложение не запустится, а не будет принимать логи со стандартным ключом. Встроенный сервер Flask подходит для проверки; в рабочей установке приложение запускают через WSGI-сервер (например, gunicorn).

Достоинства

  • Минимальные затраты — стоимость самого дешёвого VPS.
  • Полный контроль над хранением.
  • Развёртывание за полчаса.

Недостатки

  • Плохо масштабируется: запись в один файл быстро становится узким местом.
  • Нужно обслуживание: ротация журналов, контроль диска, обновления.
  • Анализ — вручную, через SSH и grep.

Вариант 2. Сборщик Vector и хранилище Loki

Развитие первого варианта на готовых открытых компонентах.

Что потребуется

  • VPS с 1–2 ГБ памяти.
  • Vector, Loki и Grafana.

Как это работает

  1. Vector принимает логи по HTTP, обрабатывает и пересылает их.
  2. Loki хранит логи и индексирует только метки (labels), поэтому хранение обходится дёшево.
  3. Grafana — интерфейс для поиска и анализа логов на языке LogQL.

Подробнее о Loki и Grafana — в статье «Централизованное логирование: Часть 5 — Loki и Grafana».

Пример конфигурации Vector (vector.yaml)

У источника http_server есть два способа проверки доступа: basic (логин и пароль) и custom — проверка на языке VRL. Ниже ключ проверяется по заголовку X-API-Key через custom.

yaml
sources:
  http_logs:
    type: "http_server"
    address: "0.0.0.0:8080"
    decoding:
      codec: "json"
    auth:
      strategy: "custom"
      source: |-
        .headers."x-api-key" == "super-secret-key"

transforms:
  my_transform:
    type: "remap"
    inputs: ["http_logs"]
    source: |
      .app = "my_mobile_app"
      if !exists(.level) { .level = "info" }

sinks:
  loki:
    type: "loki"
    inputs: ["my_transform"]
    endpoint: "http://localhost:3100"
    labels:
      app: "{{ app }}"
      level: "{{ level }}"
    encoding:
      codec: "json"

Замените super-secret-key на длинный случайный ключ. Vector в этом примере слушает порт 8080 по HTTP, поэтому перед ним должен стоять Nginx или другой обратный прокси с HTTPS, а сам порт 8080 не должен быть доступен из интернета.

Достоинства

  • Высокая производительность.
  • Поиск и фильтрация в Grafana.
  • Vector умеет отправлять логи не только в Loki, но и в S3, ClickHouse и другие хранилища.

Недостатки

  • Сложнее настройка: три компонента, которые нужно запускать через Docker или systemd и обновлять.

Вариант 3. Бессерверный

Сервер не нужен: оплачиваются только запросы и хранение.

Что потребуется

  • Аккаунт в облаке. Для российских компаний доступен Yandex Cloud: Cloud Functions, API Gateway и Object Storage. Сервисы AWS и Google Cloud российские компании оплатить не могут.
  • Стек (пример ниже — AWS): API Gateway + Lambda + S3. В Yandex Cloud схема та же: API Gateway вызывает Cloud Function, которая пишет в Object Storage через S3-совместимый API.

Как это работает

  1. Клиент отправляет POST-запрос.
  2. API Gateway принимает его на управляемом HTTP-адресе.
  3. Шлюз вызывает функцию.
  4. Функция сохраняет JSON в объектное хранилище, раскладывая файлы по дате.

Пример функции на Python (запись в S3)

python
import json
import boto3
import time
import os

s3 = boto3.client('s3')
BUCKET_NAME = os.environ['LOG_BUCKET_NAME']

def lambda_handler(event, context):
    body = event.get('body')
    if not body:
        return {'statusCode': 400, 'body': 'No data'}

    try:
        log_data = json.loads(body)
    except json.JSONDecodeError:
        return {'statusCode': 400, 'body': 'Invalid JSON'}

    now = time.strftime('%Y/%m/%d/%H', time.gmtime())
    file_name = f"{context.aws_request_id}.json"
    s3_key = f"logs/{now}/{file_name}"

    try:
        s3.put_object(
            Bucket=BUCKET_NAME,
            Key=s3_key,
            Body=json.dumps(log_data),
            ContentType='application/json'
        )
        return {'statusCode': 200, 'body': 'OK'}
    except Exception as e:
        print(e)
        return {'statusCode': 500, 'body': 'Error saving log'}

Достоинства

  • При небольшом объёме логов затраты укладываются в бесплатные лимиты облака или близки к нулю — условия лимитов уточняйте у провайдера.
  • Масштабирование без вашего участия.
  • Нет серверов, которые нужно обновлять.

Недостатки

  • Для анализа нужны дополнительные сервисы (например, Athena, BigQuery или запросы к хранилищу).
  • Привязка к конкретному облачному провайдеру.
  • Каждый запрос создаёт отдельный объект: при большом потоке логов это неэффективно, поэтому клиентам стоит отправлять логи пачками.

Рекомендации для клиента

  1. Асинхронная отправка — не блокируйте основной поток приложения.
  2. Отправка пачками — копите записи и отправляйте их, например, раз в 30 секунд или по 50 событий.
  3. Повторные попытки — сохраняйте неотправленные логи локально (например, в SQLite) и отправляйте позже.
  4. Фильтрация — отправляйте только нужные уровни (INFO, WARN, ERROR).

Итог

Собрать логи с удалённых клиентов можно недорого — нужно выбрать подход под задачу.

СитуацияВариант
Проект только начинаетсяБессерверный
Уже есть VPS, хватает grepСкрипт на VPS
Нужны поиск и анализ на своём сервереVector + Loki + Grafana

// Contact

Нужна помощь?

Свяжись со мной и я помогу решить проблему

Написать в Telegram

Отвечаю в течение рабочего дня (03:00–13:00 GMT)

Или оставьте заявку здесь:

Подтвердите, что вы не бот.

Написать и получить быстрый ответ