docs
This commit is contained in:
341
docs/monitoring.md
Normal file
341
docs/monitoring.md
Normal file
@@ -0,0 +1,341 @@
|
||||
# Мониторинг
|
||||
|
||||
## Обзор
|
||||
|
||||
Мониторинг 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",
|
||||
"User quota usage in bytes"
|
||||
).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. **Документация**: Документируйте все кастомные метрики
|
||||
Reference in New Issue
Block a user