[feat] add guides
This commit is contained in:
parent
1fbf77f879
commit
c5d5e243c1
7 changed files with 836 additions and 43 deletions
178
docs/OBSERVABILITY_RU.md
Normal file
178
docs/OBSERVABILITY_RU.md
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
# Логи, метрики и трейсы
|
||||
|
||||
## Зачем нужны три сигнала
|
||||
|
||||
### Логи
|
||||
|
||||
Логи отвечают на вопрос: что произошло.
|
||||
|
||||
Примеры:
|
||||
|
||||
- пришел HTTP-запрос
|
||||
- пользователь не найден
|
||||
- сервис стартовал
|
||||
|
||||
### Метрики
|
||||
|
||||
Метрики отвечают на вопрос: как часто и насколько долго что-то происходит.
|
||||
|
||||
Примеры:
|
||||
|
||||
- сколько было запросов
|
||||
- сколько было ошибок
|
||||
- сколько миллисекунд заняла операция
|
||||
|
||||
### Трейсы
|
||||
|
||||
Трейсы отвечают на вопрос: как прошел путь одного конкретного запроса через систему.
|
||||
|
||||
Примеры:
|
||||
|
||||
- HTTP request -> use case -> repository
|
||||
- где именно замедление
|
||||
- на каком шаге произошла ошибка
|
||||
|
||||
## Как observability устроена в этом проекте
|
||||
|
||||
Внутренние слои не знают про OpenTelemetry напрямую.
|
||||
Они работают только через порты из `usecase/interface.py`:
|
||||
|
||||
- `Logger`
|
||||
- `Metrics`
|
||||
- `Tracer`
|
||||
- `Span`
|
||||
|
||||
Такой подход сохраняет чистую архитектуру: use case зависит от абстракций, а не от SDK.
|
||||
|
||||
## Где лежат реализации
|
||||
|
||||
### Логирование
|
||||
|
||||
- `adapter/observability/logging.py` - логирование в `stdout` или файл
|
||||
- `adapter/otel/logging.py` - логирование через OpenTelemetry exporter
|
||||
|
||||
Поддерживаются режимы:
|
||||
|
||||
- `stdout`
|
||||
- `file`
|
||||
- `otel`
|
||||
|
||||
Поддерживаются форматы:
|
||||
|
||||
- `text`
|
||||
- `json`
|
||||
|
||||
### Метрики
|
||||
|
||||
- `adapter/otel/metrics.py` - адаптер над OTel `Meter`
|
||||
|
||||
Метрики создаются лениво: счетчик или гистограмма заводятся при первом обращении по имени.
|
||||
|
||||
### Трейсинг
|
||||
|
||||
- `adapter/otel/tracing.py` - адаптер над OTel `Tracer`
|
||||
|
||||
Use case или repository может открыть span через `with tracer.start_span(...):`.
|
||||
|
||||
### Runtime factory
|
||||
|
||||
- `adapter/observability/factory.py` - выбирает, какой runtime собрать
|
||||
- `adapter/observability/noop.py` - noop-реализации, когда сигнал отключен
|
||||
- `adapter/otel/bootstrap.py` - создает OTel providers и exporters
|
||||
|
||||
## Как это работает на старте приложения
|
||||
|
||||
1. Загружается `AppConfig`
|
||||
2. `build_observability(config)` выбирает нужные реализации
|
||||
3. Если хотя бы один сигнал должен идти через OTel, создается OTel runtime
|
||||
4. В контейнер кладутся готовые `logger`, `metrics`, `tracer`
|
||||
5. Use case и repository получают их через конструктор
|
||||
|
||||
## Как observability работает в HTTP
|
||||
|
||||
### Request logging
|
||||
|
||||
В `adapter/http/fastapi/middleware.py` есть HTTP middleware, которое:
|
||||
|
||||
- замеряет длительность запроса
|
||||
- фиксирует метод, путь и статус
|
||||
- пишет событие `http_request`
|
||||
|
||||
### HTTP traces и HTTP metrics
|
||||
|
||||
В `adapter/http/fastapi/app.py` вызывается `FastAPIInstrumentor.instrument_app(...)`.
|
||||
|
||||
Это дает стандартную OTel instrumentation для FastAPI/ASGI без кастомного trace middleware.
|
||||
|
||||
## Как пользоваться логгером в use case
|
||||
|
||||
Пример:
|
||||
|
||||
```py
|
||||
class CreateUser:
|
||||
def __init__(self, logger: Logger) -> None:
|
||||
self._logger = logger
|
||||
|
||||
def execute(self, email: str) -> None:
|
||||
self._logger.info('user_create', attrs={'user.email': email})
|
||||
```
|
||||
|
||||
Рекомендации:
|
||||
|
||||
- message короткий и стабильный
|
||||
- важные поля передавать в `attrs`
|
||||
- не шить JSON руками в строку сообщения
|
||||
|
||||
## Как пользоваться метриками
|
||||
|
||||
Пример:
|
||||
|
||||
```py
|
||||
class CreateUser:
|
||||
def __init__(self, metrics: Metrics) -> None:
|
||||
self._metrics = metrics
|
||||
|
||||
def execute(self, email: str) -> None:
|
||||
self._metrics.increment('user.create.total', attrs={'source': 'api'})
|
||||
self._metrics.record('user.create.duration_ms', 12.5)
|
||||
```
|
||||
|
||||
Практика именования:
|
||||
|
||||
- счетчики: `something.total`, `something.error`
|
||||
- длительности: `something.duration_ms`
|
||||
- атрибуты делать короткими и стабильными
|
||||
|
||||
## Как пользоваться трейсами
|
||||
|
||||
Пример:
|
||||
|
||||
```py
|
||||
class CreateUser:
|
||||
def __init__(self, tracer: Tracer) -> None:
|
||||
self._tracer = tracer
|
||||
|
||||
def execute(self, user_id: str) -> None:
|
||||
with self._tracer.start_span('usecase.create_user', attrs={'user.id': user_id}) as span:
|
||||
span.set_attribute('user.flow', 'signup')
|
||||
```
|
||||
|
||||
Если внутри возникнет ошибка, адаптер трейсинга сможет записать ее в span.
|
||||
|
||||
## Как включать и выключать сигналы
|
||||
|
||||
В `config/app.yaml` и env есть основные флаги:
|
||||
|
||||
- `logging.output=stdout|file|otel`
|
||||
- `logging.format=text|json`
|
||||
- `logging.file_path=...`
|
||||
- `metrics.enabled=true|false`
|
||||
- `tracing.enabled=true|false`
|
||||
|
||||
Когда метрики или трейсы выключены, внутренние слои получают `Noop`-реализации.
|
||||
Это значит, что код use case не приходится усложнять проверками `if enabled`.
|
||||
|
||||
## Главное правило
|
||||
|
||||
`domain/` и `usecase/` не импортируют OpenTelemetry.
|
||||
Весь OTel код остается во внешних слоях.
|
||||
Loading…
Add table
Add a link
Reference in a new issue