UNPKG

perf-observer-kit

Version:

A lightweight, flexible library for monitoring web performance metrics including Core Web Vitals, resource loading performance, long tasks, and navigation timing.

543 lines 20.9 kB
import { MetricType } from './types'; import { browserSupport } from './utils'; import { logger, LogLevel } from './utils/logger'; // 导入各个监视器 import { CoreWebVitalsObserver } from './metrics/core-web-vitals'; import { ResourceTimingObserver } from './metrics/resource-timing'; import { LongTasksObserver } from './metrics/long-tasks'; import { NavigationObserver } from './metrics/navigation-timing'; import { BrowserInfoObserver } from './metrics/browser-observer'; // 从package.json获取版本号 - 这个值会在构建时被rollup插件替换 // 使用字符串形式,避免TypeScript编译错误 const VERSION = '__VERSION__'; /** * 性能观察工具包 - 性能监控的主类 */ export class PerfObserverKit { /** * 创建性能观察工具包实例 */ constructor(options = {}) { this.coreWebVitalsObserver = null; this.resourceTimingObserver = null; this.longTasksObserver = null; this.navigationObserver = null; this.browserInfoObserver = null; this.metrics = { coreWebVitals: {}, resources: [], longTasks: [], navigation: {}, browserInfo: {} }; this.isRunning = false; // 验证选项 this.validateOptions(options); // 初始化日志级别 const logLevel = this.determineLogLevel(options); // 构建内部选项配置 this.options = { // 回调函数,在指标更新时调用 onMetrics: options.onMetrics || null, // 通用设置 debug: options.debug || false, logLevel: logLevel, autoStart: options.autoStart || false, samplingRate: options.samplingRate === undefined ? 0 : options.samplingRate, // 核心Web指标配置 coreWebVitals: this.normalizeCoreWebVitalsOptions(options.coreWebVitals), // 资源计时配置 resources: this.normalizeResourceOptions(options.resources), // 长任务配置 longTasks: this.normalizeModuleOptions(options.longTasks, false), // 导航计时配置 navigation: this.normalizeModuleOptions(options.navigation, false), // 浏览器信息配置 - 唯一默认启用的模块 browserInfo: this.normalizeModuleOptions(options.browserInfo, true) }; // 初始化日志系统 - 通过显式设置选项,确保即使在生产环境也能使用调试模式 logger.setOptions({ level: this.options.logLevel, disableInProduction: false // 确保生产环境中也能使用日志 }); // 输出初始化日志 logger.debug('PerfObserverKit初始化完成,配置:', this.options); // 检查浏览器支持 this.checkBrowserCompatibility(); // 如果启用了自动开始,则自动启动监控 if (this.options.autoStart) { this.start(); } } /** * 验证传入的选项 */ validateOptions(options) { // 验证采样率 if (options.samplingRate !== undefined && (typeof options.samplingRate !== 'number' || options.samplingRate < 0 || options.samplingRate > 1)) { logger.warn('无效的采样率设置,应为0到1之间的数字,将使用默认值0'); options.samplingRate = 0; } // 验证onMetrics回调 if (options.onMetrics !== undefined && typeof options.onMetrics !== 'function') { logger.warn('onMetrics必须是一个函数,将使用默认空函数'); options.onMetrics = () => { }; } } /** * 检查浏览器兼容性 */ checkBrowserCompatibility() { if (!browserSupport.hasPerformanceAPI()) { logger.warn('当前浏览器不支持Performance API'); } if (!browserSupport.hasPerformanceObserver()) { logger.warn('当前浏览器不支持PerformanceObserver'); } } /** * 确定使用的日志级别 */ determineLogLevel(options) { if (options.logLevel !== undefined) { // 确保值在有效范围内 const level = Number(options.logLevel); if (level >= LogLevel.NONE && level <= LogLevel.DEBUG) { return level; } } // 基于debug选项设置默认日志级别 return options.debug ? LogLevel.DEBUG : LogLevel.WARN; } /** * 规范化模块配置为标准格式 * @param options 用户提供的模块配置 * @param defaultEnabled 默认是否启用 */ normalizeModuleOptions(options, defaultEnabled) { try { // 处理布尔值情况 if (typeof options === 'boolean') { return { enabled: options }; } // 处理对象情况 if (options && typeof options === 'object') { return { ...options, enabled: options.enabled !== undefined ? options.enabled : defaultEnabled }; } // 处理未定义情况 return { enabled: defaultEnabled }; } catch (error) { logger.error('规范化模块配置失败:', error); return { enabled: defaultEnabled }; } } /** * 规范化资源计时选项 */ normalizeResourceOptions(options) { try { // 首先使用通用方法获取基础选项 const normalizedOptions = this.normalizeModuleOptions(options, true); // 处理配置选项,设置默认值 return { ...normalizedOptions, maxResources: normalizedOptions.maxResources !== undefined ? normalizedOptions.maxResources : 100, excludedPatterns: normalizedOptions.excludedPatterns || [], allowedTypes: normalizedOptions.allowedTypes || [], // 默认允许所有类型 captureNetworkInfo: normalizedOptions.captureNetworkInfo !== undefined ? normalizedOptions.captureNetworkInfo : true, maxEntries: normalizedOptions.maxEntries !== undefined ? normalizedOptions.maxEntries : 1000 }; } catch (error) { logger.error('规范化资源计时选项失败:', error); return { enabled: false, maxResources: 100, excludedPatterns: [], allowedTypes: [], captureNetworkInfo: true, maxEntries: 1000 }; } } /** * 规范化核心Web指标选项 */ normalizeCoreWebVitalsOptions(options) { try { // 首先使用通用方法获取基础选项 const normalizedOptions = this.normalizeModuleOptions(options, false); // 如果传入的是布尔值true,启用所有指标 if (options === true) { return { enabled: true, fcp: true, lcp: true, fid: false, cls: false, inp: false, maxLongTasks: 50, maxResources: 100 }; } // 处理配置选项,设置默认值 return { ...normalizedOptions, fcp: normalizedOptions.fcp !== undefined ? normalizedOptions.fcp : false, lcp: normalizedOptions.lcp !== undefined ? normalizedOptions.lcp : false, fid: normalizedOptions.fid !== undefined ? normalizedOptions.fid : false, cls: normalizedOptions.cls !== undefined ? normalizedOptions.cls : false, inp: normalizedOptions.inp !== undefined ? normalizedOptions.inp : false, maxLongTasks: normalizedOptions.maxLongTasks !== undefined ? normalizedOptions.maxLongTasks : 50, maxResources: normalizedOptions.maxResources !== undefined ? normalizedOptions.maxResources : 100 }; } catch (error) { logger.error('规范化核心Web指标选项失败:', error); return { enabled: false, fcp: false, lcp: false, fid: false, cls: false, inp: false, maxLongTasks: 50, maxResources: 100 }; } } /** * 开始监控性能指标 */ start() { if (this.isRunning) { logger.warn('性能监控已经在运行中'); return; } logger.info('开始监控性能指标'); // 采样率检查 - 如果配置了采样率,根据随机数决定是否收集数据 if (this.options.samplingRate > 0 && Math.random() > this.options.samplingRate) { logger.debug(`根据采样率(${this.options.samplingRate})决定不收集此会话的性能数据`); // 将状态设置为运行中,但实际不启动监控 this.isRunning = true; return; } // 使用通用方法启动各个观察器 this.startObserver('coreWebVitals', this.startCoreWebVitalsMonitoring.bind(this)); this.startObserver('resources', this.startResourceTimingMonitoring.bind(this)); this.startObserver('longTasks', this.startLongTasksMonitoring.bind(this)); this.startObserver('navigation', this.startNavigationMonitoring.bind(this)); // 浏览器信息 - 默认启用,无论配置如何都启动 this.startBrowserInfoMonitoring(); this.isRunning = true; logger.debug('所有启用的性能监控模块已启动'); } /** * 通用启动观察器方法 */ startObserver(name, startMethod) { const option = this.options[name]; if (option && option.enabled) { startMethod(); } } /** * 停止监控性能指标 */ stop() { if (!this.isRunning) { logger.warn('性能监控未在运行中'); return; } logger.info('停止监控性能指标'); // 停止并清理所有观察器 this.cleanupObserver(this.coreWebVitalsObserver); this.cleanupObserver(this.resourceTimingObserver); this.cleanupObserver(this.longTasksObserver); this.cleanupObserver(this.navigationObserver); this.cleanupObserver(this.browserInfoObserver); // 重置所有观察器引用 this.coreWebVitalsObserver = null; this.resourceTimingObserver = null; this.longTasksObserver = null; this.navigationObserver = null; this.browserInfoObserver = null; this.isRunning = false; logger.debug('所有性能监控模块已停止'); } /** * 清理观察器 */ cleanupObserver(observer) { if (observer) { try { observer.stop(); } catch (error) { logger.error('停止观察器失败:', error); } } } /** * 获取当前的指标数据 */ getMetrics() { return this.metrics; } /** * 清除指标数据 */ clearMetrics() { logger.debug('清除指标数据,保留浏览器信息'); // 保存当前的浏览器信息 const currentBrowserInfo = this.metrics.browserInfo; this.metrics = { coreWebVitals: {}, resources: [], longTasks: [], navigation: {}, // 保留浏览器信息不清除 browserInfo: currentBrowserInfo }; } /** * 设置日志级别 * @param level 日志级别 */ setLogLevel(level) { if (level >= LogLevel.NONE && level <= LogLevel.DEBUG) { this.options.logLevel = level; logger.setLevel(level); logger.debug('已更新日志级别为:', level); } else { logger.warn('无效的日志级别:', level); } } /** * 设置调试模式 * @param enabled 是否启用调试模式 */ setDebugMode(enabled) { this.options.debug = enabled; // 如果启用了调试模式,自动将日志级别设置为DEBUG if (enabled && this.options.logLevel < LogLevel.DEBUG) { this.setLogLevel(LogLevel.DEBUG); } // 确保生产环境也可以看到日志 logger.setOptions({ disableInProduction: false }); logger.debug('调试模式已' + (enabled ? '启用' : '禁用')); // 输出更详细的诊断信息 if (enabled) { const config = logger.getConfiguration(); logger.debug('日志配置状态:', config); logger.debug('当前监控状态:', { isRunning: this.isRunning, activeObservers: { coreWebVitals: !!this.coreWebVitalsObserver, resources: !!this.resourceTimingObserver, longTasks: !!this.longTasksObserver, navigation: !!this.navigationObserver, browserInfo: !!this.browserInfoObserver } }); } } /** * 开始监控核心Web指标 */ startCoreWebVitalsMonitoring() { try { const requiredEntryTypes = [ 'paint', // For FCP 'largest-contentful-paint', 'first-input', 'layout-shift' ]; // 检查所需的条目类型是否受支持 const unsupportedTypes = requiredEntryTypes.filter(type => !browserSupport.supportsEntryType(type)); if (unsupportedTypes.length > 0) { logger.warn(`部分核心Web指标在当前浏览器中不受支持: ${unsupportedTypes.join(', ')}`); } // 将配置传递给观察者 const options = this.options.coreWebVitals; this.coreWebVitalsObserver = new CoreWebVitalsObserver({ onUpdate: (coreWebVitalsMetrics) => { this.metrics.coreWebVitals = coreWebVitalsMetrics; this.notifyMetricsUpdate(MetricType.WEB_VITALS, coreWebVitalsMetrics); }, enabled: options.enabled, fcp: options.fcp, lcp: options.lcp, fid: options.fid, cls: options.cls, inp: options.inp, backgroundLoadThreshold: options.backgroundLoadThreshold }); this.coreWebVitalsObserver.start(); logger.debug('核心Web指标监控已启动'); } catch (error) { logger.error('启动核心Web指标监控失败:', error); } } /** * 开始监控资源计时 */ startResourceTimingMonitoring() { try { if (!browserSupport.supportsEntryType('resource')) { logger.warn('当前浏览器不支持资源计时监控'); return; } const options = this.options.resources; this.resourceTimingObserver = new ResourceTimingObserver((resources) => { this.notifyMetricsUpdate(MetricType.RESOURCES, resources); this.metrics.resources.push(...resources); this.metrics.resources = this.metrics.resources.slice(-options.maxResources); }, options.excludedPatterns, options.allowedTypes); this.resourceTimingObserver.start(); logger.debug('资源计时监控已启动'); } catch (error) { logger.error('启动资源计时监控失败:', error); } } /** * 开始监控长任务 */ startLongTasksMonitoring() { try { if (!browserSupport.supportsEntryType('longtask')) { logger.warn('当前浏览器不支持长任务监控'); return; } const options = this.options.longTasks; const maxLongTasks = options.maxLongTasks || 50; // 提供默认值防止未定义 this.longTasksObserver = new LongTasksObserver({ onUpdate: (longTasks) => { this.notifyMetricsUpdate(MetricType.LONG_TASKS, longTasks); this.metrics.longTasks.push(...longTasks); this.metrics.longTasks = this.metrics.longTasks.slice(-maxLongTasks); }, enabled: options.enabled, threshold: options.threshold, maxEntries: options.maxEntries }); this.longTasksObserver.start(); logger.debug('长任务监控已启动'); } catch (error) { logger.error('启动长任务监控失败:', error); } } /** * 开始监控导航计时 */ startNavigationMonitoring() { try { if (!browserSupport.supportsEntryType('navigation')) { logger.warn('当前浏览器不支持导航计时监控'); return; } const options = this.options.navigation; this.navigationObserver = new NavigationObserver({ onUpdate: (navigationMetrics) => { this.metrics.navigation = navigationMetrics; this.notifyMetricsUpdate(MetricType.NAVIGATION, navigationMetrics); }, enabled: options.enabled, includeRawTiming: options.includeRawTiming }); this.navigationObserver.start(); logger.debug('导航计时监控已启动'); } catch (error) { logger.error('启动导航计时监控失败:', error); } } /** * 开始监控浏览器信息 */ startBrowserInfoMonitoring() { try { const options = this.options.browserInfo; this.browserInfoObserver = new BrowserInfoObserver({ onUpdate: (browserInfo) => { this.metrics.browserInfo = browserInfo; this.notifyMetricsUpdate(MetricType.BROWSER_INFO, browserInfo); }, enabled: true, // 强制启用,无论配置如何 trackResize: options.trackResize, includeOSDetails: options.includeOSDetails, includeSizeInfo: options.includeSizeInfo }); this.browserInfoObserver.start(); logger.debug('浏览器信息监控已启动'); } catch (error) { logger.error('启动浏览器信息监控失败:', error); } } /** * 通知指标更新给回调函数 */ notifyMetricsUpdate(type, metrics) { try { if (this.options.onMetrics) { // 调用回调函数 this.options.onMetrics(type, metrics); } } catch (error) { logger.error('指标更新回调执行失败:', error); } } /** * 获取库版本信息 */ getVersion() { return VERSION; } /** * 检查当前环境是否支持所有必要的API */ static checkBrowserSupport() { const details = { performanceAPI: browserSupport.hasPerformanceAPI(), performanceObserver: browserSupport.hasPerformanceObserver(), navigation: browserSupport.supportsEntryType('navigation'), longtask: browserSupport.supportsEntryType('longtask'), resources: browserSupport.supportsEntryType('resource'), paint: browserSupport.supportsEntryType('paint'), largestContentfulPaint: browserSupport.supportsEntryType('largest-contentful-paint'), firstInput: browserSupport.supportsEntryType('first-input'), layoutShift: browserSupport.supportsEntryType('layout-shift') }; // 基本支持需要Performance API和PerformanceObserver const supported = details.performanceAPI && details.performanceObserver; return { supported, details }; } getStatus() { return { isRunning: this.isRunning, metrics: { coreWebVitals: !!this.coreWebVitalsObserver, resources: !!this.resourceTimingObserver, longTasks: !!this.longTasksObserver, navigation: !!this.navigationObserver, browserInfo: !!this.browserInfoObserver } }; } } //# sourceMappingURL=perf-observer-kit.js.map