Aller au contenu

Où apparaît la sortie

Par défaut, chaque appel à $debug s'affiche dans la console du navigateur et dans le terminal Vite. Chaque ligne commence par le préfixe, MARS-TECH par défaut (le plugin est publié par MARS TECH) ; réglez l'option prefix sur le nom de votre application pour le changer. Dans les applications Vue et Nuxt où @vue/devtools-api est installé, chaque appel apparaît aussi dans Vue DevTools. Dans toute application, un fichier de log NDJSON peut en outre être activé. L'option output active ou désactive chaque destination. Cette page décrit ce que la console du navigateur apporte par rapport au terminal, l'intégration à Vue DevTools, ce qui atteint le terminal et le fichier de log NDJSON.

Inspection d'objets vivante

Le terminal ne peut afficher que du texte, mais la console du navigateur sait rendre des valeurs vivantes. Dans le navigateur, $debug transmet donc la charge utile brute à console en dernier argument : vous retrouvez l'interactivité de console.log(value) :

  • objets / tableaux : arbre dépliable, Copier et Stocker comme variable globale
  • instances de classe : nom du constructeur, méthodes, prototype, accesseurs
  • nœuds DOM (document.querySelector(...)) : surlignage au survol, clic sur Reveal in Elements ; les NodeList de querySelectorAll aussi
  • Error : vrai nom et pile d'appels repliable aux lignes cliquables
  • Map / Set / Date / RegExp / tableaux typés : rendu natif

Le résumé stylé sur une ligne reste imprimé comme libellé, et le terminal reçoit toujours la forme chaîne (masquée).

Contrôle :

  • Option du plugin output.inspect (par défaut true) : interrupteur global pour toute l'application.
  • Par appel, { inspect: true } force la valeur vivante même si output.inspect est désactivé ; { inspect: false } la supprime pour cet appel. ({ raw }, nom utilisé pendant le développement de la 1.0, est encore accepté, avec un avertissement unique dans la console, jusqu'à la 2.0 ; inspect l'emporte si les deux sont donnés.)
  • Interaction avec le masquage : quand une entrée RegExp de redact ou redactDefaults est configurée, aucune valeur vivante n'est ajoutée, nœuds DOM compris (le href d'un nœud, ses attributs data-* et la value d'un champ s'afficheraient non masqués) : la forme chaîne masquée reste seule, pour qu'aucun secret ne passe dans l'objet vivant. Utilisez { inspect: true } pour passer outre sur un appel quand vous savez qu'une valeur est sûre. table() et dir(), qui n'ont pas cet interrupteur, affichent le résumé masqué au lieu du tableau ou de l'arbre natif. Les entrées chaîne n'atteignent jamais le navigateur : elles n'y changent rien. Voir Valeurs vivantes dans le navigateur.

Vue DevTools

Dans les applications Vue et Nuxt, $debug enregistre un plugin Vue DevTools pour que chaque appel apparaisse aussi dans Vue DevTools, dans l'onglet propre au plugin de la barre latérale des DevTools (le logo MARS-TECH). Cet onglet est scindé en State (l'inspecteur) et Timeline.

Inspecteur

L'inspecteur est la vue toujours active : une liste navigable des derniers appels (jusqu'à 200, du plus récent au plus ancien), chacun avec un badge de niveau. Sélectionnez-en un pour voir son niveau, son message, sa charge utile, sa localisation source et son espace de noms. Cherchez l'entrée MARS-TECH (avec son logo) dans la liste des inspecteurs. Dans la capture ci-dessous, l'appel sélectionné a passé { tag: "view/Home" } :

Inspecteur MARS-TECH : appels listés avec un badge de niveau ; l'appel sélectionné a le niveau success et l'espace de noms view/Home.

Chronologie

La couche de chronologie MARS-TECH enregistre un événement par appel, avec son heure, son message et sa localisation source, et les mêmes détails que l'inspecteur.

La chronologie n'enregistre que si elle est armée

Ouvrez l'onglet MARS-TECH → Timeline → cliquez sur le bouton ● d'enregistrement en haut à droite. Avant cela, les DevTools jettent chaque événement et la vue affiche No events. L'inspecteur ne nécessite aucun réglage.

