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:
cedricandClaude Opus 5.5 committed 2026-11-01 15:47:57 +01:00
1 parent d61384ed2c
commit c8f45044a3
12 files changed
+1327 -7

No files matched your search

+80 -2
View File
@@ -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` |