front-cpu 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/dist/Pipeline.d.ts +2 -4
  3. package/dist/Pipeline.d.ts.map +1 -1
  4. package/dist/Pipeline.js +2 -2
  5. package/dist/Pipeline.js.map +1 -1
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/logging/CPUConsole.d.ts +1 -1
  10. package/dist/logging/CPUConsole.d.ts.map +1 -1
  11. package/dist/logging/CPUConsole.js +9 -4
  12. package/dist/logging/CPUConsole.js.map +1 -1
  13. package/dist/logging/console-i18n.d.ts +1 -1
  14. package/dist/logging/console-i18n.d.ts.map +1 -1
  15. package/dist/logging/console-i18n.js +0 -3
  16. package/dist/logging/console-i18n.js.map +1 -1
  17. package/dist/logging/stack-parser.d.ts +9 -0
  18. package/dist/logging/stack-parser.d.ts.map +1 -1
  19. package/dist/logging/stack-parser.js +157 -40
  20. package/dist/logging/stack-parser.js.map +1 -1
  21. package/dist/plugins/vite-call-source.d.ts +33 -0
  22. package/dist/plugins/vite-call-source.d.ts.map +1 -0
  23. package/dist/plugins/vite-call-source.js +312 -0
  24. package/dist/plugins/vite-call-source.js.map +1 -0
  25. package/dist/types.d.ts +12 -0
  26. package/dist/types.d.ts.map +1 -1
  27. package/dist/vite.d.ts +2 -0
  28. package/dist/vite.d.ts.map +1 -0
  29. package/dist/vite.js +2 -0
  30. package/dist/vite.js.map +1 -0
  31. package/package.json +5 -1
  32. package/src/Pipeline.ts +4 -4
  33. package/src/debug.ts +3 -0
  34. package/src/index.ts +1 -0
  35. package/src/logging/CPUConsole.ts +692 -688
  36. package/src/logging/console-i18n.ts +157 -160
  37. package/src/logging/runtime.ts +3 -0
  38. package/src/logging/stack-parser.ts +300 -168
  39. package/src/plugins/vite-call-source.ts +388 -0
  40. package/src/types.ts +13 -0
  41. package/src/vite.ts +5 -0
