前言

上一篇介绍了Vite启动,HMR等时间的获取。

但各阶段详细的耗时信息,只能通过debug的日志获取

本文就实现一下debug日志的拦截

插件效果预览

--debug做了什么

项目启动指令

vite --debug

在源码中搜索 --debug,可以在vite/packages/vite/bin/vite.js文件中定位到目标代码

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

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

可以看到如果使用了--debug或者-d参数,process.env上挂载DEBUG变量标识开启了Debug

定位打印日志方法

debug下每条日志都是以vite:label开头,比如

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

全局搜一下vite:load就定位到了如下的代码,可以看到createDebugger是返回了一个可以打印日志的方法

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

createDebugger 的源码如下,其返回一个自定函数,简单捋一下就能看出,负责打印的方法是log(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)
}
}

其中log实例通过debug方法创建,但这个debug方法是一个第三方的库visionmedia/debug

这个方库虽小,能在Vite中被用上想必也不简单,在线查看源码

debug方法源码分析

入口文件比较简单,这里直接去看./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');
}

这部分代码一共只有264行,关键代码如下

exports.log = log;

function log(...args) {
return process.stderr.write(util.format(...args) + '\n');
} module.exports = require('./common')(exports);

./common.js中部分代码

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;

到此能够确定日志的打印都是通过process.stderr.write方法输出的内容

这个方法的好处就是,输出内容不会直接换行

那么我们在插件中重新定义一下这个方法就能拦截到打印的内容

debug日志拦截实现

定义插件入参

interface PluginOptions {
/**
* 是否在终端中输出原来的日志
*/
log?: boolean
/**
* 默认回调
*/
monitor?: MonitorCallback
/**
* debug回调
*/
debug?: DebugCallback
}

直接在调用插件方法的时候进行write方法重写,具体实现逻辑如下

  • 启用了--debug,传入了monitordebug方法才重新定义write方法
  • 将获取到的日志信息做简单解析,通过monitor方法传递给外部
  • 原始参数传递给外部的debug方法

其中解析出的几个参数几个参数与原日志内容对应关系如下

import type { Plugin } from 'vite';
import type { PluginOptions } from './types'; export default function Monitor(ops: PluginOptions = {}): Plugin {
const { log, monitor, debug } = ops;
// 如果debug方法且启动时添加了--debug参数
if ((typeof debug === 'function' || typeof monitor === 'function') && process.env.DEBUG) {
const { write } = process.stderr;
Object.defineProperty(process.stderr, 'write', {
get() {
return function _write(...argv) { // log为true才执行原来的打印逻辑
if (log && typeof argv[0] === 'string') {
process.stdout.write(argv[0]);
}
const originStr = argv[0]; // 解析日志的label与打印的时间信息
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',
},
};
}

到此拦截日志的feature就完成了,最初定下目标也已完成

体验插件

插件源码

安装依赖

yarn add vite-plugin-monitor --dev

引入插件,修改vite.config.js文件

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) {
// 打印完整日志
// process.stdout.write(str)
},
}),
],
})

启动指令中添加--debug

vite --debug

通过monitordebug方法中就能拿到原始的日志和简单处理后的日志,在此处加入自定义的埋点监控代码即可

一点补充:logfalse的时,并且定义了monitordebug方法,那么原来的日志内容都将会被这两个方法拦截

小结

目前已经能够完全拦截到debug下的所有内容,但内容由于有彩色打印相关的字符,提取信息比较麻烦

下一步将对日志的提取再做一些格式化,确保能够解析出完整的日志内容

