# API Документация LogBoard+ **Автор:** Сергей Антропов **Сайт:** https://devops.org.ru ## Содержание 1. [Обзор API](#обзор-api) 2. [Аутентификация](#аутентификация) 3. [REST API](#rest-api) 4. [WebSocket API](#websocket-api) 5. [Коды ошибок](#коды-ошибок) 6. [Примеры использования](#примеры-использования) ## Обзор API LogBoard+ предоставляет REST API и WebSocket API для работы с логами Docker контейнеров. API разработан для интеграции с системами мониторинга и автоматизации, а также для создания собственных клиентов. ### 🎯 **Применение API** - **Интеграция с CI/CD** - автоматический мониторинг логов в пайплайнах - **Собственные дашборды** - создание кастомных интерфейсов мониторинга - **Автоматизация** - скрипты для анализа логов и алертинга - **Локальная разработка** - интеграция с IDE и инструментами разработки ### Базовый URL ``` http://localhost:9001 ``` ### Форматы данных - **Content-Type:** `application/json` - **Кодировка:** UTF-8 - **Временные метки:** ISO 8601 (UTC) ### Аутентификация Все API endpoints (кроме `/healthz` и `/api/auth/login`) требуют JWT токен в заголовке: ``` Authorization: Bearer ``` ## Аутентификация ### POST /api/auth/login Вход в систему и получение JWT токена. **Запрос:** ```json { "username": "admin", "password": "your-password" } ``` **Ответ:** ```json { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "bearer" } ``` **Пример curl:** ```bash curl -X POST "http://localhost:9001/api/auth/login" \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"your-password"}' ``` ### POST /api/auth/logout Выход из системы (удаление токена из cookies). **Ответ:** ```json { "message": "Успешный выход из системы" } ``` ### GET /api/auth/me Получение информации о текущем пользователе. **Заголовки:** ``` Authorization: Bearer ``` **Ответ:** ```json { "username": "admin" } ``` ## REST API ### Контейнеры и сервисы #### GET /api/services Получение списка всех контейнеров. **Параметры запроса:** | Параметр | Тип | Описание | По умолчанию | |----------|-----|----------|--------------| | `projects` | string | Фильтр по проектам (через запятую) | Все проекты | | `include_stopped` | boolean | Включить остановленные контейнеры | false | **Пример запроса:** ```bash curl -X GET "http://localhost:9001/api/services?projects=myproject&include_stopped=true" \ -H "Authorization: Bearer YOUR_TOKEN" ``` **Ответ:** ```json [ { "id": "abc123def456", "name": "myproject_web_1", "status": "running", "image": "nginx:latest", "service": "web", "project": "myproject", "health": "healthy", "ports": ["80/tcp"], "url": "http://localhost:8080", "host_port": "8080" }, { "id": "def456ghi789", "name": "myproject_db_1", "status": "running", "image": "postgres:13", "service": "db", "project": "myproject", "health": "healthy", "ports": ["5432/tcp"], "url": null, "host_port": null } ] ``` #### GET /api/projects Получение списка всех проектов Docker Compose. **Ответ:** ```json [ "myproject", "another-project", "standalone" ] ``` ### Логи #### GET /api/logs/{container_id} Получение логов контейнера. **Параметры пути:** - `container_id` - ID контейнера (первые 12 символов) **Параметры запроса:** | Параметр | Тип | Описание | По умолчанию | |----------|-----|----------|--------------| | `tail` | string | Количество строк или 'all' | 500 | | `since` | string | Время начала (ISO 8601 или относительное) | null | **Пример запроса:** ```bash curl -X GET "http://localhost:9001/api/logs/abc123def456?tail=100&since=2024-01-15T10:00:00Z" \ -H "Authorization: Bearer YOUR_TOKEN" ``` **Ответ:** ```json { "container": { "id": "abc123def456", "name": "myproject_web_1", "status": "running", "image": "nginx:latest", "created": "2024-01-15T09:00:00.000000000Z", "state": { "Status": "running", "Running": true, "Paused": false, "Restarting": false, "OOMKilled": false, "Dead": false, "Pid": 1234, "ExitCode": 0, "Error": "", "StartedAt": "2024-01-15T09:00:00.000000000Z", "FinishedAt": "0001-01-01T00:00:00Z" } }, "logs": [ { "timestamp": "2024-01-15T10:30:15.123456789Z", "message": "2024/01/15 10:30:15 [notice] 1#1: start worker processes", "raw": "2024-01-15T10:30:15.123456789Z 2024/01/15 10:30:15 [notice] 1#1: start worker processes" }, { "timestamp": "2024-01-15T10:30:15.123456789Z", "message": "2024/01/15 10:30:15 [notice] 1#1: start worker process 1234", "raw": "2024-01-15T10:30:15.123456789Z 2024/01/15 10:30:15 [notice] 1#1: start worker process 1234" } ], "total_lines": 2, "tail": "100", "since": "2024-01-15T10:00:00Z", "timestamp": "2024-01-15T10:30:15.123456789Z" } ``` #### GET /api/logs/stats/{container_id} Получение статистики логов контейнера. **Ответ:** ```json { "debug": 15, "info": 42, "warn": 8, "error": 3 } ``` ### Управление #### GET /api/settings Получение настроек приложения. **Ответ:** ```json { "ajax_update_interval": 2000, "default_tail": 500, "skip_unhealthy": true } ``` #### GET /api/excluded-containers Получение списка исключенных контейнеров. **Ответ:** ```json { "excluded_containers": [ "noisy-container-1", "noisy-container-2" ] } ``` #### POST /api/excluded-containers Обновление списка исключенных контейнеров. **Запрос:** ```json [ "noisy-container-1", "noisy-container-2", "another-noisy-container" ] ``` **Ответ:** ```json { "status": "success", "message": "Список исключенных контейнеров обновлен" } ``` #### POST /api/snapshot Создание снимка логов. **Запрос:** ```json { "container_id": "abc123def456", "service": "web", "content": "Логи контейнера..." } ``` **Ответ:** ```json { "file": "web-20240115-103015.log", "url": "/snapshots/web-20240115-103015.log" } ``` #### GET /api/websocket/status Получение статуса WebSocket соединений. **Заголовки:** ``` Authorization: Bearer ``` **Ответ:** ```json { "status": "available", "message": "WebSocket соединения доступны", "containers_count": 5, "timestamp": "2024-01-15T10:30:15.123456" } ``` **Возможные статусы:** - `available` - WebSocket соединения доступны, есть активные контейнеры - `no_containers` - Нет доступных контейнеров для подключения - `error` - Ошибка проверки статуса **Примечание:** Когда сервер возвращает статус `available`, клиент всегда показывает `ws: on`, независимо от наличия активных клиентских соединений. ### Системные #### GET /healthz Health check endpoint. **Ответ:** ``` ok ``` ## WebSocket API ### Подключение Все WebSocket endpoints требуют JWT токен в параметре `token`. ### ws://host:port/ws/logs/{container_id} Получение логов отдельного контейнера. **Параметры:** - `container_id` - ID контейнера - `tail` - Количество строк (по умолчанию 500) - `token` - JWT токен - `service` - Имя сервиса (опционально) - `project` - Имя проекта (опционально) **Пример подключения:** ```javascript const token = "your-jwt-token"; const containerId = "abc123def456"; const ws = new WebSocket(`ws://localhost:9001/ws/logs/${containerId}?token=${token}&tail=100`); ws.onmessage = function(event) { console.log('Получены логи:', event.data); }; ws.onerror = function(error) { console.error('WebSocket ошибка:', error); }; ``` ### ws://host:port/ws/fan/{service_name} Получение логов сервиса (все реплики). **Параметры:** - `service_name` - Имя сервиса Docker Compose - `tail` - Количество строк (по умолчанию 500) - `token` - JWT токен - `project` - Имя проекта (опционально) **Пример подключения:** ```javascript const token = "your-jwt-token"; const serviceName = "web"; const ws = new WebSocket(`ws://localhost:9001/ws/fan/${serviceName}?token=${token}&tail=100`); ws.onmessage = function(event) { console.log('Логи сервиса:', event.data); }; ``` ### ws://host:port/ws/fan_group Получение логов группы сервисов. **Параметры:** - `services` - Имена сервисов через запятую - `tail` - Количество строк (по умолчанию 500) - `token` - JWT токен - `project` - Имя проекта (опционально) **Пример подключения:** ```javascript const token = "your-jwt-token"; const services = "web,db,redis"; const ws = new WebSocket(`ws://localhost:9001/ws/fan_group?services=${services}&token=${token}&tail=100`); ws.onmessage = function(event) { console.log('Логи группы сервисов:', event.data); }; ``` ## Коды ошибок ### HTTP коды состояния | Код | Описание | |-----|----------| | 200 | Успешный запрос | | 400 | Неверный запрос | | 401 | Не авторизован | | 403 | Доступ запрещен | | 404 | Ресурс не найден | | 500 | Внутренняя ошибка сервера | ### Формат ошибок ```json { "error": "error_type", "message": "Описание ошибки", "details": "Дополнительная информация" } ``` ### Примеры ошибок #### 401 Unauthorized ```json { "error": "unauthorized", "message": "Требуется авторизация", "details": "Для доступа к этому API необходимо войти в систему." } ``` #### 404 Not Found ```json { "error": "http_404", "message": "Контейнер не найден", "details": "URL: /api/logs/invalid-id" } ``` #### 500 Internal Server Error ```json { "error": "http_500", "message": "Ошибка подключения к Docker", "details": "URL: /api/services" } ``` ## Примеры использования ### Python ```python import requests import json # Базовый URL BASE_URL = "http://localhost:9001" # Аутентификация def login(username, password): response = requests.post(f"{BASE_URL}/api/auth/login", json={ "username": username, "password": password }) return response.json()["access_token"] # Получение списка контейнеров def get_containers(token): headers = {"Authorization": f"Bearer {token}"} response = requests.get(f"{BASE_URL}/api/services", headers=headers) return response.json() # Получение логов контейнера def get_logs(token, container_id, tail=100): headers = {"Authorization": f"Bearer {token}"} params = {"tail": tail} response = requests.get(f"{BASE_URL}/api/logs/{container_id}", headers=headers, params=params) return response.json() # Использование token = login("admin", "your-password") containers = get_containers(token) print(f"Найдено контейнеров: {len(containers)}") for container in containers: logs = get_logs(token, container["id"], tail=10) print(f"Контейнер {container['name']}: {len(logs['logs'])} строк логов") ``` ### JavaScript ```javascript // Класс для работы с LogBoard+ API class LogBoardAPI { constructor(baseUrl, token) { this.baseUrl = baseUrl; this.token = token; this.headers = { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }; } // Получение списка контейнеров async getContainers(projects = null, includeStopped = false) { const params = new URLSearchParams(); if (projects) params.append('projects', projects); if (includeStopped) params.append('include_stopped', 'true'); const response = await fetch(`${this.baseUrl}/api/services?${params}`, { headers: this.headers }); return await response.json(); } // Получение логов контейнера async getLogs(containerId, tail = 100, since = null) { const params = new URLSearchParams(); params.append('tail', tail); if (since) params.append('since', since); const response = await fetch(`${this.baseUrl}/api/logs/${containerId}?${params}`, { headers: this.headers }); return await response.json(); } // Получение статистики логов async getLogStats(containerId) { const response = await fetch(`${this.baseUrl}/api/logs/stats/${containerId}`, { headers: this.headers }); return await response.json(); } // WebSocket для live-логов createLogsWebSocket(containerId, tail = 100) { const ws = new WebSocket( `ws://${this.baseUrl.replace('http://', '')}/ws/logs/${containerId}?token=${this.token}&tail=${tail}` ); return ws; } } // Использование async function main() { // Получение токена const loginResponse = await fetch('http://localhost:9001/api/auth/login', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ username: 'admin', password: 'your-password' }) }); const {access_token} = await loginResponse.json(); // Создание API клиента const api = new LogBoardAPI('http://localhost:9001', access_token); // Получение контейнеров const containers = await api.getContainers(); console.log('Контейнеры:', containers); // Получение логов первого контейнера if (containers.length > 0) { const logs = await api.getLogs(containers[0].id, 50); console.log('Логи:', logs); // WebSocket для live-логов const ws = api.createLogsWebSocket(containers[0].id, 100); ws.onmessage = (event) => { console.log('Новые логи:', event.data); }; } } main().catch(console.error); ``` ### cURL ```bash #!/bin/bash # Переменные BASE_URL="http://localhost:9001" USERNAME="admin" PASSWORD="your-password" # Получение токена TOKEN=$(curl -s -X POST "$BASE_URL/api/auth/login" \ -H "Content-Type: application/json" \ -d "{\"username\":\"$USERNAME\",\"password\":\"$PASSWORD\"}" \ | jq -r '.access_token') echo "Токен получен: ${TOKEN:0:20}..." # Получение списка контейнеров echo "Получение списка контейнеров..." curl -s -X GET "$BASE_URL/api/services" \ -H "Authorization: Bearer $TOKEN" \ | jq '.[] | {name: .name, status: .status, project: .project}' # Получение логов первого контейнера CONTAINER_ID=$(curl -s -X GET "$BASE_URL/api/services" \ -H "Authorization: Bearer $TOKEN" \ | jq -r '.[0].id') echo "Получение логов контейнера $CONTAINER_ID..." curl -s -X GET "$BASE_URL/api/logs/$CONTAINER_ID?tail=10" \ -H "Authorization: Bearer $TOKEN" \ | jq '.logs[] | .message' ``` ## Ограничения и рекомендации ### Ограничения - Максимальное количество строк логов: 10000 - Таймаут WebSocket соединения: 60 секунд - Максимальный размер снимка логов: 10 MB ### Рекомендации 1. **Используйте фильтрацию по времени** для больших объемов логов 2. **Настройте исключение контейнеров** с избыточным логированием 3. **Используйте WebSocket** для real-time мониторинга 4. **Регулярно очищайте снимки логов** для экономии места ### Производительность - REST API: до 1000 запросов в минуту - WebSocket: до 100 одновременных соединений - Размер ответа: до 1 MB на запрос