@@ -1,688 +1,692 @@
1
- /**
2
- * CPU 控制台打印系统
3
- *
4
- * 职责:
5
- * 1. 实时打印指令执行过程
6
- * 2. 美观的彩色输出
7
- * 3. 分级别控制详细程度
8
- * 4. 可折叠的详细信息
9
- */
10
-
11
- import type { QueuedInstruction } from '../types'
12
- import { ConsoleLevel } from './types'
13
- import { formatCallSourceShort } from './stack-parser'
14
- import {
15
- createCPUConsoleTranslator,
16
- type CPUConsoleLocale,
17
- type CPUConsoleTranslate,
18
- } from './console-i18n'
19
-
20
- export class CPUConsole {
21
- private enabled: boolean = true
22
- private level: ConsoleLevel = ConsoleLevel.NORMAL
23
- private filter: Set<string> = new Set() // 指令类型过滤
24
- private locale: CPUConsoleLocale = this.getDefaultLocale()
25
- private translator: CPUConsoleTranslate = createCPUConsoleTranslator(this.locale)
26
-
27
- constructor() {
28
- this.loadSettings()
29
- }
30
-
31
- /**
32
- * 设置语言(en / zh-CN / bilingual
33
- */
34
- setLocale(locale: CPUConsoleLocale): void {
35
- this.locale = locale
36
- this.translator = createCPUConsoleTranslator(locale)
37
- localStorage.setItem('cpu-console-locale', locale)
38
- }
39
-
40
- /**
41
- * 自定义翻译函数(不依赖任何 i18n 框架)
42
- */
43
- setI18nTranslator(t: CPUConsoleTranslate): void {
44
- this.translator = t
45
- }
46
-
47
- getLocale(): CPUConsoleLocale {
48
- return this.locale
49
- }
50
-
51
- /**
52
- * 加载设置
53
- */
54
- private loadSettings(): void {
55
- const savedLevel = localStorage.getItem('cpu-console-level')
56
- if (savedLevel) {
57
- this.level = parseInt(savedLevel) as ConsoleLevel
58
- }
59
-
60
- const savedFilter = localStorage.getItem('cpu-console-filter')
61
- if (savedFilter) {
62
- try {
63
- const types = JSON.parse(savedFilter)
64
- this.filter = new Set(types)
65
- } catch (e) {
66
- // 忽略解析错误
67
- }
68
- }
69
-
70
- const savedLocale = localStorage.getItem('cpu-console-locale') as CPUConsoleLocale | null
71
- if (savedLocale === 'en' || savedLocale === 'zh-CN' || savedLocale === 'bilingual') {
72
- this.locale = savedLocale
73
- this.translator = createCPUConsoleTranslator(savedLocale)
74
- }
75
- }
76
-
77
- private t(key: Parameters<CPUConsoleTranslate>[0], params?: Parameters<CPUConsoleTranslate>[1]): string {
78
- return this.translator(key as any, params)
79
- }
80
-
81
- private getDefaultLocale(): CPUConsoleLocale {
82
- // 默认策略:中文环境输出双语,否则英文
83
- if (typeof navigator !== 'undefined') {
84
- const lang = navigator.language || ''
85
- if (lang.toLowerCase().startsWith('zh')) return 'bilingual'
86
- }
87
- return 'en'
88
- }
89
-
90
- /**
91
- * 配置方法
92
- */
93
- setLevel(level: ConsoleLevel): void {
94
- this.level = level
95
- localStorage.setItem('cpu-console-level', level.toString())
96
- }
97
-
98
- setFilter(types: string[]): void {
99
- this.filter = new Set(types)
100
- localStorage.setItem('cpu-console-filter', JSON.stringify(types))
101
- }
102
-
103
- enable(): void {
104
- this.enabled = true
105
- }
106
-
107
- disable(): void {
108
- this.enabled = false
109
- }
110
-
111
- getLevel(): ConsoleLevel {
112
- return this.level
113
- }
114
-
115
- // ==================== 打印方法 ====================
116
-
117
- /**
118
- * 指令创建
119
- */
120
- onInstructionCreated(instruction: QueuedInstruction): void {
121
- if (!this.shouldPrint(instruction.type)) return
122
-
123
- if (this.level >= ConsoleLevel.NORMAL) {
124
- // 🔍 格式化调用源信息
125
- const callSourceInfo = instruction.context.callSource
126
- ? ` %c📍 ${formatCallSourceShort(instruction.context.callSource)}`
127
- : ''
128
-
129
- // 🎯 使用与指令成功一致的分组格式
130
- console.groupCollapsed(
131
- `%c[${this.t('instruction.created')}] %c${this.formatTime()} %c${instruction.type}%c${callSourceInfo}`,
132
- 'color: #3b82f6; font-weight: bold',
133
- 'color: #666; font-size: 11px',
134
- 'color: #3b82f6; font-weight: bold; background: #3b82f615; padding: 2px 6px; border-radius: 3px',
135
- 'color: #3b82f6',
136
- ...(callSourceInfo ? ['color: #8b5cf6; font-weight: bold'] : [])
137
- )
138
-
139
- // 🔥 显示指令基本信息
140
- console.log(`%c📋 ${this.t('label.instructionInfo')}`, 'color: #3b82f6; font-weight: bold')
141
- console.table({
142
- 'Instruction ID': instruction.id,
143
- 'Correlation ID': instruction.context.correlationId,
144
- 'Type': instruction.type,
145
- 'Status': instruction.status,
146
- 'Source': instruction.context.source,
147
- 'Retry Count': instruction.context.retryCount,
148
- })
149
-
150
- // 🔥 显示指令参数
151
- if (this.level >= ConsoleLevel.DEBUG) {
152
- console.log(`%c📝 ${this.t('label.payload')}`, 'color: #3b82f6; font-weight: bold')
153
- console.log(instruction.payload)
154
- } else {
155
- console.log(`%c📝 ${this.t('label.payloadDebugOnly')}`, 'color: #666; font-style: italic')
156
- }
157
-
158
- // 🔥 显示调用源详情
159
- if (instruction.context.callSource && this.level >= ConsoleLevel.VERBOSE) {
160
- console.log(`%c📍 ${this.t('label.callSourceDetails')}`, 'color: #8b5cf6; font-weight: bold')
161
- console.table({
162
- 'File': instruction.context.callSource.file,
163
- 'Line': instruction.context.callSource.line,
164
- 'Column': instruction.context.callSource.column,
165
- 'Function': instruction.context.callSource.function || 'N/A',
166
- })
167
- }
168
-
169
- console.groupEnd()
170
- }
171
- }
172
-
173
- /**
174
- * 指令成功
175
- */
176
- onInstructionSuccess(instruction: QueuedInstruction, duration: number): void {
177
- if (!this.shouldPrint(instruction.type)) return
178
-
179
- // 🔍 格式化调用源信息
180
- const callSourceInfo = instruction.context.callSource
181
- ? ` %c📍 ${formatCallSourceShort(instruction.context.callSource)}`
182
- : ''
183
-
184
- // 🎯 核心:折叠分组,方便查看
185
- console.groupCollapsed(
186
- `%c[${this.t('instruction.success')}] %c${this.formatTime()} %c${instruction.type}%c %c${duration}ms${callSourceInfo}`,
187
- 'color: #10b981; font-weight: bold',
188
- 'color: #666; font-size: 11px',
189
- 'color: #10b981; font-weight: bold; background: #10b98115; padding: 2px 6px; border-radius: 3px',
190
- 'color: #10b981',
191
- 'color: #10b981; font-weight: bold',
192
- ...(callSourceInfo ? ['color: #8b5cf6; font-weight: bold'] : [])
193
- )
194
-
195
- // 🔥 显示指令输入参数
196
- if (this.level >= ConsoleLevel.NORMAL) {
197
- console.log(`%c📝 ${this.t('label.payload')}`, 'color: #3b82f6; font-weight: bold')
198
- console.log(instruction.payload)
199
- }
200
-
201
- // 🔥 显示后端返回结果
202
- if (instruction.result && this.level >= ConsoleLevel.NORMAL) {
203
- console.log(`%c📥 ${this.t('label.backendResult')}`, 'color: #10b981; font-weight: bold')
204
- console.log(instruction.result)
205
- }
206
-
207
- // 🔥 显示WB阶段真实执行内容
208
- if (this.level >= ConsoleLevel.VERBOSE && instruction.writeBackExecution) {
209
- const wbExec = instruction.writeBackExecution
210
- console.log(`%c💾 ${this.t('label.wbExecution')}`, 'color: #8b5cf6; font-weight: bold')
211
-
212
- if (wbExec.hasCommit) {
213
- if (wbExec.commitSuccess === true) {
214
- console.log(` ${this.t('wb.commitOk')}`)
215
- console.log(` ${this.t('wb.commitArgs')}`, wbExec.commitArgs)
216
- } else if (wbExec.commitSuccess === false) {
217
- console.log(` ${this.t('wb.commitFail')}`)
218
- console.log(` ${this.t('wb.commitArgs')}`, wbExec.commitArgs)
219
- console.log(` ${this.t('wb.commitError')}`, wbExec.commitError)
220
- } else {
221
- console.log(` ${this.t('wb.commitUnknown')}`)
222
- }
223
- } else {
224
- console.log(` ${this.t('wb.noCommit')}`)
225
- }
226
-
227
- if (wbExec.rollbackExecuted) {
228
- console.log(` ${this.t('wb.rollbackExecuted')}`)
229
- console.log(` ${this.t('wb.rollbackSnapshot')}`, wbExec.rollbackSnapshot)
230
- if (wbExec.rollbackError) {
231
- console.log(` ${this.t('wb.rollbackError')}`, wbExec.rollbackError)
232
- }
233
- }
234
-
235
- // 显示中断处理器注册(成功时)
236
- if (instruction.status === 'committed') {
237
- console.log(` ${this.t('wb.interruptRegistered')}`)
238
- }
239
- }
240
-
241
- // 显示流水线阶段
242
- if (this.level >= ConsoleLevel.VERBOSE) {
243
- this.printPipelineStages(instruction)
244
- }
245
-
246
- // 显示详细信息
247
- if (this.level >= ConsoleLevel.DEBUG) {
248
- this.printInstructionDetails(instruction)
249
- }
250
-
251
- console.groupEnd()
252
- }
253
-
254
- /**
255
- * 指令被取消
256
- */
257
- onInstructionCancelled(instruction: QueuedInstruction, duration: number): void {
258
- if (!this.shouldPrint(instruction.type)) return
259
-
260
- // 🔍 格式化调用源信息
261
- const callSourceInfo = instruction.context.callSource
262
- ? ` %c📍 ${formatCallSourceShort(instruction.context.callSource)}`
263
- : ''
264
-
265
- // 使用橙色表示取消(不同于红色的失败)
266
- console.groupCollapsed(
267
- `%c[${this.t('instruction.cancelled')}] %c${this.formatTime()} %c${instruction.type}%c %c${duration}ms${callSourceInfo}`,
268
- 'color: #f59e0b; font-weight: bold',
269
- 'color: #666; font-size: 11px',
270
- 'color: #f59e0b; font-weight: bold; background: #f59e0b15; padding: 2px 6px; border-radius: 3px',
271
- 'color: #f59e0b',
272
- 'color: #f59e0b; font-weight: bold',
273
- ...(callSourceInfo ? ['color: #8b5cf6; font-weight: bold'] : [])
274
- )
275
-
276
- // 显示取消原因
277
- console.warn(
278
- `%c${this.t('label.reason')} ${instruction.cancelReason || this.t('common.unknownReason')}`,
279
- 'color: #f59e0b; font-weight: bold'
280
- )
281
-
282
- // 显示取消阶段
283
- if (instruction.cancelledAt) {
284
- console.log(
285
- `%c🔸 ${this.t('label.cancelStage')} ${instruction.cancelledAt}`,
286
- 'color: #f59e0b'
287
- )
288
- }
289
-
290
- // 🔥 显示指令输入参数
291
- if (this.level >= ConsoleLevel.NORMAL) {
292
- console.log(`%c📝 ${this.t('label.payload')}`, 'color: #3b82f6; font-weight: bold')
293
- console.log(instruction.payload)
294
- }
295
-
296
- // 显示流水线阶段
297
- if (this.level >= ConsoleLevel.VERBOSE) {
298
- this.printPipelineStages(instruction)
299
- }
300
-
301
- console.groupEnd()
302
- }
303
-
304
- /**
305
- * 指令失败
306
- */
307
- onInstructionFailure(instruction: QueuedInstruction, error: Error, duration: number): void {
308
- if (!this.shouldPrint(instruction.type)) return
309
-
310
- // 🔍 格式化调用源信息
311
- const callSourceInfo = instruction.context.callSource
312
- ? ` %c📍 ${formatCallSourceShort(instruction.context.callSource)}`
313
- : ''
314
-
315
- // 🔥 失败时自动展开,方便排查
316
- console.group(
317
- `%c[${this.t('instruction.failed')}] %c${this.formatTime()} %c${instruction.type}%c %c${duration}ms${callSourceInfo}`,
318
- 'color: #ef4444; font-weight: bold',
319
- 'color: #666; font-size: 11px',
320
- 'color: #ef4444; font-weight: bold; background: #ef444415; padding: 2px 6px; border-radius: 3px',
321
- 'color: #ef4444',
322
- 'color: #ef4444; font-weight: bold',
323
- ...(callSourceInfo ? ['color: #8b5cf6; font-weight: bold'] : [])
324
- )
325
-
326
- // 显示错误信息
327
- console.error(`%c${this.t('label.reason')} ${error.message}`, 'color: #ef4444; font-weight: bold')
328
-
329
- // 🔥 显示指令输入参数
330
- if (this.level >= ConsoleLevel.NORMAL) {
331
- console.log(`%c📝 ${this.t('label.payload')}`, 'color: #3b82f6; font-weight: bold')
332
- console.log(instruction.payload)
333
- }
334
-
335
- // 显示是否回滚
336
- if (instruction.optimisticSnapshot) {
337
- console.log(`%c🔄 ${this.t('optimistic.rolledBack')}`, 'color: #f59e0b; font-weight: bold')
338
- }
339
-
340
- // 显示流水线阶段
341
- if (this.level >= ConsoleLevel.VERBOSE) {
342
- this.printPipelineStages(instruction)
343
- }
344
-
345
- // 显示详细信息
346
- if (this.level >= ConsoleLevel.VERBOSE) {
347
- this.printInstructionDetails(instruction)
348
- console.error('Error Stack:', error.stack)
349
- }
350
-
351
- // 🔥 智能建议
352
- this.printSuggestions(instruction, error)
353
-
354
- console.groupEnd()
355
- }
356
-
357
- /**
358
- * 乐观更新应用
359
- */
360
- onOptimisticApplied(instruction: QueuedInstruction): void {
361
- if (!this.shouldPrint(instruction.type)) return
362
-
363
- if (this.level >= ConsoleLevel.VERBOSE) {
364
- // 🔍 格式化调用源信息
365
- const callSourceInfo = instruction.context.callSource
366
- ? ` %c📍 ${formatCallSourceShort(instruction.context.callSource)}`
367
- : ''
368
-
369
- console.groupCollapsed(
370
- `%c[${this.t('optimistic.applied')}] %c${this.formatTime()} %c${instruction.type}%c${callSourceInfo}`,
371
- 'color: #8b5cf6; font-weight: bold',
372
- 'color: #666; font-size: 11px',
373
- 'color: #8b5cf6; font-weight: bold; background: #8b5cf615; padding: 2px 6px; border-radius: 3px',
374
- 'color: #8b5cf6',
375
- ...(callSourceInfo ? ['color: #8b5cf6; font-weight: bold'] : [])
376
- )
377
-
378
- // 显示乐观更新的 payload
379
- if (this.level >= ConsoleLevel.DEBUG) {
380
- console.log(`%c📝 ${this.t('label.payload')}`, 'color: #8b5cf6; font-weight: bold')
381
- console.log(instruction.payload)
382
- }
383
-
384
- // 显示快照信息
385
- if (instruction.optimisticSnapshot) {
386
- console.log(`%c💾 ${this.t('label.snapshotSaved')}`, 'color: #10b981; font-size: 11px')
387
- }
388
-
389
- console.groupEnd()
390
- }
391
- }
392
-
393
- /**
394
- * 乐观更新回滚
395
- */
396
- onOptimisticRolledBack(instruction: QueuedInstruction, reason: string): void {
397
- if (!this.shouldPrint(instruction.type)) return
398
-
399
- // 回滚是重要事件,总是显示
400
- if (this.level >= ConsoleLevel.MINIMAL) {
401
- // 🔍 格式化调用源信息
402
- const callSourceInfo = instruction.context.callSource
403
- ? ` %c📍 ${formatCallSourceShort(instruction.context.callSource)}`
404
- : ''
405
-
406
- // 🔥 回滚重要,使用展开分组便于立即查看
407
- console.group(
408
- `%c[${this.t('optimistic.rolledBack')}] %c${this.formatTime()} %c${instruction.type}%c${callSourceInfo}`,
409
- 'color: #f59e0b; font-weight: bold',
410
- 'color: #666; font-size: 11px',
411
- 'color: #f59e0b; font-weight: bold; background: #f59e0b15; padding: 2px 6px; border-radius: 3px',
412
- 'color: #f59e0b',
413
- ...(callSourceInfo ? ['color: #8b5cf6; font-weight: bold'] : [])
414
- )
415
-
416
- // 显示回滚原因
417
- console.log(`%c⚠️ ${this.t('label.rollbackReason')}`, 'color: #f59e0b; font-weight: bold')
418
- console.log(reason)
419
-
420
- // 显示指令信息
421
- console.log(`%c📋 ${this.t('label.instructionInfo')}`, 'color: #f59e0b; font-weight: bold')
422
- console.table({
423
- 'Instruction ID': instruction.id,
424
- 'Correlation ID': instruction.context.correlationId,
425
- 'Type': instruction.type,
426
- })
427
-
428
- console.groupEnd()
429
- }
430
- }
431
-
432
- /**
433
- * 资源冲突
434
- */
435
- onSchedulerConflict(
436
- instruction: QueuedInstruction,
437
- conflictingWith: string[],
438
- waitTime: number
439
- ): void {
440
- if (!this.shouldPrint(instruction.type)) return
441
-
442
- if (this.level >= ConsoleLevel.VERBOSE) {
443
- console.log(`%c ⏳ ${this.formatTime()} ${this.t('scheduler.conflictWait', { waitTime })}`, 'color: #f59e0b', {
444
- instructionId: instruction.id,
445
- conflictingWith,
446
- })
447
- }
448
- }
449
-
450
- /**
451
- * 网络请求
452
- */
453
- onNetworkRequest(instruction: QueuedInstruction, method: string, url: string): void {
454
- if (!this.shouldPrint(instruction.type)) return
455
-
456
- if (this.level >= ConsoleLevel.DEBUG) {
457
- console.log(`%c 🌐 ${this.formatTime()} ${method} ${url}`, 'color: #06b6d4', {
458
- instructionId: instruction.id,
459
- correlationId: instruction.context.correlationId,
460
- })
461
- }
462
- }
463
-
464
- /**
465
- * 网络响应
466
- */
467
- onNetworkResponse(instruction: QueuedInstruction, status: number, latency: number): void {
468
- if (!this.shouldPrint(instruction.type)) return
469
-
470
- if (this.level >= ConsoleLevel.DEBUG) {
471
- const statusColor = status >= 200 && status < 300 ? '#10b981' : '#ef4444'
472
- console.log(
473
- `%c ← ${this.formatTime()} HTTP ${status} (${latency}ms)`,
474
- `color: ${statusColor}`,
475
- {
476
- instructionId: instruction.id,
477
- }
478
- )
479
- }
480
- }
481
-
482
- // ==================== 辅助方法 ====================
483
-
484
- /**
485
- * 打印流水线阶段
486
- */
487
- private printPipelineStages(instruction: QueuedInstruction): void {
488
- const timestamps = instruction.timestamps
489
-
490
- console.log(`%c${this.t('label.pipelineStages')}`, 'color: #666; font-weight: bold')
491
-
492
- // 打印各阶段之间的耗时
493
- const transitions = []
494
-
495
- if (timestamps.IF && timestamps.SCH) {
496
- transitions.push({ label: 'IF→SCH', duration: timestamps.SCH - timestamps.IF })
497
- }
498
- if (timestamps.SCH && timestamps.EX) {
499
- transitions.push({ label: 'SCH→EX', duration: timestamps.EX - timestamps.SCH })
500
- }
501
- if (timestamps.EX && timestamps.WB) {
502
- transitions.push({ label: 'EX→WB', duration: timestamps.WB - timestamps.EX })
503
- }
504
-
505
- for (const transition of transitions) {
506
- const bar = this.createDurationBar(transition.duration)
507
- console.log(
508
- ` %c${transition.label}%c ${bar} %c${transition.duration}ms`,
509
- 'color: #3b82f6; font-weight: bold',
510
- 'color: #666',
511
- 'color: #666; font-weight: bold'
512
- )
513
- }
514
-
515
- // 打印总耗时
516
- if (timestamps.IF && timestamps.WB) {
517
- const total = timestamps.WB - timestamps.IF
518
- console.log(
519
- ` %c${this.t('label.totalDuration')} %c${total}ms`,
520
- 'color: #666; font-weight: bold',
521
- 'color: #10b981; font-weight: bold; font-size: 14px'
522
- )
523
- }
524
-
525
- // 特殊标记
526
- if (instruction.optimisticSnapshot) {
527
- console.log(` %c✓ ${this.t('label.optimisticUpdate')}`, 'color: #8b5cf6')
528
- }
529
- }
530
-
531
- /**
532
- * 打印指令详情
533
- */
534
- private printInstructionDetails(instruction: QueuedInstruction): void {
535
- console.log(`%c${this.t('label.details')}`, 'color: #666; font-weight: bold')
536
- console.table({
537
- 'Instruction ID': instruction.id,
538
- 'Correlation ID': instruction.context.correlationId,
539
- Type: instruction.type,
540
- Status: instruction.status,
541
- 'Created At': instruction.timestamps.IF
542
- ? new Date(instruction.timestamps.IF).toISOString()
543
- : 'N/A',
544
- })
545
-
546
- if (this.level >= ConsoleLevel.DEBUG) {
547
- console.log(`%c${this.t('label.payload')}`, 'color: #666; font-weight: bold', instruction.payload)
548
-
549
- if (instruction.result) {
550
- console.log(`%c${this.t('label.backendResult')}`, 'color: #666; font-weight: bold', instruction.result)
551
- }
552
- }
553
- }
554
-
555
- /**
556
- * 打印智能建议
557
- */
558
- private printSuggestions(instruction: QueuedInstruction, error: Error): void {
559
- const suggestions: string[] = []
560
-
561
- // 根据错误类型给出建议
562
- if (error.message.includes('database is locked')) {
563
- suggestions.push(this.t('suggest.dbLocked'))
564
- }
565
-
566
- if (error.message.includes('Network')) {
567
- suggestions.push(this.t('suggest.network'))
568
- }
569
-
570
- if (error.message.includes('timeout')) {
571
- suggestions.push(this.t('suggest.timeout'))
572
- }
573
-
574
- // 根据指令类型给出建议
575
- const duration =
576
- instruction.timestamps.WB && instruction.timestamps.IF
577
- ? instruction.timestamps.WB - instruction.timestamps.IF
578
- : 0
579
-
580
- if (duration > 1000) {
581
- suggestions.push(this.t('suggest.slowInstruction', { duration }))
582
- }
583
-
584
- if (suggestions.length > 0) {
585
- console.log(`%c💡 ${this.t('label.suggestions')}`, 'color: #f59e0b; font-weight: bold')
586
- suggestions.forEach((s) => {
587
- console.log(` • ${s}`)
588
- })
589
- }
590
- }
591
-
592
- /**
593
- * 创建耗时条形图
594
- */
595
- private createDurationBar(duration: number): string {
596
- const maxWidth = 20
597
- const width = Math.min(Math.round(duration / 50), maxWidth)
598
- const bar = '█'.repeat(width)
599
-
600
- return bar
601
- }
602
-
603
- /**
604
- * 格式化时间
605
- */
606
- private formatTime(): string {
607
- const now = new Date()
608
- const timeLocale =
609
- this.locale === 'zh-CN'
610
- ? 'zh-CN'
611
- : this.locale === 'en'
612
- ? 'en-GB'
613
- : typeof navigator !== 'undefined' && (navigator.language || '').toLowerCase().startsWith('zh')
614
- ? 'zh-CN'
615
- : 'en-GB'
616
- return now.toLocaleTimeString(timeLocale, {
617
- hour12: false,
618
- hour: '2-digit',
619
- minute: '2-digit',
620
- second: '2-digit',
621
- fractionalSecondDigits: 3,
622
- } as any)
623
- }
624
-
625
- /**
626
- * 判断是否应该打印
627
- */
628
- private shouldPrint(instructionType: string): boolean {
629
- if (!this.enabled) return false
630
- if (this.level === ConsoleLevel.SILENT) return false
631
- if (this.filter.size > 0 && !this.filter.has(instructionType)) return false
632
- return true
633
- }
634
-
635
- // ==================== 便捷方法 ====================
636
-
637
- /**
638
- * 打印分隔线
639
- */
640
- printSeparator(title?: string): void {
641
- if (!this.enabled) return
642
-
643
- if (title) {
644
- console.log(
645
- `%c━━━━━━━━━━━━━━━━━━━━━━━━━━ ${title} ━━━━━━━━━━━━━━━━━━━━━━━━━━`,
646
- 'color: #666; font-weight: bold'
647
- )
648
- } else {
649
- console.log('%c━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', 'color: #666')
650
- }
651
- }
652
-
653
- /**
654
- * 打印统计信息
655
- */
656
- printStats(stats: { total: number; success: number; failed: number; avgLatency: number }): void {
657
- if (!this.enabled) return
658
-
659
- console.group(`%c📊 ${this.t('label.stats')}`, 'color: #3b82f6; font-weight: bold; font-size: 14px')
660
-
661
- console.log(` ${this.t('stats.total')} %c${stats.total}`, 'color: #3b82f6; font-weight: bold')
662
-
663
- console.log(
664
- ` ${this.t('stats.success')} %c${stats.success} %c(${((stats.success / stats.total) * 100).toFixed(1)}%)`,
665
- 'color: #10b981; font-weight: bold',
666
- 'color: #666'
667
- )
668
-
669
- console.log(
670
- ` ${this.t('stats.failed')} %c${stats.failed} %c(${((stats.failed / stats.total) * 100).toFixed(1)}%)`,
671
- 'color: #ef4444; font-weight: bold',
672
- 'color: #666'
673
- )
674
-
675
- console.log(
676
- ` ${this.t('stats.avgLatency')} %c${stats.avgLatency.toFixed(0)}ms`,
677
- 'color: #666; font-weight: bold'
678
- )
679
-
680
- console.groupEnd()
681
- }
682
- }
683
-
684
- // 导出全局单例
685
- export const cpuConsole = new CPUConsole()
686
-
687
- // 导出枚举
688
- export { ConsoleLevel }
1
+ /**
2
+ * CPU 控制台打印系统
3
+ *
4
+ * 职责:
5
+ * 1. 实时打印指令执行过程
6
+ * 2. 美观的彩色输出
7
+ * 3. 分级别控制详细程度
8
+ * 4. 可折叠的详细信息
9
+ */
10
+
11
+ import type { QueuedInstruction } from '../types'
12
+ import { ConsoleLevel } from './types'
13
+ import { formatCallSourceShort } from './stack-parser'
14
+ import {
15
+ createCPUConsoleTranslator,
16
+ type CPUConsoleLocale,
17
+ type CPUConsoleTranslate,
18
+ } from './console-i18n'
19
+
20
+ export class CPUConsole {
21
+ private enabled: boolean = true
22
+ private level: ConsoleLevel = ConsoleLevel.NORMAL
23
+ private filter: Set<string> = new Set() // 指令类型过滤
24
+ private locale: CPUConsoleLocale = this.getDefaultLocale()
25
+ private translator: CPUConsoleTranslate = createCPUConsoleTranslator(this.locale)
26
+
27
+ constructor() {
28
+ this.loadSettings()
29
+ }
30
+
31
+ /**
32
+ * 设置语言(en / zh-CN)
33
+ */
34
+ setLocale(locale: CPUConsoleLocale): void {
35
+ this.locale = locale
36
+ this.translator = createCPUConsoleTranslator(locale)
37
+ localStorage.setItem('cpu-console-locale', locale)
38
+ }
39
+
40
+ /**
41
+ * 自定义翻译函数(不依赖任何 i18n 框架)
42
+ */
43
+ setI18nTranslator(t: CPUConsoleTranslate): void {
44
+ this.translator = t
45
+ }
46
+
47
+ getLocale(): CPUConsoleLocale {
48
+ return this.locale
49
+ }
50
+
51
+ /**
52
+ * 加载设置
53
+ */
54
+ private loadSettings(): void {
55
+ const savedLevel = localStorage.getItem('cpu-console-level')
56
+ if (savedLevel) {
57
+ this.level = parseInt(savedLevel) as ConsoleLevel
58
+ }
59
+
60
+ const savedFilter = localStorage.getItem('cpu-console-filter')
61
+ if (savedFilter) {
62
+ try {
63
+ const types = JSON.parse(savedFilter)
64
+ this.filter = new Set(types)
65
+ } catch (e) {
66
+ // 忽略解析错误
67
+ }
68
+ }
69
+
70
+ const savedLocale = localStorage.getItem('cpu-console-locale') as CPUConsoleLocale | null
71
+ // 兼容旧值:bilingual 直接回落到 zh-CN
72
+ if (savedLocale === 'en' || savedLocale === 'zh-CN') {
73
+ this.locale = savedLocale
74
+ this.translator = createCPUConsoleTranslator(savedLocale)
75
+ } else if ((savedLocale as any) === 'bilingual') {
76
+ this.locale = 'zh-CN'
77
+ this.translator = createCPUConsoleTranslator('zh-CN')
78
+ }
79
+ }
80
+
81
+ private t(key: Parameters<CPUConsoleTranslate>[0], params?: Parameters<CPUConsoleTranslate>[1]): string {
82
+ return this.translator(key as any, params)
83
+ }
84
+
85
+ private getDefaultLocale(): CPUConsoleLocale {
86
+ // 默认策略:中文环境输出中文,否则英文
87
+ if (typeof navigator !== 'undefined') {
88
+ const lang = navigator.language || ''
89
+ if (lang.toLowerCase().startsWith('zh')) return 'zh-CN'
90
+ }
91
+ return 'en'
92
+ }
93
+
94
+ /**
95
+ * 配置方法
96
+ */
97
+ setLevel(level: ConsoleLevel): void {
98
+ this.level = level
99
+ localStorage.setItem('cpu-console-level', level.toString())
100
+ }
101
+
102
+ setFilter(types: string[]): void {
103
+ this.filter = new Set(types)
104
+ localStorage.setItem('cpu-console-filter', JSON.stringify(types))
105
+ }
106
+
107
+ enable(): void {
108
+ this.enabled = true
109
+ }
110
+
111
+ disable(): void {
112
+ this.enabled = false
113
+ }
114
+
115
+ getLevel(): ConsoleLevel {
116
+ return this.level
117
+ }
118
+
119
+ // ==================== 打印方法 ====================
120
+
121
+ /**
122
+ * 指令创建
123
+ */
124
+ onInstructionCreated(instruction: QueuedInstruction): void {
125
+ if (!this.shouldPrint(instruction.type)) return
126
+
127
+ if (this.level >= ConsoleLevel.NORMAL) {
128
+ // 🔍 格式化调用源信息
129
+ const callSourceInfo = instruction.context.callSource
130
+ ? ` %c📍 ${formatCallSourceShort(instruction.context.callSource)}`
131
+ : ''
132
+
133
+ // 🎯 使用与指令成功一致的分组格式
134
+ console.groupCollapsed(
135
+ `%c[${this.t('instruction.created')}] %c${this.formatTime()} %c${instruction.type}%c${callSourceInfo}`,
136
+ 'color: #3b82f6; font-weight: bold',
137
+ 'color: #666; font-size: 11px',
138
+ 'color: #3b82f6; font-weight: bold; background: #3b82f615; padding: 2px 6px; border-radius: 3px',
139
+ 'color: #3b82f6',
140
+ ...(callSourceInfo ? ['color: #8b5cf6; font-weight: bold'] : [])
141
+ )
142
+
143
+ // 🔥 显示指令基本信息
144
+ console.log(`%c📋 ${this.t('label.instructionInfo')}`, 'color: #3b82f6; font-weight: bold')
145
+ console.table({
146
+ 'Instruction ID': instruction.id,
147
+ 'Correlation ID': instruction.context.correlationId,
148
+ 'Type': instruction.type,
149
+ 'Status': instruction.status,
150
+ 'Source': instruction.context.source,
151
+ 'Retry Count': instruction.context.retryCount,
152
+ })
153
+
154
+ // 🔥 显示指令参数
155
+ if (this.level >= ConsoleLevel.DEBUG) {
156
+ console.log(`%c📝 ${this.t('label.payload')}`, 'color: #3b82f6; font-weight: bold')
157
+ console.log(instruction.payload)
158
+ } else {
159
+ console.log(`%c📝 ${this.t('label.payloadDebugOnly')}`, 'color: #666; font-style: italic')
160
+ }
161
+
162
+ // 🔥 显示调用源详情
163
+ if (instruction.context.callSource && this.level >= ConsoleLevel.VERBOSE) {
164
+ console.log(`%c📍 ${this.t('label.callSourceDetails')}`, 'color: #8b5cf6; font-weight: bold')
165
+ console.table({
166
+ 'File': instruction.context.callSource.file,
167
+ 'Line': instruction.context.callSource.line,
168
+ 'Column': instruction.context.callSource.column,
169
+ 'Function': instruction.context.callSource.function || 'N/A',
170
+ })
171
+ }
172
+
173
+ console.groupEnd()
174
+ }
175
+ }
176
+
177
+ /**
178
+ * 指令成功
179
+ */
180
+ onInstructionSuccess(instruction: QueuedInstruction, duration: number): void {
181
+ if (!this.shouldPrint(instruction.type)) return
182
+
183
+ // 🔍 格式化调用源信息
184
+ const callSourceInfo = instruction.context.callSource
185
+ ? ` %c📍 ${formatCallSourceShort(instruction.context.callSource)}`
186
+ : ''
187
+
188
+ // 🎯 核心:折叠分组,方便查看
189
+ console.groupCollapsed(
190
+ `%c[${this.t('instruction.success')}] %c${this.formatTime()} %c${instruction.type}%c %c${duration}ms${callSourceInfo}`,
191
+ 'color: #10b981; font-weight: bold',
192
+ 'color: #666; font-size: 11px',
193
+ 'color: #10b981; font-weight: bold; background: #10b98115; padding: 2px 6px; border-radius: 3px',
194
+ 'color: #10b981',
195
+ 'color: #10b981; font-weight: bold',
196
+ ...(callSourceInfo ? ['color: #8b5cf6; font-weight: bold'] : [])
197
+ )
198
+
199
+ // 🔥 显示指令输入参数
200
+ if (this.level >= ConsoleLevel.NORMAL) {
201
+ console.log(`%c📝 ${this.t('label.payload')}`, 'color: #3b82f6; font-weight: bold')
202
+ console.log(instruction.payload)
203
+ }
204
+
205
+ // 🔥 显示后端返回结果
206
+ if (instruction.result && this.level >= ConsoleLevel.NORMAL) {
207
+ console.log(`%c📥 ${this.t('label.backendResult')}`, 'color: #10b981; font-weight: bold')
208
+ console.log(instruction.result)
209
+ }
210
+
211
+ // 🔥 显示WB阶段真实执行内容
212
+ if (this.level >= ConsoleLevel.VERBOSE && instruction.writeBackExecution) {
213
+ const wbExec = instruction.writeBackExecution
214
+ console.log(`%c💾 ${this.t('label.wbExecution')}`, 'color: #8b5cf6; font-weight: bold')
215
+
216
+ if (wbExec.hasCommit) {
217
+ if (wbExec.commitSuccess === true) {
218
+ console.log(` ${this.t('wb.commitOk')}`)
219
+ console.log(` ${this.t('wb.commitArgs')}`, wbExec.commitArgs)
220
+ } else if (wbExec.commitSuccess === false) {
221
+ console.log(` ${this.t('wb.commitFail')}`)
222
+ console.log(` ${this.t('wb.commitArgs')}`, wbExec.commitArgs)
223
+ console.log(` ${this.t('wb.commitError')}`, wbExec.commitError)
224
+ } else {
225
+ console.log(` ${this.t('wb.commitUnknown')}`)
226
+ }
227
+ } else {
228
+ console.log(` ${this.t('wb.noCommit')}`)
229
+ }
230
+
231
+ if (wbExec.rollbackExecuted) {
232
+ console.log(` ${this.t('wb.rollbackExecuted')}`)
233
+ console.log(` ${this.t('wb.rollbackSnapshot')}`, wbExec.rollbackSnapshot)
234
+ if (wbExec.rollbackError) {
235
+ console.log(` ${this.t('wb.rollbackError')}`, wbExec.rollbackError)
236
+ }
237
+ }
238
+
239
+ // 显示中断处理器注册(成功时)
240
+ if (instruction.status === 'committed') {
241
+ console.log(` ${this.t('wb.interruptRegistered')}`)
242
+ }
243
+ }
244
+
245
+ // 显示流水线阶段
246
+ if (this.level >= ConsoleLevel.VERBOSE) {
247
+ this.printPipelineStages(instruction)
248
+ }
249
+
250
+ // 显示详细信息
251
+ if (this.level >= ConsoleLevel.DEBUG) {
252
+ this.printInstructionDetails(instruction)
253
+ }
254
+
255
+ console.groupEnd()
256
+ }
257
+
258
+ /**
259
+ * 指令被取消
260
+ */
261
+ onInstructionCancelled(instruction: QueuedInstruction, duration: number): void {
262
+ if (!this.shouldPrint(instruction.type)) return
263
+
264
+ // 🔍 格式化调用源信息
265
+ const callSourceInfo = instruction.context.callSource
266
+ ? ` %c📍 ${formatCallSourceShort(instruction.context.callSource)}`
267
+ : ''
268
+
269
+ // 使用橙色表示取消(不同于红色的失败)
270
+ console.groupCollapsed(
271
+ `%c[${this.t('instruction.cancelled')}] %c${this.formatTime()} %c${instruction.type}%c %c${duration}ms${callSourceInfo}`,
272
+ 'color: #f59e0b; font-weight: bold',
273
+ 'color: #666; font-size: 11px',
274
+ 'color: #f59e0b; font-weight: bold; background: #f59e0b15; padding: 2px 6px; border-radius: 3px',
275
+ 'color: #f59e0b',
276
+ 'color: #f59e0b; font-weight: bold',
277
+ ...(callSourceInfo ? ['color: #8b5cf6; font-weight: bold'] : [])
278
+ )
279
+
280
+ // 显示取消原因
281
+ console.warn(
282
+ `%c${this.t('label.reason')} ${instruction.cancelReason || this.t('common.unknownReason')}`,
283
+ 'color: #f59e0b; font-weight: bold'
284
+ )
285
+
286
+ // 显示取消阶段
287
+ if (instruction.cancelledAt) {
288
+ console.log(
289
+ `%c🔸 ${this.t('label.cancelStage')} ${instruction.cancelledAt}`,
290
+ 'color: #f59e0b'
291
+ )
292
+ }
293
+
294
+ // 🔥 显示指令输入参数
295
+ if (this.level >= ConsoleLevel.NORMAL) {
296
+ console.log(`%c📝 ${this.t('label.payload')}`, 'color: #3b82f6; font-weight: bold')
297
+ console.log(instruction.payload)
298
+ }
299
+
300
+ // 显示流水线阶段
301
+ if (this.level >= ConsoleLevel.VERBOSE) {
302
+ this.printPipelineStages(instruction)
303
+ }
304
+
305
+ console.groupEnd()
306
+ }
307
+
308
+ /**
309
+ * 指令失败
310
+ */
311
+ onInstructionFailure(instruction: QueuedInstruction, error: Error, duration: number): void {
312
+ if (!this.shouldPrint(instruction.type)) return
313
+
314
+ // 🔍 格式化调用源信息
315
+ const callSourceInfo = instruction.context.callSource
316
+ ? ` %c📍 ${formatCallSourceShort(instruction.context.callSource)}`
317
+ : ''
318
+
319
+ // 🔥 失败时自动展开,方便排查
320
+ console.group(
321
+ `%c[${this.t('instruction.failed')}] %c${this.formatTime()} %c${instruction.type}%c %c${duration}ms${callSourceInfo}`,
322
+ 'color: #ef4444; font-weight: bold',
323
+ 'color: #666; font-size: 11px',
324
+ 'color: #ef4444; font-weight: bold; background: #ef444415; padding: 2px 6px; border-radius: 3px',
325
+ 'color: #ef4444',
326
+ 'color: #ef4444; font-weight: bold',
327
+ ...(callSourceInfo ? ['color: #8b5cf6; font-weight: bold'] : [])
328
+ )
329
+
330
+ // 显示错误信息
331
+ console.error(`%c${this.t('label.reason')} ${error.message}`, 'color: #ef4444; font-weight: bold')
332
+
333
+ // 🔥 显示指令输入参数
334
+ if (this.level >= ConsoleLevel.NORMAL) {
335
+ console.log(`%c📝 ${this.t('label.payload')}`, 'color: #3b82f6; font-weight: bold')
336
+ console.log(instruction.payload)
337
+ }
338
+
339
+ // 显示是否回滚
340
+ if (instruction.optimisticSnapshot) {
341
+ console.log(`%c🔄 ${this.t('optimistic.rolledBack')}`, 'color: #f59e0b; font-weight: bold')
342
+ }
343
+
344
+ // 显示流水线阶段
345
+ if (this.level >= ConsoleLevel.VERBOSE) {
346
+ this.printPipelineStages(instruction)
347
+ }
348
+
349
+ // 显示详细信息
350
+ if (this.level >= ConsoleLevel.VERBOSE) {
351
+ this.printInstructionDetails(instruction)
352
+ console.error('Error Stack:', error.stack)
353
+ }
354
+
355
+ // 🔥 智能建议
356
+ this.printSuggestions(instruction, error)
357
+
358
+ console.groupEnd()
359
+ }
360
+
361
+ /**
362
+ * 乐观更新应用
363
+ */
364
+ onOptimisticApplied(instruction: QueuedInstruction): void {
365
+ if (!this.shouldPrint(instruction.type)) return
366
+
367
+ if (this.level >= ConsoleLevel.VERBOSE) {
368
+ // 🔍 格式化调用源信息
369
+ const callSourceInfo = instruction.context.callSource
370
+ ? ` %c📍 ${formatCallSourceShort(instruction.context.callSource)}`
371
+ : ''
372
+
373
+ console.groupCollapsed(
374
+ `%c[${this.t('optimistic.applied')}] %c${this.formatTime()} %c${instruction.type}%c${callSourceInfo}`,
375
+ 'color: #8b5cf6; font-weight: bold',
376
+ 'color: #666; font-size: 11px',
377
+ 'color: #8b5cf6; font-weight: bold; background: #8b5cf615; padding: 2px 6px; border-radius: 3px',
378
+ 'color: #8b5cf6',
379
+ ...(callSourceInfo ? ['color: #8b5cf6; font-weight: bold'] : [])
380
+ )
381
+
382
+ // 显示乐观更新的 payload
383
+ if (this.level >= ConsoleLevel.DEBUG) {
384
+ console.log(`%c📝 ${this.t('label.payload')}`, 'color: #8b5cf6; font-weight: bold')
385
+ console.log(instruction.payload)
386
+ }
387
+
388
+ // 显示快照信息
389
+ if (instruction.optimisticSnapshot) {
390
+ console.log(`%c💾 ${this.t('label.snapshotSaved')}`, 'color: #10b981; font-size: 11px')
391
+ }
392
+
393
+ console.groupEnd()
394
+ }
395
+ }
396
+
397
+ /**
398
+ * 乐观更新回滚
399
+ */
400
+ onOptimisticRolledBack(instruction: QueuedInstruction, reason: string): void {
401
+ if (!this.shouldPrint(instruction.type)) return
402
+
403
+ // 回滚是重要事件,总是显示
404
+ if (this.level >= ConsoleLevel.MINIMAL) {
405
+ // 🔍 格式化调用源信息
406
+ const callSourceInfo = instruction.context.callSource
407
+ ? ` %c📍 ${formatCallSourceShort(instruction.context.callSource)}`
408
+ : ''
409
+
410
+ // 🔥 回滚重要,使用展开分组便于立即查看
411
+ console.group(
412
+ `%c[${this.t('optimistic.rolledBack')}] %c${this.formatTime()} %c${instruction.type}%c${callSourceInfo}`,
413
+ 'color: #f59e0b; font-weight: bold',
414
+ 'color: #666; font-size: 11px',
415
+ 'color: #f59e0b; font-weight: bold; background: #f59e0b15; padding: 2px 6px; border-radius: 3px',
416
+ 'color: #f59e0b',
417
+ ...(callSourceInfo ? ['color: #8b5cf6; font-weight: bold'] : [])
418
+ )
419
+
420
+ // 显示回滚原因
421
+ console.log(`%c⚠️ ${this.t('label.rollbackReason')}`, 'color: #f59e0b; font-weight: bold')
422
+ console.log(reason)
423
+
424
+ // 显示指令信息
425
+ console.log(`%c📋 ${this.t('label.instructionInfo')}`, 'color: #f59e0b; font-weight: bold')
426
+ console.table({
427
+ 'Instruction ID': instruction.id,
428
+ 'Correlation ID': instruction.context.correlationId,
429
+ 'Type': instruction.type,
430
+ })
431
+
432
+ console.groupEnd()
433
+ }
434
+ }
435
+
436
+ /**
437
+ * 资源冲突
438
+ */
439
+ onSchedulerConflict(
440
+ instruction: QueuedInstruction,
441
+ conflictingWith: string[],
442
+ waitTime: number
443
+ ): void {
444
+ if (!this.shouldPrint(instruction.type)) return
445
+
446
+ if (this.level >= ConsoleLevel.VERBOSE) {
447
+ console.log(`%c ⏳ ${this.formatTime()} ${this.t('scheduler.conflictWait', { waitTime })}`, 'color: #f59e0b', {
448
+ instructionId: instruction.id,
449
+ conflictingWith,
450
+ })
451
+ }
452
+ }
453
+
454
+ /**
455
+ * 网络请求
456
+ */
457
+ onNetworkRequest(instruction: QueuedInstruction, method: string, url: string): void {
458
+ if (!this.shouldPrint(instruction.type)) return
459
+
460
+ if (this.level >= ConsoleLevel.DEBUG) {
461
+ console.log(`%c 🌐 ${this.formatTime()} ${method} ${url}`, 'color: #06b6d4', {
462
+ instructionId: instruction.id,
463
+ correlationId: instruction.context.correlationId,
464
+ })
465
+ }
466
+ }
467
+
468
+ /**
469
+ * 网络响应
470
+ */
471
+ onNetworkResponse(instruction: QueuedInstruction, status: number, latency: number): void {
472
+ if (!this.shouldPrint(instruction.type)) return
473
+
474
+ if (this.level >= ConsoleLevel.DEBUG) {
475
+ const statusColor = status >= 200 && status < 300 ? '#10b981' : '#ef4444'
476
+ console.log(
477
+ `%c ← ${this.formatTime()} HTTP ${status} (${latency}ms)`,
478
+ `color: ${statusColor}`,
479
+ {
480
+ instructionId: instruction.id,
481
+ }
482
+ )
483
+ }
484
+ }
485
+
486
+ // ==================== 辅助方法 ====================
487
+
488
+ /**
489
+ * 打印流水线阶段
490
+ */
491
+ private printPipelineStages(instruction: QueuedInstruction): void {
492
+ const timestamps = instruction.timestamps
493
+
494
+ console.log(`%c${this.t('label.pipelineStages')}`, 'color: #666; font-weight: bold')
495
+
496
+ // 打印各阶段之间的耗时
497
+ const transitions = []
498
+
499
+ if (timestamps.IF && timestamps.SCH) {
500
+ transitions.push({ label: 'IF→SCH', duration: timestamps.SCH - timestamps.IF })
501
+ }
502
+ if (timestamps.SCH && timestamps.EX) {
503
+ transitions.push({ label: 'SCH→EX', duration: timestamps.EX - timestamps.SCH })
504
+ }
505
+ if (timestamps.EX && timestamps.WB) {
506
+ transitions.push({ label: 'EX→WB', duration: timestamps.WB - timestamps.EX })
507
+ }
508
+
509
+ for (const transition of transitions) {
510
+ const bar = this.createDurationBar(transition.duration)
511
+ console.log(
512
+ ` %c${transition.label}%c ${bar} %c${transition.duration}ms`,
513
+ 'color: #3b82f6; font-weight: bold',
514
+ 'color: #666',
515
+ 'color: #666; font-weight: bold'
516
+ )
517
+ }
518
+
519
+ // 打印总耗时
520
+ if (timestamps.IF && timestamps.WB) {
521
+ const total = timestamps.WB - timestamps.IF
522
+ console.log(
523
+ ` %c${this.t('label.totalDuration')} %c${total}ms`,
524
+ 'color: #666; font-weight: bold',
525
+ 'color: #10b981; font-weight: bold; font-size: 14px'
526
+ )
527
+ }
528
+
529
+ // 特殊标记
530
+ if (instruction.optimisticSnapshot) {
531
+ console.log(` %c✓ ${this.t('label.optimisticUpdate')}`, 'color: #8b5cf6')
532
+ }
533
+ }
534
+
535
+ /**
536
+ * 打印指令详情
537
+ */
538
+ private printInstructionDetails(instruction: QueuedInstruction): void {
539
+ console.log(`%c${this.t('label.details')}`, 'color: #666; font-weight: bold')
540
+ console.table({
541
+ 'Instruction ID': instruction.id,
542
+ 'Correlation ID': instruction.context.correlationId,
543
+ Type: instruction.type,
544
+ Status: instruction.status,
545
+ 'Created At': instruction.timestamps.IF
546
+ ? new Date(instruction.timestamps.IF).toISOString()
547
+ : 'N/A',
548
+ })
549
+
550
+ if (this.level >= ConsoleLevel.DEBUG) {
551
+ console.log(`%c${this.t('label.payload')}`, 'color: #666; font-weight: bold', instruction.payload)
552
+
553
+ if (instruction.result) {
554
+ console.log(`%c${this.t('label.backendResult')}`, 'color: #666; font-weight: bold', instruction.result)
555
+ }
556
+ }
557
+ }
558
+
559
+ /**
560
+ * 打印智能建议
561
+ */
562
+ private printSuggestions(instruction: QueuedInstruction, error: Error): void {
563
+ const suggestions: string[] = []
564
+
565
+ // 根据错误类型给出建议
566
+ if (error.message.includes('database is locked')) {
567
+ suggestions.push(this.t('suggest.dbLocked'))
568
+ }
569
+
570
+ if (error.message.includes('Network')) {
571
+ suggestions.push(this.t('suggest.network'))
572
+ }
573
+
574
+ if (error.message.includes('timeout')) {
575
+ suggestions.push(this.t('suggest.timeout'))
576
+ }
577
+
578
+ // 根据指令类型给出建议
579
+ const duration =
580
+ instruction.timestamps.WB && instruction.timestamps.IF
581
+ ? instruction.timestamps.WB - instruction.timestamps.IF
582
+ : 0
583
+
584
+ if (duration > 1000) {
585
+ suggestions.push(this.t('suggest.slowInstruction', { duration }))
586
+ }
587
+
588
+ if (suggestions.length > 0) {
589
+ console.log(`%c💡 ${this.t('label.suggestions')}`, 'color: #f59e0b; font-weight: bold')
590
+ suggestions.forEach((s) => {
591
+ console.log(` • ${s}`)
592
+ })
593
+ }
594
+ }
595
+
596
+ /**
597
+ * 创建耗时条形图
598
+ */
599
+ private createDurationBar(duration: number): string {
600
+ const maxWidth = 20
601
+ const width = Math.min(Math.round(duration / 50), maxWidth)
602
+ const bar = '█'.repeat(width)
603
+
604
+ return bar
605
+ }
606
+
607
+ /**
608
+ * 格式化时间
609
+ */
610
+ private formatTime(): string {
611
+ const now = new Date()
612
+ const timeLocale =
613
+ this.locale === 'zh-CN'
614
+ ? 'zh-CN'
615
+ : this.locale === 'en'
616
+ ? 'en-GB'
617
+ : typeof navigator !== 'undefined' && (navigator.language || '').toLowerCase().startsWith('zh')
618
+ ? 'zh-CN'
619
+ : 'en-GB'
620
+ return now.toLocaleTimeString(timeLocale, {
621
+ hour12: false,
622
+ hour: '2-digit',
623
+ minute: '2-digit',
624
+ second: '2-digit',
625
+ fractionalSecondDigits: 3,
626
+ } as any)
627
+ }
628
+
629
+ /**
630
+ * 判断是否应该打印
631
+ */
632
+ private shouldPrint(instructionType: string): boolean {
633
+ if (!this.enabled) return false
634
+ if (this.level === ConsoleLevel.SILENT) return false
635
+ if (this.filter.size > 0 && !this.filter.has(instructionType)) return false
636
+ return true
637
+ }
638
+
639
+ // ==================== 便捷方法 ====================
640
+
641
+ /**
642
+ * 打印分隔线
643
+ */
644
+ printSeparator(title?: string): void {
645
+ if (!this.enabled) return
646
+
647
+ if (title) {
648
+ console.log(
649
+ `%c━━━━━━━━━━━━━━━━━━━━━━━━━━ ${title} ━━━━━━━━━━━━━━━━━━━━━━━━━━`,
650
+ 'color: #666; font-weight: bold'
651
+ )
652
+ } else {
653
+ console.log('%c━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━', 'color: #666')
654
+ }
655
+ }
656
+
657
+ /**
658
+ * 打印统计信息
659
+ */
660
+ printStats(stats: { total: number; success: number; failed: number; avgLatency: number }): void {
661
+ if (!this.enabled) return
662
+
663
+ console.group(`%c📊 ${this.t('label.stats')}`, 'color: #3b82f6; font-weight: bold; font-size: 14px')
664
+
665
+ console.log(` ${this.t('stats.total')} %c${stats.total}`, 'color: #3b82f6; font-weight: bold')
666
+
667
+ console.log(
668
+ ` ${this.t('stats.success')} %c${stats.success} %c(${((stats.success / stats.total) * 100).toFixed(1)}%)`,
669
+ 'color: #10b981; font-weight: bold',
670
+ 'color: #666'
671
+ )
672
+
673
+ console.log(
674
+ ` ${this.t('stats.failed')} %c${stats.failed} %c(${((stats.failed / stats.total) * 100).toFixed(1)}%)`,
675
+ 'color: #ef4444; font-weight: bold',
676
+ 'color: #666'
677
+ )
678
+
679
+ console.log(
680
+ ` ${this.t('stats.avgLatency')} %c${stats.avgLatency.toFixed(0)}ms`,
681
+ 'color: #666; font-weight: bold'
682
+ )
683
+
684
+ console.groupEnd()
685
+ }
686
+ }
687
+
688
+ // 导出全局单例
689
+ export const cpuConsole = new CPUConsole()
690
+
691
+ // 导出枚举
692
+ export { ConsoleLevel }