@zhaogyna/utils
v1.0.1
Published
Utility functions for Monitor SDK - no browser API dependencies
Readme
@zhaogyna/utils
Monitor SDK 的工具函数库,不依赖浏览器 API,可运行于任意 JS 环境。
安装
npm install @zhaogyna/utils本包为内部依赖层,终端用户通常无需单独安装。框架包已自动包含。
导出 API
数据序列化
normalize(data, options?)
深度递归序列化,处理循环引用、深度限制、特殊类型。
import { normalize } from '@zhaogyna/utils'
const data = { a: { b: { c: { d: 'deep' } } } }
normalize(data, { depth: 3 }) // 超过深度的对象显示为 [Object]
normalize({ date: new Date() }) // Date → ISO 字符串
normalize({ big: BigInt(123) }) // BigInt → "123"
normalize({ map: new Map([['k','v']]) }) // Map → { k: 'v' }
normalize({ el: document.body }) // HTMLElement → { tagName, id, className }| NormalizeOptions | 类型 | 默认值 | 说明 |
|-----|------|--------|------|
| depth | number | 3 | 递归深度 |
| maxStringLength | number | 250 | 字符串截断长度 |
| maxArrayLength | number | 50 | 数组截断长度 |
| maxSize | number | 1000 | 最大规范化条目数 |
处理能力:循环引用([Circular])、BigInt、Symbol、Date、RegExp、Map、Set、Error、ArrayBuffer/TypedArray、HTMLElement(仅 tagName/id/className)、Proxy([Proxy])。
堆栈解析
parseStackFrames(stack)
解析 V8 和 SpiderMonkey 格式的 Error.stack 字符串,返回 StackFrame[](外层帧在前)。
import { parseStackFrames } from '@zhaogyna/utils'
const frames = parseStackFrames(new Error('test').stack)
// [{ filename, function, lineno, colno, inApp }, ...]- 自动检测
inApp:文件名排除node_modules、vendor、cdn等第三方路径 - 从末尾截断(保留最外层帧),最大帧数可通过
maxStackFrames控制
extractExceptionFromError(error)
从 Error 对象提取 type 和 message。
import { extractExceptionFromError } from '@zhaogyna/utils'
const { type, value } = extractExceptionFromError(new TypeError('invalid'))
// { type: 'TypeError', value: 'invalid' }ID 生成
import { uuid4, generateTraceId, generateSpanId } from '@zhaogyna/utils'
uuid4() // 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
generateTraceId() // 32 位十六进制字符串(W3C traceId)
generateSpanId() // 16 位十六进制字符串(W3C spanId)DSN 解析
import { parseDsn, getEnvelopeEndpoint, getEnvelopeEndpointWithUrlAuth } from '@zhaogyna/utils'
const dsn = parseDsn('https://[email protected]/1')
// { protocol: 'https', host: 'monitor.example.com', projectId: '1', publicKey: 'key' }
const url = getEnvelopeEndpoint(dsn)
// 'https://monitor.example.com/api/1/envelope'Envelope 格式
import { createEnvelope, addItemToEnvelope, serializeEnvelope, createEnvelopeFromEvents } from '@zhaogyna/utils'
// 手动构建
const envelope = createEnvelope({ sdk: { name: 'monitor', version: '0.1.0' } })
const withItem = addItemToEnvelope(envelope, [{ type: 'event' }, eventBody])
const body = serializeEnvelope(withItem) // 换行分隔的 JSON 字符串
// 从事件列表构建
const envelope = createEnvelopeFromEvents([errorEvent, transactionEvent])采样
import { sampleDecision, sessionSampleDecision, simpleHash } from '@zhaogyna/utils'
// 确定性采样:同一 eventId 始终返回相同结果
sampleDecision('event-abc-123', 0.3) // true 或 false
// 会话级采样:整个会话内采样决策一致
sessionSampleDecision('session-xyz', 0.5) // true 或 false
// DJB2 哈希算法
simpleHash('hello') // 数字哈希值限流
RateLimiter
令牌桶限流器,按时间比例补充令牌。
import { RateLimiter } from '@zhaogyna/utils'
const limiter = new RateLimiter({ capacity: 60, refillRate: 1 }) // 60 次/分钟
limiter.consume(1) // true(允许)或 false(限流)ServerRateLimitParser
解析服务端 x-monitor-rate-limits 响应头。
import { ServerRateLimitParser } from '@zhaogyna/utils'
const parser = new ServerRateLimitParser()
parser.parse('60:error, 120:transaction;track, 300:*')日志
import { logger, setLogLevel, setDebugMode } from '@zhaogyna/utils'
setLogLevel('warn') // debug / info / warn / error / none
setDebugMode(true) // 等价于 setLogLevel('debug')
logger.debug('debug msg') // 仅当级别 <= debug 时输出
logger.info('info msg')
logger.warn('warning msg')
logger.error('error msg')所有日志以 [Monitor] 前缀输出。
通用工具
import {
safeAccess,
deepClone,
shallowMerge,
truncate,
timestampInSeconds,
isNullOrEmpty,
stripUrlParams,
stripObjectKeys,
} from '@zhaogyna/utils'
safeAccess(obj, 'a.b.c') // 安全深层属性访问
deepClone(obj) // 深拷贝(手动字段拷贝,保留函数数组)
shallowMerge(target, source) // 浅合并
truncate('long text...', 10) // 截断字符串
timestampInSeconds() // Date.now() / 1000
isNullOrEmpty(value) // null / undefined / '' / [] 检测
stripUrlParams(url, ['token']) // 移除 URL 敏感参数
stripObjectKeys(obj, ['secret']) // 移除对象敏感键发送队列
SendQueue
FIFO 发送队列,支持重试和退避。
import { SendQueue } from '@zhaogyna/utils'
const queue = new SendQueue({
maxRetries: 3, // 最大重试次数(默认 3)
retryBaseDelay: 1000, // 退避基数 ms(默认 1000)
maxQueueSize: 30, // 队列容量(默认 30)
}, async (item) => {
await fetch(item.url, { body: item.body })
})
queue.enqueue({ url: '...', body: '...' })
queue.drain() // 立即发送全部(用于 sendBeacon 场景)
queue.updateOptions({ maxQueueSize: 50 }) // 动态更新配置指数退避:1s → 2s → 4s,防止并发 flush。
License
MIT
