feat: Добавлена поддержка удаленных клиентов для LogBoard+

- Создан LogBoard клиент для отправки логов с удаленных серверов
- Добавлен API эндпоинт /api/logs/remote с аутентификацией
- Реализована структурированная система сохранения логов
- Исправлена совместимость Docker client библиотеки
- Добавлена полная документация и тестирование
This commit is contained in:
Сергей Антропов 2025-08-20 19:25:29 +03:00
parent c40b2b312e
commit 04dfe30d58
19 changed files with 1759 additions and 5 deletions

View File

@ -98,6 +98,51 @@ rebuild: ## Пересобрать и запустить сервисы
docker compose -f $(COMPOSE_FILE) down docker compose -f $(COMPOSE_FILE) down
docker compose -f $(COMPOSE_FILE) build --no-cache docker compose -f $(COMPOSE_FILE) build --no-cache
@mkdir -p snapshots @mkdir -p snapshots
@mkdir -p logs/remote
docker compose -f $(COMPOSE_FILE) up -d
@echo "$(GREEN)Сервисы пересобраны и запущены!$(NC)"
# Команды для работы с удаленными клиентами
client-setup: ## Настроить клиент
@echo "$(GREEN)Настройка LogBoard клиента...$(NC)"
@if [ ! -f client/.env ]; then \
cp client/env.example client/.env; \
echo "$(GREEN)Файл client/.env создан из примера$(NC)"; \
echo "$(YELLOW)Отредактируйте client/.env перед запуском!$(NC)"; \
else \
echo "$(YELLOW)Файл client/.env уже существует.$(NC)"; \
fi
@mkdir -p client/logs
client-build: ## Собрать образ клиента
@echo "$(GREEN)Сборка образа LogBoard клиента...$(NC)"
docker compose -f $(COMPOSE_FILE) build logboard-client
@echo "$(GREEN)Образ клиента собран!$(NC)"
client-logs: ## Показать логи клиента
@echo "$(GREEN)Логи LogBoard клиента:$(NC)"
docker compose -f $(COMPOSE_FILE) logs -f logboard-client
client-shell: ## Подключиться к контейнеру клиента
@echo "$(GREEN)Подключение к контейнеру logboard-client...$(NC)"
docker compose -f $(COMPOSE_FILE) exec logboard-client /bin/bash
test-remote: ## Тестирование системы удаленных клиентов
@echo "$(GREEN)Тестирование системы удаленных клиентов...$(NC)"
python test_remote_system.py
test-client: ## Тестирование клиента
@echo "$(GREEN)Тестирование LogBoard клиента...$(NC)"
cd client && python test_client.py
remote-logs: ## Показать удаленные логи
@echo "$(GREEN)Удаленные логи:$(NC)"
@if [ -d "logs/remote" ]; then \
find logs/remote -name "*.log" -type f | head -10; \
echo "$(YELLOW)Используйте 'ls -la logs/remote/' для полного списка$(NC)"; \
else \
echo "$(YELLOW)Директория logs/remote не существует$(NC)"; \
fi
docker compose -f $(COMPOSE_FILE) up -d docker compose -f $(COMPOSE_FILE) up -d
@echo "$(GREEN)Сервисы пересобраны и запущены!$(NC)" @echo "$(GREEN)Сервисы пересобраны и запущены!$(NC)"
@if [ -f .env ]; then \ @if [ -f .env ]; then \

View File

@ -42,7 +42,8 @@ LogBoard+ особенно полезен для разработчиков, р
- **Просмотр логов в реальном времени** - WebSocket соединения для live-логов - **Просмотр логов в реальном времени** - WebSocket соединения для live-логов
- **Поддержка множественных проектов** - Фильтрация по проектам Docker Compose - **Поддержка множественных проектов** - Фильтрация по проектам Docker Compose
- **Безопасность** - JWT аутентификация и авторизация - **Удаленные клиенты** - Сбор логов с множества серверов
- **Безопасность** - JWT аутентификация и API ключи для клиентов
- **Фильтрация контейнеров** - Исключение проблемных контейнеров - **Фильтрация контейнеров** - Исключение проблемных контейнеров
- **Снимки логов** - Сохранение логов в файлы для анализа - **Снимки логов** - Сохранение логов в файлы для анализа
- **Статистика** - Анализ уровней логирования - **Статистика** - Анализ уровней логирования
@ -91,6 +92,61 @@ LogBoard+ особенно полезен для разработчиков, р
- 1 GB RAM - 1 GB RAM
- 1 CPU core - 1 CPU core
## Удаленные клиенты
LogBoard+ поддерживает работу с удаленными клиентами для централизованного сбора логов с множества серверов.
### Архитектура
```
┌─────────────────┐ HTTP/JSON ┌─────────────────┐
│ Server A │ ──────────────► │ LogBoard │
│ (Client) │ │ Server │
│ │ │ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │LogBoard │ │ │ │API │ │
│ │Client │ │ │ │Endpoint │ │
│ │Container │ │ │ │/logs/remote │ │
│ └─────────────┘ │ │ └─────────────┘ │
│ ▲ │ │ │ │
│ │ │ │ ▼ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │Docker │ │ │ │File │ │
│ │Socket │ │ │ │Storage │ │
│ └─────────────┘ │ │ └─────────────┘ │
└─────────────────┘ └─────────────────┘
```
### Установка клиента
```bash
# Клонирование и настройка
cd client
cp env.example .env
# Отредактируйте .env файл
# Запуск клиента
make install
```
### Конфигурация
**На сервере LogBoard:**
```bash
# В .env файле
LOGBOARD_API_KEYS=key1,key2,key3
```
**На клиенте:**
```bash
# В .env файле клиента
LOGBOARD_SERVER_URL=http://logboard.example.com:8000
LOGBOARD_API_KEY=key1
HOSTNAME=production-server-01
```
Подробная документация: [docs/remote-clients.md](docs/remote-clients.md)
### Установка и запуск ### Установка и запуск
1. **Клонирование репозитория** 1. **Клонирование репозитория**

View File

