当前位置: 首页 > article >正文

工程化与框架系列(30)--前端日志系统实现

前端日志系统实现 📝

引言

前端日志系统是应用监控和问题诊断的重要工具。本文将深入探讨前端日志系统的设计与实现,包括日志收集、处理、存储和分析等方面,帮助开发者构建完整的前端日志解决方案。

日志系统概述

前端日志系统主要包括以下方面:

  • 日志收集:用户行为、性能指标、错误信息等
  • 日志处理:过滤、格式化、压缩等
  • 日志存储:本地存储、远程上传等
  • 日志分析:统计分析、可视化展示等
  • 实时监控:告警、通知等

日志系统实现

日志管理器

// 日志管理器类
class LogManager {
    private static instance: LogManager;
    private config: LogConfig;
    private logQueue: LogEntry[];
    private timer: number | null;
    private storage: Storage;
    
    private constructor() {
        this.config = {
            appId: '',
            appVersion: '',
            maxQueueSize: 100,
            flushInterval: 5000,
            logLevel: LogLevel.INFO,
            uploadUrl: '',
            enableConsole: true,
            enableStorage: true,
            maxStorageSize: 5 * 1024 * 1024 // 5MB
        };
        
        this.logQueue = [];
        this.timer = null;
        this.storage = new Storage('logs', this.config.maxStorageSize);
        
        this.initialize();
    }
    
    // 获取单例实例
    static getInstance(): LogManager {
        if (!LogManager.instance) {
            LogManager.instance = new LogManager();
        }
        return LogManager.instance;
    }
    
    // 初始化日志管理器
    initialize(config?: Partial<LogConfig>): void {
        if (config) {
            this.config = { ...this.config, ...config };
        }
        
        // 加载本地存储的日志
        if (this.config.enableStorage) {
            this.loadStoredLogs();
        }
        
        // 启动定时上传
        this.startAutoUpload();
        
        // 注册页面卸载事件
        window.addEventListener('beforeunload', () => {
            this.flush();
        });
    }
    
    // 记录日志
    log(
        level: LogLevel,
        message: string,
        data?: any,
        tags?: string[]
    ): void {
        // 检查日志级别
        if (level < this.config.logLevel) {
            return;
        }
        
        const logEntry = this.createLogEntry(level, message, data, tags);
        
        // 输出到控制台
        if (this.config.enableConsole) {
            this.printToConsole(logEntry);
        }
        
        // 添加到队列
        this.addToQueue(logEntry);
    }
    
    // 创建日志条目
    private createLogEntry(
        level: LogLevel,
        message: string,
        data?: any,
        tags?: string[]
    ): LogEntry {
        return {
            appId: this.config.appId,
            appVersion: this.config.appVersion,
            timestamp: Date.now(),
            level,
            message,
            data,
            tags,
            url: window.location.href,
            userAgent: navigator.userAgent
        };
    }
    
    // 添加到日志队列
    private addToQueue(entry: LogEntry): void {
        this.logQueue.push(entry);
        
        // 保存到本地存储
        if (this.config.enableStorage) {
            this.storage.append(entry);
        }
        
        // 队列超出限制时立即上传
        if (this.logQueue.length >= this.config.maxQueueSize) {
            this.flush();
        }
    }
    
    // 输出到控制台
    private printToConsole(entry: LogEntry): void {
        const { level, message, data } = entry;
        const timestamp = new Date(entry.timestamp).toISOString();
        
        const style = this.getConsoleStyle(level);
        const prefix = `%c[${timestamp}][${LogLevel[level]}]`;
        
        if (data) {
            console.log(prefix, style, message, data);
        } else {
            console.log(prefix, style, message);
        }
    }
    
    // 获取控制台样式
    private getConsoleStyle(level: LogLevel): string {
        switch (level) {
            case LogLevel.ERROR:
                return 'color: #ff4444; font-weight: bold';
            case LogLevel.WARN:
                return 'color: #ffbb33; font-weight: bold';
            case LogLevel.INFO:
                return 'color: #33b5e5';
            case LogLevel.DEBUG:
                return 'color: #999999';
            default:
                return '';
        }
    }
    
    // 启动自动上传
    private startAutoUpload(): void {
        if (this.timer !== null) {
            return;
        }
        
        this.timer = window.setInterval(() => {
            this.flush();
        }, this.config.flushInterval);
    }
    
    // 停止自动上传
    private stopAutoUpload(): void {
        if (this.timer === null) {
            return;
        }
        
        window.clearInterval(this.timer);
        this.timer = null;
    }
    
