Reading API /api/v1/logs with a stable cursor, and API tokens
GET /api/v1/logs?query=<LogsQL>&since=<cursor|time|duration>&limit=<n> returns the logs after a cursor, oldest first, with next_cursor and has_more. The cursor is a position on _time (the reception time) plus the number of rows already returned among those sharing that exact time, ordered by a hash of their content, so following the cursors gives every row once even within groups cut by a page. Rows count once older than the lag (30 s), and nothing is returned while the disk spool still holds logs. API tokens (Settings > API) give read-only access to /api/v1/ as "Authorization: Bearer lst_...", in every auth mode; only their SHA-256 is kept in /data/api_tokens.json. README (EN/FR) documents the route and an n8n workflow keeping the cursor in $getWorkflowStaticData. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
d61384ed2c
commit
c8f45044a3
12 files changed
+1327
-7
No files matched your search
+80
-2
@@ -390,6 +390,79 @@ charge (pas de `| pipes`).
|
||||
|
||||
En ligne de commande : `curl -o logs.csv 'localhost:8080/api/export.csv?q=error&range=24h&tz=Europe/Paris'`.
|
||||
|
||||
## API de lecture (n8n, scripts)
|
||||
|
||||
`GET /api/v1/logs` renvoie les logs stockés qui suivent un curseur, du plus ancien au plus
|
||||
récent : un outil appelé à intervalle régulier (n8n, un script, un connecteur SIEM) reçoit ainsi
|
||||
chaque nouveau log une fois, sans trou ni doublon, même quand beaucoup de logs ont le même
|
||||
horodatage.
|
||||
|
||||
L'accès passe par un **jeton d'API**, créé dans *Paramètres > API* par un administrateur et
|
||||
envoyé dans `Authorization: Bearer lst_…`. Un jeton est en lecture seule et n'ouvre que
|
||||
`/api/v1/` ; il fonctionne quel que soit le mode d'[authentification](#authentification)
|
||||
(`local`, `oidc` ou aucune). Il n'est affiché qu'une fois, à sa création : LogStream n'en garde
|
||||
que l'empreinte SHA-256, dans `/data/api_tokens.json`. Le révoquer coupe l'accès immédiatement ;
|
||||
la liste indique quand et d'où chaque jeton a servi pour la dernière fois. Une session ouverte
|
||||
dans le navigateur peut aussi appeler la route.
|
||||
|
||||
| Paramètre | Rôle |
|
||||
|---|---|
|
||||
| `query` | filtre LogsQL (par défaut `*`), par exemple `severity:in(err,crit) app:sshd`. Les pipes (`\| ...`) sont refusés. |
|
||||
| `since` | point de départ : le `next_cursor` de la réponse précédente, une date RFC 3339 (`2026-11-01T08:00:00Z`) ou une durée avant maintenant (`15m`, `6h`, `7d`). Par défaut : `1h`. |
|
||||
| `limit` | lignes par réponse, de 1 à 10 000 (1 000 par défaut). |
|
||||
| `lag` | seuls les logs reçus depuis au moins cette durée sont renvoyés (`30s` par défaut, `1h` au plus), pour ne pas sauter les logs encore dans la file ou en attente de leur nom DNS. |
|
||||
|
||||
```json
|
||||
{
|
||||
"rows": [{"_time": "2026-11-01T14:02:11.52Z", "_msg": "…", "host": "fw01", "app": "sshd", "severity": "err", …}],
|
||||
"next_cursor": "eyJ0IjoiMjAyNi0xMS0wMVQxNDowMjoxMS41MjAwMDAwMDFaIiwicSI6IjJlYjE4YzNhIn0",
|
||||
"has_more": false,
|
||||
"until": "2026-11-01T14:05:30.1Z"
|
||||
}
|
||||
```
|
||||
|
||||
Les lignes contiennent les champs stockés (comme la vue JSON de l'interface), triées par
|
||||
`_time`, l'heure de réception. Renvoyez toujours `next_cursor`, même quand `rows` est vide.
|
||||
`has_more: true` signifie que d'autres logs sont prêts : rappelez tout de suite.
|
||||
`waiting: true` signifie que des logs du [tampon disque](#configuration) sont encore en cours
|
||||
d'envoi à VictoriaLogs : rien n'est renvoyé tant qu'ils ne sont pas stockés, pour n'en manquer
|
||||
aucun. Le curseur est lié à sa `query` : changer la requête garde la position dans le temps. Les
|
||||
erreurs arrivent sous la forme `{"error", "code"}` avec le statut 400 (paramètre ou LogsQL
|
||||
invalide), 401 (jeton invalide ou révoqué) ou 502 (VictoriaLogs injoignable).
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer lst_…" \
|
||||
"https://logs.example.org/api/v1/logs?query=severity:err&since=15m&limit=1000"
|
||||
```
|
||||
|
||||
### Exemple : workflow n8n
|
||||
|
||||
Quatre nœuds, le curseur gardé dans les données statiques du workflow d'une exécution à l'autre :
|
||||
|
||||
1. **Schedule Trigger** : toutes les minutes.
|
||||
2. **Code** (*Run Once for All Items*), lit le curseur :
|
||||
```js
|
||||
const data = $getWorkflowStaticData('global');
|
||||
return [{ json: { since: data.cursor || '15m' } }];
|
||||
```
|
||||
3. **HTTP Request** : méthode `GET`, URL `https://logs.example.org/api/v1/logs`.
|
||||
*Authentication* : Generic Credential Type > Header Auth, avec le nom `Authorization` et la
|
||||
valeur `Bearer lst_…`. *Send Query Parameters* : `query` = `severity:in(emerg,alert,crit,err)`,
|
||||
`since` = `{{ $json.since }}`, `limit` = `1000`.
|
||||
4. **Code** (*Run Once for All Items*), enregistre le curseur et fait de chaque log un item :
|
||||
```js
|
||||
const data = $getWorkflowStaticData('global');
|
||||
const page = $input.first().json;
|
||||
data.cursor = page.next_cursor;
|
||||
return page.rows.map((row) => ({ json: row }));
|
||||
```
|
||||
|
||||
Branchez ensuite ce que les logs doivent alimenter (Slack, mail, ticket, base de données…). n8n ne
|
||||
garde les données statiques que pour les exécutions du workflow actif, pas pour les tests
|
||||
manuels : pendant les essais, chaque exécution repart de `15m`. Au-delà de 1 000 logs par minute,
|
||||
`has_more` vaut true et les exécutions suivantes rattrapent le retard ; augmentez `limit` ou la
|
||||
fréquence du déclencheur si besoin.
|
||||
|
||||
## Paramètres
|
||||
|
||||
L'icône en forme d'engrenage ouvre les paramètres, organisés en onglets. Les modifications
|
||||
@@ -397,8 +470,8 @@ s'affichent tout de suite mais ne sont conservées qu'avec le bouton **Valider**
|
||||
des onglets : il prend la couleur d'accent tant que des modifications sont en attente. **Annuler**
|
||||
revient aux paramètres enregistrés, et fermer la fenêtre avec des modifications non validées
|
||||
demande confirmation avant de les abandonner. Localisation et Affichage sont mémorisés par
|
||||
navigateur ; les tags de couleur et les sources de logs sont enregistrés sur le serveur. La purge
|
||||
et la réinitialisation des colonnes agissent immédiatement.
|
||||
navigateur ; les tags de couleur et les sources de logs sont enregistrés sur le serveur. La purge,
|
||||
les jetons d'API et la réinitialisation des colonnes agissent immédiatement.
|
||||
|
||||
- **Localisation et langue**
|
||||
- *Langue* : anglais ou français.
|
||||
@@ -452,6 +525,9 @@ et la réinitialisation des colonnes agissent immédiatement.
|
||||
désactivée par défaut : mettez `ALLOW_PURGE=true` pour l'autoriser. Tout administrateur peut
|
||||
alors purger : activez l'[authentification](#authentification) si l'interface est accessible
|
||||
à d'autres.
|
||||
- **API** : jetons de l'[API de lecture](#api-de-lecture-n8n-scripts) : en créer un avec un nom
|
||||
(affiché une seule fois, à copier tout de suite), voir quand et d'où chacun a servi pour la
|
||||
dernière fois, le révoquer. Réservé aux administrateurs.
|
||||
|
||||
## Authentification
|
||||
|
||||
@@ -631,6 +707,8 @@ Pour mettre à jour l'une d'elles :
|
||||
| `hub.go` | envoie les nouveaux messages aux navigateurs (SSE) |
|
||||
| `rdns.go` | résolutions DNS inverses avec cache |
|
||||
| `export.go` | export CSV en flux |
|
||||
| `logsapi.go` | API de lecture `/api/v1/logs` : curseur, groupes de logs de même heure |
|
||||
| `apitokens.go` | jetons d'API : stockage (haché), contrôle Bearer, `/api/tokens` |
|
||||
| `docker.go` | logs des conteneurs Docker (API, lecteurs, positions, détection du niveau) |
|
||||
| `syslogserver.go` | écoutes syslog ouvertes et fermées depuis Paramètres > Sources de logs |
|
||||
| `hostlogs.go`, `journal.go` | logs système de l'hôte : lecteur du journal systemd (sans `journalctl`) et suivi de `/var/log` |
|
||||
|
||||
Reference in new issue
Block a user