692 lines
18 KiB
Markdown
692 lines
18 KiB
Markdown
# 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 <token>
|
||
```
|
||
|
||
## Аутентификация
|
||
|
||
### 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 <token>
|
||
```
|
||
|
||
**Ответ:**
|
||
|
||
```json
|
||
{
|
||
"username": "admin"
|
||
}
|
||
```
|
||
|
||
## REST API
|
||
|
||
### Контейнеры и сервисы
|
||
|
||
#### GET /api/containers/services
|
||
|
||
Получение списка всех контейнеров.
|
||
|
||
**Параметры запроса:**
|
||
|
||
| Параметр | Тип | Описание | По умолчанию |
|
||
|----------|-----|----------|--------------|
|
||
| `projects` | string | Фильтр по проектам (через запятую) | Все проекты |
|
||
| `include_stopped` | boolean | Включить остановленные контейнеры | false |
|
||
|
||
**Пример запроса:**
|
||
|
||
```bash
|
||
curl -X GET "http://localhost:9001/api/containers/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/containers/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/containers/excluded
|
||
|
||
Получение списка исключенных контейнеров.
|
||
|
||
**Ответ:**
|
||
|
||
```json
|
||
{
|
||
"excluded_containers": [
|
||
"noisy-container-1",
|
||
"noisy-container-2"
|
||
]
|
||
}
|
||
```
|
||
|
||
#### POST /api/containers/excluded
|
||
|
||
Обновление списка исключенных контейнеров.
|
||
|
||
**Запрос:**
|
||
|
||
```json
|
||
[
|
||
"noisy-container-1",
|
||
"noisy-container-2",
|
||
"another-noisy-container"
|
||
]
|
||
```
|
||
|
||
**Ответ:**
|
||
|
||
```json
|
||
{
|
||
"status": "success",
|
||
"message": "Список исключенных контейнеров обновлен"
|
||
}
|
||
```
|
||
|
||
#### POST /api/logs/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 <token>
|
||
```
|
||
|
||
**Ответ:**
|
||
|
||
```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/api/websocket/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/api/websocket/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/api/websocket/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/api/websocket/fan/${serviceName}?token=${token}&tail=100`);
|
||
|
||
ws.onmessage = function(event) {
|
||
console.log('Логи сервиса:', event.data);
|
||
};
|
||
```
|
||
|
||
### ws://host:port/api/websocket/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/api/websocket/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/containers/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/containers/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://', '')}/api/websocket/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/containers/services" \
|
||
-H "Authorization: Bearer $TOKEN" \
|
||
| jq '.[] | {name: .name, status: .status, project: .project}'
|
||
|
||
# Получение логов первого контейнера
|
||
CONTAINER_ID=$(curl -s -X GET "$BASE_URL/api/containers/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 на запрос
|