Vite插件开发纪实:vite-plugin-monitor(下)的更多相关文章

  1. Vite插件开发纪实:vite-plugin-monitor(上)

    背景 最近在webpack项目里接入了Vite(dev mode),为开发提效.效果是真的猛. 项目启动速度提升70%-80%,HMR直接碾压webpack dev server 为了更加精准的计算收 ...

  2. 使用 Vite 插件开发构建 Tampermonkey 用户脚本

    起因 一直以来,我都是直接在浏览器 Tampermonkey 扩展页面直接新建用户脚本来开发的: 对于一些简单的脚本,这没有什么问题,即改即看.但当代码多了以后问题就来了,自带编辑器开发体验确实不太舒 ...

  3. 关于Eclipse插件开发(四)-------给视图加下拉菜单和按钮和加入编辑器.

    本例将给视图加入下拉菜单和按钮,同时再为列表添加一个右键菜单. 创建ActionGroup类 加入菜单和按钮的方法与SWT和JFace组件的一样,先创建一个ActionGroup代码如下: MyAct ...

  4. Vite ❤ Electron——基于Vite搭建Electron+Vue3的开发环境【一】

    背景 目前社区两大Vue+Electron的脚手架:electron-vue和vue-cli-plugin-electron-builder, 都有这样那样的问题,且都还不支持Vue3,然而Vue3已 ...

  5. 解决Hadoop-Eclipse-Plugin放在Plugin目录下没反应的问题

    有时候自己编译或者下载的Hadoop-Eclipse-Plugin放到Eclipse的Plugin目录下面,启动Eclipse没有反应,即看不到小象和Map/Reduce视图.可以通过查看Eclips ...

  6. Mac IntelliJ IDEA插件开发,IDEA Plugin SDK路径

    On Mac, select application icon in /Applications/ 官方文档: Setting Up a Development Environment

  7. 使用 vite 构建一个表情选择插件

    初始化 Vite 基于原生 ES 模块提供了丰富的内建功能,开箱即用.同时,插件足够简单,它不需要任何运行时依赖,只需要安装 vite (用于开发与构建)和 sass (用于开发环境编译 .scss ...

  8. vue2+vite初体验

    前言 自从 vite 发布之后,社区赞誉无数,而我也一直心水 vite 的轻量快速的热重载的特性,特别是公司的项目巨大,已经严重拖慢了热重载的速度了,每次热重载都要等上一小会,所以急需寻找一个解决方案 ...

  9. 快速新建并配置一个eslint+prettier+husky+commitlint+vue3+vite+ts+pnpm的项目

    前置准备 一台电脑 vscode pnpm vscode插件:ESLint v2.2.6及以上 vscode插件:Prettier - Code formatter v9.5.0及以上 vscode插 ...

随机推荐

  1. IIS 站点一键导入 导出

    C:\Windows\System32\inetsrv\appcmd list site /config /xml > c:\sites.xml C:\Windows\System32\inet ...

  2. 安全|常见的Web攻击手段之CSRF攻击

    对于常规的Web攻击手段,如XSS.CRSF.SQL注入.(常规的不包括文件上传漏洞.DDoS攻击)等,防范措施相对来说比较容易,对症下药即可,比如XSS的防范需要转义掉输入的尖括号,防止CRSF攻击 ...

  3. 10.SpringMVC之格式化、校验

    数据格式化 数据格式化的注解: 数据校验JSR303 Hibernate Validator扩展注解 启动 springMVC数据校验 转换.格式化.校验出错处理:

  4. 十一:JavaWeb中的监听器(二)

    一.监听域对象中属性的变更的监听器 域对象中属性的变更的事件监听器就是用来监听 ServletContext, HttpSession, HttpServletRequest 这三个对象中的属性变更信 ...

  5. mysql ORDER BY 中文出现错误问题

    在MySQL中,我们经常会对一个字段进行排序查询,但进行中文排序和查找的时候,对汉字的排序和查找结果往往都是错误的. 这种情况在MySQL的很多版本中都存在. 如果这个问题不解决,那么MySQL将无法 ...

  6. MySQL时间戳、字符串、日期

    1.时间转字符串:date_format(date, format) SELECT date_format(now(), '%Y-%m-%d') 2.时间转时间戳:unix_timestamp() S ...

  7. js调试之firbug

    说下几种方法吧: 1.用alert 这个最最直观 把你想要的内容弹出来给你看,但是要看哪里 就要在哪里加,比较麻烦 2.用firefox 或者chrome浏览器 里面有debug工具的 3.如果想用i ...

  8. Nginx从安装到虚拟主机、https加密、重定向的设置

    编译前的设置: 在源代码文件中把版本号注释掉,这是为了防止针对特定版本的恶意攻击 关闭编译时的调试模式 解决编译前的依赖性 进行配置参数: 对参数进行解读: 编译和安装: 做软链接方便调用: 创建ng ...

  9. (一)Superset 1.3图表篇——Table

    本系列文章基于Superset 1.3.0版本.1.3.0版本目前支持分布,趋势,地理等等类型共59张图表.本次1.3版本的更新图表有了一些新的变化,而之前也一直没有做过非常细致的图表教程. 而且目前 ...

  10. JQ动画

    /* //基本 show([s,[e],[fn]]) 显示元素 hide([s,[e],[fn]]) 隐藏元素 //滑动 slideDown([s],[e],[fn]) 向下滑动 slideUp([s ...