@ezijing/log
v0.1.1
Published
A lightweight and powerful log collection SDK for web applications
Readme
@ezijing/log
轻量级前端日志采集 SDK,用于收集和上报 Web 应用的日志信息。
特性
- 🚀 轻量级 - 无外部依赖,gzip 后 < 5KB
- 📦 批量上报 - 自动批量发送,减少网络请求
- 🔄 离线支持 - 网络断开时自动缓存,恢复后重新发送
- 🎯 多种日志类型 - 支持 API、错误、事件、行为等多种日志类型
- 🛡️ 自动捕获 - 自动捕获未处理错误、Promise 拒绝、API 请求
- 📱 设备信息 - 自动收集设备、浏览器、操作系统信息
- 🔧 高度可配置 - 支持自定义 Transport、钩子函数等
安装
npm install @ezijing/log
# or
pnpm add @ezijing/log
# or
yarn add @ezijing/log快速开始
import { EzijingLog } from '@ezijing/log'
// 初始化 SDK
const logger = EzijingLog.init({
appName: 'my-app',
endpoint: 'https://api.example.com/logs',
env: 'production',
appVersion: '1.0.0',
})
// 记录日志
logger.info('用户登录成功', { userId: '12345' })
logger.error('操作失败', { action: 'submit' })API 文档
初始化配置
interface LogConfig {
/** 应用名称(必填) */
appName: string
/** API 端点 */
endpoint?: string
/** 环境:development | staging | production */
env?: Environment
/** 应用版本 */
appVersion?: string
/** 用户 ID */
userId?: string
/** 批量发送的日志数量,默认 50 */
batchSize?: number
/** 自动发送间隔(毫秒),默认 5000 */
flushInterval?: number
/** 是否启用调试模式,默认 false */
debug?: boolean
/** 最低日志级别,默认 debug */
minLevel?: LogLevel
/** 是否自动捕获错误,默认 true */
captureErrors?: boolean
/** 是否自动捕获 console,默认 false */
captureConsole?: boolean
/** 是否自动捕获 API 请求,默认 false */
captureApi?: boolean
/** API 捕获过滤器 */
apiFilter?: (url: string) => boolean
/** 自定义请求头 */
headers?: Record<string, string>
/** 最大重试次数,默认 3 */
maxRetries?: number
/** 重试延迟(毫秒),默认 1000 */
retryDelay?: number
/** 发送前钩子,返回 null 则不发送 */
beforeSend?: (entry: LogEntry) => LogEntry | null
}日志方法
// 基础日志
logger.debug(message: string, metadata?: Record<string, unknown>)
logger.info(message: string, metadata?: Record<string, unknown>)
logger.warn(message: string, metadata?: Record<string, unknown>)
logger.error(message: string | Error, metadata?: Record<string, unknown>)
logger.fatal(message: string | Error, metadata?: Record<string, unknown>)
// 事件日志
logger.event(message: string, metadata?: Record<string, unknown>)
// 行为日志
logger.behavior(message: string, metadata?: Record<string, unknown>)
// API 日志
logger.api(details: ApiDetails, message?: string)上下文管理
// 设置用户 ID
logger.setUser('user-123')
// 清除用户 ID
logger.clearUser()
// 设置全局上下文(会附加到所有日志)
logger.setContext({ tenant: 'company-a' })
// 清除全局上下文
logger.clearContext()手动刷新
// 立即发送缓冲区中的所有日志
await logger.flush()使用示例
基础使用
import { EzijingLog } from '@ezijing/log'
const logger = EzijingLog.init({
appName: 'my-app',
endpoint: '/api/logs',
env: 'production',
})
// 信息日志
logger.info('页面加载完成', { page: '/home' })
// 警告日志
logger.warn('API 响应慢', { duration: 5000 })
// 错误日志
try {
throw new Error('Something went wrong')
} catch (e) {
logger.error(e, { context: 'user-action' })
}自动捕获 API 请求
const logger = EzijingLog.init({
appName: 'my-app',
endpoint: '/api/logs',
captureApi: true,
apiFilter: (url) => !url.includes('/health'), // 过滤健康检查
})
// 之后所有的 fetch 和 XMLHttpRequest 请求都会被自动记录
fetch('/api/users')记录用户行为
// 用户点击按钮
logger.behavior('点击购买按钮', {
productId: 'prod-123',
price: 99.99,
})
// 用户浏览页面
logger.behavior('浏览商品详情', {
productId: 'prod-123',
duration: 30000,
})手动记录 API 请求
const startTime = performance.now()
try {
const response = await fetch('/api/users')
const duration = Math.round(performance.now() - startTime)
logger.api({
method: 'GET',
url: '/api/users',
statusCode: response.status,
duration,
})
} catch (error) {
logger.api({
method: 'GET',
url: '/api/users',
statusCode: 0,
duration: Math.round(performance.now() - startTime),
}, '请求失败')
}使用 beforeSend 钩子
const logger = EzijingLog.init({
appName: 'my-app',
endpoint: '/api/logs',
beforeSend: (entry) => {
// 过滤敏感信息
if (entry.metadata?.password) {
delete entry.metadata.password
}
// 返回 null 则不发送
if (entry.message.includes('debug-only')) {
return null
}
return entry
},
})在 React 中使用
import { useEffect } from 'react'
import { EzijingLog } from '@ezijing/log'
// 初始化(在应用入口)
const logger = EzijingLog.init({
appName: 'react-app',
endpoint: '/api/logs',
env: import.meta.env.MODE as 'development' | 'production',
captureErrors: true,
})
// 错误边界组件
class ErrorBoundary extends React.Component {
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
logger.error(error, {
componentStack: errorInfo.componentStack,
})
}
render() {
return this.props.children
}
}
// 在组件中使用
function MyComponent() {
useEffect(() => {
logger.behavior('组件挂载', { component: 'MyComponent' })
return () => {
logger.behavior('组件卸载', { component: 'MyComponent' })
}
}, [])
const handleClick = () => {
logger.event('按钮点击', { button: 'submit' })
}
return <button onClick={handleClick}>提交</button>
}在 Vue 中使用
// main.ts
import { createApp } from 'vue'
import { EzijingLog } from '@ezijing/log'
const logger = EzijingLog.init({
appName: 'vue-app',
endpoint: '/api/logs',
env: import.meta.env.MODE as 'development' | 'production',
})
const app = createApp(App)
// 全局错误处理
app.config.errorHandler = (error, instance, info) => {
logger.error(error as Error, {
component: instance?.$options.name,
info,
})
}
// 提供给组件使用
app.provide('logger', logger)<script setup>
import { inject, onMounted } from 'vue'
const logger = inject('logger')
onMounted(() => {
logger.info('组件已挂载')
})
function handleClick() {
logger.event('用户点击', { action: 'click' })
}
</script>日志数据结构
发送到服务器的日志格式:
interface LogEntry {
type: 'api' | 'error' | 'event' | 'behavior' | 'custom'
level: 'debug' | 'info' | 'warn' | 'error' | 'fatal'
message: string
appName: string
userId?: string
env?: 'development' | 'staging' | 'production'
appVersion?: string
url?: string
userAgent?: string
device?: {
type: 'mobile' | 'tablet' | 'desktop'
os: string
browser: string
}
api?: {
method: string
url: string
statusCode: number
duration: number
request?: unknown
response?: unknown
}
error?: {
name: string
message: string
stack?: string
componentStack?: string
}
metadata?: Record<string, unknown>
}请求体格式:
{
"logs": [
{ /* LogEntry */ },
{ /* LogEntry */ }
]
}License
MIT
