Dans l’article précédent, on a posé le vocabulaire commun : logs, métriques, traces, et le trace_id qui les relie. Reste une question pratique :
où stocker tout ça, et avec quel langage pour l’interroger ?
La réponse classique fait cohabiter plusieurs outils telle que le présente la LGTM stack avec:
- Loki pour les logs,
- Tempo pour les traces,
- Mimir ou Prometheus pour les métriques,
- Grafana pour la visualisation des métriques et des traces.
Chacun avec son propre langage de requête, son API et sa syntaxe.
Une CLI, ou demain un agent, qui interroge cette télémétrie doit croiser les résultats à la main.
OpenObserve (O2) propose un seul binaire pour les trois signaux et le SQL comme langage d’interrogation.
Remarque : cet article a pour vocation de présenter OpenObserve mais et en aucun cas dénigrer la stack LGTM. Cette dernière est très bien faite et peut très bien être utilisée dans de nombreux cas.
En général, l’usage de l’un ou de l’autre dépendra aussi des habitudes des équipes.
La série s’appuie sur ce choix, jusqu’au skill Claude Code de l’article 7.
Un binaire pour trois signaux, interrogeable en SQL, avec un serveur MCP intégré
O2 ingère les logs, les métriques et les traces dans un seul binaire.
Les trois signaux s’interrogent en SQL via une seule route HTTP.
Un serveur MCP est intégré au binaire auto-hébergé, sous licence AGPL, qu’un agent peut interroger pour la télémétrie sans installer de composant supplémentaire.
Un binaire Rust, un format de stockage, une API
OpenObserve est écrit en Rust et distribué comme un binaire unique[1].
Il ingère logs, métriques et traces. Il les stocke au format Parquet et les expose derrière une seule API HTTP. Le projet a franchi sa version 1.0 GA le 11 septembre 2026[2] et la 1.0.4 est sortie le 24 septembre.
O2 couvre les fonctionnalités suivantes :
- SLO,
- alertes composites,
- monitoring de bases de données,
- et une brique « AI Observability » pensée pour la télémétrie LLM et agents.
Le projet totalise environ 22k étoiles sur GitHub à date (16 octobre 2026).
Les logs, métriques et traces se requêtent en SQL via une seule route :
POST /api/{org}/_search[8]
Une même syntaxe sert pour les trois signaux, sans LogQL ni TraceQL.
Nous verrons dans un prochain article comment on peut écrire une CLI qui transforme des filtres en requêtes SQL et l’exploiter via un agent.
Licence : AGPL-3.0 pour le cœur et une version Enterprise
Le cœur d’OpenObserve est sous licence AGPL-3.0.
La version Enterprise, sous licence commerciale[12], est gratuite jusqu’à un plafond d’ingestion journalière[7].
Notre série utilisera la version open source.
Architecture : cinq composant et un chemin d’écriture
En interne, l’architecture de O2 est composé de cinq composants : router, ingester, compactor, querier et le scheduler.
Le chemin d’écriture passe par l’ingester, en quatre étapes[3] :
- chaque requête est d’abord écrite dans un WAL (write-ahead log), un fichier sur disque en ajout seul qui permet de récupérer les données après un crash, puis dans une memtable, un tampon en mémoire au format Arrow. Un WAL et une memtable forment une paire, une par organisation et par type de signal ;
- quand la memtable atteint 256 Mo, ou son WAL 128 Mo, elle est figée en lecture seule (immutable) et une nouvelle paire prend le relais ;
- toutes les 5 secondes, les immutables sont écrites en petits fichiers Parquet sur le disque local de l’ingester, par stream et par partition ;
- toutes les 10 secondes, les petits fichiers d’une partition qui dépasse 128 Mo, ou qui attendent depuis 10 minutes, sont fusionnés en un fichier d’au plus 2 Go et envoyés vers le stockage final : disque local ou stockage objet selon le mode (S3, GCS, MinIO, Azure ou RustFS).
Ces seuils sont les valeurs par défaut. Le compactor fusionne ensuite les fichiers du stockage final pour accélérer les recherches. Une donnée est interrogeable dès la première étape : le querier lit aussi la memtable, les immutables et les Parquet locaux pas encore envoyés.
Comment fonctionne une memtable ?
Une memtable est un tampon en mémoire, modifiable, qui contient les données reçues récemment et pas encore écrites dans un fichier définitif. Le concept vient des moteurs de stockage de type LSM-tree, comme RocksDB[13] : les écritures s'accumulent en mémoire, puis partent en un seul gros fichier.
Dans OpenObserve, l'ingester convertit chaque lot reçu en RecordBatch Arrow, un format colonnaire en mémoire, et l'ajoute à la memtable. Il existe une memtable par organisation et par type de signal. Chacune est créée avec son WAL[3].
Le WAL et la memtable contiennent les mêmes données sous deux formes :
- le WAL est un journal sur disque, écrit dans l'ordre d'arrivée. Il sert à reconstruire la memtable après un crash ;
- la memtable est en RAM, au format colonnaire. Elle sert aux recherches sur les données récentes et à la conversion en Parquet.
Leur cycle de vie suit quatre temps :
- chaque lot est écrit dans le WAL, puis dans la memtable ;
- à 256 Mo de memtable ou 128 Mo de WAL, la memtable devient immutable et une nouvelle paire prend le relais ;
- toutes les 5 secondes, les immutables sont écrites en Parquet sur le disque local ;
- après un crash, l'ingester s'appuie sur le WAL pour récupérer les données qui n'avaient pas atteint le Parquet.
Dans RocksDB, la memtable par défaut est une structure triée (une skiplist)[13]. La documentation d'OpenObserve ne précise pas de tri pour la sienne.
À quoi ressemble un RecordBatch Arrow ?
Un RecordBatch est un lot de lignes rangé en colonnes, en mémoire. Il réunit un schéma (nom, type et nullabilité de chaque colonne), une longueur commune et une colonne par champ[14]. Chaque colonne est un tableau Arrow stocké dans des zones mémoire contiguës, les buffers.
Prenons trois logs reçus dans une même requête. Les noms de champs sont illustratifs, ce n'est pas le schéma exact d'OpenObserve :
_timestamp level message trace_id
1760600000000000 error timeout 4bf92f35…4736
1760600000000100 info ok null
1760600000000200 warn retry 4bf92f35…4736
Arrow range ces lignes colonne par colonne :
Schema
_timestamp : Timestamp(microsecond), non null
level : Utf8, non null
message : Utf8, non null
trace_id : Utf8, nullable
length = 3
Colonne _timestamp
values : [1760600000000000, 1760600000000100, 1760600000000200]
Colonne level
offsets : [0, 5, 9, 13]
data : "errorinfowarn"
Colonne trace_id
validity : 0b00000101
offsets : [0, 32, 32, 64]
data : "4bf92f35…47364bf92f35…4736"
Une colonne utilise jusqu'à trois buffers :
- validity : un bit par ligne, à 1 si la valeur existe. Une colonne sans
nullpeut s'en passer ; - values : pour les types de taille fixe (entiers, flottants, timestamps), les valeurs se suivent. La valeur de la ligne
id'une colonne Int64 se trouve ài × 8octets ; - offsets et data : pour les chaînes, toutes les valeurs sont collées dans
data, et la chaîneiest la tranchedata[offsets[i]..offsets[i+1]]. Ici,level[1]vautdata[5..9], soit"info".
La spécification fixe cette disposition en mémoire, identique dans tous les langages. Elle permet de lire une colonne sans toucher aux autres, de traiter les valeurs par instructions vectorisées (SIMD) et de partager un batch entre composants sans copie[14]. Une requête comme SELECT count(*) WHERE level = 'error' ne parcourt que les buffers de level.
OpenObserve dépend des bibliothèques Rust arrow, parquet et datafusion[18]. DataFusion est un moteur de requête SQL dont le format en mémoire est Arrow[19].
Parquet est lui aussi colonnaire, mais sur disque :
Arrow (RecordBatch) | Parquet | |
|---|---|---|
| Où | en mémoire | sur disque |
| Objectif | accès et calcul rapides | stockage compact |
| Encodage | brut, accès direct à chaque valeur | dictionnaire, RLE[16], puis compression[17] |
| Découpage | un lot de lignes | fichier, row groups, un chunk par colonne[15] |
La memtable garde les données récentes en Arrow pour les interroger immédiatement. Le flush les écrit en Parquet.
Comment est organisé un fichier Parquet ?
Parquet est un format de fichier open source orienté colonnes, conçu pour stocker et relire efficacement de gros volumes de données[20]. Un fichier s'organise en trois niveaux[21] :
- row group : un découpage horizontal des lignes ;
- column chunk : les valeurs d'une colonne pour un row group, contiguës dans le fichier ;
- page : la plus petite unité d'encodage et de compression, à l'intérieur d'un column chunk.
PAR1
row group 1 : [chunk _timestamp] [chunk level] [chunk message] …
row group 2 : [chunk _timestamp] [chunk level] [chunk message] …
métadonnées du fichier (schéma, position de chaque chunk)
longueur des métadonnées
PAR1
Les métadonnées sont écrites après les données, ce qui permet d'écrire le fichier en une seule passe. Un lecteur lit d'abord ces métadonnées, puis seulement les column chunks dont il a besoin[15]. Un index de pages optionnel conserve le minimum et le maximum de chaque page, ce qui permet de sauter des pages entières pendant une lecture[22].
Nous reviendrons sur ce format dans un prochain article.
Trois modes de déploiement existent :
- Standalone SQLite + disque local : le mode par défaut, sans Postgres ni stockage objet.
- Standalone SQLite + stockage objet : même topologie, mais les Parquet finissent sur S3 ou équivalent.
- Haute disponibilité : ce mode exige Kubernetes, un stockage objet, PostgreSQL et NATS, avec au moins un nœud par rôle. La série ne l’utilise pas.
Nous utiliserons le mode Standalone SQLite + disque local dans notre série d’articles.
Ingestion OTLP, interrogation SQL et PromQL
Côté ingestion, O2 parle OTLP nativement.
En HTTP, les endpoints sont /api/{org}/v1/{traces|logs|metrics}, donc une base /api/{org} pour un exporter otlphttp classique[9].
En gRPC, le port est 5081, avec les en-têtes organization et stream-name.
L’authentification passe par du Basic Auth (base64(email:password)).
Côté lecture, la route _search accepte du SQL brut et renvoie des temps en microsecondes.
Le paramètre agent_options.output_format: md_table formate la réponse en table Markdown, destinée à la lecture par un agent.
Pour les métriques, PromQL reste disponible en complément du SQL.
MCP en open source, SRE Agent réservé à l’Enterprise
O2 expose un serveur MCP natif, en open source, sur /api/{org}/mcp, en HTTP avec Basic Auth[4].
Les outils exposés sont SearchSQL, PrometheusRangeQuery, GetLatestTraces, StreamList, StreamSchema, plus la gestion des alertes et des dashboards.
Un agent (Claude Code, par exemple) peut donc interroger la télémétrie sans passer par une CLI intermédiaire.
Remarque : chez mon client, nous avons quand même développé une CLI pour interroger en REST O2 car au moment, nous avions commencé à l’utiliser il y a 2 ans et demi, le serveur MCP n’existait pas.
Cela ne nous a pas empêché de développer des skills de diagnostic fonctionnel.
Le SRE Agent, qui pousse plus loin vers le diagnostic automatique d’incidents (root cause analysis), est réservé à l’offre Enterprise, avec des providers Anthropic, OpenAI et Gemini[5].
Au cours de notre série, nous construirons notre propre skill de diagnostic au-dessus de l’API
_search, par l’intermédiaire d’une CLI. Le serveur MCP sera présenté en comparaison.
En OSS, un compte de service a les pleins droits et il n’existe pas de RBAC[6].
Un jeton donné à un agent permet la lecture, l’écriture et l’administration des alertes.
Face à Datadog et à la stack Grafana
| OpenObserve | Stack Grafana (Loki/Tempo/Mimir) | Datadog | |
|---|---|---|---|
| Composants | 1 binaire | 3 backends spécialisés | SaaS managé |
| Langage de requête | SQL (+ PromQL) | LogQL, TraceQL, PromQL | syntaxe de requête propre (+ DDSQL) |
| Stockage | Parquet, objet ou local | 3 formats distincts | géré par l’éditeur |
| Modèle | auto-hébergé, AGPL | auto-hébergé, AGPL-3.0 | facturé à l’hôte ou au volume |
| Interrogation agent | MCP OSS natif, dans le même binaire | MCP officiel via Grafana (mcp-grafana)[10] |
MCP officiel, en SaaS[11] |
Grafana et Datadog proposent chacun un MCP officiel.
O2 intègre le sien au binaire auto-hébergé, et il s’interroge en SQL.
Conclusion
OpenObserve est un binaire Rust unique qui ingère les logs, les métriques et les traces, les stocke au format Parquet et les expose derrière une seule API HTTP. L’ingestion se fait en OTLP. L’interrogation se fait en SQL via POST /api/{org}/_search, avec PromQL en complément pour les métriques.
Le cœur est sous licence AGPL-3.0 et inclut un serveur MCP natif.
Nous verrons dans un prochain article son installation sur un cluster k8s avec le chart Helm openobserve-standalone.
Ressources
- OpenObserve sur GitHub
- Release notes v1.0.0 (11/09/2026)
- Architecture OpenObserve
- Intégration MCP
- SRE Agent (Enterprise)
- Contrôle d’accès basé sur les rôles
- Licence et pricing Enterprise
- Référence API : Search
- Ingestion des logs en OTLP
- grafana/mcp-grafana
- Datadog MCP Server
- Licence Enterprise (EULA)
- MemTable (wiki RocksDB)
- Arrow Columnar Format
- Parquet : format de fichier
- Parquet : encodages
- Parquet : compression
Cargo.tomld’OpenObserve- Apache DataFusion
- Parquet : présentation
- Parquet : concepts
- Parquet : page index
