# CentralLogging — centrální logování (Loki + Grafana + FastAPI gateway)

**Verze:** 1.0 · **Datum:** 2026-06-08 · **Autor:** Vladimír Buzalka

Řešení pro sjednocení logů z rozházených skriptů projektu Janssen do jednoho
centrálního místa na Unraidu, **bez zrušení stávajícího souborového logování**.
Soubory a centrál běží paralelně; po ~měsíci ověřování se souborové logování
vypne jedním přepínačem.

---

## 1. Architektura

```
  ┌────────────────┐     HTTP POST /log/batch      ┌──────────────┐   push   ┌────────┐
  │  Python skript │  ── Bearer token, JSON ──────▶ │ Log Gateway  │ ───────▶ │  Loki  │
  │  (central_     │      (na pozadí, v dávkách)    │  (FastAPI)   │  /push   │ (90 d) │
  │   logging.py)  │                                └──────────────┘          └────┬───┘
  │                │                                                                │
  │  + soubor .log │  (stávající RotatingFileHandler — zatím zůstává)               │
  └────────────────┘                                                          ┌─────▼────┐
                                                                              │ Grafana  │
   Při výpadku gateway → lokální spool .ndjson, přehraje se po obnovení.      │ (dashbrd)│
                                                                              └──────────┘
```

**Proč takhle:**
- **Loki** — průmyslový standard pro centrální logy, levné úložiště (chunky na
  Unraid share), retence vynucená automaticky, vizualizace v Grafaně.
- **FastAPI gateway** (jako tvůj `msgreceiver`) — skripty neznají interní detaily
  Loki ani DB hesla, jen jednoduchý JSON + sdílený token. Backend lze kdykoli
  vyměnit beze změny skriptů.
- **Klient jen stdlib** (`urllib`) — žádné `pip install` do desítek skriptů.
- **Neblokující + odolné** — emit jen vloží do fronty, odesílá vlákno na pozadí
  v dávkách; při výpadku spool soubor → žádný log se neztratí.

---

## 2. Adresářová struktura

```
CentralLogging/
├── README.md                       ← tento soubor
├── docker/
│   ├── docker-compose.yml          Loki + Grafana + Gateway
│   ├── loki-config.yml             Loki, retence 90 dní
│   └── grafana-datasource.yml      auto-přidání Loki do Grafany
├── gateway/
│   ├── log_gateway_v1.0.py         FastAPI brána
│   ├── log_gateway_v1.0.md         dokumentace brány
│   ├── requirements.txt
│   └── Dockerfile
└── client/
    ├── central_logging.py          stabilní import shim
    ├── central_logging_v1.0.py     implementace (verze ve jméně)
    ├── central_logging_v1.0.md     dokumentace knihovny
    └── example_usage_v1.0.py       ukázka integrace
```

---

## 3. Nasazení na Unraid

1. Zkopíruj adresář `CentralLogging/` na Unraid (např. do
   `/mnt/user/appdata/central-logging/src`).
2. Uprav cesty volumes v `docker-compose.yml` (výchozí
   `/mnt/user/appdata/central-logging/{loki,grafana}`).
3. Nastav tajemství (soubor `.env` vedle compose, NE do gitu):
   ```env
   LOG_TOKEN=nejaky-dlouhy-nahodny-retezec
   GRAFANA_PASSWORD=silne-heslo
   ```
4. Spusť:
   ```bash
   cd docker
   docker compose up -d
   docker compose ps
   ```
5. Kontrola:
   - Gateway health: `curl http://192.168.1.76:8770/health`
     → očekávané `{"status":"ok","loki":"ready",...}`
   - Grafana: `http://192.168.1.76:3001` (admin / GRAFANA_PASSWORD)
     → Explore → datasource Loki → dotaz `{app="..."}`

> Porty: Loki 3100 (interní), Grafana 3001 (3000 drží Gitea), Gateway 8770. Uprav, pokud kolidují
> (msgreceiver běží na 8765).

---

## 4. Integrace do stávajících skriptů

Typický skript dnes obsahuje:
```python
import logging
logging.basicConfig(filename=str(LOG_FILE), level=logging.ERROR,
                    format="%(asctime)s | %(message)s",
                    datefmt="%Y-%m-%d %H:%M:%S", encoding="utf-8")
```

Nahradíš celý blok `basicConfig(...)` jedním voláním — **zbytek skriptu
(`logging.error(...)`, `log.info(...)`) zůstává beze změny:**
```python
import sys
sys.path.insert(0, r"U:\PythonProject\Janssen\CentralLogging\client")
from central_logging import setup_logging

log = setup_logging("nazev_skriptu")    # label aplikace v Loki
```

Konfigurace přes ENV (nebo argumenty `setup_logging`):

| ENV                     | Default                     | Význam                                |
|-------------------------|-----------------------------|---------------------------------------|
| `CENTRAL_LOG_GATEWAY`   | `http://192.168.1.76:8770`  | URL brány                             |
| `CENTRAL_LOG_TOKEN`     | `change-this-shared-secret` | sdílené tajemství (musí sedět s bránou)|
| `CENTRAL_LOG_ENV`       | `prod`                      | label prostředí                        |
| `CENTRAL_LOG_KEEP_FILE` | `1`                         | psát i do souboru (po měsíci → `0`)    |
| `CENTRAL_LOG_LEVEL`     | `INFO`                      | min. úroveň                            |

---

## 5. Plán migrace (souběh → vypnutí souborů)

1. **Fáze A — souběh (cca 1 měsíc):** `keep_file=True` (default). Logy jdou do
   souboru i do Loki. Ověřuješ úplnost, ladíš dashboardy, labely, úrovně.
2. **Fáze B — ověření:** porovnáš soubor vs. Loki na vybraných skriptech, že
   nic nechybí (spool funguje i při výpadcích).
3. **Fáze C — vypnutí souborů:** nastav globálně `CENTRAL_LOG_KEEP_FILE=0`
   (nebo `setup_logging(..., keep_file=False)`). Skripty pak píší jen do
   centrálu. Staré `.log` soubory přesuň do `TRASH/` (nemazat — konvence).

---

## 6. Dotazy v Grafaně (LogQL)

```logql
{app="parse_emails_graph"}                       # vše z jednoho skriptu
{app="parse_emails_graph", level="ERROR"}        # jen chyby
{env="prod"} |= "bulk_write"                     # fulltext napříč skripty
{app="edc_import"} | json | line > 100           # parsování JSON pole z těla
sum by (app) (count_over_time({level="ERROR"}[1h]))   # počet chyb / skript / h
```

Labely (nízká kardinalita): `app`, `host`, `level`, `env`.
Vše ostatní (`logger`, `func`, `line`, `exc`, `extra`) je v těle řádku jako
JSON → v Grafaně dostupné přes `| json`.

---

## 7. Bezpečnost a poznámky

- Token je sdílené tajemství; drž ho v `.env` / ENV, ne v gitu.
- Gateway běží v interní docker síti; ven publikuje jen port 8770. Pokud má být
  dostupná i mimo LAN, dej před ni reverzní proxy s TLS.
- Loki má `auth_enabled: false` (single-tenant, interní). Pro veřejné vystavení
  přidej autentizaci na proxy.
- Spool soubory (`_log_spool/*.ndjson`) vznikají jen při výpadku brány a samy
  se po obnovení spojení vyprázdní.