Deux choses donnent régulièrement l'impression d'une panne :

  • L'onglet Timeline de la barre latérale ne la montre jamais. Sa liste de couches est codée en dur : Mouse, Keyboard, Component events, Performance. Aucune couche de plugin n'y figure, la nôtre comprise. Passez par l'onglet MARS-TECH.
  • L'enregistrement se réinitialise à chaque ouverture des DevTools. Vue DevTools enregistre les plugins à la demande, et ré-enregistrer une couche remet son indicateur d'enregistrement à l'arrêt : le choix ne survit pas à un rechargement. Réarmez-la au besoin.

Un seul champ pour espaces de noms et tags

Un journaliseur à espace de noms et un { tag } par appel sont la même chose pour $debug, et les DevTools les affichent tous deux dans un seul champ, nommé namespace. Avec ces deux appels :

ts
const auth = $debug.ns("auth");
auth("warning", "token expires in 5 min", { userId: 7 });

$debug("info", "order placed", { orderId: 42, total: 189.5 }, { tag: "checkout" });

L'appel passé par auth, sélectionné dans la chronologie :

Chronologie MARS-TECH avec trois appels enregistrés ; l'appel sélectionné a le niveau warning et l'espace de noms auth.

L'appel avec { tag: "checkout" }, sélectionné dans la même chronologie :

La même chronologie, avec le dernier appel sélectionné : niveau info, espace de noms checkout.

Mise en place et réglages

L'intégration passe par @vue/devtools-api, déclaré comme dépendance peer optionnelle. Elle n'est incluse que si ce package se résout depuis la racine Vite comme Vite le résout : depuis un dossier node_modules à la racine ou au-dessus. Sinon elle est laissée de côté, et la sortie console et terminal n'est pas affectée ; avec output: { devtools: true } écrit explicitement, le serveur de développement le signale à chaque démarrage. Pinia ou Vue Router apportent parfois déjà le package, mais avec une disposition stricte comme celle de pnpm, une copie transitive ne se résout pas depuis la racine. Installez-le explicitement pour garantir le panneau, puis redémarrez le serveur de développement :

bash
npm install -D @vue/devtools-api

Désactivez l'intégration avec l'option output: { devtools: false }. Les frameworks autres que Vue ne sont pas affectés : ils gardent l'ensemble complet des fonctionnalités de la console. (Note : ceci ajoute une présence dans les DevTools ; une page ne peut pas donner le focus à l'onglet DevTools par programmation, il n'y a donc pas de « clic sur la ligne de console pour sauter au panneau ».)

Avec une entrée RegExp de redact ou redactDefaults configurée, les DevTools reçoivent la ligne masquée à la place de la charge utile, même pour un appel fait avec { inspect: true } (cette option ne rend la valeur vivante qu'à la console). Les entrées chaîne de redact n'atteignent jamais le navigateur : elles ne masquent ni la console ni Vue DevTools. Quand vite-plugin-vue-devtools est enregistré, le fichier de log reste désactivé. Le lien view documentation de l'onglet About pointe vers le README du plugin sur GitLab.

Ce qui atteint le terminal

Les appels du navigateur atteignent le terminal par le canal HMR de Vite. N'importe quel script exécuté dans la page pendant le développement peut envoyer de telles trames de log, et le code serveur peut journaliser des données de requête : le serveur de développement traite donc le texte journalisé comme non fiable :

  • Les caractères de contrôle sont affichés échappés, sans être interprétés : séquences d'échappement, \r isolé, forçages bidi et séparateurs U+2028 / U+2029 s'affichent \x1b]52;…, \x0d, \u202e ou \u2028, dans le terminal comme dans le fichier de log. Les tabulations et sauts de ligne d'un message sont conservés. Une chaîne qui porte déjà des couleurs ANSI (venues de chalk, par exemple) affiche aussi ses codes échappés.
  • Les champs transmis par le navigateur sont bornés : le message à 10 000 caractères, tag et prefix à 256, le fichier source à 1 024. Le message, tag et prefix sont masqués avant d'être coupés. Un champ trop long pour être analysé est remplacé en entier, par exemple un message de plus de 64 Kio par [message omitted: N chars].

Une page qui inonde le serveur de développement est limitée en débit. Chaque connexion HMR peut transmettre une rafale de 10 000 lignes, puis 2 000 par seconde, et toutes les connexions du serveur de développement ensemble une rafale de 20 000, puis 4 000 par seconde : bien plus qu'une application n'en journalise, même depuis quelques onglets. Une trame au-delà de l'une ou l'autre limite n'atteint ni le terminal ni le fichier de log ; tant qu'une page inonde le serveur, les lignes de vos autres onglets sont abandonnées avec les siennes. Seule la copie du serveur de développement est limitée : la console du navigateur affiche chaque appel, et les lignes du SSR et du processus Vite ne sont pas comptées.