@ -7,15 +7,17 @@ LogBoard+ - Логи API
""" """
import re import re
import json
import os
from datetime import datetime from datetime import datetime
from typing import Optional from typing import Optional, List, Dict
from fastapi import APIRouter, Depends, HTTPException, Query, Body from fastapi import APIRouter, Depends, HTTPException, Query, Body, Header
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
import docker import docker
from core.auth import get_current_user from core.auth import get_current_user, verify_api_key
from core.docker import docker_client, DEFAULT_TAIL from core.docker import docker_client, DEFAULT_TAIL
from core.logger import api_logger from core.logger import api_logger
@ -219,3 +221,78 @@ def api_snapshot(
f.write(content) f.write(content)
url = f"/snapshots/{fname}" url = f"/snapshots/{fname}"
return {"file": fname, "url": url} return {"file": fname, "url": url}
@router.post("/remote")
async def api_remote_logs(
request_data: Dict = Body(...),
authorization: str = Header(None)
):
"""
Прием логов от удаленных клиентов
Args:
request_data: Данные запроса с логами
authorization: Заголовок авторизации
Returns:
JSON с результатом обработки
"""
try:
# Проверяем авторизацию
if not authorization or not authorization.startswith('Bearer '):
api_logger.warning("Unauthorized remote log request - missing or invalid authorization header")
raise HTTPException(status_code=401, detail="Unauthorized")
api_key = authorization.replace('Bearer ', '')
if not verify_api_key(api_key):
api_logger.warning("Unauthorized remote log request - invalid API key")
raise HTTPException(status_code=401, detail="Invalid API key")
# Извлекаем данные из запроса
hostname = request_data.get('hostname')
container_name = request_data.get('container_name')
logs = request_data.get('logs', [])
timestamp = request_data.get('timestamp')
if not all([hostname, container_name, logs]):
api_logger.error("Invalid remote log request - missing required fields")
raise HTTPException(status_code=400, detail="Missing required fields")
# Создаем директорию для удаленных логов, если не существует
remote_logs_dir = os.path.join(os.getcwd(), 'logs', 'remote', hostname)
os.makedirs(remote_logs_dir, exist_ok=True)
# Формируем имя файла для логов
safe_container_name = re.sub(r"[^a-zA-Z0-9_.-]+", "_", container_name)
log_filename = f"{safe_container_name}-{datetime.now().strftime('%Y%m%d')}.log"
log_filepath = os.path.join(remote_logs_dir, log_filename)
# Записываем логи в файл
with open(log_filepath, 'a', encoding='utf-8') as f:
for log_line in logs:
f.write(f"{log_line}\n")
# Логируем информацию о полученных логах
api_logger.info(
f"Received {len(logs)} log lines from host '{hostname}' "
f"container '{container_name}' at {timestamp}"
)
return JSONResponse(
content={
"status": "success",
"message": f"Received {len(logs)} log lines",
"hostname": hostname,
"container_name": container_name,
"timestamp": timestamp,
"log_file": log_filename
},
status_code=200
)
except HTTPException:
raise
except Exception as e:
api_logger.error(f"Error processing remote logs: {e}")
raise HTTPException(status_code=500, detail="Internal server error")

View File

@ -25,7 +25,7 @@ app = FastAPI(
) )
# Инициализация шаблонов # Инициализация шаблонов
templates = Jinja2Templates(directory="app/templates") from core.config import templates
# serve snapshots directory (downloadable files) # serve snapshots directory (downloadable files)
os.makedirs(SNAP_DIR, exist_ok=True) os.makedirs(SNAP_DIR, exist_ok=True)

View File

@ -80,3 +80,26 @@ def authenticate_user(username: str, password: str) -> bool:
# В продакшене рекомендуется использовать хешированные пароли # В продакшене рекомендуется использовать хешированные пароли
return password == ADMIN_PASSWORD return password == ADMIN_PASSWORD
return False return False
# Функция для проверки API ключей
def verify_api_key(api_key: str) -> bool:
"""
Проверяет API ключ для удаленных клиентов
Args:
api_key: API ключ для проверки
Returns:
bool: True если ключ валидный, False в противном случае
"""
# Получаем список разрешенных API ключей из переменной окружения
allowed_keys = os.getenv('LOGBOARD_API_KEYS', '').split(',')
allowed_keys = [key.strip() for key in allowed_keys if key.strip()]
# Если ключи не настроены, разрешаем только для разработки
if not allowed_keys:
# В продакшене это должно быть отключено
return api_key == os.getenv('LOGBOARD_DEFAULT_API_KEY', 'dev-key-123')
return api_key in allowed_keys

View File

@ -75,5 +75,9 @@ DOCKER_NETWORKS = os.getenv("DOCKER_NETWORKS", "iaas,infrastructure_iaas")
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO") LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
LOG_FORMAT = os.getenv("LOG_FORMAT", "json") LOG_FORMAT = os.getenv("LOG_FORMAT", "json")
# Настройки API ключей для удаленных клиентов
LOGBOARD_API_KEYS = os.getenv("LOGBOARD_API_KEYS", "")
LOGBOARD_DEFAULT_API_KEY = os.getenv("LOGBOARD_DEFAULT_API_KEY", "dev-key-123")
# Временная зона # Временная зона
TZ_TS = os.getenv("TZ_TS", "Europe/Moscow") TZ_TS = os.getenv("TZ_TS", "Europe/Moscow")

45
client/Dockerfile Normal file
View File