    // 立即上传日志
    async flush(): Promise<void> {
        if (this.logQueue.length === 0) {
            return;
        }
        
        const logs = [...this.logQueue];
        this.logQueue = [];
        
        try {
            await this.uploadLogs(logs);
            
            // 清理已上传的本地存储日志
            if (this.config.enableStorage) {
                this.storage.clear();
            }
        } catch (error) {
            console.error('Failed to upload logs:', error);
            // 重新加入队列
            this.logQueue.push(...logs);
        }
    }
    
    // 上传日志到服务器
    private async uploadLogs(logs: LogEntry[]): Promise<void> {
        const response = await fetch(this.config.uploadUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify(logs)
        });
        
        if (!response.ok) {
            throw new Error('Failed to upload logs');
        }
    }
    
    // 加载存储的日志
    private loadStoredLogs(): void {
        const logs = this.storage.getAll();
        this.logQueue.push(...logs);
    }
}

// 日志级别枚举
enum LogLevel {
    DEBUG,
    INFO,
    WARN,
    ERROR
}

// 日志配置接口
interface LogConfig {
    appId: string;
    appVersion: string;
    maxQueueSize: number;
    flushInterval: number;
    logLevel: LogLevel;
    uploadUrl: string;
    enableConsole: boolean;
    enableStorage: boolean;
    maxStorageSize: number;
}

// 日志条目接口
interface LogEntry {
    appId: string;
    appVersion: string;
    timestamp: number;
    level: LogLevel;
    message: string;
    data?: any;
    tags?: string[];
    url: string;
    userAgent: string;
}

// 本地存储类
class Storage {
    private key: string;
    private maxSize: number;
    
    constructor(key: string, maxSize: number) {
        this.key = key;
        this.maxSize = maxSize;
    }
    
    // 追加日志
    append(entry: LogEntry): void {
        const logs = this.getAll();
        logs.push(entry);
        
        // 检查存储大小
        while (this.getStorageSize(logs) > this.maxSize) {
            logs.shift();
        }
        
        localStorage.setItem(this.key, JSON.stringify(logs));
    }
    
    // 获取所有日志
    getAll(): LogEntry[] {
        const data = localStorage.getItem(this.key);
        return data ? JSON.parse(data) : [];
    }
    
    // 清空日志
    clear(): void {
        localStorage.removeItem(this.key);
    }
    
    // 获取存储大小
    private getStorageSize(data: any): number {
        return new Blob([JSON.stringify(data)]).size;
    }
}

// 使用示例
const logger = LogManager.getInstance();

// 初始化日志系统
logger.initialize({
    appId: 'my-app',
    appVersion: '1.0.0',
    uploadUrl: '/api/logs',
    logLevel: LogLevel.DEBUG
});

// 记录不同级别的日志
logger.log(LogLevel.DEBUG, 'Debug message', { detail: 'debug info' });
logger.log(LogLevel.INFO, 'Info message', { user: 'John' });
logger.log(LogLevel.WARN, 'Warning message', null, ['auth']);
logger.log(LogLevel.ERROR, 'Error message', new Error('Something went wrong'));

性能监控

// 性能监控类
class PerformanceMonitor {
    private static instance: PerformanceMonitor;
    private logger: LogManager;
    private metrics: Map<string, number>;
    
    private constructor() {
        this.logger = LogManager.getInstance();
        this.metrics = new Map();
        
        this.initialize();
    }
    
    // 获取单例实例
    static getInstance(): PerformanceMonitor {
        if (!PerformanceMonitor.instance) {
            PerformanceMonitor.instance = new PerformanceMonitor();
        }
        return PerformanceMonitor.instance;
    }
    
    // 初始化监控器
    private initialize(): void {
        // 监听性能时间
        this.observePerformanceTimings();
        
        // 监听资源加载
        this.observeResourceTimings();
        
        // 监听长任务
        this.observeLongTasks();
        
        // 监听首次绘制
        this.observePaintTimings();
    }
    
    // 开始计时
    startTimer(name: string): void {
        this.metrics.set(name, performance.now());
    }
    
    // 结束计时
    endTimer(name: string): void {
        const startTime = this.metrics.get(name);
        if (startTime) {
            const duration = performance.now() - startTime;
            this.metrics.delete(name);
            
            this.logger.log(LogLevel.INFO, `Timer: ${name}`, {
                duration,
                type: 'timer'
            });
        }
    }
    
