// DevOps

¿Cómo organizar de forma económica la recopilación de logs de clientes remotos vía HTTP?

Publicado el 22.09.2026

Si la aplicación funciona en cientos de dispositivos cliente o tiene una flota de sensores con telemetría, tarde o temprano necesitará ver qué ocurre en el terreno. Los sistemas comerciales como Splunk o Datadog son excesivos y caros para esto, y si los clientes saben enviar peticiones HTTP, gran parte de la tarea ya está resuelta: HTTPS pasa por casi cualquier cortafuegos y solo hace falta un servidor que reciba los logs.

A continuación — tres maneras económicas de organizar la recopilación de logs por HTTP: desde un script sencillo hasta una opción serverless. Los principios generales de logging centralizado se tratan en el artículo Registro centralizado: Parte 1 — ¿Por qué recopilar los registros en un solo lugar?.


Por qué HTTP

  • Universalidad — hay cliente HTTP en cualquier lenguaje de programación y en cualquier plataforma.
  • Sencillez — es fácil formar y depurar un POST con cuerpo JSON.
  • Accesibilidad — el puerto 443 está abierto casi en todas partes, a diferencia de los puertos de syslog o GELF.

Utilice solo HTTPS. Un certificado gratuito de Let’s Encrypt se configura en minutos y protege los logs contra interceptación; sobre centros de certificación gratuitos lea el artículo Más allá de Let’s Encrypt: reseña de las mejores alternativas gratuitas para SSL/TLS.


Opción 1. Script en un VPS

La forma más sencilla, que se despliega más rápido. Adecuada para proyectos pequeños y un número reducido de clientes.

Qué se necesita

  • Un VPS económico de cualquier proveedor.
  • Nginx y una pequeña aplicación en cualquier lenguaje (Python/Flask, Node.js/Express, PHP).

Cómo funciona

  1. Nginx recibe peticiones HTTPS y las pasa a la aplicación.

  2. La aplicación (unas veinte líneas de código):

    • acepta POST en /log;
    • verifica una clave secreta en el encabezado X-API-Key;
    • añade el JSON a un archivo de log.

Ejemplo en 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)

La clave se toma de la variable de entorno LOG_API_KEY sin valor por defecto: si la variable no está definida, la aplicación no se iniciará, en lugar de aceptar logs con una clave estándar. El servidor integrado de Flask sirve para pruebas; en producción la aplicación se ejecuta mediante un servidor WSGI (por ejemplo, gunicorn).

Ventajas

  • Costes mínimos — el coste del VPS más barato.
  • Control total sobre el almacenamiento.
  • Despliegue en media hora.

Desventajas

  • Escalado pobre: escribir en un único archivo se convierte pronto en cuello de botella.
  • Requiere mantenimiento: rotación de logs, control de disco, actualizaciones.
  • El análisis — manualmente, vía SSH y grep.

Opción 2. Recolector Vector y almacenamiento Loki

Evolución de la primera opción usando componentes abiertos ya hechos.

Qué se necesita

  • VPS con 1–2 GB de memoria.
  • Vector, Loki y Grafana.

Cómo funciona

  1. Vector acepta logs por HTTP, los procesa y reenvía.
  2. Loki almacena los logs e indexa solo las etiquetas (labels), por eso el almacenamiento sale barato.
  3. Grafana — interfaz para búsqueda y análisis de logs con LogQL.

Más detalles sobre Loki y Grafana en el artículo Registro centralizado: Parte 5 — Loki y Grafana.

Ejemplo de configuración de Vector (vector.yaml)

La fuente http_server tiene dos maneras de verificar el acceso: basic (usuario y contraseña) y custom — verificación en VRL. Más abajo la clave se comprueba por el encabezado X-API-Key usando 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"

Reemplace super-secret-key por una clave larga y aleatoria. Vector en este ejemplo escucha el puerto 8080 por HTTP, por lo que delante debe haber Nginx u otro proxy inverso con HTTPS, y el propio puerto 8080 no debe ser accesible desde Internet.

Ventajas

  • Alto rendimiento.
  • Búsqueda y filtrado en Grafana.
  • Vector puede enviar logs no solo a Loki, sino también a S3, ClickHouse y otros almacenamientos.

Desventajas

  • Configuración más compleja: tres componentes que hay que ejecutar (vía Docker o systemd) y actualizar.

Opción 3. Serverless

No hace falta servidor: se paga solo por peticiones y almacenamiento.

Qué se necesita

  • Cuenta en la nube. Para empresas rusas está disponible Yandex Cloud: Cloud Functions, API Gateway y Object Storage. Los servicios de AWS y Google Cloud las empresas rusas no pueden pagar.
  • Stack (ejemplo abajo — AWS): API Gateway + Lambda + S3. En Yandex Cloud el esquema es el mismo: API Gateway invoca una Cloud Function que escribe en Object Storage mediante la API compatible con S3.

Cómo funciona

  1. El cliente envía un POST.
  2. API Gateway lo recibe en una dirección HTTP gestionada.
  3. El gateway invoca la función.
  4. La función guarda el JSON en el almacenamiento de objetos, ordenando los archivos por fecha.

Ejemplo de función en Python (escritura en 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'}

Ventajas

  • Convolumen de logs pequeño los costes caben en los límites gratuitos de la nube o son casi nulos — consulte las condiciones de los límites con el proveedor.
  • Escalado sin su intervención.
  • No hay servidores que actualizar.

Desventajas

  • Para el análisis hacen falta servicios adicionales (por ejemplo, Athena, BigQuery o consultas sobre el almacenamiento).
  • Dependencia de un proveedor de nube concreto.
  • Cada petición crea un objeto independiente: con un flujo alto de logs esto es ineficiente, por lo que conviene que los clientes envíen logs en lotes.

Recomendaciones para el cliente

  1. Envió asíncrono — no bloquee el hilo principal de la aplicación.
  2. Enviar en lotes — acumule registros y envíelos, por ejemplo, cada 30 segundos o cada 50 eventos.
  3. Reintentos — guarde localmente los logs no enviados (por ejemplo, en SQLite) y envíelos más tarde.
  4. Filtrado — envíe solo los niveles necesarios (INFO, WARN, ERROR).

Conclusión

Recopilar logs de clientes remotos puede salir barato — haga la elección adecuada según la tarea.

SituaciónOpción
El proyecto acaba de empezarServerless
Ya hay un VPS, y grep bastaScript en un VPS
Necesita búsqueda y análisis en su servidorVector + Loki + Grafana

// Contact

¿Necesitas ayuda?

Escríbeme y te ayudaré a resolver el problema

Respondo en un día laborable (03:00-13:00 GMT)

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

Confirme que no es un bot.

Escribir y recibir una respuesta rápida