Files
cmr-mini/miniprogram/engine/debug/mockSimulatorDebugLogger.ts

266 lines
5.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const DEFAULT_DEBUG_LOG_URL = 'wss://gs.gotomars.xyz/debug-log'
const MAX_QUEUED_LOGS = 80
export type MockSimulatorDebugLogLevel = 'info' | 'warn' | 'error'
export interface MockSimulatorDebugLoggerState {
enabled: boolean
connected: boolean
connecting: boolean
url: string
statusText: string
}
export interface MockSimulatorDebugLogEntry {
type: 'debug-log'
timestamp: number
channelId?: string
scope: string
level: MockSimulatorDebugLogLevel
message: string
payload?: Record<string, unknown>
}
function normalizeMockSimulatorChannelId(rawChannelId: string | null | undefined): string {
const trimmed = String(rawChannelId || '').trim()
return trimmed || 'default'
}
function normalizeMockSimulatorLogUrl(rawUrl: string): string {
const trimmed = String(rawUrl || '').trim()
if (!trimmed) {
return DEFAULT_DEBUG_LOG_URL
}
let normalized = trimmed
if (!/^wss?:\/\//i.test(normalized)) {
normalized = `ws://${normalized.replace(/^\/+/, '')}`
}
if (!/\/debug-log(?:\?.*)?$/i.test(normalized)) {
normalized = normalized.replace(/\/+$/, '')
normalized = `${normalized}/debug-log`
}
return normalized
}
export class MockSimulatorDebugLogger {
socketTask: WechatMiniprogram.SocketTask | null
enabled: boolean
connected: boolean
connecting: boolean
url: string
channelId: string
queue: MockSimulatorDebugLogEntry[]
onStateChange?: (state: MockSimulatorDebugLoggerState) => void
constructor(onStateChange?: (state: MockSimulatorDebugLoggerState) => void) {
this.socketTask = null
this.enabled = false
this.connected = false
this.connecting = false
this.url = DEFAULT_DEBUG_LOG_URL
this.channelId = 'default'
this.queue = []
this.onStateChange = onStateChange
}
getState(): MockSimulatorDebugLoggerState {
return {
enabled: this.enabled,
connected: this.connected,
connecting: this.connecting,
url: this.url,
statusText: !this.enabled
? `已关闭 (${this.url})`
: this.connected
? `已连接 (${this.url})`
: this.connecting
? `连接中 (${this.url})`
: `未连接 (${this.url})`,
}
}
emitState(): void {
if (this.onStateChange) {
this.onStateChange(this.getState())
}
}
setEnabled(enabled: boolean): void {
if (this.enabled === enabled) {
return
}
this.enabled = enabled
if (!enabled) {
this.disconnect()
this.queue = []
this.emitState()
return
}
this.emitState()
this.connect()
}
setUrl(url: string): void {
const nextUrl = normalizeMockSimulatorLogUrl(url)
if (this.url === nextUrl) {
return
}
this.url = nextUrl
if (!this.enabled) {
this.emitState()
return
}
this.disconnect()
this.emitState()
this.connect()
}
setChannelId(channelId: string): void {
this.channelId = normalizeMockSimulatorChannelId(channelId)
}
log(
scope: string,
level: MockSimulatorDebugLogLevel,
message: string,
payload?: Record<string, unknown>,
): void {
if (!this.enabled) {
return
}
const entry: MockSimulatorDebugLogEntry = {
type: 'debug-log',
timestamp: Date.now(),
channelId: this.channelId,
scope,
level,
message,
...(payload ? { payload } : {}),
}
if (this.connected && this.socketTask) {
this.send(entry)
return
}
this.queue.push(entry)
if (this.queue.length > MAX_QUEUED_LOGS) {
this.queue.splice(0, this.queue.length - MAX_QUEUED_LOGS)
}
this.connect()
}
disconnect(): void {
const socketTask = this.socketTask
this.socketTask = null
this.connected = false
this.connecting = false
if (socketTask) {
try {
socketTask.close({})
} catch (_error) {
// noop
}
}
this.emitState()
}
destroy(): void {
this.disconnect()
this.queue = []
}
connect(): void {
if (!this.enabled || this.connected || this.connecting) {
return
}
this.connecting = true
this.emitState()
try {
const socketTask = wx.connectSocket({
url: this.url,
})
this.socketTask = socketTask
socketTask.onOpen(() => {
this.connected = true
this.connecting = false
this.emitState()
this.send({
type: 'debug-log',
timestamp: Date.now(),
channelId: this.channelId,
scope: 'logger',
level: 'info',
message: 'logger channel connected',
payload: {
url: this.url,
channelId: this.channelId,
},
})
this.flush()
})
socketTask.onClose(() => {
this.connected = false
this.connecting = false
this.socketTask = null
this.emitState()
})
socketTask.onError(() => {
this.connected = false
this.connecting = false
this.socketTask = null
this.emitState()
})
socketTask.onMessage(() => {
// 模拟器会广播所有消息debug logger 不消费回包。
})
} catch (_error) {
this.connected = false
this.connecting = false
this.socketTask = null
this.emitState()
}
}
flush(): void {
if (!this.connected || !this.socketTask || !this.queue.length) {
return
}
const pending = this.queue.splice(0, this.queue.length)
pending.forEach((entry) => {
this.send(entry)
})
}
send(entry: MockSimulatorDebugLogEntry): void {
if (!this.socketTask || !this.connected) {
return
}
try {
this.socketTask.send({
data: JSON.stringify(entry),
})
} catch (_error) {
this.connected = false
this.connecting = false
this.socketTask = null
this.emitState()
}
}
}