@f2a/network 0.1.2 → 0.1.3
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.
- package/package.json +8 -1
- package/.github/workflows/ci.yml +0 -113
- package/.github/workflows/publish.yml +0 -60
- package/MONOREPO.md +0 -58
- package/SKILL.md +0 -137
- package/dist/adapters/openclaw.d.ts +0 -103
- package/dist/adapters/openclaw.d.ts.map +0 -1
- package/dist/adapters/openclaw.js +0 -297
- package/dist/adapters/openclaw.js.map +0 -1
- package/dist/core/connection-manager.d.ts +0 -80
- package/dist/core/connection-manager.d.ts.map +0 -1
- package/dist/core/connection-manager.js +0 -235
- package/dist/core/connection-manager.js.map +0 -1
- package/dist/core/connection-manager.test.d.ts +0 -2
- package/dist/core/connection-manager.test.d.ts.map +0 -1
- package/dist/core/connection-manager.test.js +0 -52
- package/dist/core/connection-manager.test.js.map +0 -1
- package/dist/core/identity.d.ts +0 -47
- package/dist/core/identity.d.ts.map +0 -1
- package/dist/core/identity.js +0 -130
- package/dist/core/identity.js.map +0 -1
- package/dist/core/identity.test.d.ts +0 -2
- package/dist/core/identity.test.d.ts.map +0 -1
- package/dist/core/identity.test.js +0 -43
- package/dist/core/identity.test.js.map +0 -1
- package/dist/core/serverless.d.ts +0 -155
- package/dist/core/serverless.d.ts.map +0 -1
- package/dist/core/serverless.js +0 -615
- package/dist/core/serverless.js.map +0 -1
- package/dist/daemon/webhook.test.d.ts +0 -2
- package/dist/daemon/webhook.test.d.ts.map +0 -1
- package/dist/daemon/webhook.test.js +0 -24
- package/dist/daemon/webhook.test.js.map +0 -1
- package/dist/protocol/messages.d.ts +0 -739
- package/dist/protocol/messages.d.ts.map +0 -1
- package/dist/protocol/messages.js +0 -188
- package/dist/protocol/messages.js.map +0 -1
- package/dist/protocol/messages.test.d.ts +0 -2
- package/dist/protocol/messages.test.d.ts.map +0 -1
- package/dist/protocol/messages.test.js +0 -55
- package/dist/protocol/messages.test.js.map +0 -1
- package/docs/F2A-PROTOCOL.md +0 -61
- package/docs/MOBILE_BOOTSTRAP_DESIGN.md +0 -126
- package/docs/a2a-lessons.md +0 -316
- package/docs/middleware-guide.md +0 -448
- package/docs/readme-update-checklist.md +0 -90
- package/docs/reputation-guide.md +0 -396
- package/docs/rfcs/001-reputation-system.md +0 -712
- package/docs/security-design.md +0 -247
- package/install.sh +0 -231
- package/packages/openclaw-adapter/README.md +0 -510
- package/packages/openclaw-adapter/openclaw.plugin.json +0 -106
- package/packages/openclaw-adapter/package.json +0 -40
- package/packages/openclaw-adapter/src/announcement-queue.test.ts +0 -449
- package/packages/openclaw-adapter/src/announcement-queue.ts +0 -403
- package/packages/openclaw-adapter/src/capability-detector.test.ts +0 -99
- package/packages/openclaw-adapter/src/capability-detector.ts +0 -183
- package/packages/openclaw-adapter/src/claim-handlers.test.ts +0 -974
- package/packages/openclaw-adapter/src/claim-handlers.ts +0 -482
- package/packages/openclaw-adapter/src/connector.business.test.ts +0 -583
- package/packages/openclaw-adapter/src/connector.ts +0 -795
- package/packages/openclaw-adapter/src/index.test.ts +0 -82
- package/packages/openclaw-adapter/src/index.ts +0 -18
- package/packages/openclaw-adapter/src/integration.e2e.test.ts +0 -829
- package/packages/openclaw-adapter/src/logger.ts +0 -51
- package/packages/openclaw-adapter/src/network-client.test.ts +0 -266
- package/packages/openclaw-adapter/src/network-client.ts +0 -251
- package/packages/openclaw-adapter/src/network-recovery.test.ts +0 -465
- package/packages/openclaw-adapter/src/node-manager.test.ts +0 -136
- package/packages/openclaw-adapter/src/node-manager.ts +0 -429
- package/packages/openclaw-adapter/src/plugin.test.ts +0 -439
- package/packages/openclaw-adapter/src/plugin.ts +0 -104
- package/packages/openclaw-adapter/src/reputation.test.ts +0 -221
- package/packages/openclaw-adapter/src/reputation.ts +0 -368
- package/packages/openclaw-adapter/src/task-guard.test.ts +0 -502
- package/packages/openclaw-adapter/src/task-guard.ts +0 -860
- package/packages/openclaw-adapter/src/task-queue.concurrency.test.ts +0 -462
- package/packages/openclaw-adapter/src/task-queue.edge-cases.test.ts +0 -284
- package/packages/openclaw-adapter/src/task-queue.persistence.test.ts +0 -408
- package/packages/openclaw-adapter/src/task-queue.ts +0 -668
- package/packages/openclaw-adapter/src/tool-handlers.test.ts +0 -906
- package/packages/openclaw-adapter/src/tool-handlers.ts +0 -574
- package/packages/openclaw-adapter/src/types.ts +0 -361
- package/packages/openclaw-adapter/src/webhook-pusher.test.ts +0 -188
- package/packages/openclaw-adapter/src/webhook-pusher.ts +0 -220
- package/packages/openclaw-adapter/src/webhook-server.test.ts +0 -580
- package/packages/openclaw-adapter/src/webhook-server.ts +0 -202
- package/packages/openclaw-adapter/tsconfig.json +0 -20
- package/src/cli/commands.test.ts +0 -157
- package/src/cli/commands.ts +0 -129
- package/src/cli/index.test.ts +0 -77
- package/src/cli/index.ts +0 -234
- package/src/core/autonomous-economy.test.ts +0 -291
- package/src/core/autonomous-economy.ts +0 -428
- package/src/core/e2ee-crypto.test.ts +0 -125
- package/src/core/e2ee-crypto.ts +0 -246
- package/src/core/f2a.test.ts +0 -269
- package/src/core/f2a.ts +0 -618
- package/src/core/p2p-network.test.ts +0 -199
- package/src/core/p2p-network.ts +0 -1432
- package/src/core/reputation-security.test.ts +0 -403
- package/src/core/reputation-security.ts +0 -562
- package/src/core/reputation.test.ts +0 -260
- package/src/core/reputation.ts +0 -576
- package/src/core/review-committee.test.ts +0 -380
- package/src/core/review-committee.ts +0 -401
- package/src/core/token-manager.test.ts +0 -133
- package/src/core/token-manager.ts +0 -140
- package/src/daemon/control-server.test.ts +0 -216
- package/src/daemon/control-server.ts +0 -292
- package/src/daemon/index.test.ts +0 -85
- package/src/daemon/index.ts +0 -89
- package/src/daemon/main.ts +0 -44
- package/src/daemon/start.ts +0 -29
- package/src/daemon/webhook.test.ts +0 -68
- package/src/daemon/webhook.ts +0 -105
- package/src/index.test.ts +0 -436
- package/src/index.ts +0 -72
- package/src/types/index.test.ts +0 -87
- package/src/types/index.ts +0 -341
- package/src/types/result.ts +0 -68
- package/src/utils/benchmark.ts +0 -237
- package/src/utils/logger.ts +0 -331
- package/src/utils/middleware.ts +0 -229
- package/src/utils/rate-limiter.ts +0 -207
- package/src/utils/signature.ts +0 -136
- package/src/utils/validation.ts +0 -186
- package/tests/docker/Dockerfile.node +0 -23
- package/tests/docker/Dockerfile.runner +0 -18
- package/tests/docker/docker-compose.test.yml +0 -73
- package/tests/integration/message-passing.test.ts +0 -109
- package/tests/integration/multi-node.test.ts +0 -92
- package/tests/integration/p2p-connection.test.ts +0 -83
- package/tests/integration/test-config.ts +0 -32
- package/tsconfig.json +0 -21
- package/vitest.config.ts +0 -26
package/src/utils/benchmark.ts
DELETED
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* F2A 性能基准测试工具
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { performance } from 'perf_hooks';
|
|
6
|
-
import { Logger } from './logger.js';
|
|
7
|
-
|
|
8
|
-
export interface BenchmarkConfig {
|
|
9
|
-
/** 测试名称 */
|
|
10
|
-
name: string;
|
|
11
|
-
/** 迭代次数 */
|
|
12
|
-
iterations: number;
|
|
13
|
-
/** 预热次数 */
|
|
14
|
-
warmup?: number;
|
|
15
|
-
/** 测试函数 */
|
|
16
|
-
fn: () => void | Promise<void>;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface BenchmarkResult {
|
|
20
|
-
name: string;
|
|
21
|
-
iterations: number;
|
|
22
|
-
totalTime: number;
|
|
23
|
-
avgTime: number;
|
|
24
|
-
minTime: number;
|
|
25
|
-
maxTime: number;
|
|
26
|
-
opsPerSecond: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* 性能基准测试运行器
|
|
31
|
-
*/
|
|
32
|
-
export class BenchmarkRunner {
|
|
33
|
-
private results: BenchmarkResult[] = [];
|
|
34
|
-
private logger: Logger;
|
|
35
|
-
|
|
36
|
-
constructor() {
|
|
37
|
-
this.logger = new Logger({ component: 'Benchmark' });
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* 运行单个基准测试
|
|
42
|
-
*/
|
|
43
|
-
async run(config: BenchmarkConfig): Promise<BenchmarkResult> {
|
|
44
|
-
const { name, iterations, warmup = 10, fn } = config;
|
|
45
|
-
|
|
46
|
-
this.logger.info(`Running benchmark: ${name}`, { iterations, warmup });
|
|
47
|
-
|
|
48
|
-
// 预热
|
|
49
|
-
for (let i = 0; i < warmup; i++) {
|
|
50
|
-
await fn();
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// 正式测试
|
|
54
|
-
const times: number[] = [];
|
|
55
|
-
const startTotal = performance.now();
|
|
56
|
-
|
|
57
|
-
for (let i = 0; i < iterations; i++) {
|
|
58
|
-
const start = performance.now();
|
|
59
|
-
await fn();
|
|
60
|
-
const end = performance.now();
|
|
61
|
-
times.push(end - start);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const endTotal = performance.now();
|
|
65
|
-
|
|
66
|
-
// 计算统计
|
|
67
|
-
const totalTime = endTotal - startTotal;
|
|
68
|
-
const avgTime = totalTime / iterations;
|
|
69
|
-
const minTime = Math.min(...times);
|
|
70
|
-
const maxTime = Math.max(...times);
|
|
71
|
-
const opsPerSecond = 1000 / avgTime;
|
|
72
|
-
|
|
73
|
-
const result: BenchmarkResult = {
|
|
74
|
-
name,
|
|
75
|
-
iterations,
|
|
76
|
-
totalTime,
|
|
77
|
-
avgTime,
|
|
78
|
-
minTime,
|
|
79
|
-
maxTime,
|
|
80
|
-
opsPerSecond
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
this.results.push(result);
|
|
84
|
-
this.logResult(result);
|
|
85
|
-
|
|
86
|
-
return result;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* 运行多个基准测试
|
|
91
|
-
*/
|
|
92
|
-
async runAll(configs: BenchmarkConfig[]): Promise<BenchmarkResult[]> {
|
|
93
|
-
for (const config of configs) {
|
|
94
|
-
await this.run(config);
|
|
95
|
-
}
|
|
96
|
-
return this.results;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* 打印结果
|
|
101
|
-
*/
|
|
102
|
-
private logResult(result: BenchmarkResult): void {
|
|
103
|
-
this.logger.info(`Benchmark: ${result.name}`, {
|
|
104
|
-
iterations: result.iterations,
|
|
105
|
-
totalTime: `${result.totalTime.toFixed(2)}ms`,
|
|
106
|
-
avgTime: `${result.avgTime.toFixed(3)}ms`,
|
|
107
|
-
minTime: `${result.minTime.toFixed(3)}ms`,
|
|
108
|
-
maxTime: `${result.maxTime.toFixed(3)}ms`,
|
|
109
|
-
opsPerSecond: result.opsPerSecond.toFixed(2)
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* 生成报告
|
|
115
|
-
*/
|
|
116
|
-
generateReport(): string {
|
|
117
|
-
const lines: string[] = [];
|
|
118
|
-
lines.push('# F2A Performance Benchmark Report');
|
|
119
|
-
lines.push('');
|
|
120
|
-
lines.push(`Generated: ${new Date().toISOString()}`);
|
|
121
|
-
lines.push('');
|
|
122
|
-
|
|
123
|
-
for (const result of this.results) {
|
|
124
|
-
lines.push(`## ${result.name}`);
|
|
125
|
-
lines.push('');
|
|
126
|
-
lines.push(`- Iterations: ${result.iterations}`);
|
|
127
|
-
lines.push(`- Total Time: ${result.totalTime.toFixed(2)}ms`);
|
|
128
|
-
lines.push(`- Average Time: ${result.avgTime.toFixed(3)}ms`);
|
|
129
|
-
lines.push(`- Min Time: ${result.minTime.toFixed(3)}ms`);
|
|
130
|
-
lines.push(`- Max Time: ${result.maxTime.toFixed(3)}ms`);
|
|
131
|
-
lines.push(`- Ops/Second: ${result.opsPerSecond.toFixed(2)}`);
|
|
132
|
-
lines.push('');
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
return lines.join('\n');
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* 获取所有结果
|
|
140
|
-
*/
|
|
141
|
-
getResults(): BenchmarkResult[] {
|
|
142
|
-
return [...this.results];
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* 清空结果
|
|
147
|
-
*/
|
|
148
|
-
clear(): void {
|
|
149
|
-
this.results = [];
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// ============================================================================
|
|
154
|
-
// 常用基准测试
|
|
155
|
-
// ============================================================================
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* 加密性能测试
|
|
159
|
-
*/
|
|
160
|
-
export async function benchmarkEncryption(
|
|
161
|
-
runner: BenchmarkRunner,
|
|
162
|
-
encryptFn: (data: string) => string,
|
|
163
|
-
decryptFn: (data: string) => string
|
|
164
|
-
): Promise<void> {
|
|
165
|
-
const testData = 'x'.repeat(1024); // 1KB 数据
|
|
166
|
-
|
|
167
|
-
await runner.run({
|
|
168
|
-
name: 'Encryption (1KB)',
|
|
169
|
-
iterations: 1000,
|
|
170
|
-
fn: () => {
|
|
171
|
-
encryptFn(testData);
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
const encrypted = encryptFn(testData);
|
|
176
|
-
await runner.run({
|
|
177
|
-
name: 'Decryption (1KB)',
|
|
178
|
-
iterations: 1000,
|
|
179
|
-
fn: () => {
|
|
180
|
-
decryptFn(encrypted);
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* 消息序列化性能测试
|
|
187
|
-
*/
|
|
188
|
-
export async function benchmarkSerialization(
|
|
189
|
-
runner: BenchmarkRunner
|
|
190
|
-
): Promise<void> {
|
|
191
|
-
const message = {
|
|
192
|
-
id: 'test-uuid',
|
|
193
|
-
type: 'TASK_REQUEST',
|
|
194
|
-
from: 'peer-123',
|
|
195
|
-
to: 'peer-456',
|
|
196
|
-
timestamp: Date.now(),
|
|
197
|
-
payload: { data: 'x'.repeat(100) }
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
await runner.run({
|
|
201
|
-
name: 'Message Serialization',
|
|
202
|
-
iterations: 10000,
|
|
203
|
-
fn: () => {
|
|
204
|
-
JSON.stringify(message);
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
const serialized = JSON.stringify(message);
|
|
209
|
-
await runner.run({
|
|
210
|
-
name: 'Message Deserialization',
|
|
211
|
-
iterations: 10000,
|
|
212
|
-
fn: () => {
|
|
213
|
-
JSON.parse(serialized);
|
|
214
|
-
}
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* 哈希计算性能测试
|
|
220
|
-
*/
|
|
221
|
-
export async function benchmarkHash(
|
|
222
|
-
runner: BenchmarkRunner,
|
|
223
|
-
hashFn: (data: string) => string
|
|
224
|
-
): Promise<void> {
|
|
225
|
-
const sizes = [100, 1000, 10000]; // 100B, 1KB, 10KB
|
|
226
|
-
|
|
227
|
-
for (const size of sizes) {
|
|
228
|
-
const data = 'x'.repeat(size);
|
|
229
|
-
await runner.run({
|
|
230
|
-
name: `Hash (${size}B)`,
|
|
231
|
-
iterations: 1000,
|
|
232
|
-
fn: () => {
|
|
233
|
-
hashFn(data);
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
}
|
package/src/utils/logger.ts
DELETED
|
@@ -1,331 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* F2A 结构化日志系统
|
|
3
|
-
* 基于 Pino 的日志实现,支持结构化输出和级别控制
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { LogLevel } from '../types/index.js';
|
|
7
|
-
import * as fs from 'fs';
|
|
8
|
-
import * as path from 'path';
|
|
9
|
-
|
|
10
|
-
// 日志级别权重
|
|
11
|
-
const LOG_LEVELS: Record<LogLevel, number> = {
|
|
12
|
-
'DEBUG': 0,
|
|
13
|
-
'INFO': 1,
|
|
14
|
-
'WARN': 2,
|
|
15
|
-
'ERROR': 3
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
// 日志条目接口
|
|
19
|
-
export interface LogEntry {
|
|
20
|
-
level: string;
|
|
21
|
-
msg: string;
|
|
22
|
-
timestamp: string;
|
|
23
|
-
component?: string;
|
|
24
|
-
[key: string]: unknown;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// 日志选项
|
|
28
|
-
export interface LoggerOptions {
|
|
29
|
-
level?: LogLevel;
|
|
30
|
-
component?: string;
|
|
31
|
-
enableConsole?: boolean;
|
|
32
|
-
enableFile?: boolean;
|
|
33
|
-
filePath?: string;
|
|
34
|
-
/** 是否使用 JSON 格式输出(生产环境推荐) */
|
|
35
|
-
jsonMode?: boolean;
|
|
36
|
-
/** 文件流重试配置 */
|
|
37
|
-
maxRetries?: number;
|
|
38
|
-
/** 重试间隔(毫秒) */
|
|
39
|
-
retryDelayMs?: number;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* 结构化日志记录器
|
|
44
|
-
*/
|
|
45
|
-
export class Logger {
|
|
46
|
-
private level: LogLevel;
|
|
47
|
-
private component: string;
|
|
48
|
-
private enableConsole: boolean;
|
|
49
|
-
private enableFile: boolean;
|
|
50
|
-
private filePath: string | undefined;
|
|
51
|
-
private jsonMode: boolean;
|
|
52
|
-
private fileStream: fs.WriteStream | undefined;
|
|
53
|
-
private maxRetries: number;
|
|
54
|
-
private retryDelayMs: number;
|
|
55
|
-
private retryCount: number = 0;
|
|
56
|
-
private isReconnecting: boolean = false;
|
|
57
|
-
|
|
58
|
-
constructor(options: LoggerOptions = {}) {
|
|
59
|
-
this.level = options.level || 'INFO';
|
|
60
|
-
this.component = options.component || 'F2A';
|
|
61
|
-
this.enableConsole = options.enableConsole !== false;
|
|
62
|
-
this.enableFile = options.enableFile ?? false;
|
|
63
|
-
this.filePath = options.filePath;
|
|
64
|
-
// 自动检测:生产环境默认使用 JSON 格式
|
|
65
|
-
this.jsonMode = options.jsonMode ?? (process.env.NODE_ENV === 'production');
|
|
66
|
-
this.maxRetries = options.maxRetries ?? 3;
|
|
67
|
-
this.retryDelayMs = options.retryDelayMs ?? 1000;
|
|
68
|
-
|
|
69
|
-
// 初始化文件流
|
|
70
|
-
if (this.enableFile && this.filePath) {
|
|
71
|
-
this.initFileStream();
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* 初始化文件写入流(带重试机制)
|
|
77
|
-
*/
|
|
78
|
-
private initFileStream(): void {
|
|
79
|
-
if (!this.filePath) return;
|
|
80
|
-
|
|
81
|
-
const attemptInit = (attempt: number): void => {
|
|
82
|
-
if (attempt > this.maxRetries) {
|
|
83
|
-
console.error(`[Logger] Failed to initialize file stream after ${this.maxRetries} attempts`);
|
|
84
|
-
this.enableFile = false;
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
// 确保目录存在
|
|
90
|
-
const dir = path.dirname(this.filePath!);
|
|
91
|
-
if (!fs.existsSync(dir)) {
|
|
92
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// 创建写入流(追加模式)
|
|
96
|
-
this.fileStream = fs.createWriteStream(this.filePath!, {
|
|
97
|
-
flags: 'a',
|
|
98
|
-
encoding: 'utf8'
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
this.fileStream.on('error', (err) => {
|
|
102
|
-
console.error(`[Logger] File stream error: ${err.message}`);
|
|
103
|
-
this.handleStreamError(err);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
this.fileStream.on('open', () => {
|
|
107
|
-
// 成功打开,重置重试计数
|
|
108
|
-
this.retryCount = 0;
|
|
109
|
-
this.isReconnecting = false;
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
this.fileStream.on('close', () => {
|
|
113
|
-
// 流关闭,如果需要文件日志则尝试重连
|
|
114
|
-
if (this.enableFile && !this.isReconnecting) {
|
|
115
|
-
this.scheduleReconnect();
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
} catch (err) {
|
|
120
|
-
console.error(`[Logger] Failed to initialize file stream (attempt ${attempt}/${this.maxRetries}): ${err}`);
|
|
121
|
-
this.retryCount = attempt;
|
|
122
|
-
|
|
123
|
-
// 延迟后重试
|
|
124
|
-
setTimeout(() => attemptInit(attempt + 1), this.retryDelayMs);
|
|
125
|
-
}
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
attemptInit(1);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* 处理流错误
|
|
133
|
-
*/
|
|
134
|
-
private handleStreamError(err: Error): void {
|
|
135
|
-
// 检查是否是可恢复的错误
|
|
136
|
-
const isRecoverable =
|
|
137
|
-
err.message.includes('ENOENT') || // 文件不存在
|
|
138
|
-
err.message.includes('EACCES') || // 权限问题(可能临时)
|
|
139
|
-
err.message.includes('EMFILE') || // 文件描述符不足
|
|
140
|
-
err.message.includes('ENOSPC'); // 磁盘空间不足
|
|
141
|
-
|
|
142
|
-
if (isRecoverable && this.retryCount < this.maxRetries) {
|
|
143
|
-
this.scheduleReconnect();
|
|
144
|
-
} else {
|
|
145
|
-
// 不可恢复或超过重试次数,降级到控制台
|
|
146
|
-
console.warn(`[Logger] File logging disabled due to unrecoverable error: ${err.message}`);
|
|
147
|
-
this.fileStream = undefined;
|
|
148
|
-
this.enableFile = false;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* 调度重连
|
|
154
|
-
*/
|
|
155
|
-
private scheduleReconnect(): void {
|
|
156
|
-
if (this.isReconnecting) return;
|
|
157
|
-
|
|
158
|
-
this.isReconnecting = true;
|
|
159
|
-
this.retryCount++;
|
|
160
|
-
|
|
161
|
-
if (this.retryCount > this.maxRetries) {
|
|
162
|
-
console.warn(`[Logger] Max reconnection attempts (${this.maxRetries}) reached, file logging disabled`);
|
|
163
|
-
this.enableFile = false;
|
|
164
|
-
this.isReconnecting = false;
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
console.log(`[Logger] Attempting to reconnect file stream (attempt ${this.retryCount}/${this.maxRetries})...`);
|
|
169
|
-
|
|
170
|
-
// 关闭旧流
|
|
171
|
-
if (this.fileStream) {
|
|
172
|
-
try {
|
|
173
|
-
this.fileStream.destroy();
|
|
174
|
-
} catch { /* ignore */ }
|
|
175
|
-
this.fileStream = undefined;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// 延迟后重试
|
|
179
|
-
setTimeout(() => {
|
|
180
|
-
this.isReconnecting = false;
|
|
181
|
-
this.initFileStream();
|
|
182
|
-
}, this.retryDelayMs * this.retryCount); // 指数退避
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* 检查日志级别是否启用
|
|
187
|
-
*/
|
|
188
|
-
private isLevelEnabled(level: LogLevel): boolean {
|
|
189
|
-
return LOG_LEVELS[level] >= LOG_LEVELS[this.level];
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* 格式化日志条目
|
|
194
|
-
*/
|
|
195
|
-
private formatLog(level: string, message: string, meta?: Record<string, unknown>): LogEntry {
|
|
196
|
-
return {
|
|
197
|
-
level,
|
|
198
|
-
msg: message,
|
|
199
|
-
timestamp: new Date().toISOString(),
|
|
200
|
-
component: this.component,
|
|
201
|
-
...meta
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* 输出日志
|
|
207
|
-
*/
|
|
208
|
-
private output(entry: LogEntry): void {
|
|
209
|
-
const jsonLine = JSON.stringify(entry);
|
|
210
|
-
|
|
211
|
-
// 控制台输出
|
|
212
|
-
if (this.enableConsole) {
|
|
213
|
-
if (this.jsonMode) {
|
|
214
|
-
// 生产环境:JSON 一行,便于日志系统收集
|
|
215
|
-
console.log(jsonLine);
|
|
216
|
-
} else {
|
|
217
|
-
// 开发环境:人类可读
|
|
218
|
-
const { level, msg, timestamp, component, ...meta } = entry;
|
|
219
|
-
const prefix = `[${timestamp.split('T')[1].split('.')[0]}] [${component}] [${level}]`;
|
|
220
|
-
|
|
221
|
-
if (Object.keys(meta).length > 0) {
|
|
222
|
-
// 结构化输出(开发环境可读格式)
|
|
223
|
-
console.log(`${prefix} ${msg}`, meta);
|
|
224
|
-
} else {
|
|
225
|
-
console.log(`${prefix} ${msg}`);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// 文件输出
|
|
231
|
-
if (this.enableFile && this.fileStream && this.fileStream.writable) {
|
|
232
|
-
this.fileStream.write(jsonLine + '\n');
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* 记录 DEBUG 级别日志
|
|
238
|
-
*/
|
|
239
|
-
debug(message: string, meta?: Record<string, unknown>): void {
|
|
240
|
-
if (!this.isLevelEnabled('DEBUG')) return;
|
|
241
|
-
this.output(this.formatLog('DEBUG', message, meta));
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* 记录 INFO 级别日志
|
|
246
|
-
*/
|
|
247
|
-
info(message: string, meta?: Record<string, unknown>): void {
|
|
248
|
-
if (!this.isLevelEnabled('INFO')) return;
|
|
249
|
-
this.output(this.formatLog('INFO', message, meta));
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* 记录 WARN 级别日志
|
|
254
|
-
*/
|
|
255
|
-
warn(message: string, meta?: Record<string, unknown>): void {
|
|
256
|
-
if (!this.isLevelEnabled('WARN')) return;
|
|
257
|
-
this.output(this.formatLog('WARN', message, meta));
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* 记录 ERROR 级别日志
|
|
262
|
-
*/
|
|
263
|
-
error(message: string, meta?: Record<string, unknown>): void {
|
|
264
|
-
if (!this.isLevelEnabled('ERROR')) return;
|
|
265
|
-
this.output(this.formatLog('ERROR', message, meta));
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* 设置日志级别
|
|
270
|
-
*/
|
|
271
|
-
setLevel(level: LogLevel): void {
|
|
272
|
-
this.level = level;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* 获取当前日志级别
|
|
277
|
-
*/
|
|
278
|
-
getLevel(): LogLevel {
|
|
279
|
-
return this.level;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* 启用文件日志
|
|
284
|
-
*/
|
|
285
|
-
setFileLogging(enabled: boolean, filePath?: string): void {
|
|
286
|
-
// 关闭现有流
|
|
287
|
-
if (this.fileStream) {
|
|
288
|
-
this.fileStream.end();
|
|
289
|
-
this.fileStream = undefined;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
this.enableFile = enabled;
|
|
293
|
-
if (filePath) {
|
|
294
|
-
this.filePath = filePath;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
if (enabled && this.filePath) {
|
|
298
|
-
this.initFileStream();
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* 创建子日志记录器
|
|
304
|
-
*/
|
|
305
|
-
child(component: string): Logger {
|
|
306
|
-
return new Logger({
|
|
307
|
-
level: this.level,
|
|
308
|
-
component: `${this.component}:${component}`,
|
|
309
|
-
enableConsole: this.enableConsole,
|
|
310
|
-
enableFile: this.enableFile,
|
|
311
|
-
filePath: this.filePath,
|
|
312
|
-
jsonMode: this.jsonMode
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
/**
|
|
317
|
-
* 关闭日志记录器,释放资源
|
|
318
|
-
*/
|
|
319
|
-
close(): void {
|
|
320
|
-
if (this.fileStream) {
|
|
321
|
-
this.fileStream.end();
|
|
322
|
-
this.fileStream = undefined;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// 默认日志记录器实例
|
|
328
|
-
export const defaultLogger = new Logger({ component: 'F2A' });
|
|
329
|
-
|
|
330
|
-
// 便捷导出
|
|
331
|
-
export const logger = defaultLogger;
|