@ -0,0 +1,45 @@
# Dockerfile для LogBoard клиента
# Автор: Сергей Антропов
# Сайт: https://devops.org.ru
FROM python:3.11-slim
# Установка метаданных
LABEL maintainer="Сергей Антропов <https://devops.org.ru>"
LABEL description="LogBoard клиент для отправки логов на удаленный сервер"
LABEL version="1.0"
# Установка системных зависимостей
RUN apt-get update && apt-get install -y \
curl \
&& rm -rf /var/lib/apt/lists/*
# Создание пользователя для безопасности
RUN groupadd -r logboard && useradd -r -g logboard logboard
# Создание директорий
RUN mkdir -p /app /var/log
WORKDIR /app
# Копирование файлов зависимостей
COPY requirements.txt .
# Установка Python зависимостей
RUN pip install --no-cache-dir -r requirements.txt
# Копирование исходного кода
COPY app/ ./app/
# Создание директории для логов
RUN mkdir -p /var/log && \
chown -R logboard:logboard /app /var/log
# Переключение на пользователя logboard
USER logboard
# Проверка здоровья
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
# Команда запуска
CMD ["python", "app/main.py"]

97
client/Makefile Normal file
View File

@ -0,0 +1,97 @@
# Makefile для LogBoard клиента
# Автор: Сергей Антропов
# Сайт: https://devops.org.ru
.PHONY: help build up down logs clean test setup
# Переменные
COMPOSE_FILE = docker-compose.yml
SERVICE_NAME = logboard-client
help: ## Показать справку
@echo "LogBoard Client - Управление клиентом"
@echo ""
@echo "Доступные команды:"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
setup: ## Настройка окружения
@echo "Настройка окружения для LogBoard клиента..."
@if [ ! -f .env ]; then \
echo "Создание файла .env из примера..."; \
cp env.example .env; \
echo "Файл .env создан. Отредактируйте его перед запуском."; \
else \
echo "Файл .env уже существует."; \
fi
@echo "Создание директорий для логов..."
@mkdir -p logs
build: ## Сборка Docker образа
@echo "Сборка Docker образа для LogBoard клиента..."
docker-compose -f $(COMPOSE_FILE) build
up: ## Запуск клиента
@echo "Запуск LogBoard клиента..."
docker-compose -f $(COMPOSE_FILE) up -d
down: ## Остановка клиента
@echo "Остановка LogBoard клиента..."
docker-compose -f $(COMPOSE_FILE) down
restart: ## Перезапуск клиента
@echo "Перезапуск LogBoard клиента..."
docker-compose -f $(COMPOSE_FILE) restart
logs: ## Просмотр логов клиента
@echo "Логи LogBoard клиента:"
docker-compose -f $(COMPOSE_FILE) logs -f $(SERVICE_NAME)
logs-all: ## Просмотр всех логов
@echo "Все логи:"
docker-compose -f $(COMPOSE_FILE) logs -f
status: ## Статус сервисов
@echo "Статус сервисов:"
docker-compose -f $(COMPOSE_FILE) ps
clean: ## Очистка (удаление контейнеров и образов)
@echo "Очистка Docker ресурсов..."
docker-compose -f $(COMPOSE_FILE) down --rmi all --volumes --remove-orphans
clean-logs: ## Очистка логов
@echo "Очистка логов..."
@rm -rf logs/*
test: ## Запуск тестового контейнера
@echo "Запуск тестового контейнера..."
docker-compose -f $(COMPOSE_FILE) up -d test-container
test-logs: ## Просмотр логов тестового контейнера
@echo "Логи тестового контейнера:"
docker-compose -f $(COMPOSE_FILE) logs -f test-container
stop-test: ## Остановка тестового контейнера
@echo "Остановка тестового контейнера..."
docker-compose -f $(COMPOSE_FILE) stop test-container
health: ## Проверка здоровья клиента
@echo "Проверка здоровья LogBoard клиента..."
@docker-compose -f $(COMPOSE_FILE) exec $(SERVICE_NAME) python -c "import requests; print('Health check:', requests.get('http://localhost:8080/health').status_code)" 2>/dev/null || echo "Health check недоступен"
shell: ## Вход в контейнер клиента
@echo "Вход в контейнер LogBoard клиента..."
docker-compose -f $(COMPOSE_FILE) exec $(SERVICE_NAME) /bin/bash
install: setup build up ## Полная установка (настройка + сборка + запуск)
@echo "LogBoard клиент успешно установлен и запущен!"
uninstall: down clean ## Полное удаление
@echo "LogBoard клиент удален!"
dev: ## Запуск в режиме разработки
@echo "Запуск в режиме разработки..."
docker-compose -f $(COMPOSE_FILE) up
dev-build: ## Сборка и запуск в режиме разработки
@echo "Сборка и запуск в режиме разработки..."
docker-compose -f $(COMPOSE_FILE) up --build

211
client/README.md Normal file
View File

@ -0,0 +1,211 @@
# LogBoard Клиент
Клиент для отправки логов Docker контейнеров на удаленный сервер LogBoard.
**Автор:** Сергей Антропов
**Сайт:** https://devops.org.ru
## Описание
LogBoard клиент - это легковесное приложение, которое:
- Мониторит Docker контейнеры на удаленном сервере
- Собирает логи контейнеров
- Отправляет их на центральный сервер LogBoard
- Работает в Docker контейнере
## Возможности
- ✅ Автоматический мониторинг всех Docker контейнеров
- ✅ Сбор логов stdout и stderr
- ✅ Асинхронная отправка на сервер
- ✅ Аутентификация по API ключу
- ✅ Настраиваемый интервал отправки
- ✅ Подробное логирование
- ✅ Graceful shutdown
## Установка и запуск
### 1. Клонирование репозитория
```bash
git clone <repository-url>
cd logboard/client
```
### 2. Настройка переменных окружения
```bash
cp env.example .env
# Отредактируйте .env файл
```
### 3. Запуск с Docker Compose
```bash
docker-compose up -d
```
### 4. Проверка работы
```bash
# Просмотр логов клиента
docker-compose logs -f logboard-client
# Проверка статуса
docker-compose ps
```
## Конфигурация
### Переменные окружения
| Переменная | Описание | Обязательно | По умолчанию |
|------------|----------|-------------|--------------|
| `LOGBOARD_SERVER_URL` | URL сервера LogBoard | Да | `http://localhost:8000` |
| `LOGBOARD_API_KEY` | API ключ для аутентификации | Да | - |
| `HOSTNAME` | Имя хоста | Нет | Автоопределение |
| `LOGBOARD_INTERVAL` | Интервал отправки (сек) | Нет | `60` |
### Пример .env файла
```env
LOGBOARD_SERVER_URL=http://logboard.example.com:8000
LOGBOARD_API_KEY=your_secret_api_key_here
HOSTNAME=production-server-01
LOGBOARD_INTERVAL=30
```
## Архитектура
```
┌─────────────────┐ HTTP/JSON ┌─────────────────┐
│ Docker Host │ ──────────────► │ LogBoard Server│
│ │ │ │
│ ┌─────────────┐ │ │ │
│ │LogBoard │ │ │ │
│ │Client │ │ │ │
│ │Container │ │ │ │
│ └─────────────┘ │ │ │
│ ▲ │ │ │
│ │ │ │ │
│ ┌─────────────┐ │ │ │
│ │Docker │ │ │ │
│ │Socket │ │ │ │
│ └─────────────┘ │ │ │
└─────────────────┘ └─────────────────┘
```
## API Endpoints
Клиент отправляет данные на следующие эндпоинты сервера:
### POST /api/v1/logs/remote
Отправка логов контейнера.
**Заголовки:**
```
Authorization: Bearer <api_key>
Content-Type: application/json
```
**Тело запроса:**
```json
{
"hostname": "server-01",
"container_name": "nginx",
"logs": [
"2024-01-01T12:00:00.000Z nginx: [info] Server started",
"2024-01-01T12:00:01.000Z nginx: [info] Listening on port 80"
],
"timestamp": "2024-01-01T12:00:01.000Z"
}
```
## Мониторинг
### Логи клиента
```bash
# Просмотр логов в реальном времени
docker-compose logs -f logboard-client
# Просмотр последних 100 строк
docker-compose logs --tail=100 logboard-client
```
### Проверка здоровья
```bash
# Статус контейнера
docker-compose ps logboard-client
# Проверка здоровья
docker inspect logboard-client | grep Health -A 10
```
## Устранение неполадок
### Проблемы подключения
1. **Ошибка аутентификации:**
- Проверьте правильность API ключа
- Убедитесь, что ключ активен на сервере
2. **Ошибка подключения к серверу:**
- Проверьте URL сервера
- Убедитесь, что сервер доступен
- Проверьте сетевые настройки
3. **Ошибка доступа к Docker:**
- Убедитесь, что Docker socket доступен
- Проверьте права доступа
### Отладка
```bash
# Запуск в режиме отладки
docker-compose run --rm logboard-client python -u app/main.py
# Просмотр переменных окружения
docker-compose exec logboard-client env
```
## Разработка
### Локальная разработка
```bash
# Установка зависимостей
pip install -r requirements.txt
# Запуск клиента
python app/main.py
```
### Тестирование
```bash
# Запуск тестового контейнера
docker-compose up test-container
# Проверка отправки логов
docker-compose logs logboard-client
```
## Безопасность
- API ключи хранятся в переменных окружения
- Docker socket монтируется в режиме read-only
- Клиент работает под непривилегированным пользователем
- Все HTTP соединения используют HTTPS (рекомендуется)
## Лицензия
MIT License
## Поддержка
- **Автор:** Сергей Антропов
- **Сайт:** https://devops.org.ru
- **Issues:** Создавайте issues в репозитории проекта

214
client/app/main.py Normal file
View File

@ -0,0 +1,214 @@
#!/usr/bin/env python3
"""
Клиент для отправки логов на удаленный сервер LogBoard
Автор: Сергей Антропов
Сайт: https://devops.org.ru
"""
import asyncio
import json
import logging
import os
import sys
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional
import aiofiles
import aiohttp
import docker
from docker.errors import DockerException
# Настройка логирования
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(sys.stdout),
logging.FileHandler('/var/log/logboard-client.log')
]
)
logger = logging.getLogger(__name__)
class LogBoardClient:
"""Клиент для отправки логов в LogBoard сервер"""
def __init__(self, server_url: str, api_key: str, hostname: str):
"""
Инициализация клиента
Args:
server_url: URL сервера LogBoard
api_key: API ключ для аутентификации
hostname: Имя хоста для идентификации
"""
self.server_url = server_url.rstrip('/')
self.api_key = api_key
self.hostname = hostname
self.session: Optional[aiohttp.ClientSession] = None
try:
# Используем тот же способ, что и в основном сервисе
self.docker_client = docker.from_env()
# Проверяем подключение
self.docker_client.ping()
logger.info("Docker клиент успешно инициализирован")
except Exception as e:
logger.error(f"Критическая ошибка Docker клиента: {e}")
raise
async def __aenter__(self):
"""Асинхронный контекстный менеджер - вход"""
self.session = aiohttp.ClientSession(
headers={
'Authorization': f'Bearer {self.api_key}',
'Content-Type': 'application/json',
'User-Agent': 'LogBoard-Client/1.0'
}
)
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
"""Асинхронный контекстный менеджер - выход"""
if self.session:
await self.session.close()
async def send_logs(self, container_name: str, logs: List[str]) -> bool:
"""
Отправка логов на сервер
Args:
container_name: Имя контейнера
logs: Список строк логов
Returns:
bool: True если отправка успешна, False в противном случае
"""
if not self.session:
logger.error("Сессия не инициализирована")
return False
payload = {
"hostname": self.hostname,
"container_name": container_name,
"logs": logs,
"timestamp": datetime.utcnow().isoformat()
}
try:
async with self.session.post(
f"{self.server_url}/api/logs/remote",
json=payload,
timeout=aiohttp.ClientTimeout(total=30)
) as response:
if response.status == 200:
logger.info(f"Логи контейнера {container_name} успешно отправлены")
return True
else:
logger.error(f"Ошибка отправки логов: {response.status} - {await response.text()}")
return False
except Exception as e:
logger.error(f"Ошибка при отправке логов: {e}")
return False
def get_containers(self) -> List[Dict]:
"""
Получение списка контейнеров Docker
Returns:
List[Dict]: Список контейнеров с информацией
"""
try:
containers = []
for container in self.docker_client.containers.list():
containers.append({
"id": container.id,
"name": container.name,
"status": container.status,
"image": container.image.tags[0] if container.image.tags else container.image.id,
"created": container.attrs['Created']
})
return containers
except DockerException as e:
logger.error(f"Ошибка при получении списка контейнеров: {e}")
return []
async def collect_container_logs(self, container_name: str, lines: int = 100) -> List[str]:
"""
Сбор логов контейнера
Args:
container_name: Имя контейнера
lines: Количество строк логов для сбора
Returns:
List[str]: Список строк логов
"""
try:
container = self.docker_client.containers.get(container_name)
logs = container.logs(
stdout=True,
stderr=True,
tail=lines,
timestamps=True
).decode('utf-8')
return logs.splitlines() if logs else []
except DockerException as e:
logger.error(f"Ошибка при получении логов контейнера {container_name}: {e}")
return []
async def monitor_containers(self, interval: int = 60):
"""
Мониторинг контейнеров и отправка логов
Args:
interval: Интервал мониторинга в секундах
"""
logger.info(f"Запуск мониторинга контейнеров с интервалом {interval} секунд")
while True:
try:
containers = self.get_containers()
logger.info(f"Найдено {len(containers)} контейнеров")
for container in containers:
container_name = container['name']
if container['status'] == 'running':
logs = await self.collect_container_logs(container_name)
if logs:
await self.send_logs(container_name, logs)
except Exception as e:
logger.error(f"Ошибка в мониторинге: {e}")
await asyncio.sleep(interval)
async def main():
"""Основная функция"""
# Получение переменных окружения
server_url = os.getenv('LOGBOARD_SERVER_URL', 'http://localhost:8000')
api_key = os.getenv('LOGBOARD_API_KEY')
hostname = os.getenv('HOSTNAME', os.uname().nodename)
interval = int(os.getenv('LOGBOARD_INTERVAL', '60'))
if not api_key:
logger.error("LOGBOARD_API_KEY не установлен")
sys.exit(1)
logger.info(f"Запуск LogBoard клиента для хоста: {hostname}")
logger.info(f"Подключение к серверу: {server_url}")
async with LogBoardClient(server_url, api_key, hostname) as client:
await client.monitor_containers(interval)
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
logger.info("Получен сигнал прерывания, завершение работы")
except Exception as e:
logger.error(f"Критическая ошибка: {e}")
sys.exit(1)

61
client/docker-compose.yml Normal file
View File

@ -0,0 +1,61 @@
# Docker Compose для LogBoard клиента
# Автор: Сергей Антропов
# Сайт: https://devops.org.ru
version: '3.8'
services:
logboard-client:
build:
context: .
dockerfile: Dockerfile
container_name: logboard-client
restart: unless-stopped
environment:
# URL сервера LogBoard
- LOGBOARD_SERVER_URL=${LOGBOARD_SERVER_URL:-http://localhost:8000}
# API ключ для аутентификации (обязательно)
- LOGBOARD_API_KEY=${LOGBOARD_API_KEY}
# Имя хоста (автоматически определяется)
- HOSTNAME=${HOSTNAME:-$(hostname)}
# Интервал отправки логов в секундах
- LOGBOARD_INTERVAL=${LOGBOARD_INTERVAL:-60}
volumes:
# Доступ к Docker socket для получения логов контейнеров
- /var/run/docker.sock:/var/run/docker.sock:ro
# Логи клиента
- ./logs:/var/log
networks:
- logboard-network
depends_on:
- logboard-server
healthcheck:
test: ["CMD", "python", "-c", "import requests; requests.get('http://localhost:8080/health')"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# Тестовый контейнер для демонстрации
test-container:
image: nginx:alpine
container_name: test-nginx
restart: unless-stopped
ports:
- "8080:80"
volumes:
- ./logs/nginx:/var/log/nginx
networks:
- logboard-network
command: >
sh -c "echo 'Test container started at $$(date)' > /var/log/nginx/access.log &&
tail -f /var/log/nginx/access.log"
networks:
logboard-network:
driver: bridge
name: logboard-client-network
volumes:
client-logs:
driver: local

20
client/env.example Normal file
View File

@ -0,0 +1,20 @@
# Переменные окружения для LogBoard клиента
# Автор: Сергей Антропов
# Сайт: https://devops.org.ru
# URL сервера LogBoard (обязательно)
LOGBOARD_SERVER_URL=http://localhost:8000
# API ключ для аутентификации (обязательно)
# Получите ключ у администратора сервера LogBoard
LOGBOARD_API_KEY=your_api_key_here
# Имя хоста (опционально, определяется автоматически)
HOSTNAME=my-server-01
# Интервал отправки логов в секундах (по умолчанию 60)
LOGBOARD_INTERVAL=60
# Настройки логирования
LOG_LEVEL=INFO
LOG_FILE=/var/log/logboard-client.log

19
client/requirements.txt Normal file
View File

@ -0,0 +1,19 @@
# Зависимости для LogBoard клиента
# Автор: Сергей Антропов
# Сайт: https://devops.org.ru
# HTTP клиент для отправки логов
aiohttp==3.9.1
# Работа с Docker API
docker==6.1.3
# Асинхронная работа с файлами
aiofiles==23.2.1
# Утилиты для работы с данными
python-dateutil==2.8.2
# Фиксируем версии зависимостей для совместимости с основным сервисом
urllib3==2.1.0
requests==2.31.0

137
client/test_client.py Normal file
View File

@ -0,0 +1,137 @@
#!/usr/bin/env python3
"""
Тестовый скрипт для проверки работы LogBoard клиента
Автор: Сергей Антропов
Сайт: https://devops.org.ru
"""
import asyncio
import aiohttp
import json
import os
from datetime import datetime
# Конфигурация теста
SERVER_URL = os.getenv('LOGBOARD_SERVER_URL', 'http://localhost:8000')
API_KEY = os.getenv('LOGBOARD_API_KEY', 'dev-key-123')
HOSTNAME = os.getenv('HOSTNAME', 'test-host')
async def test_remote_logs_endpoint():
"""Тестирование эндпоинта для удаленных логов"""
print(f"Тестирование подключения к серверу: {SERVER_URL}")
# Тестовые данные
test_data = {
"hostname": HOSTNAME,
"container_name": "test-nginx",
"logs": [
f"{datetime.now().isoformat()} nginx: [info] Test log line 1",
f"{datetime.now().isoformat()} nginx: [info] Test log line 2",
f"{datetime.now().isoformat()} nginx: [error] Test error log",
f"{datetime.now().isoformat()} nginx: [warn] Test warning log"
],
"timestamp": datetime.now().isoformat()
}
headers = {
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json',
'User-Agent': 'LogBoard-Client-Test/1.0'
}
try:
async with aiohttp.ClientSession() as session:
async with session.post(
f"{SERVER_URL}/api/v1/logs/remote",
json=test_data,
headers=headers,
timeout=aiohttp.ClientTimeout(total=30)
) as response:
print(f"Статус ответа: {response.status}")
if response.status == 200:
result = await response.json()
print("✅ Успешно отправлены логи:")
print(f" - Хост: {result.get('hostname')}")
print(f" - Контейнер: {result.get('container_name')}")
print(f" - Количество строк: {result.get('message')}")
print(f" - Файл логов: {result.get('log_file')}")
return True
else:
error_text = await response.text()
print(f"❌ Ошибка отправки логов: {response.status}")
print(f" Ответ сервера: {error_text}")
return False
except aiohttp.ClientError as e:
print(f"❌ Ошибка подключения к серверу: {e}")
return False
except Exception as e:
print(f"❌ Неожиданная ошибка: {e}")
return False
async def test_server_health():
"""Тестирование доступности сервера"""
print(f"Проверка доступности сервера: {SERVER_URL}")
try:
async with aiohttp.ClientSession() as session:
async with session.get(
f"{SERVER_URL}/",
timeout=aiohttp.ClientTimeout(total=10)
) as response:
if response.status == 200:
print("✅ Сервер доступен")
return True
else:
print(f"❌ Сервер недоступен, статус: {response.status}")
return False
except Exception as e:
print(f"❌ Ошибка подключения к серверу: {e}")
return False
async def main():
"""Основная функция тестирования"""
print("=" * 60)
print("LogBoard Client - Тестирование")
print("=" * 60)
print(f"Сервер: {SERVER_URL}")
print(f"API ключ: {API_KEY[:10]}..." if len(API_KEY) > 10 else f"API ключ: {API_KEY}")
print(f"Хост: {HOSTNAME}")
print()
# Тест 1: Проверка доступности сервера
print("1. Проверка доступности сервера...")
server_available = await test_server_health()
print()
if not server_available:
print("❌ Сервер недоступен. Проверьте настройки и запустите сервер.")
return
# Тест 2: Отправка тестовых логов
print("2. Отправка тестовых логов...")
logs_sent = await test_remote_logs_endpoint()
print()
# Результаты тестирования
print("=" * 60)
print("РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ")
print("=" * 60)
if server_available and logs_sent:
print("Все тесты пройдены успешно!")
print(" LogBoard клиент готов к работе.")
elif server_available and not logs_sent:
print("⚠️ Сервер доступен, но есть проблемы с отправкой логов.")
print(" Проверьте настройки API ключа.")
else:
print("❌ Тесты не пройдены.")
print(" Проверьте настройки подключения к серверу.")
print()
print("Для запуска клиента используйте: make up")
print("Для просмотра логов: make logs")
if __name__ == "__main__":
asyncio.run(main())

View File

@ -9,11 +9,66 @@ services:
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro - /var/run/docker.sock:/var/run/docker.sock:ro
- ./snapshots:/app/snapshots - ./snapshots:/app/snapshots
- ./logs:/app/logs
restart: unless-stopped restart: unless-stopped
user: 0:0 user: 0:0
networks: networks:
- iaas - iaas
- infrastructure_iaas - infrastructure_iaas
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:${LOGBOARD_PORT}/"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# LogBoard клиент для демонстрации
logboard-client:
build:
context: ./client
dockerfile: Dockerfile
container_name: logboard-client
env_file:
- ./client/.env
environment:
- LOGBOARD_SERVER_URL=http://logboard:${LOGBOARD_PORT}
- LOGBOARD_API_KEY=${LOGBOARD_DEFAULT_API_KEY:-dev-key-123}
- HOSTNAME=${HOSTNAME:-$(hostname)}
- LOGBOARD_INTERVAL=30
- DOCKER_HOST=unix:///var/run/docker.sock
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./client/logs:/var/log
restart: unless-stopped
user: 0:0
depends_on:
- logboard
networks:
- iaas
- infrastructure_iaas
healthcheck:
test: ["CMD", "ps", "aux"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
# Тестовый контейнер для демонстрации работы клиента
test-nginx:
image: nginx:alpine
container_name: test-nginx
restart: unless-stopped
ports:
- "8080:80"
volumes:
- ./client/logs/nginx:/var/log/nginx
networks:
- iaas
- infrastructure_iaas
command: >
sh -c "echo 'Test container started at $$(date)' > /var/log/nginx/access.log &&
echo 'Test error log at $$(date)' > /var/log/nginx/error.log &&
tail -f /var/log/nginx/access.log /var/log/nginx/error.log"
networks: networks:
iaas: iaas:

112
docs/CHANGELOG.md Normal file
View File

@ -0,0 +1,112 @@
# История изменений LogBoard+
**Автор:** Сергей Антропов
**Сайт:** https://devops.org.ru
## [1.1.0] - 2024-01-XX
### Добавлено ✨
#### Удаленные клиенты
- **Новая функциональность:** Поддержка удаленных клиентов для сбора логов с множества серверов
- **API эндпоинт:** `/api/v1/logs/remote` для приема логов от клиентов
- **Аутентификация:** API ключи для безопасной связи между клиентами и сервером
- **Хранение логов:** Структурированное хранение логов по хостам и контейнерам
#### Клиент LogBoard
- **Docker контейнер:** Легковесный клиент для отправки логов
- **Автоматический мониторинг:** Сбор логов всех Docker контейнеров
- **Настраиваемый интервал:** Конфигурируемый интервал отправки логов
- **Асинхронная работа:** Оптимизированная производительность
#### Безопасность
- **API ключи:** Система аутентификации для удаленных клиентов
- **Переменные окружения:** Безопасное хранение конфигурации
- **Валидация:** Проверка входящих запросов
#### Документация
- **Подробная документация:** Руководство по установке и настройке клиентов
- **Примеры конфигурации:** Готовые примеры для быстрого старта
- **Устранение неполадок:** Решения типичных проблем
#### Тестирование
- **Тестовые скрипты:** Автоматизированное тестирование системы
- **Проверка здоровья:** Мониторинг состояния сервисов
- **Интеграционные тесты:** Проверка взаимодействия компонентов
### Изменено 🔄
- **Конфигурация:** Добавлены новые переменные окружения для API ключей
- **Структура проекта:** Создана папка `client/` для клиентской части
- **Docker Compose:** Обновлен для поддержки клиентов и тестовых контейнеров
- **Makefile:** Добавлены команды для управления клиентами
### Технические детали 🔧
#### Новые файлы
```
client/
├── app/main.py # Основной код клиента
├── requirements.txt # Зависимости клиента
├── Dockerfile # Образ клиента
├── docker-compose.yml # Конфигурация клиента
├── Makefile # Управление клиентом
├── README.md # Документация клиента
├── test_client.py # Тесты клиента
└── env.example # Пример конфигурации
```
#### Новые эндпоинты
- `POST /api/v1/logs/remote` - Прием логов от удаленных клиентов
#### Новые переменные окружения
- `LOGBOARD_API_KEYS` - Список разрешенных API ключей
- `LOGBOARD_DEFAULT_API_KEY` - Ключ по умолчанию для разработки
### Совместимость ✅
- **Обратная совместимость:** Все существующие функции работают без изменений
- **Минимальные требования:** Docker 20.10+, Docker Compose 2.0+
- **API совместимость:** Все существующие API эндпоинты сохранены
## [1.0.0] - 2024-01-XX
### Первый релиз 🎉
#### Основные возможности
- Веб-интерфейс для просмотра логов Docker контейнеров
- Поддержка множественных проектов Docker Compose
- JWT аутентификация и авторизация
- WebSocket соединения для логов в реальном времени
- Адаптивный интерфейс с поддержкой темной и светлой темы
- Снимки логов и статистика
- Фильтрация контейнеров
#### Технологии
- FastAPI 0.104.1
- Python 3.11
- Docker & Docker Compose
- WebSocket
- JWT аутентификация
---
## Планы на будущее 🚀
### Версия 1.2.0
- [ ] Веб-интерфейс для просмотра удаленных логов
- [ ] Фильтрация и поиск по удаленным логам
- [ ] Уведомления о критических ошибках
- [ ] Экспорт логов в различные форматы
### Версия 1.3.0
- [ ] Кластеризация и балансировка нагрузки
- [ ] База данных для хранения метаданных
- [ ] Продвинутая аналитика логов
- [ ] Интеграция с внешними системами мониторинга
### Версия 2.0.0
- [ ] Микросервисная архитектура
- [ ] Kubernetes поддержка
- [ ] Масштабируемость до тысяч серверов
- [ ] Машинное обучение для анализа логов

300
docs/remote-clients.md Normal file
View File

@ -0,0 +1,300 @@
# Удаленные клиенты LogBoard
**Автор:** Сергей Антропов
**Сайт:** https://devops.org.ru
## Обзор
LogBoard поддерживает работу с удаленными клиентами, которые могут отправлять логи с других серверов в центральный LogBoard сервер. Это позволяет централизованно собирать и анализировать логи с множества серверов.
## Архитектура
```
┌─────────────────┐ HTTP/JSON ┌─────────────────┐
│ Server A │ ──────────────► │ LogBoard │
│ (Client) │ │ Server │
│ │ │ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │LogBoard │ │ │ │API │ │
│ │Client │ │ │ │Endpoint │ │
│ │Container │ │ │ │/logs/remote │ │
│ └─────────────┘ │ │ └─────────────┘ │
│ ▲ │ │ │ │
│ │ │ │ ▼ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │Docker │ │ │ │File │ │
│ │Socket │ │ │ │Storage │ │
│ └─────────────┘ │ │ └─────────────┘ │
└─────────────────┘ └─────────────────┘
│ ▲
│ │
▼ │
┌─────────────────┐ │
│ Server B │ ──────────────────────────┘
│ (Client) │
│ │
│ ┌─────────────┐ │
│ │LogBoard │ │
│ │Client │ │
│ │Container │ │
│ └─────────────┘ │
│ ▲ │
│ │ │
│ ┌─────────────┐ │
│ │Docker │ │
│ │Socket │ │
│ └─────────────┘ │
└─────────────────┘
```
## Установка клиента
### 1. Клонирование репозитория
```bash
git clone <repository-url>
cd logboard/client
```
### 2. Настройка переменных окружения
```bash
cp env.example .env
# Отредактируйте .env файл
```
### 3. Запуск клиента
```bash
# Используя Makefile
make install
# Или вручную
docker-compose up -d
```
## Конфигурация
### Переменные окружения клиента
| Переменная | Описание | Обязательно | По умолчанию |
|------------|----------|-------------|--------------|
| `LOGBOARD_SERVER_URL` | URL сервера LogBoard | Да | `http://localhost:8000` |
| `LOGBOARD_API_KEY` | API ключ для аутентификации | Да | - |
| `HOSTNAME` | Имя хоста | Нет | Автоопределение |
| `LOGBOARD_INTERVAL` | Интервал отправки (сек) | Нет | `60` |
### Переменные окружения сервера
| Переменная | Описание | Обязательно | По умолчанию |
|------------|----------|-------------|--------------|
| `LOGBOARD_API_KEYS` | Список разрешенных API ключей | Нет | - |
| `LOGBOARD_DEFAULT_API_KEY` | Ключ по умолчанию для разработки | Нет | `dev-key-123` |
## API Endpoints
### POST /api/v1/logs/remote
Прием логов от удаленных клиентов.
**Заголовки:**
```
Authorization: Bearer <api_key>
Content-Type: application/json
```
**Тело запроса:**
```json
{
"hostname": "server-01",
"container_name": "nginx",
"logs": [
"2024-01-01T12:00:00.000Z nginx: [info] Server started",
"2024-01-01T12:00:01.000Z nginx: [info] Listening on port 80"
],
"timestamp": "2024-01-01T12:00:01.000Z"
}
```
**Ответ:**
```json
{
"status": "success",
"message": "Received 2 log lines",
"hostname": "server-01",
"container_name": "nginx",
"timestamp": "2024-01-01T12:00:01.000Z",
"log_file": "nginx-20240101.log"
}
```
## Структура хранения логов
Логи от удаленных клиентов сохраняются в следующей структуре:
```
logs/
├── remote/
│ ├── server-01/
│ │ ├── nginx-20240101.log
│ │ ├── mysql-20240101.log
│ │ └── app-20240101.log
│ └── server-02/
│ ├── nginx-20240101.log
│ └── redis-20240101.log
```
## Безопасность
### Аутентификация
- Все запросы от клиентов должны содержать валидный API ключ
- API ключи передаются в заголовке `Authorization: Bearer <key>`
- Сервер проверяет ключи против списка разрешенных ключей
### Настройка API ключей
1. **На сервере LogBoard:**
```bash
# В .env файле сервера
LOGBOARD_API_KEYS=key1,key2,key3
```
2. **На клиенте:**
```bash
# В .env файле клиента
LOGBOARD_API_KEY=key1
```
### Рекомендации по безопасности
- Используйте уникальные API ключи для каждого клиента
- Регулярно ротируйте API ключи
- Используйте HTTPS для передачи данных
- Ограничьте доступ к серверу LogBoard по IP адресам
## Мониторинг
### Логи клиента
```bash
# Просмотр логов клиента
docker-compose logs -f logboard-client
# Проверка статуса
docker-compose ps logboard-client
```
### Логи сервера
```bash
# Просмотр логов сервера
docker-compose logs -f logboard
# Проверка принятых логов
ls -la logs/remote/
```
## Устранение неполадок
### Проблемы подключения
1. **Ошибка аутентификации (401):**
- Проверьте правильность API ключа
- Убедитесь, что ключ добавлен в `LOGBOARD_API_KEYS` на сервере
2. **Ошибка подключения к серверу:**
- Проверьте URL сервера в `LOGBOARD_SERVER_URL`
- Убедитесь, что сервер доступен по сети
- Проверьте настройки firewall
3. **Ошибка доступа к Docker:**
- Убедитесь, что Docker socket доступен
- Проверьте права доступа к `/var/run/docker.sock`
### Отладка
```bash
# Тестирование подключения
cd client
python test_client.py
# Проверка переменных окружения
docker-compose exec logboard-client env
# Просмотр логов в реальном времени
docker-compose logs -f logboard-client
```
## Примеры использования
### Множественные серверы
```yaml
# docker-compose.yml на сервере A
services:
logboard-client:
environment:
- LOGBOARD_SERVER_URL=http://logboard.example.com:8000
- LOGBOARD_API_KEY=server-a-key
- HOSTNAME=production-server-a
```
```yaml
# docker-compose.yml на сервере B
services:
logboard-client:
environment:
- LOGBOARD_SERVER_URL=http://logboard.example.com:8000
- LOGBOARD_API_KEY=server-b-key
- HOSTNAME=production-server-b
```
### Настройка на центральном сервере
```bash
# .env на сервере LogBoard
LOGBOARD_API_KEYS=server-a-key,server-b-key,server-c-key
```
## Производительность
### Рекомендации
- Установите разумный интервал отправки логов (30-60 секунд)
- Используйте фильтрацию логов на стороне клиента
- Мониторьте размер логовых файлов
- Настройте ротацию логов
### Ограничения
- Максимальный размер запроса: 10MB
- Таймаут запроса: 30 секунд
- Максимальное количество строк в одном запросе: 1000
## Разработка
### Локальная разработка
```bash
# Запуск в режиме разработки
cd client
make dev
# Тестирование
python test_client.py
```
### Добавление новых функций
1. Расширьте API эндпоинты в `app/api/v1/endpoints/logs.py`
2. Обновите клиент в `client/app/main.py`
3. Добавьте тесты в `client/test_client.py`
4. Обновите документацию
## Поддержка
- **Автор:** Сергей Антропов
- **Сайт:** https://devops.org.ru
- **Issues:** Создавайте issues в репозитории проекта

View File

@ -172,3 +172,15 @@ SMTP_FROM=
# Интервал AJAX обновления логов в миллисекундах # Интервал AJAX обновления логов в миллисекундах
LOGBOARD_AJAX_UPDATE_INTERVAL=2000 LOGBOARD_AJAX_UPDATE_INTERVAL=2000
# =============================================================================
# НАСТРОЙКИ УДАЛЕННЫХ КЛИЕНТОВ
# =============================================================================
# API ключи для удаленных клиентов (разделенные запятыми)
# Каждый клиент должен использовать один из этих ключей для аутентификации
# Пример: LOGBOARD_API_KEYS=key1,key2,key3
LOGBOARD_API_KEYS=
# Ключ по умолчанию для разработки (не использовать в продакшене)
LOGBOARD_DEFAULT_API_KEY=dev-key-123

266
test_remote_system.py Normal file
View File

@ -0,0 +1,266 @@
#!/usr/bin/env python3
"""
Тестовый скрипт для проверки системы удаленных клиентов LogBoard
Автор: Сергей Антропов
Сайт: https://devops.org.ru
"""
import asyncio
import aiohttp
import json
import os
import subprocess
import time
from datetime import datetime
# Конфигурация теста
SERVER_URL = os.getenv('LOGBOARD_SERVER_URL', 'http://localhost:9001')
API_KEY = os.getenv('LOGBOARD_API_KEY', 'dev-key-123')
HOSTNAME = os.getenv('HOSTNAME', 'test-host')
async def test_server_availability():
"""Тестирование доступности сервера"""
print("🔍 Проверка доступности сервера LogBoard...")
try:
async with aiohttp.ClientSession() as session:
async with session.get(
f"{SERVER_URL}/",
timeout=aiohttp.ClientTimeout(total=10)
) as response:
if response.status == 200:
print("✅ Сервер LogBoard доступен")
return True
else:
print(f"❌ Сервер недоступен, статус: {response.status}")
return False
except Exception as e:
print(f"❌ Ошибка подключения к серверу: {e}")
return False
async def test_remote_logs_endpoint():
"""Тестирование эндпоинта для удаленных логов"""
print("📤 Тестирование отправки логов...")
test_data = {
"hostname": HOSTNAME,
"container_name": "test-nginx",
"logs": [
f"{datetime.now().isoformat()} nginx: [info] Test log line 1",
f"{datetime.now().isoformat()} nginx: [info] Test log line 2",
f"{datetime.now().isoformat()} nginx: [error] Test error log",
f"{datetime.now().isoformat()} nginx: [warn] Test warning log"
],
"timestamp": datetime.now().isoformat()
}
headers = {
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json',
'User-Agent': 'LogBoard-System-Test/1.0'
}
try:
async with aiohttp.ClientSession() as session:
async with session.post(
f"{SERVER_URL}/api/logs/remote",
json=test_data,
headers=headers,
timeout=aiohttp.ClientTimeout(total=30)
) as response:
print(f"📊 Статус ответа: {response.status}")
if response.status == 200:
result = await response.json()
print("✅ Логи успешно отправлены:")
print(f" - Хост: {result.get('hostname')}")
print(f" - Контейнер: {result.get('container_name')}")
print(f" - Сообщение: {result.get('message')}")
print(f" - Файл: {result.get('log_file')}")
return True
else:
error_text = await response.text()
print(f"❌ Ошибка отправки логов: {response.status}")
print(f" Ответ сервера: {error_text}")
return False
except Exception as e:
print(f"❌ Ошибка при отправке логов: {e}")
return False
def check_docker_services():
"""Проверка статуса Docker сервисов"""
print("🐳 Проверка статуса Docker сервисов...")
try:
# Проверяем статус контейнеров
result = subprocess.run(
['docker-compose', 'ps'],
capture_output=True,
text=True,
cwd='.'
)
if result.returncode == 0:
print("✅ Docker Compose сервисы:")
print(result.stdout)
return True
else:
print(f"❌ Ошибка проверки сервисов: {result.stderr}")
return False
except Exception as e:
print(f"❌ Ошибка проверки Docker: {e}")
return False
def check_log_files():
"""Проверка создания логовых файлов"""
print("📁 Проверка логовых файлов...")
log_dir = "logs/remote"
if os.path.exists(log_dir):
files = os.listdir(log_dir)
if files:
print(f"✅ Найдены логовые файлы в {log_dir}:")
for file in files:
file_path = os.path.join(log_dir, file)
if os.path.isdir(file_path):
subfiles = os.listdir(file_path)
print(f" 📂 {file}/ ({len(subfiles)} файлов)")
for subfile in subfiles[:3]: # Показываем первые 3 файла
print(f" 📄 {subfile}")
if len(subfiles) > 3:
print(f" ... и еще {len(subfiles) - 3} файлов")
return True
else:
print(f"⚠️ Директория {log_dir} пуста")
return False
else:
print(f"❌ Директория {log_dir} не существует")
return False
async def test_client_communication():
"""Тестирование связи с клиентом"""
print("🔗 Тестирование связи с клиентом...")
try:
# Проверяем, запущен ли клиент
result = subprocess.run(
['docker-compose', 'ps', 'logboard-client'],
capture_output=True,
text=True,
cwd='.'
)
if 'Up' in result.stdout:
print("✅ LogBoard клиент запущен")
# Проверяем логи клиента
logs_result = subprocess.run(
['docker-compose', 'logs', '--tail=10', 'logboard-client'],
capture_output=True,
text=True,
cwd='.'
)
if logs_result.returncode == 0:
print("📋 Последние логи клиента:")
print(logs_result.stdout)
return True
else:
print("⚠️ Не удалось получить логи клиента")
return False
else:
print("❌ LogBoard клиент не запущен")
return False
except Exception as e:
print(f"❌ Ошибка проверки клиента: {e}")
return False
async def main():
"""Основная функция тестирования"""
print("=" * 70)
print("LogBoard Remote System - Полное тестирование")
print("=" * 70)
print(f"Сервер: {SERVER_URL}")
print(f"API ключ: {API_KEY[:10]}..." if len(API_KEY) > 10 else f"API ключ: {API_KEY}")
print(f"Хост: {HOSTNAME}")
print()
tests = []
# Тест 1: Проверка Docker сервисов
print("1⃣ Проверка Docker сервисов...")
docker_ok = check_docker_services()
tests.append(("Docker сервисы", docker_ok))
print()
# Тест 2: Проверка доступности сервера
print("2⃣ Проверка доступности сервера...")
server_ok = await test_server_availability()
tests.append(("Доступность сервера", server_ok))
print()
if server_ok:
# Тест 3: Отправка тестовых логов
print("3⃣ Отправка тестовых логов...")
logs_ok = await test_remote_logs_endpoint()
tests.append(("Отправка логов", logs_ok))
print()
# Тест 4: Проверка логовых файлов
print("4⃣ Проверка логовых файлов...")
files_ok = check_log_files()
tests.append(("Логовые файлы", files_ok))
print()
# Тест 5: Проверка клиента
print("5⃣ Проверка клиента...")
client_ok = await test_client_communication()
tests.append(("Связь с клиентом", client_ok))
print()
else:
print("⚠️ Пропуск тестов 3-5 из-за недоступности сервера")
tests.extend([
("Отправка логов", False),
("Логовые файлы", False),
("Связь с клиентом", False)
])
# Результаты тестирования
print("=" * 70)
print("РЕЗУЛЬТАТЫ ТЕСТИРОВАНИЯ")
print("=" * 70)
passed = 0
total = len(tests)
for test_name, result in tests:
status = "✅ ПРОЙДЕН" if result else "НЕ ПРОЙДЕН"
print(f"{test_name:<20} {status}")
if result:
passed += 1
print()
print(f"Итого: {passed}/{total} тестов пройдено")
if passed == total:
print("🎉 Все тесты пройдены успешно!")
print(" Система удаленных клиентов работает корректно.")
elif passed >= total * 0.7:
print("⚠️ Большинство тестов пройдено.")
print(" Проверьте настройки для неудачных тестов.")
else:
print("❌ Много тестов не пройдено.")
print(" Проверьте конфигурацию и запуск сервисов.")
print()
print("Полезные команды:")
print(" make up - Запуск всех сервисов")
print(" make logs - Просмотр логов")
print(" make status - Статус сервисов")
print(" cd client && make test - Тестирование клиента")
if __name__ == "__main__":
asyncio.run(main())