2025-08-02 00:18:09 +03:00
|
|
|
|
# Мониторинг
|
|
|
|
|
|
|
|
|
|
|
|
## Обзор
|
|
|
|
|
|
|
|
|
|
|
|
Мониторинг Quoter включает в себя логирование, метрики производительности и отслеживание состояния системы.
|
|
|
|
|
|
|
|
|
|
|
|
## Логирование
|
|
|
|
|
|
|
|
|
|
|
|
### Уровни логирования
|
|
|
|
|
|
|
|
|
|
|
|
Quoter использует библиотеку `log` с различными уровнями:
|
|
|
|
|
|
|
|
|
|
|
|
- **error** - Критические ошибки, требующие немедленного внимания
|
|
|
|
|
|
- **warn** - Предупреждения, которые могут указывать на проблемы
|
|
|
|
|
|
- **info** - Информационные сообщения о нормальной работе
|
|
|
|
|
|
- **debug** - Отладочная информация для разработчиков
|
|
|
|
|
|
- **trace** - Максимальная детализация для глубокой отладки
|
|
|
|
|
|
|
|
|
|
|
|
### Настройка логирования
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# Только ошибки
|
|
|
|
|
|
RUST_LOG=error cargo run
|
|
|
|
|
|
|
|
|
|
|
|
# Предупреждения и ошибки
|
|
|
|
|
|
RUST_LOG=warn cargo run
|
|
|
|
|
|
|
|
|
|
|
|
# Информационные сообщения (рекомендуется для продакшена)
|
|
|
|
|
|
RUST_LOG=info cargo run
|
|
|
|
|
|
|
|
|
|
|
|
# Отладка
|
|
|
|
|
|
RUST_LOG=debug cargo run
|
|
|
|
|
|
|
|
|
|
|
|
# Максимальная детализация
|
|
|
|
|
|
RUST_LOG=trace cargo run
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Структура логов
|
|
|
|
|
|
|
|
|
|
|
|
#### Загрузка файла
|
|
|
|
|
|
```
|
|
|
|
|
|
[INFO] Started
|
|
|
|
|
|
[WARN] file abc123.jpg uploaded to storj, incrementing quota by 1048576 bytes
|
|
|
|
|
|
[WARN] New quota for user user123: 2097152 bytes
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### Получение файла
|
|
|
|
|
|
```
|
|
|
|
|
|
[WARN] >>> GET image_300.jpg [START]
|
|
|
|
|
|
[WARN] detected file extension: jpg
|
|
|
|
|
|
[WARN] base_filename: image
|
|
|
|
|
|
[WARN] requested width: 300
|
|
|
|
|
|
[WARN] Found stored path in DB: production/image/image.jpg
|
|
|
|
|
|
[WARN] File exists in Storj: production/image/image.jpg
|
|
|
|
|
|
[WARN] Processing image file with width: 300
|
|
|
|
|
|
[WARN] Calculated closest width: 300 for requested: 300
|
|
|
|
|
|
[WARN] serve existed thumb file: image_300.jpg
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### Ошибки
|
|
|
|
|
|
```
|
|
|
|
|
|
[ERROR] Failed to upload to Storj: image.jpg - Error: Network error
|
|
|
|
|
|
[ERROR] Database error while getting path: image.jpg - Full error: Connection timeout
|
|
|
|
|
|
[ERROR] unsupported file format
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Метрики
|
|
|
|
|
|
|
|
|
|
|
|
### Основные метрики для мониторинга
|
|
|
|
|
|
|
|
|
|
|
|
#### Производительность
|
|
|
|
|
|
- **Requests per second (RPS)** - количество запросов в секунду
|
|
|
|
|
|
- **Response time** - время ответа API
|
|
|
|
|
|
- **Error rate** - процент ошибок
|
|
|
|
|
|
- **Upload success rate** - процент успешных загрузок
|
|
|
|
|
|
|
|
|
|
|
|
#### Ресурсы
|
|
|
|
|
|
- **Memory usage** - использование памяти
|
|
|
|
|
|
- **CPU usage** - использование процессора
|
|
|
|
|
|
- **Disk I/O** - операции с диском
|
|
|
|
|
|
- **Network I/O** - сетевой трафик
|
|
|
|
|
|
|
|
|
|
|
|
#### Бизнес-метрики
|
|
|
|
|
|
- **Files uploaded** - количество загруженных файлов
|
|
|
|
|
|
- **Quota usage** - использование квот пользователями
|
|
|
|
|
|
- **Thumbnail generation** - количество сгенерированных миниатюр
|
|
|
|
|
|
- **Storage usage** - использование хранилища
|
|
|
|
|
|
|
|
|
|
|
|
### Prometheus метрики
|
|
|
|
|
|
|
|
|
|
|
|
Добавьте в `Cargo.toml`:
|
|
|
|
|
|
|
|
|
|
|
|
```toml
|
|
|
|
|
|
[dependencies]
|
|
|
|
|
|
prometheus = "0.13"
|
|
|
|
|
|
actix-web-prom = "0.6"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Настройте в `main.rs`:
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
use actix_web_prom::{PrometheusMetrics, PrometheusMetricsBuilder};
|
|
|
|
|
|
|
|
|
|
|
|
#[actix_web::main]
|
|
|
|
|
|
async fn main() -> std::io::Result<()> {
|
|
|
|
|
|
let prometheus = PrometheusMetricsBuilder::new("quoter")
|
|
|
|
|
|
.endpoint("/metrics")
|
|
|
|
|
|
.build()
|
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
HttpServer::new(move || {
|
|
|
|
|
|
App::new()
|
|
|
|
|
|
.wrap(prometheus.clone())
|
|
|
|
|
|
// ... остальные настройки
|
|
|
|
|
|
})
|
|
|
|
|
|
.bind(addr)?
|
|
|
|
|
|
.run()
|
|
|
|
|
|
.await
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Кастомные метрики
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
use prometheus::{Counter, Histogram, Registry};
|
|
|
|
|
|
|
|
|
|
|
|
lazy_static! {
|
|
|
|
|
|
pub static ref UPLOAD_COUNTER: Counter = Counter::new(
|
|
|
|
|
|
"quoter_uploads_total",
|
|
|
|
|
|
"Total number of file uploads"
|
|
|
|
|
|
).unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
pub static ref UPLOAD_SIZE: Histogram = Histogram::new(
|
|
|
|
|
|
"quoter_upload_size_bytes",
|
|
|
|
|
|
"File upload size in bytes"
|
|
|
|
|
|
).unwrap();
|
|
|
|
|
|
|
|
|
|
|
|
pub static ref QUOTA_USAGE: Histogram = Histogram::new(
|
|
|
|
|
|
"quoter_quota_usage_bytes",
|
2025-09-01 20:36:15 +03:00
|
|
|
|
"Author quota usage in bytes"
|
2025-08-02 00:18:09 +03:00
|
|
|
|
).unwrap();
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Алерты
|
|
|
|
|
|
|
|
|
|
|
|
### Критические алерты
|
|
|
|
|
|
|
|
|
|
|
|
- **Service down** - сервис недоступен
|
|
|
|
|
|
- **High error rate** - высокий процент ошибок (>5%)
|
|
|
|
|
|
- **High response time** - медленные ответы (>2s)
|
|
|
|
|
|
- **Memory usage high** - высокое использование памяти (>80%)
|
|
|
|
|
|
- **Redis connection failed** - потеря соединения с Redis
|
|
|
|
|
|
|
|
|
|
|
|
### Предупреждения
|
|
|
|
|
|
|
|
|
|
|
|
- **Quota usage high** - пользователи приближаются к лимиту квоты
|
|
|
|
|
|
- **Storage usage high** - высокое использование хранилища
|
|
|
|
|
|
- **Thumbnail generation slow** - медленная генерация миниатюр
|
|
|
|
|
|
|
|
|
|
|
|
### Настройка алертов в Prometheus
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
groups:
|
|
|
|
|
|
- name: quoter
|
|
|
|
|
|
rules:
|
|
|
|
|
|
- alert: QuoterServiceDown
|
|
|
|
|
|
expr: up{job="quoter"} == 0
|
|
|
|
|
|
for: 1m
|
|
|
|
|
|
labels:
|
|
|
|
|
|
severity: critical
|
|
|
|
|
|
annotations:
|
|
|
|
|
|
summary: "Quoter service is down"
|
|
|
|
|
|
|
|
|
|
|
|
- alert: HighErrorRate
|
|
|
|
|
|
expr: rate(http_requests_total{job="quoter",status=~"5.."}[5m]) > 0.05
|
|
|
|
|
|
for: 2m
|
|
|
|
|
|
labels:
|
|
|
|
|
|
severity: warning
|
|
|
|
|
|
annotations:
|
|
|
|
|
|
summary: "High error rate detected"
|
|
|
|
|
|
|
|
|
|
|
|
- alert: HighResponseTime
|
|
|
|
|
|
expr: histogram_quantile(0.95, rate(http_request_duration_seconds_bucket{job="quoter"}[5m])) > 2
|
|
|
|
|
|
for: 5m
|
|
|
|
|
|
labels:
|
|
|
|
|
|
severity: warning
|
|
|
|
|
|
annotations:
|
|
|
|
|
|
summary: "High response time detected"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Дашборды
|
|
|
|
|
|
|
|
|
|
|
|
### Grafana дашборд
|
|
|
|
|
|
|
|
|
|
|
|
Создайте дашборд с панелями:
|
|
|
|
|
|
|
|
|
|
|
|
#### Обзор системы
|
|
|
|
|
|
- Статус сервиса (up/down)
|
|
|
|
|
|
- Количество запросов в секунду
|
|
|
|
|
|
- Время ответа (p50, p95, p99)
|
|
|
|
|
|
- Процент ошибок
|
|
|
|
|
|
|
|
|
|
|
|
#### Загрузка файлов
|
|
|
|
|
|
- Количество загрузок в час/день
|
|
|
|
|
|
- Размер загружаемых файлов
|
|
|
|
|
|
- Успешность загрузок
|
|
|
|
|
|
- Использование квот
|
|
|
|
|
|
|
|
|
|
|
|
#### Ресурсы
|
|
|
|
|
|
- Использование CPU и памяти
|
|
|
|
|
|
- Сетевой трафик
|
|
|
|
|
|
- Операции с диском
|
|
|
|
|
|
- Соединения с Redis
|
|
|
|
|
|
|
|
|
|
|
|
#### Бизнес-метрики
|
|
|
|
|
|
- Топ пользователей по использованию квоты
|
|
|
|
|
|
- Популярные размеры миниатюр
|
|
|
|
|
|
- Использование хранилища по типам файлов
|
|
|
|
|
|
|
|
|
|
|
|
### Пример запроса для Grafana
|
|
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- Количество загрузок по часам
|
|
|
|
|
|
SELECT
|
|
|
|
|
|
time_bucket('1 hour', timestamp) AS time,
|
|
|
|
|
|
COUNT(*) as uploads
|
|
|
|
|
|
FROM quoter_uploads
|
|
|
|
|
|
WHERE timestamp > NOW() - INTERVAL '24 hours'
|
|
|
|
|
|
GROUP BY time
|
|
|
|
|
|
ORDER BY time;
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Здоровье системы
|
|
|
|
|
|
|
|
|
|
|
|
### Health check endpoint
|
|
|
|
|
|
|
|
|
|
|
|
Добавьте endpoint для проверки здоровья:
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
async fn health_check() -> HttpResponse {
|
|
|
|
|
|
// Проверка Redis
|
|
|
|
|
|
let redis_ok = check_redis_connection().await;
|
|
|
|
|
|
|
|
|
|
|
|
// Проверка S3
|
|
|
|
|
|
let s3_ok = check_s3_connection().await;
|
|
|
|
|
|
|
|
|
|
|
|
if redis_ok && s3_ok {
|
|
|
|
|
|
HttpResponse::Ok().json(json!({
|
|
|
|
|
|
"status": "healthy",
|
|
|
|
|
|
"timestamp": chrono::Utc::now(),
|
|
|
|
|
|
"services": {
|
|
|
|
|
|
"redis": "ok",
|
|
|
|
|
|
"s3": "ok"
|
|
|
|
|
|
}
|
|
|
|
|
|
}))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
HttpResponse::ServiceUnavailable().json(json!({
|
|
|
|
|
|
"status": "unhealthy",
|
|
|
|
|
|
"timestamp": chrono::Utc::now(),
|
|
|
|
|
|
"services": {
|
|
|
|
|
|
"redis": if redis_ok { "ok" } else { "error" },
|
|
|
|
|
|
"s3": if s3_ok { "ok" } else { "error" }
|
|
|
|
|
|
}
|
|
|
|
|
|
}))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Kubernetes liveness/readiness probes
|
|
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
livenessProbe:
|
|
|
|
|
|
httpGet:
|
|
|
|
|
|
path: /health
|
|
|
|
|
|
port: 8080
|
|
|
|
|
|
initialDelaySeconds: 30
|
|
|
|
|
|
periodSeconds: 10
|
|
|
|
|
|
timeoutSeconds: 5
|
|
|
|
|
|
failureThreshold: 3
|
|
|
|
|
|
|
|
|
|
|
|
readinessProbe:
|
|
|
|
|
|
httpGet:
|
|
|
|
|
|
path: /health
|
|
|
|
|
|
port: 8080
|
|
|
|
|
|
initialDelaySeconds: 5
|
|
|
|
|
|
periodSeconds: 5
|
|
|
|
|
|
timeoutSeconds: 3
|
|
|
|
|
|
failureThreshold: 3
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Трассировка
|
|
|
|
|
|
|
|
|
|
|
|
### Distributed tracing
|
|
|
|
|
|
|
|
|
|
|
|
Для отслеживания запросов через микросервисы:
|
|
|
|
|
|
|
|
|
|
|
|
```toml
|
|
|
|
|
|
[dependencies]
|
|
|
|
|
|
opentelemetry = "0.20"
|
|
|
|
|
|
tracing = "0.1"
|
|
|
|
|
|
tracing-opentelemetry = "0.20"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### Логирование с контекстом
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
use tracing::{info, warn, error, instrument};
|
|
|
|
|
|
|
|
|
|
|
|
#[instrument(skip(state))]
|
|
|
|
|
|
async fn upload_handler(
|
|
|
|
|
|
req: HttpRequest,
|
|
|
|
|
|
payload: Multipart,
|
|
|
|
|
|
state: web::Data<AppState>,
|
|
|
|
|
|
) -> Result<HttpResponse> {
|
|
|
|
|
|
let user_id = get_user_id(&req).await?;
|
|
|
|
|
|
info!(user_id = %user_id, "Starting file upload");
|
|
|
|
|
|
|
|
|
|
|
|
// ... логика загрузки
|
|
|
|
|
|
|
|
|
|
|
|
info!(user_id = %user_id, filename = %filename, "File uploaded successfully");
|
|
|
|
|
|
Ok(response)
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Рекомендации
|
|
|
|
|
|
|
|
|
|
|
|
### Продакшен
|
|
|
|
|
|
|
|
|
|
|
|
1. **Логирование**: Используйте структурированные логи в JSON формате
|
|
|
|
|
|
2. **Метрики**: Настройте Prometheus + Grafana
|
|
|
|
|
|
3. **Алерты**: Настройте уведомления для критических событий
|
|
|
|
|
|
4. **Ротация логов**: Настройте logrotate или отправку в ELK stack
|
|
|
|
|
|
5. **Мониторинг ресурсов**: Отслеживайте CPU, память, диск, сеть
|
|
|
|
|
|
|
|
|
|
|
|
### Разработка
|
|
|
|
|
|
|
|
|
|
|
|
1. **Локальное логирование**: Используйте `RUST_LOG=debug`
|
|
|
|
|
|
2. **Отладка**: Включите trace логи для детальной отладки
|
|
|
|
|
|
3. **Тестирование**: Создайте тесты для проверки метрик
|
|
|
|
|
|
4. **Документация**: Документируйте все кастомные метрики
|