Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

preface

The previous article introduced the Vite startup, HMR and other time acquisition.

You can obtain detailed time information about each phase only from debug logs

This article will implement the interception of debug logs

Plugin Preview

— What does debug do

Project start instruction

vite --debug
Copy the code

Search – debug in the source code, can be in vite/packages/vite/bin/vite, js file location to the target code

const debugIndex = process.argv.findIndex((arg) = > / ^ (? :-d|--debug)$/.test(arg))

if (debugIndex > 0) {
  let value = process.argv[debugIndex + 1]
  if(! value || value.startsWith(The '-')) {
    value = 'vite:*'
  } else {
    // support debugging multiple flags with comma-separated list
    value = value
      .split(', ')
      .map((v) = > `vite:${v}`)
      .join(', ')
  }
  process.env.DEBUG = value
}
Copy the code

You can see that if the –debug or -d arguments are used, the debug variable is mounted on process.env to indicate that debug is enabled

Locate the log printing method

Each log under debug starts with vite:label, for example

vite:load 1ms   [fs] /src/router/routes/index.ts
Copy the code

A global search of vite: Load turns up the following code, which shows that createDebugger returns a method that prints logs

import {
  createDebugger,
} from '.. /utils'
const debugLoad = createDebugger('vite:load')
constisDebug = !! process.env.DEBUG/ /.. code
isDebug && debugLoad(`${timeFrom(loadStart)} [fs] ${prettyUrl}`)
Copy the code

CreateDebugger (MSG,…); createDebugger (MSG,… args)

import debug from 'debug'

export function createDebugger(namespace: ViteDebugScope, options: DebuggerOptions = {}) :debug.Debugger['log'] {
  const log = debug(namespace)
  const { onlyWhenFocused } = options
  const focus =
    typeof onlyWhenFocused === 'string' ? onlyWhenFocused : namespace
  return (msg: string, ... args: any[]) = > {
    if(filter && ! msg.includes(filter)) {return
    }
    if(onlyWhenFocused && ! DEBUG? .includes(focus)) {return} log(msg, ... args) } }Copy the code

The log instance is created using the Debug method, but this debug method is a third-party library visionMedia/Debug

Although this library is small, can be used in Vite must not be simple, view the source code online

Debug method source code analysis

The entry file is relatively simple, so go directly to the logic in./node.js

if (typeof process === 'undefined' || process.type === 'renderer' || process.browser === true || process.__nwjs) {
	module.exports = require('./browser.js');
} else {
	module.exports = require('./node.js');
}
Copy the code

This section of code is only 264 lines, and the key code is as follows

exports.log = log;

function log(. args) {
	returnprocess.stderr.write(util.format(... args) +'\n');
}

module.exports = require('./common') (exports);
Copy the code

./common.js part of the code

function setup(env) {
	createDebug.debug = createDebug;
	createDebug.default = createDebug;

	function createDebug(namespace) {
		function debug(. args) {
			const self = debug;
			const logFn = self.log || createDebug.log;
			logFn.apply(self, args);
		}
		return debug;
	}
	return createDebug;
}

module.exports = setup;
Copy the code

At this point you can determine that the log is printed as output by the process.stderr.write method

The advantage of this approach is that the output does not wrap directly

So let’s redefine this method in the plug-in to intercept the printed content

Debug Log interception

Define plug-in entry parameters

interface PluginOptions {
    /** * Whether to output the original log in the terminal */log? :boolean
    /** * default callback */monitor? : MonitorCallback/** * debug callback */debug? : DebugCallback }Copy the code

The write method is overridden directly when the plug-in method is called, and the logic is as follows

  • To enable the--debugAnd introduced intomonitorordebugMethod to redefine the write method
  • Parses the obtained log information and passesmonitorMethod passed externally
  • The original parameters are passed to an external debug method

The parsed parameters correspond to the original log content as follows

import type { Plugin } from 'vite';
import type { PluginOptions } from './types';

export default function Monitor(ops: PluginOptions = {}) :Plugin {
  const { log, monitor, debug } = ops;
  // If the debug method is started with the --debug parameter added
  if ((typeof debug === 'function' || typeof monitor === 'function') && process.env.DEBUG) {
    const { write } = process.stderr;
    Object.defineProperty(process.stderr, 'write', {
      get() {
        return function _write(. argv) {

          // If log is true, the original printing logic will be executed
          if (log && typeof argv[0= = ='string') {
            process.stdout.write(argv[0]);
          }
          const originStr = argv[0];

          // Parse the label and print time of the log
          const tag = (originStr.match(/vite:(.*?) \s/) | | []) [1];
          const time1 = (originStr.replace(/\+\d+ms/.' ').match(/(\d+)ms/) | | []) [1];
          const time2 = (originStr.match(/\+(\d+)ms/) | | []) [1];
          const time = +(time1 || 0) + +(time2 || 0);


          if (tag && monitor) {
            monitor(tag, time, {
              time1: +(time1 || 0),
              time2: +(time2 || 0),
              originValue: originStr,
            });
          }

          if(debug) { debug(... argv); }}; }}); }return {
    name: 'vite-plugin-monitor'.apply: 'serve',}}; }Copy the code

At this point, the feature to intercept the log is complete, and the initial goal has been set

Experience the plugin

Plug-in source

Install dependencies

yarn add vite-plugin-monitor --dev
Copy the code

Modify the viet.config.js file by importing the plug-in

import { defineConfig } from 'vite'
import vitePluginMonitor from 'vite-plugin-monitor'

export default defineConfig({
  plugins: [
    vitePluginMonitor({
      // log: false,
      monitor(label, time, originData) {
        const { time1, time2, originValue } = originVal
        console.log(originValue)
        console.log(label, time1, time2, `${time}ms`)},debug(str) {
        // Prints a complete log
        // process.stdout.write(str)}}),],})Copy the code

Add –debug to the startup directive

vite --debug
Copy the code

You can get the original log and the simple processed log by using the Monitor and debug methods. Add custom buried monitoring code here

One caveat: when log is false and monitor or DEBUG methods are defined, the original log content will be intercepted by both methods

summary

At present, all contents under debug can be completely intercepted, but it is difficult to extract information due to the characters related to color printing

The next step is to do some formatting of the log extraction to ensure that the full log content is parsed