feat: 统一日志系统——结构化滚动文件 + api.log 插件通道 + 73 个 IPC 通道追踪 + pi 事件/渲染层错误归档

This commit is contained in:
Rick Ye
2026-09-11 02:12:40 +08:00
parent 2ad1743e10
commit 8e4b5496e8
9 changed files with 332 additions and 98 deletions
+38 -7
View File
@@ -36,6 +36,10 @@ import type {
} from '@aso/plugin-sdk'
import type { PluginInfo, PluginStatusEntry, PluginToolbarEntry, PluginsUiState } from '@shared/types'
import { BUILTIN_PLUGINS, type BuiltinPlugin } from './builtin'
import { getLogger } from '../logger'
const log = getLogger('plugin-host')
const pluginLog = getLogger('plugin')
/** harness 插件契约版本(manifest.minContract 与之比对;破坏性变更时提升) */
export const HARNESS_CONTRACT_VERSION = '0.2.0'
@@ -122,6 +126,10 @@ class PluginHost {
} catch {
// 已存在或创建失败都继续(扫描时再报)
}
log.info('plugin host init', {
externalRoot: this.externalRoot,
externalLoadingEnabled: EXTERNAL_PLUGINS_ENABLED
})
try {
const raw = JSON.parse(readFileSync(this.settingsFile, 'utf8')) as { disabled?: unknown; settingsNav?: unknown }
if (Array.isArray(raw.disabled)) this.disabled = new Set(raw.disabled.filter((d): d is string => typeof d === 'string'))
@@ -234,6 +242,13 @@ class PluginHost {
}
this.send('plugins:settingsSchemas', this.settingsSchemasSnapshot())
await this.loadExternalMains()
const externalCount = [...byId.values()].filter((p) => p.source === 'external').length
log.info('scan complete', {
builtin: byId.size - externalCount,
external: externalCount,
disabled: this.disabled.size,
projects: this.projectExtensionsDir ?? '-'
})
this.send('plugins:listChanged', null)
}
@@ -279,27 +294,33 @@ class PluginHost {
const mod = (await import(url)) as { default?: unknown }
if (typeof mod.default !== 'function') {
plugin.error = 'main.mjs 默认导出必须是插件工厂函数'
log.error('external main invalid export', { pluginId: plugin.manifest.id, file })
continue
}
plugin.externalFactory = mod.default as HarnessPluginFactory
plugin.error = undefined
log.info('external main loaded', { pluginId: plugin.manifest.id, version: plugin.manifest.version })
} catch (err) {
plugin.externalFactory = undefined
plugin.error = `main 加载失败: ${err instanceof Error ? err.message : String(err)}`
log.error('external main load failed', { pluginId: plugin.manifest.id, file, err })
}
}
}
/** 重新扫描 + 重新导入 + 清 UI 状态 + 重建 runtime(= pi /reload 等效) */
async reload(): Promise<void> {
log.info('reload begin')
this.statusItems.clear()
this.toolbarButtons.clear()
await this.scan()
this.send('plugins:uiState', this.uiState())
if (this.rebuildRuntime) await this.rebuildRuntime()
log.info('reload done')
}
async setEnabled(id: string, enabled: boolean): Promise<void> {
log.info('set plugin enabled', { pluginId: id, enabled })
if (enabled) this.disabled.delete(id)
else this.disabled.add(id)
this.persistPluginPrefs()
@@ -310,6 +331,7 @@ class PluginHost {
async setSettingsNav(id: string, on: boolean): Promise<void> {
const plugin = this.resolved.get(id)
if (!plugin || !this.settingsSchemas.has(id)) return
log.info('set settings nav', { pluginId: id, on })
if (on) this.settingsNavSet.add(id)
else this.settingsNavSet.delete(id)
this.persistPluginPrefs()
@@ -324,7 +346,7 @@ class PluginHost {
'utf8'
)
} catch (err) {
console.error('[plugins] save settings failed:', err)
log.error('save plugin prefs failed', { err })
}
}
@@ -377,17 +399,18 @@ class PluginHost {
try {
fn({ ...(this.settingsValues.get(id) ?? {}) })
} catch (err) {
console.error('[plugins] settings listener error:', err)
log.error('settings listener error', { pluginId: id, err })
}
}
}
settingsAction(id: string, key: string): void {
log.debug('settings action', { pluginId: id, key })
for (const fn of this.settingsActions.get(id) ?? []) {
try {
fn(key)
} catch (err) {
console.error('[plugins] settings action error:', err)
log.error('settings action error', { pluginId: id, key, err })
}
}
}
@@ -401,7 +424,7 @@ class PluginHost {
try {
writeFileSync(this.valuesFile, JSON.stringify(doc, null, 2), 'utf8')
} catch (err) {
console.error('[plugins] save values failed:', err)
log.error('save values failed', { err })
}
}, 300)
}
@@ -554,7 +577,7 @@ class PluginHost {
try {
writeFileSync(this.storageFile, JSON.stringify(doc, null, 2), 'utf8')
} catch (err) {
console.error('[plugins] save storage failed:', err)
log.error('save storage failed', { err })
}
}, 300)
}
@@ -574,6 +597,7 @@ class PluginHost {
this.resetSettingsCallbacks(plugin.manifest.id)
try {
const result = factory(this.buildApi(plugin, pi))
log.info('plugin activated', { pluginId: plugin.manifest.id, source: plugin.source })
if (typeof result === 'function') {
disposers.push(result)
} else if (result && typeof (result as Promise<unknown>).then === 'function') {
@@ -583,6 +607,7 @@ class PluginHost {
}
} catch (err) {
plugin.error = `激活失败: ${err instanceof Error ? err.message : String(err)}`
log.error('plugin activation failed', { pluginId: plugin.manifest.id, err })
this.clearPluginUi(plugin.manifest.id)
this.send('plugins:listChanged', null)
}
@@ -592,7 +617,7 @@ class PluginHost {
try {
dispose()
} catch (err) {
console.error('[plugins] dispose error:', err)
log.error('plugin dispose error', { err })
}
}
disposers.length = 0
@@ -623,6 +648,12 @@ class PluginHost {
pi,
manifest: plugin.manifest,
storage: this.storageFor(id),
log: {
debug: (message: string, kv?: Record<string, unknown>) => pluginLog.debug(message, { pluginId: id, ...kv }),
info: (message: string, kv?: Record<string, unknown>) => pluginLog.info(message, { pluginId: id, ...kv }),
warn: (message: string, kv?: Record<string, unknown>) => pluginLog.warn(message, { pluginId: id, ...kv }),
error: (message: string, kv?: Record<string, unknown>) => pluginLog.error(message, { pluginId: id, ...kv })
},
ui: {
openView: (view: string, params?: unknown) => this.openView(view, params),
setStatusItem: (itemId: string, item: StatusItem) => this.setStatusItem(id, itemId, item),
@@ -721,7 +752,7 @@ class PluginHost {
}
})
} catch (err) {
console.error('[plugins] serve failed:', file, err)
log.error('plugin:// serve failed', { file, err })
return new Response('serve error', { status: 500 })
}
}