refactor: @xuqm/rn-log → @xuqm/rn-bugcollect
- packages/log → packages/bugcollect - XLog → BugCollect - logApiUrl/logEnabled → bugCollectApiUrl/bugCollectEnabled - withXuqmLog → withBugCollect - typecheck 通过 Co-Authored-By: Claude <noreply@anthropic.com>
这个提交包含在:
父节点
a53438a3f0
当前提交
57579d2871
@ -29,7 +29,7 @@
|
|||||||
"@xuqm/rn-common": ">=0.4.0",
|
"@xuqm/rn-common": ">=0.4.0",
|
||||||
"@xuqm/rn-im": ">=0.2.0",
|
"@xuqm/rn-im": ">=0.2.0",
|
||||||
"@xuqm/rn-license": ">=0.3.0",
|
"@xuqm/rn-license": ">=0.3.0",
|
||||||
"@xuqm/rn-log": ">=0.1.0",
|
"@xuqm/rn-bugcollect": ">=0.1.0",
|
||||||
"@xuqm/rn-push": ">=0.2.0",
|
"@xuqm/rn-push": ">=0.2.0",
|
||||||
"@xuqm/rn-update": ">=0.4.0",
|
"@xuqm/rn-update": ">=0.4.0",
|
||||||
"@xuqm/rn-xwebview": ">=0.2.0"
|
"@xuqm/rn-xwebview": ">=0.2.0"
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* withXuqmLog(metroConfig)
|
* withBugCollect(metroConfig)
|
||||||
* 包裹 Metro 配置,打 Release 包时自动上传 SourceMap。
|
* 包裹 Metro 配置,打 Release 包时自动上传 SourceMap。
|
||||||
* 当前为存根实现,后续补全 SourceMap 上传逻辑。
|
* 当前为存根实现,后续补全 SourceMap 上传逻辑。
|
||||||
*/
|
*/
|
||||||
function withXuqmLog(metroConfig) {
|
function withBugCollect(metroConfig) {
|
||||||
return {
|
return {
|
||||||
...metroConfig,
|
...metroConfig,
|
||||||
serializer: {
|
serializer: {
|
||||||
@ -20,9 +20,9 @@ function withXuqmLog(metroConfig) {
|
|||||||
// 仅 Release 包上传 SourceMap(dev 模式跳过)
|
// 仅 Release 包上传 SourceMap(dev 模式跳过)
|
||||||
if (!options.dev) {
|
if (!options.dev) {
|
||||||
// TODO: 补全 SourceMap 上传逻辑
|
// TODO: 补全 SourceMap 上传逻辑
|
||||||
// 1. 读取 .xuqmconfig 或 xuqm.config.js 获取 logApiUrl
|
// 1. 读取 .xuqmconfig 或 xuqm.config.js 获取 bugCollectApiUrl
|
||||||
// 2. 读取 sourceMapUrl 对应的 .map 文件
|
// 2. 读取 sourceMapUrl 对应的 .map 文件
|
||||||
// 3. 上传到 logApiUrl/log/v1/sourcemaps/upload
|
// 3. 上传到 bugCollectApiUrl/log/v1/sourcemaps/upload
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@ -31,4 +31,4 @@ function withXuqmLog(metroConfig) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { withXuqmLog }
|
module.exports = { withBugCollect }
|
||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "@xuqm/rn-log",
|
"name": "@xuqm/rn-bugcollect",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "XuqmGroup RN SDK — log collection, error tracking, funnel analysis",
|
"description": "XuqmGroup RN SDK — log collection, error tracking, funnel analysis",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
@ -4,7 +4,7 @@ import { LogQueue } from './queue/LogQueue'
|
|||||||
import { ErrorCapture } from './capture/ErrorCapture'
|
import { ErrorCapture } from './capture/ErrorCapture'
|
||||||
import { computeFingerprint } from './fingerprint'
|
import { computeFingerprint } from './fingerprint'
|
||||||
import { FunnelTracker } from './funnel/FunnelTracker'
|
import { FunnelTracker } from './funnel/FunnelTracker'
|
||||||
import type { LogLevel, Environment, IssueEvent, LogEvent, XLogEvent } from './types'
|
import type { LogLevel, Environment, IssueEvent, LogEvent, BugCollectEvent } from './types'
|
||||||
|
|
||||||
// ─── Internal state ───────────────────────────────────────────────────────────
|
// ─── Internal state ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@ -35,18 +35,18 @@ function _getAppVersion(): string {
|
|||||||
return typeof v === 'string' ? v : String(v ?? '0.0.0')
|
return typeof v === 'string' ? v : String(v ?? '0.0.0')
|
||||||
}
|
}
|
||||||
|
|
||||||
function _enqueue(event: XLogEvent): void {
|
function _enqueue(event: BugCollectEvent): void {
|
||||||
if (!_queue) {
|
if (!_queue) {
|
||||||
const cfg = getConfig()
|
const cfg = getConfig()
|
||||||
if (!cfg.logApiUrl || !cfg.logEnabled) return
|
if (!cfg.bugCollectApiUrl || !cfg.bugCollectEnabled) return
|
||||||
_queue = new LogQueue({ logApiUrl: cfg.logApiUrl, appKey: cfg.appKey })
|
_queue = new LogQueue({ bugCollectApiUrl: cfg.bugCollectApiUrl, appKey: cfg.appKey })
|
||||||
}
|
}
|
||||||
void _queue.push(event)
|
void _queue.push(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── XLog public API ──────────────────────────────────────────────────────────
|
// ─── BugCollect public API ────────────────────────────────────────────────────
|
||||||
|
|
||||||
export const XLog = {
|
export const BugCollect = {
|
||||||
/** Set the minimum log level. Events below this level are discarded. */
|
/** Set the minimum log level. Events below this level are discarded. */
|
||||||
setLogLevel(level: LogLevel): void {
|
setLogLevel(level: LogLevel): void {
|
||||||
_logLevel = level
|
_logLevel = level
|
||||||
@ -101,13 +101,13 @@ export const XLog = {
|
|||||||
/** Log a warning (if log level allows). */
|
/** Log a warning (if log level allows). */
|
||||||
warn(message: string, metadata?: Record<string, unknown>): void {
|
warn(message: string, metadata?: Record<string, unknown>): void {
|
||||||
if (_levelNum(_logLevel) > _levelNum('warn')) return
|
if (_levelNum(_logLevel) > _levelNum('warn')) return
|
||||||
XLog.captureError(new Error(message), { ...metadata, level: 'warn' })
|
BugCollect.captureError(new Error(message), { ...metadata, level: 'warn' })
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Log an informational event (if log level allows). */
|
/** Log an informational event (if log level allows). */
|
||||||
info(message: string, metadata?: Record<string, unknown>): void {
|
info(message: string, metadata?: Record<string, unknown>): void {
|
||||||
if (_levelNum(_logLevel) > _levelNum('info')) return
|
if (_levelNum(_logLevel) > _levelNum('info')) return
|
||||||
XLog.event('__log_info', { message, ...metadata })
|
BugCollect.event('__log_info', { message, ...metadata })
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -117,7 +117,7 @@ export const XLog = {
|
|||||||
startCapture(): void {
|
startCapture(): void {
|
||||||
if (_errorCaptureStarted) return
|
if (_errorCaptureStarted) return
|
||||||
_errorCaptureStarted = true
|
_errorCaptureStarted = true
|
||||||
ErrorCapture.start(XLog.captureError.bind(XLog))
|
ErrorCapture.start(BugCollect.captureError.bind(BugCollect))
|
||||||
},
|
},
|
||||||
|
|
||||||
/** Define a funnel for step-by-step conversion tracking. */
|
/** Define a funnel for step-by-step conversion tracking. */
|
||||||
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* ErrorCapture — hooks into the RN global error handler and unhandled promise
|
* ErrorCapture — hooks into the RN global error handler and unhandled promise
|
||||||
* rejections to automatically forward errors to XLog.captureError.
|
* rejections to automatically forward errors to BugCollect.captureError.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// React Native exposes ErrorUtils on the global object
|
// React Native exposes ErrorUtils on the global object
|
||||||
@ -26,7 +26,7 @@ export const FunnelTracker = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called automatically by XLog.event for each event.
|
* Called automatically by BugCollect.event for each event.
|
||||||
* Advances any funnel whose next expected step matches eventName.
|
* Advances any funnel whose next expected step matches eventName.
|
||||||
*/
|
*/
|
||||||
track(eventName: string, _properties?: Record<string, unknown>): void {
|
track(eventName: string, _properties?: Record<string, unknown>): void {
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
export { BugCollect } from './BugCollect'
|
||||||
|
export type { LogLevel, Environment, LogEvent, IssueEvent, BugCollectEvent } from './types'
|
||||||
@ -1,9 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* HttpInterceptor — wraps the global fetch to automatically report
|
* HttpInterceptor — wraps the global fetch to automatically report
|
||||||
* HTTP error responses (4xx / 5xx) through XLog.captureError.
|
* HTTP error responses (4xx / 5xx) through BugCollect.captureError.
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
* HttpInterceptor.start(XLog.captureError.bind(XLog))
|
* HttpInterceptor.start(BugCollect.captureError.bind(BugCollect))
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type OnError = (error: unknown, meta?: Record<string, unknown>) => void
|
type OnError = (error: unknown, meta?: Record<string, unknown>) => void
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import AsyncStorage from '@react-native-async-storage/async-storage'
|
import AsyncStorage from '@react-native-async-storage/async-storage'
|
||||||
import type { XLogEvent } from '../types'
|
import type { BugCollectEvent } from '../types'
|
||||||
|
|
||||||
const QUEUE_KEY = '@xuqm_log:queue'
|
const QUEUE_KEY = '@xuqm_bugcollect:queue'
|
||||||
const BATCH_SIZE = 30
|
const BATCH_SIZE = 30
|
||||||
const FLUSH_INTERVAL_MS = 10_000 // 10 seconds
|
const FLUSH_INTERVAL_MS = 10_000 // 10 seconds
|
||||||
const MAX_QUEUE_SIZE = 500
|
const MAX_QUEUE_SIZE = 500
|
||||||
@ -11,13 +11,13 @@ export class LogQueue {
|
|||||||
private flushTimer: ReturnType<typeof setInterval> | null = null
|
private flushTimer: ReturnType<typeof setInterval> | null = null
|
||||||
private retryCount = 0
|
private retryCount = 0
|
||||||
|
|
||||||
constructor(private cfg: { logApiUrl: string; appKey: string }) {
|
constructor(private cfg: { bugCollectApiUrl: string; appKey: string }) {
|
||||||
this.flushTimer = setInterval(() => {
|
this.flushTimer = setInterval(() => {
|
||||||
void this.flush()
|
void this.flush()
|
||||||
}, FLUSH_INTERVAL_MS)
|
}, FLUSH_INTERVAL_MS)
|
||||||
}
|
}
|
||||||
|
|
||||||
async push(event: XLogEvent): Promise<void> {
|
async push(event: BugCollectEvent): Promise<void> {
|
||||||
const queue = await this._read()
|
const queue = await this._read()
|
||||||
if (queue.length >= MAX_QUEUE_SIZE) queue.shift() // drop oldest
|
if (queue.length >= MAX_QUEUE_SIZE) queue.shift() // drop oldest
|
||||||
queue.push(event)
|
queue.push(event)
|
||||||
@ -55,9 +55,9 @@ export class LogQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _post(path: string, data: XLogEvent[]): Promise<void> {
|
private async _post(path: string, data: BugCollectEvent[]): Promise<void> {
|
||||||
const body = JSON.stringify({ events: data })
|
const body = JSON.stringify({ events: data })
|
||||||
const res = await fetch(`${this.cfg.logApiUrl}${path}`, {
|
const res = await fetch(`${this.cfg.bugCollectApiUrl}${path}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@ -66,16 +66,16 @@ export class LogQueue {
|
|||||||
body,
|
body,
|
||||||
})
|
})
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
throw new Error(`[XuqmLog] Upload failed: ${res.status}`)
|
throw new Error(`[BugCollect] Upload failed: ${res.status}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _read(): Promise<XLogEvent[]> {
|
private async _read(): Promise<BugCollectEvent[]> {
|
||||||
const raw = await AsyncStorage.getItem(QUEUE_KEY)
|
const raw = await AsyncStorage.getItem(QUEUE_KEY)
|
||||||
return raw ? (JSON.parse(raw) as XLogEvent[]) : []
|
return raw ? (JSON.parse(raw) as BugCollectEvent[]) : []
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _write(queue: XLogEvent[]): Promise<void> {
|
private async _write(queue: BugCollectEvent[]): Promise<void> {
|
||||||
await AsyncStorage.setItem(QUEUE_KEY, JSON.stringify(queue))
|
await AsyncStorage.setItem(QUEUE_KEY, JSON.stringify(queue))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -29,4 +29,4 @@ export interface IssueEvent {
|
|||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type XLogEvent = LogEvent | IssueEvent
|
export type BugCollectEvent = LogEvent | IssueEvent
|
||||||
@ -4,7 +4,7 @@ type ApiErrorHandler = (error: RequestError) => void
|
|||||||
|
|
||||||
let _handler: ApiErrorHandler | null = null
|
let _handler: ApiErrorHandler | null = null
|
||||||
|
|
||||||
/** 注册全局 API 错误处理器(宿主在初始化时注入,通常将错误转发给 rn-log)。*/
|
/** 注册全局 API 错误处理器(宿主在初始化时注入,通常将错误转发给 rn-bugcollect)。*/
|
||||||
export function setGlobalApiErrorHandler(handler: ApiErrorHandler): void {
|
export function setGlobalApiErrorHandler(handler: ApiErrorHandler): void {
|
||||||
_handler = handler
|
_handler = handler
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,9 +15,9 @@ export interface XuqmConfig {
|
|||||||
imEnabled: boolean
|
imEnabled: boolean
|
||||||
pushEnabled: boolean
|
pushEnabled: boolean
|
||||||
licenseEnabled: boolean
|
licenseEnabled: boolean
|
||||||
// 日志服务(rn-log 使用)
|
// 崩溃采集服务(rn-bugcollect 使用)
|
||||||
logApiUrl: string
|
bugCollectApiUrl: string
|
||||||
logEnabled: boolean
|
bugCollectEnabled: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface XuqmUserInfo {
|
export interface XuqmUserInfo {
|
||||||
@ -47,8 +47,8 @@ export interface XuqmRemoteConfig {
|
|||||||
imEnabled?: boolean
|
imEnabled?: boolean
|
||||||
pushEnabled?: boolean
|
pushEnabled?: boolean
|
||||||
licenseEnabled?: boolean
|
licenseEnabled?: boolean
|
||||||
logApiUrl?: string
|
bugCollectApiUrl?: string
|
||||||
logEnabled?: boolean
|
bugCollectEnabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initConfigFromRemote(
|
export function initConfigFromRemote(
|
||||||
@ -66,8 +66,8 @@ export function initConfigFromRemote(
|
|||||||
imEnabled: remote.imEnabled ?? !!remote.imWsUrl,
|
imEnabled: remote.imEnabled ?? !!remote.imWsUrl,
|
||||||
pushEnabled: remote.pushEnabled ?? true,
|
pushEnabled: remote.pushEnabled ?? true,
|
||||||
licenseEnabled: remote.licenseEnabled ?? false,
|
licenseEnabled: remote.licenseEnabled ?? false,
|
||||||
logApiUrl: remote.logApiUrl ?? '',
|
bugCollectApiUrl: remote.bugCollectApiUrl ?? '',
|
||||||
logEnabled: remote.logEnabled ?? false,
|
bugCollectEnabled: remote.bugCollectEnabled ?? false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -63,8 +63,8 @@ export const XuqmSDK = {
|
|||||||
imEnabled: remote.imEnabled as boolean | undefined,
|
imEnabled: remote.imEnabled as boolean | undefined,
|
||||||
pushEnabled: remote.pushEnabled as boolean | undefined,
|
pushEnabled: remote.pushEnabled as boolean | undefined,
|
||||||
licenseEnabled: remote.licenseEnabled as boolean | undefined,
|
licenseEnabled: remote.licenseEnabled as boolean | undefined,
|
||||||
logApiUrl: remote.logApiUrl as string | undefined,
|
bugCollectApiUrl: remote.bugCollectApiUrl as string | undefined,
|
||||||
logEnabled: remote.logEnabled as boolean | undefined,
|
bugCollectEnabled: remote.bugCollectEnabled as boolean | undefined,
|
||||||
})
|
})
|
||||||
configureHttp({
|
configureHttp({
|
||||||
baseUrl: (remote.apiUrl as string | undefined) ?? baseUrl,
|
baseUrl: (remote.apiUrl as string | undefined) ?? baseUrl,
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
export { XLog } from './XLog'
|
|
||||||
export type { LogLevel, Environment, LogEvent, IssueEvent, XLogEvent } from './types'
|
|
||||||
@ -12,7 +12,7 @@
|
|||||||
"@xuqm/rn-common": ["packages/common/src"],
|
"@xuqm/rn-common": ["packages/common/src"],
|
||||||
"@xuqm/rn-im": ["packages/im/src"],
|
"@xuqm/rn-im": ["packages/im/src"],
|
||||||
"@xuqm/rn-license": ["packages/license/src"],
|
"@xuqm/rn-license": ["packages/license/src"],
|
||||||
"@xuqm/rn-log": ["packages/log/src"],
|
"@xuqm/rn-bugcollect": ["packages/bugcollect/src"],
|
||||||
"@xuqm/rn-push": ["packages/push/src"],
|
"@xuqm/rn-push": ["packages/push/src"],
|
||||||
"@xuqm/rn-update": ["packages/update/src"],
|
"@xuqm/rn-update": ["packages/update/src"],
|
||||||
"@xuqm/rn-xwebview": ["packages/xwebview/src"]
|
"@xuqm/rn-xwebview": ["packages/xwebview/src"]
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户