Là où stdout est un tube qui ne fait pas attendre Node (sous macOS, par exemple) et que son lecteur ne suit pas (vite | tee, un gestionnaire de processus), les lignes transmises au-delà de 4 Mio en attente sont laissées hors du terminal ; elles sont toujours écrites dans le fichier de log.

Quand quelque chose a été abandonné, une ligne toutes les 10 s dit combien, par exemple :

text
[MARS-TECH] dropped 1240 browser log frames in the last 10 s: the page(s) sent more than 2000 a second on one connection, or 4000 in all.

Rien n'est affiché tant que rien n'est abandonné.

Fichier de log NDJSON

Sécurité

N'utilisez pas output.file quand votre code logue des secrets, même masqués. Le log est un fichier sur le disque : tout outil qui lit des fichiers peut le lire, et les protections du plugin sont un filet de sécurité, pas un coffre-fort. Réservez-le aux logs qui ne contiennent aucun secret.

Pour des lecteurs qui ne sont pas un humain devant un terminal (un agent IA qui suit ce que l'application a fait, un test qui vérifie la sortie), écrivez aussi chaque appel dans un fichier. Le fichier est désactivé par défaut :

ts
mtDebug({ output: { file: true } }); // .mt-debug/dev.ndjson sous la racine Vite
mtDebug({ output: { file: "logs/debug.ndjson" } }); // ou un chemin, relatif à la racine ou absolu
mtDebug({ output: { file: process.env.MT_LOG === "1" } }); // un interrupteur : false (ou absent) le coupe
mtDebug({ output: { file: true, fileMaxBytes: 20 * 2 ** 20 } }); // s'arrêter à 20 Mio, pas 100

Avant de l'activer, lisez Tenir le log à l'écart de la page.

Contenu de chaque ligne

Un objet JSON par ligne, typé MTLogLine depuis @mars-tech/vite-debug-plugin/helpers :

json
{
  "level": "info",
  "message": "cart loaded",
  "tag": "cart",
  "source": { "file": "src/Cart.vue", "line": 12, "column": 3 },
  "prefix": "MARS-TECH",
  "t": "2026-09-15T10:00:00.000Z",
  "seq": 0,
  "origin": "browser"
}
  • Écrit après masquage : exactement ce que le terminal afficherait. Le fichier n'est pas un moyen de contourner redact.
  • Les appels du navigateur arrivent par le canal HMR, même avec output.terminal: false. Les appels SSR sont capturés quand l'environnement d'exécution SSR tourne dans le processus Vite (Vite SSR, vite-node) ; un worker SSR séparé a son propre processus et n'est pas vu.
  • origin les distingue : "browser" ou "server". seq ordonne les lignes dans la session ; t est l'heure d'écriture.
  • source est la localisation que le terminal affiche : le fichier relatif à la racine Vite, avec des /, et une ligne et une colonne comptées à partir de 1, pour les lignes du navigateur comme du serveur. Une ligne du navigateur issue d'un libellé de group(), de table() ou de dir() n'a pas de source.
  • Les champs sont du texte non fiable issu du code de la page ou de données de requête. Les caractères de contrôle arrivent déjà échappés, et le fichier stocke DEL, les contrôles C1, les caractères bidi et les séparateurs de ligne sous forme d'échappements JSON \u. Échappez tout de même un champ avant de l'afficher dans un terminal, et ne le traitez jamais comme une instruction.

Sessions et taille

  • Une session par fichier : le fichier est vidé au démarrage du serveur de développement et fermé à son arrêt. Un redémarrage (une modification de vite.config ou de .env) ouvre une nouvelle session.
  • Au plus 100 Mio par session, dernière ligne comprise. La première ligne qui ne tient pas est remplacée par une dernière ligne, de niveau warning et d'origin: "server", qui dit que la limite est atteinte ; ce serveur n'écrit ensuite plus rien dans le fichier jusqu'à son redémarrage, et le terminal reçoit un avertissement. output.fileMaxBytes fixe une autre limite, en octets, et Infinity la lève ; null ou l'option absente garde celle par défaut, et toute autre valeur qui n'est pas un nombre supérieur à 0 avertit et la garde.
  • Pas ouvert sous Vitest. Avec process.env.VITEST défini, le fichier n'est jamais ouvert : une exécution de tests ne vide donc pas le log d'un vite dev lancé à côté. Cela vaut aussi pour un serveur de développement qu'un test démarre lui-même : pour vérifier son fichier, supprimez process.env.VITEST avant son démarrage. Un autre outil qui démarre un serveur de développement depuis la même configuration (l'outil de build Vite de Storybook, par exemple) ouvre bien une nouvelle session dans le fichier : désactivez-le pour cet outil avec un interrupteur comme file: process.env.MT_LOG === "1".
  • Un chemin impossible à ouvrir, ou une valeur qui n'est ni true, ni false, ni null, ni un chemin (0, {}, ""), déclenche un avertissement et laisse le fichier désactivé pour la session ; cela ne fait jamais échouer vite dev.