    // 记录自定义指标
    recordMetric(
        name: string,
        value: number,
        tags?: string[]
    ): void {
        this.logger.log(LogLevel.INFO, `Metric: ${name}`, {
            value,
            type: 'metric'
        }, tags);
    }
    
    // 监听性能时间
    private observePerformanceTimings(): void {
        window.addEventListener('load', () => {
            // 等待所有资源加载完成
            setTimeout(() => {
                const timing = performance.timing;
                const metrics = {
                    dns: timing.domainLookupEnd - timing.domainLookupStart,
                    tcp: timing.connectEnd - timing.connectStart,
                    request: timing.responseEnd - timing.requestStart,
                    response: timing.responseEnd - timing.responseStart,
                    dom: timing.domComplete - timing.domLoading,
                    load: timing.loadEventEnd - timing.navigationStart
                };
                
                Object.entries(metrics).forEach(([name, value]) => {
                    this.recordMetric(name, value, ['timing']);
                });
            }, 0);
        });
    }
    
    // 监听资源加载
    private observeResourceTimings(): void {
        const observer = new PerformanceObserver((list) => {
            list.getEntries().forEach(entry => {
                if (entry.entryType === 'resource') {
                    const resource = entry as PerformanceResourceTiming;
                    
                    this.logger.log(LogLevel.INFO, `Resource: ${resource.name}`, {
                        duration: resource.duration,
                        transferSize: resource.transferSize,
                        type: resource.initiatorType
                    }, ['resource']);
                }
            });
        });
        
        observer.observe({ entryTypes: ['resource'] });
    }
    
    // 监听长任务
    private observeLongTasks(): void {
        const observer = new PerformanceObserver((list) => {
            list.getEntries().forEach(entry => {
                if (entry.entryType === 'longtask') {
                    this.logger.log(LogLevel.WARN, 'Long task detected', {
                        duration: entry.duration,
                        type: 'longtask'
                    });
                }
            });
        });
        
        observer.observe({ entryTypes: ['longtask'] });
    }
    
    // 监听首次绘制
    private observePaintTimings(): void {
        const observer = new PerformanceObserver((list) => {
            list.getEntries().forEach(entry => {
                if (entry.entryType === 'paint') {
                    this.recordMetric(entry.name, entry.startTime, ['paint']);
                }
            });
        });
        
        observer.observe({ entryTypes: ['paint'] });
    }
}

// 使用示例
const monitor = PerformanceMonitor.getInstance();

// 记录自定义计时
monitor.startTimer('operation');
// ... 执行操作
monitor.endTimer('operation');

// 记录自定义指标
monitor.recordMetric('memory_usage', performance.memory?.usedJSHeapSize || 0);

用户行为跟踪

// 用户行为跟踪类
class UserBehaviorTracker {
    private static instance: UserBehaviorTracker;
    private logger: LogManager;
    private sessionId: string;
    private pageStartTime: number;
    
    private constructor() {
        this.logger = LogManager.getInstance();
        this.sessionId = this.generateSessionId();
        this.pageStartTime = Date.now();
        
        this.initialize();
    }
    
    // 获取单例实例
    static getInstance(): UserBehaviorTracker {
        if (!UserBehaviorTracker.instance) {
            UserBehaviorTracker.instance = new UserBehaviorTracker();
        }
        return UserBehaviorTracker.instance;
    }
    
    // 初始化跟踪器
    private initialize(): void {
        // 记录页面访问
        this.trackPageView();
        
        // 监听用户交互
        this.trackUserInteractions();
        
        // 监听页面可见性
        this.trackPageVisibility();
        
        // 监听页面离开
        this.trackPageLeave();
    }
    
    // 生成会话ID
    private generateSessionId(): string {
        return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
    }
    
    // 跟踪页面访问
    private trackPageView(): void {
        this.logger.log(LogLevel.INFO, 'Page view', {
            sessionId: this.sessionId,
            title: document.title,
            referrer: document.referrer,
            type: 'pageview'
        });
    }
    
