Files
superwork/vscode/src/logging/logger.ts
T
cruldra dae1232f19 🎨 style(vscode): 清零 lint 欠账
非纯格式的改动:
- logger:info 级日志原先走 console.log(no-console 禁止),改为通过 VS Code
  LogOutputChannel(Output 面板 "Superpowers")输出 info/warn/error,并在
  activate 时注册 dispose
- esbuild.mjs:顶层 await 收进 async main(),失败时退出码 1;构建脚本打印
  进度属正当输出,文件级关闭 no-console 并注明原因
- KanbanPanel 资源路径正则:去掉 `\.\/[^"]+` 分支(已被 `[^"/][^"]*` 完全
  覆盖),匹配结果不变
- webhook/server:onError/onListening 互相引用,改为函数声明消除先用后定义
- SettingsModal:删掉针对未启用规则 react-hooks/exhaustive-deps 的失效
  disable 注释,effect 依赖 [open] 保持不变(按打开时机重置表单是有意的)
- issues.ts 提示词里两处全角空格换成普通空格
- 其余为 node:process / node:buffer 显式导入与 switch 拆行

Claude-Session: https://claude.ai/code/session_01HTMgwaE3woqM5yvsHiuSAa
2026-08-31 22:33:11 +08:00

78 lines
2.3 KiB
TypeScript

/**
* In-memory ring buffer for diagnostic log entries.
*
* Both the extension host and the webview share access to these entries
* via the panel's message bridge; the webview surfaces them in a modal
* opened from the IssueDetailPanel. Entries are also mirrored to a VS Code
* LogOutputChannel ("Superpowers" in the Output panel) so they remain
* visible without the webview and honour the user's log level.
*
* No filesystem persistence — buffer is capped at the last 500 entries
* and lost on extension reload.
*/
import type { Event, LogOutputChannel } from 'vscode'
import { EventEmitter, window } from 'vscode'
export type LogLevel = 'info' | 'warn' | 'error'
export interface LogEntry {
/** Milliseconds since epoch. */
ts: number
level: LogLevel
/** Short tag identifying the subsystem (e.g. 'webhook', 'gitea'). */
source: string
message: string
/** Optional extra blob, newline-safe (stack trace, JSON snippet, etc.). */
details?: string
}
class Logger {
private buf: LogEntry[] = []
private readonly cap = 500
private readonly emitter = new EventEmitter<LogEntry>()
private readonly channel: LogOutputChannel = window.createOutputChannel('Superpowers', { log: true })
readonly onEntry: Event<LogEntry> = this.emitter.event
/**
* Record a new entry. Stamps with the current time, trims the buffer to
* the last `cap` entries, fires the change event, and mirrors to the
* output channel.
*/
add(entry: Omit<LogEntry, 'ts'>): void {
const full: LogEntry = { ...entry, ts: Date.now() }
this.buf.push(full)
if (this.buf.length > this.cap)
this.buf.splice(0, this.buf.length - this.cap)
const line = `[${full.source}] ${full.message}`
const args = full.details === undefined ? [] : [full.details]
if (full.level === 'error')
this.channel.error(line, ...args)
else if (full.level === 'warn')
this.channel.warn(line, ...args)
else
this.channel.info(line, ...args)
this.emitter.fire(full)
}
/** Returns a copy of the current buffer (newest entry last). */
snapshot(): LogEntry[] {
return this.buf.slice()
}
/** Clears the buffer in place. Webview clearing broadcasts a separate event. */
clear(): void {
this.buf = []
}
dispose(): void {
this.channel.dispose()
this.emitter.dispose()
}
}
export const logger = new Logger()