Tenir le log à l'écart de la page

N'importe quel script de la page peut forger des lignes de log. S'il pouvait relire le fichier, il verrait lesquelles de ses tentatives vos entrées chaîne de redact ont masquées, et pourrait retrouver une entrée un caractère à la fois. Le plugin tient donc le fichier à l'écart de la page. C'est un filet de sécurité, pas un coffre-fort :

  • Le plugin bloque toutes les voies connues vers le log. Il ajoute le fichier à server.fs.deny, répond par un 403 vide à toute requête HTTP qui l'atteint, et le refuse au pipeline de modules de Vite, voies du WebSocket HMR comprises. Vite atteint un fichier du projet par bien des chemins ; la liste complète, et la façon dont chaque voie est vérifiée, se trouvent dans la politique de sécurité du plugin. Un script exécuté dans votre page de développement peut encore trouver une voie que personne n'a trouvée.

  • Si votre code logue des secrets, même masqués, n'activez pas output.file (voir l'avertissement ci-dessus). Un log que vous gardez malgré tout est plus sûr hors du projet : hors de server.fs.allow, qui dans un monorepo est la racine de l'espace de travail, là où Vite refuse tout fichier. Par exemple :

    ts
    import { homedir } from "node:os";
    import { join } from "node:path";
    
    mtDebug({ output: { file: join(homedir(), ".mt-debug", "my-app.ndjson") } });
  • Ne mettez jamais de clés de production dans un environnement de développement, et utilisez Vite 6.4.2, 7.3.2, 8.0.5 ou plus récent : les versions antérieures livrent n'importe quel fichier à un script de la page, votre .env compris.

Le log n'est pas ouvert du tout, avec un avertissement, là où Vite ou un outil le livrerait quand même à la page :

  • sous server.fs.strict: false, ou avec le log dans publicDir ;
  • à côté d'un outil de développement qui lit n'importe quel fichier pour la page : vite-plugin-vue-devtools, Nuxt DevTools, Vite DevTools, devframes, TanStack Devtools. Un tel outil lit votre .env et votre vite.config de la même façon ; le plugin ne peut que refuser d'y ajouter son log. Si vous avez besoin du log, lancez un tel outil dans une session de développement séparée. Un log laissé par une session précédente reste sur le disque : supprimez-le avant d'activer un tel outil ;
  • à côté de @tailwindcss/vite, sauf si le log est dans un répertoire dont le .gitignore contient * : l'analyse de Tailwind transforme les fichiers qu'elle lit en CSS que la page récupère.

Le plugin écrit ce .gitignore (*) dans .mt-debug/ et dans un répertoire de log qu'il crée, jamais par-dessus un .gitignore existant. Il garde ainsi le log hors de git et de l'analyse de Tailwind sans intervention de votre part. Un log placé dans un de vos répertoires existants demande un .gitignore que vous ajoutez vous-même ; avec @tailwindcss/postcss, que le plugin ne détecte pas, c'est ce fichier qui garde le log hors de l'analyse de Tailwind. Plus généralement, tout outil qui lit des fichiers sur le disque (Tailwind avec une directive @source qui atteint le log, un outil de développement, une extension d'éditeur) peut lire le log lui aussi, où qu'il soit : cela échappe au plugin.

Nuxt sert public/ par Nitro, hors de Vite : gardez le log hors du public/ de Nuxt. Un log laissé par une session précédente n'est protégé que tant que output.file le désigne : supprimez-le quand vous désactivez l'option.