    // 跟踪用户交互
    private trackUserInteractions(): void {
        // 点击事件
        document.addEventListener('click', (event) => {
            const target = event.target as HTMLElement;
            
            this.logger.log(LogLevel.INFO, 'User click', {
                sessionId: this.sessionId,
                element: target.tagName.toLowerCase(),
                id: target.id,
                class: target.className,
                text: target.textContent?.slice(0, 100),
                type: 'click'
            });
        });
        
        // 表单提交
        document.addEventListener('submit', (event) => {
            const form = event.target as HTMLFormElement;
            
            this.logger.log(LogLevel.INFO, 'Form submit', {
                sessionId: this.sessionId,
                formId: form.id,
                action: form.action,
                type: 'form'
            });
        });
        
        // 页面滚动
        let scrollTimeout: number;
        window.addEventListener('scroll', () => {
            clearTimeout(scrollTimeout);
            scrollTimeout = window.setTimeout(() => {
                const scrollDepth = Math.round(
                    (window.scrollY + window.innerHeight) /
                    document.documentElement.scrollHeight * 100
                );
                
                this.logger.log(LogLevel.INFO, 'Page scroll', {
                    sessionId: this.sessionId,
                    depth: scrollDepth,
                    type: 'scroll'
                });
            }, 500);
        });
    }
    
    // 跟踪页面可见性
    private trackPageVisibility(): void {
        document.addEventListener('visibilitychange', () => {
            const isVisible = document.visibilityState === 'visible';
            
            this.logger.log(LogLevel.INFO, 'Visibility change', {
                sessionId: this.sessionId,
                visible: isVisible,
                type: 'visibility'
            });
            
            if (!isVisible) {
                this.trackEngagementTime();
            }
        });
    }
    
    // 跟踪页面离开
    private trackPageLeave(): void {
        window.addEventListener('beforeunload', () => {
            this.trackEngagementTime();
        });
    }
    
    // 跟踪页面参与时间
    private trackEngagementTime(): void {
        const engagementTime = Date.now() - this.pageStartTime;
        
        this.logger.log(LogLevel.INFO, 'Engagement time', {
            sessionId: this.sessionId,
            duration: engagementTime,
            type: 'engagement'
        });
    }
    
    // 跟踪自定义事件
    trackEvent(
        category: string,
        action: string,
        label?: string,
        value?: number
    ): void {
        this.logger.log(LogLevel.INFO, 'Custom event', {
            sessionId: this.sessionId,
            category,
            action,
            label,
            value,
            type: 'event'
        });
    }
}

// 使用示例
const tracker = UserBehaviorTracker.getInstance();

// 跟踪自定义事件
tracker.trackEvent('video', 'play', 'intro-video', 30);

最佳实践与建议

  1. 日志设计

    • 分级管理
    • 结构化数据
    • 采样控制
    • 安全考虑
  2. 性能优化

    • 批量处理
    • 压缩数据
    • 限制频率
    • 本地缓存
  3. 数据处理

    • 过滤敏感信息
    • 数据清洗
    • 聚合分析
    • 实时监控
  4. 存储策略

    • 分级存储
    • 定期清理
    • 容量控制
    • 备份恢复

总结

前端日志系统需要考虑以下方面:

  1. 日志收集与处理
  2. 性能监控与分析
  3. 用户行为跟踪
  4. 数据存储与管理
  5. 安全性与隐私

通过完善的日志系统,可以更好地监控和优化前端应用。

学习资源

  1. 日志系统设计指南
  2. 性能监控最佳实践
  3. 用户行为分析方法
  4. 数据可视化工具
  5. 监控平台搭建

如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇

终身学习,共同成长。

咱们下一期见

💻


http://www.kler.cn/a/588519.html

相关文章:

  • cursor全栈网页开发最合适的技术架构和开发语言
  • JVM系统变量的妙用
  • 树莓派 连接 PlutoSDR 教程
  • Typedef 与enum的使用
  • 【人工智能基础2】人工神经网络、卷积神经网络基础、循环神经网络、长短时记忆网络
  • [蓝桥杯]花束搭配【算法赛】
  • python+MySQL+HTML实现产品管理系统
  • Ollama+DeepSeek+NatCross内网穿透本地部署外网访问教程
  • Flutter:竖向步骤条,类似查看物流组件
  • 一周学会Flask3 Python Web开发-SQLAlchemy更新数据操作-班级模块
  • Windows 下免安装 PostgreSQL 16、PostGIS 安装
  • Cursor插件市场打不开解决
  • CT重建笔记(四)——三维重建
  • Scheme语言的压力测试
  • 音视频缓存数学模型
  • 计算机视觉--图像数据分析基本操作
  • C# GeneticSharp包
  • 【JavaEE进阶】Spring事务
  • Linux实时内核稳定性案例
  • 精选一百道备赛蓝桥杯——5.空调