UNPKG

@tencentdb-agent-memory/memory-tencentdb

Version:

Four-layer local memory system plugin for OpenClaw — auto-captures, structures, and profiles conversational knowledge using local LLM + SQLite vector search (L0→L1→L2→L3 pipeline)

303 lines (252 loc) 8.84 kB
/** * Console Observability Backend — 控制台输出实现。 * * 将所有可观测性数据输出到 stdout/stderr,用于开发调试。 * 所有方法都是安全的(不抛异常、不阻塞业务)。 */ import type http from "node:http"; import type { ITraceBackend, ILogBackend, IMetricBackend, ILLMTraceBackend, ITraceMiddleware, ITracePropagation, IObservabilityBackend, ISpan, ISpanProcessor, TraceAttrs, LogAttrs, MetricMessage, MetricBackendConfig, ObservabilityConfig, } from "./types.js"; const TAG = "[observability][console]"; // ============================ // Console Span // ============================ /** Console Span — 输出 Span 生命周期到 stdout */ class ConsoleSpan implements ISpan { private name: string; private attrs: Record<string, string | number | boolean> = {}; private startTime = Date.now(); constructor(name: string, attrs?: Record<string, string | number | boolean>) { this.name = name; if (attrs) this.attrs = { ...attrs }; } end(): void { const durationMs = Date.now() - this.startTime; console.log(`${TAG}[trace] SPAN_END name=${this.name} duration=${durationMs}ms attrs=${JSON.stringify(this.attrs)}`); } setAttribute(key: string, value: string | number | boolean): this { this.attrs[key] = value; return this; } setAttributes(attrs: Record<string, string | number | boolean>): this { Object.assign(this.attrs, attrs); return this; } setStatus(_status: { code: number; message?: string }): this { return this; } recordException(exception: Error | string): void { const msg = exception instanceof Error ? exception.message : exception; console.error(`${TAG}[trace] EXCEPTION span=${this.name} error=${msg}`); } spanContext(): { traceId: string; spanId: string; traceFlags: number } { return { traceId: "console-trace-id", spanId: "console-span-id", traceFlags: 1 }; } isRecording(): boolean { return true; } updateName(name: string): this { this.name = name; return this; } addEvent(name: string, attrs?: Record<string, string | number | boolean>): this { console.log(`${TAG}[trace] EVENT span=${this.name} event=${name} attrs=${JSON.stringify(attrs ?? {})}`); return this; } } // ============================ // ConsoleTraceBackend // ============================ export class ConsoleTraceBackend implements ITraceBackend { readonly type = "console"; report(event: string, attrs: TraceAttrs = {}): void { try { const filtered: Record<string, unknown> = {}; for (const [k, v] of Object.entries(attrs)) { if (v !== null && v !== undefined) filtered[k] = v; } console.log(`${TAG}[trace] REPORT event=tdai.${event} attrs=${JSON.stringify(filtered)}`); } catch { // 静默 } } start(spanName: string, _kind?: number): ISpan { console.log(`${TAG}[trace] SPAN_START name=${spanName}`); return new ConsoleSpan(spanName); } startServer(spanName: string): ISpan { console.log(`${TAG}[trace] SPAN_START name=${spanName} kind=SERVER`); return new ConsoleSpan(spanName); } startClient(spanName: string): ISpan { console.log(`${TAG}[trace] SPAN_START name=${spanName} kind=CLIENT`); return new ConsoleSpan(spanName); } } // ============================ // ConsoleLogBackend // ============================ export class ConsoleLogBackend implements ILogBackend { readonly type = "console"; info(eventName: string, attrs: LogAttrs = {}): void { console.info(`${TAG}[log][INFO] ${eventName}`, attrs); } warn(eventName: string, attrs: LogAttrs = {}): void { console.warn(`${TAG}[log][WARN] ${eventName}`, attrs); } error(eventName: string, attrs: LogAttrs = {}, error?: Error): void { if (error) { console.error(`${TAG}[log][ERROR] ${eventName}`, { ...attrs, "error.message": error.message, "error.type": error.name }); } else { console.error(`${TAG}[log][ERROR] ${eventName}`, attrs); } } debug(eventName: string, attrs: LogAttrs = {}): void { console.debug(`${TAG}[log][DEBUG] ${eventName}`, attrs); } } // ============================ // ConsoleMetricBackend // ============================ export class ConsoleMetricBackend implements IMetricBackend { readonly type = "console"; send(msg: MetricMessage): void { console.log(`${TAG}[metric] SEND metric=${msg.metric} instance=${msg.instanceId} value=${msg.value}`); } async initialize(_config: MetricBackendConfig): Promise<void> { console.log(`${TAG}[metric] Initialized (console mode)`); } async destroy(): Promise<void> { console.log(`${TAG}[metric] Destroyed (console mode)`); } } // ============================ // ConsoleLLMTraceBackend // ============================ export class ConsoleLLMTraceBackend implements ILLMTraceBackend { readonly type = "console"; createSpanProcessor(): ISpanProcessor | null { // 返回一个简单的 console processor return { onStart(_span: unknown) { // no-op on start }, onEnd(span: unknown) { try { const s = span as { name?: string }; console.log(`${TAG}[llm-trace] SPAN_END name=${s.name ?? "unknown"}`); } catch { // 静默 } }, async forceFlush() {}, async shutdown() {}, }; } async flush(): Promise<void> { console.log(`${TAG}[llm-trace] Flushed (console mode)`); } async shutdown(): Promise<void> { console.log(`${TAG}[llm-trace] Shutdown (console mode)`); } } // ============================ // ConsoleTraceMiddleware // ============================ export class ConsoleTraceMiddleware implements ITraceMiddleware { readonly type = "console"; async wrapWithTrace( req: http.IncomingMessage, res: http.ServerResponse, handler: () => Promise<void>, ): Promise<void> { const method = req.method ?? "GET"; const url = req.url ?? "/"; const startTime = Date.now(); console.log(`${TAG}[middleware] REQUEST_START ${method} ${url}`); try { await handler(); const durationMs = Date.now() - startTime; console.log(`${TAG}[middleware] REQUEST_END ${method} ${url} status=${res.statusCode} duration=${durationMs}ms`); } catch (err) { const durationMs = Date.now() - startTime; const errMsg = err instanceof Error ? err.message : String(err); console.error(`${TAG}[middleware] REQUEST_ERROR ${method} ${url} error=${errMsg} duration=${durationMs}ms`); throw err; } } startChildSpan( name: string, attrs?: Record<string, string | number | boolean>, ): ISpan { console.log(`${TAG}[middleware] CHILD_SPAN name=${name}`); return new ConsoleSpan(name, attrs); } async withSpan<T>( name: string, attrs: Record<string, string | number | boolean>, fn: (span: ISpan) => Promise<T>, ): Promise<T> { const span = new ConsoleSpan(name, attrs); console.log(`${TAG}[middleware] WITH_SPAN name=${name}`); try { const result = await fn(span); span.end(); return result; } catch (err) { span.recordException(err instanceof Error ? err : new Error(String(err))); span.end(); throw err; } } } // ============================ // ConsoleTracePropagation // ============================ export class ConsoleTracePropagation implements ITracePropagation { serializeTraceContext(): Record<string, string | number> { return { _traceId: "console-trace-id", _spanId: "console-span-id", _traceFlags: 1 }; } deserializeTraceContext(_data?: Record<string, unknown>): { parentContext: unknown; parentSpanContext: { traceId: string; spanId: string; traceFlags: number; isRemote: boolean } | null; } { return { parentContext: {}, parentSpanContext: null }; } } // ============================ // ConsoleObservabilityBackend — 聚合 // ============================ /** * Console 可观测性后端 — 所有数据输出到 stdout/stderr。 * 用于开发调试环境。 */ export class ConsoleObservabilityBackend implements IObservabilityBackend { readonly type = "console"; readonly trace: ITraceBackend = new ConsoleTraceBackend(); readonly log: ILogBackend = new ConsoleLogBackend(); readonly metric: IMetricBackend = new ConsoleMetricBackend(); readonly llmTrace: ILLMTraceBackend = new ConsoleLLMTraceBackend(); readonly traceMiddleware: ITraceMiddleware = new ConsoleTraceMiddleware(); readonly tracePropagation: ITracePropagation = new ConsoleTracePropagation(); async initialize(_config: ObservabilityConfig): Promise<void> { console.log(`${TAG} ConsoleObservabilityBackend initialized`); } async shutdown(): Promise<void> { console.log(`${TAG} ConsoleObservabilityBackend shutdown`); } }