gthinking 1.2.1 → 2.1.1
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/.eslintrc.js +34 -0
- package/ANALYSIS_SUMMARY.md +363 -0
- package/README.md +230 -245
- package/dist/analysis/analysis-engine.d.ts +63 -0
- package/dist/analysis/analysis-engine.d.ts.map +1 -0
- package/dist/analysis/analysis-engine.js +322 -0
- package/dist/analysis/analysis-engine.js.map +1 -0
- package/dist/core/config.d.ts +1419 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +361 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/engine.d.ts +176 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +604 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/errors.d.ts +153 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +287 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/{types.js → core/index.js} +8 -4
- package/dist/core/index.js.map +1 -0
- package/dist/core/pipeline.d.ts +121 -0
- package/dist/core/pipeline.d.ts.map +1 -0
- package/dist/core/pipeline.js +289 -0
- package/dist/core/pipeline.js.map +1 -0
- package/dist/core/rate-limiter.d.ts +58 -0
- package/dist/core/rate-limiter.d.ts.map +1 -0
- package/dist/core/rate-limiter.js +133 -0
- package/dist/core/rate-limiter.js.map +1 -0
- package/dist/core/session-manager.d.ts +96 -0
- package/dist/core/session-manager.d.ts.map +1 -0
- package/dist/core/session-manager.js +223 -0
- package/dist/core/session-manager.js.map +1 -0
- package/dist/creativity/creativity-engine.d.ts +6 -0
- package/dist/creativity/creativity-engine.d.ts.map +1 -0
- package/dist/creativity/creativity-engine.js +17 -0
- package/dist/creativity/creativity-engine.js.map +1 -0
- package/dist/index.d.ts +24 -32
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +130 -104
- package/dist/index.js.map +1 -1
- package/dist/learning/learning-engine.d.ts +6 -0
- package/dist/learning/learning-engine.d.ts.map +1 -0
- package/dist/learning/learning-engine.js +17 -0
- package/dist/learning/learning-engine.js.map +1 -0
- package/dist/llm/index.d.ts +10 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/llm/index.js +26 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/llm/llm-service.d.ts +109 -0
- package/dist/llm/llm-service.d.ts.map +1 -0
- package/dist/llm/llm-service.js +224 -0
- package/dist/llm/llm-service.js.map +1 -0
- package/dist/llm/providers/base.d.ts +85 -0
- package/dist/llm/providers/base.d.ts.map +1 -0
- package/dist/llm/providers/base.js +57 -0
- package/dist/llm/providers/base.js.map +1 -0
- package/dist/llm/providers/cli.d.ts +23 -0
- package/dist/llm/providers/cli.d.ts.map +1 -0
- package/dist/llm/providers/cli.js +158 -0
- package/dist/llm/providers/cli.js.map +1 -0
- package/dist/llm/providers/gemini.d.ts +30 -0
- package/dist/llm/providers/gemini.d.ts.map +1 -0
- package/dist/llm/providers/gemini.js +168 -0
- package/dist/llm/providers/gemini.js.map +1 -0
- package/dist/llm/sanitization.d.ts +50 -0
- package/dist/llm/sanitization.d.ts.map +1 -0
- package/dist/llm/sanitization.js +149 -0
- package/dist/llm/sanitization.js.map +1 -0
- package/dist/{server.d.ts.map → mcp/server.d.ts.map} +1 -1
- package/dist/mcp/server.js +108 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/planning/planning-engine.d.ts +6 -0
- package/dist/planning/planning-engine.d.ts.map +1 -0
- package/dist/planning/planning-engine.js +17 -0
- package/dist/planning/planning-engine.js.map +1 -0
- package/dist/reasoning/reasoning-engine.d.ts +6 -0
- package/dist/reasoning/reasoning-engine.d.ts.map +1 -0
- package/dist/reasoning/reasoning-engine.js +17 -0
- package/dist/reasoning/reasoning-engine.js.map +1 -0
- package/dist/search/search-engine.d.ts +99 -0
- package/dist/search/search-engine.d.ts.map +1 -0
- package/dist/search/search-engine.js +271 -0
- package/dist/search/search-engine.js.map +1 -0
- package/dist/synthesis/synthesis-engine.d.ts +6 -0
- package/dist/synthesis/synthesis-engine.d.ts.map +1 -0
- package/dist/synthesis/synthesis-engine.js +17 -0
- package/dist/synthesis/synthesis-engine.js.map +1 -0
- package/dist/types/analysis.d.ts +1534 -49
- package/dist/types/analysis.d.ts.map +1 -1
- package/dist/types/analysis.js +250 -0
- package/dist/types/analysis.js.map +1 -1
- package/dist/types/core.d.ts +257 -30
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/core.js +148 -18
- package/dist/types/core.js.map +1 -1
- package/dist/types/creativity.d.ts +2871 -56
- package/dist/types/creativity.d.ts.map +1 -1
- package/dist/types/creativity.js +195 -0
- package/dist/types/creativity.js.map +1 -1
- package/dist/types/index.d.ts +6 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +17 -2
- package/dist/types/index.js.map +1 -1
- package/dist/types/learning.d.ts +851 -61
- package/dist/types/learning.d.ts.map +1 -1
- package/dist/types/learning.js +155 -0
- package/dist/types/learning.js.map +1 -1
- package/dist/types/planning.d.ts +2223 -71
- package/dist/types/planning.d.ts.map +1 -1
- package/dist/types/planning.js +190 -0
- package/dist/types/planning.js.map +1 -1
- package/dist/types/reasoning.d.ts +2209 -72
- package/dist/types/reasoning.d.ts.map +1 -1
- package/dist/types/reasoning.js +200 -1
- package/dist/types/reasoning.js.map +1 -1
- package/dist/types/search.d.ts +981 -53
- package/dist/types/search.d.ts.map +1 -1
- package/dist/types/search.js +137 -0
- package/dist/types/search.js.map +1 -1
- package/dist/types/synthesis.d.ts +583 -37
- package/dist/types/synthesis.d.ts.map +1 -1
- package/dist/types/synthesis.js +138 -0
- package/dist/types/synthesis.js.map +1 -1
- package/dist/utils/cache.d.ts +144 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +288 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/id-generator.d.ts +89 -0
- package/dist/utils/id-generator.d.ts.map +1 -0
- package/dist/utils/id-generator.js +132 -0
- package/dist/utils/id-generator.js.map +1 -0
- package/dist/utils/index.d.ts +11 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +33 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +142 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +248 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/metrics.d.ts +149 -0
- package/dist/utils/metrics.d.ts.map +1 -0
- package/dist/utils/metrics.js +296 -0
- package/dist/utils/metrics.js.map +1 -0
- package/dist/utils/timer.d.ts +7 -0
- package/dist/utils/timer.d.ts.map +1 -0
- package/dist/utils/timer.js +17 -0
- package/dist/utils/timer.js.map +1 -0
- package/dist/utils/validation.d.ts +147 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +275 -0
- package/dist/utils/validation.js.map +1 -0
- package/docs/API.md +411 -0
- package/docs/ARCHITECTURE.md +271 -0
- package/docs/CHANGELOG.md +283 -0
- package/jest.config.js +28 -0
- package/package.json +43 -30
- package/src/analysis/analysis-engine.ts +383 -0
- package/src/core/config.ts +406 -0
- package/src/core/engine.ts +785 -0
- package/src/core/errors.ts +349 -0
- package/src/core/index.ts +12 -0
- package/src/core/pipeline.ts +424 -0
- package/src/core/rate-limiter.ts +155 -0
- package/src/core/session-manager.ts +269 -0
- package/src/creativity/creativity-engine.ts +14 -0
- package/src/index.ts +178 -0
- package/src/learning/learning-engine.ts +14 -0
- package/src/llm/index.ts +10 -0
- package/src/llm/llm-service.ts +285 -0
- package/src/llm/providers/base.ts +146 -0
- package/src/llm/providers/cli.ts +186 -0
- package/src/llm/providers/gemini.ts +201 -0
- package/src/llm/sanitization.ts +178 -0
- package/src/mcp/server.ts +117 -0
- package/src/planning/planning-engine.ts +14 -0
- package/src/reasoning/reasoning-engine.ts +14 -0
- package/src/search/search-engine.ts +333 -0
- package/src/synthesis/synthesis-engine.ts +14 -0
- package/src/types/analysis.ts +337 -0
- package/src/types/core.ts +342 -0
- package/src/types/creativity.ts +268 -0
- package/src/types/index.ts +31 -0
- package/src/types/learning.ts +215 -0
- package/src/types/planning.ts +251 -0
- package/src/types/reasoning.ts +288 -0
- package/src/types/search.ts +192 -0
- package/src/types/synthesis.ts +187 -0
- package/src/utils/cache.ts +363 -0
- package/src/utils/id-generator.ts +135 -0
- package/src/utils/index.ts +22 -0
- package/src/utils/logger.ts +290 -0
- package/src/utils/metrics.ts +380 -0
- package/src/utils/timer.ts +15 -0
- package/src/utils/validation.ts +297 -0
- package/tests/setup.ts +22 -0
- package/tests/unit/cache.test.ts +189 -0
- package/tests/unit/engine.test.ts +179 -0
- package/tests/unit/validation.test.ts +218 -0
- package/tsconfig.json +17 -12
- package/GEMINI.md +0 -68
- package/analysis.ts +0 -1063
- package/creativity.ts +0 -1055
- package/dist/analysis.d.ts +0 -54
- package/dist/analysis.d.ts.map +0 -1
- package/dist/analysis.js +0 -866
- package/dist/analysis.js.map +0 -1
- package/dist/creativity.d.ts +0 -81
- package/dist/creativity.d.ts.map +0 -1
- package/dist/creativity.js +0 -828
- package/dist/creativity.js.map +0 -1
- package/dist/engine.d.ts +0 -90
- package/dist/engine.d.ts.map +0 -1
- package/dist/engine.js +0 -677
- package/dist/engine.js.map +0 -1
- package/dist/examples.d.ts +0 -7
- package/dist/examples.d.ts.map +0 -1
- package/dist/examples.js +0 -506
- package/dist/examples.js.map +0 -1
- package/dist/learning.d.ts +0 -72
- package/dist/learning.d.ts.map +0 -1
- package/dist/learning.js +0 -615
- package/dist/learning.js.map +0 -1
- package/dist/llm-service.d.ts +0 -21
- package/dist/llm-service.d.ts.map +0 -1
- package/dist/llm-service.js +0 -100
- package/dist/llm-service.js.map +0 -1
- package/dist/planning.d.ts +0 -58
- package/dist/planning.d.ts.map +0 -1
- package/dist/planning.js +0 -824
- package/dist/planning.js.map +0 -1
- package/dist/reasoning.d.ts +0 -73
- package/dist/reasoning.d.ts.map +0 -1
- package/dist/reasoning.js +0 -845
- package/dist/reasoning.js.map +0 -1
- package/dist/search-discovery.d.ts +0 -73
- package/dist/search-discovery.d.ts.map +0 -1
- package/dist/search-discovery.js +0 -548
- package/dist/search-discovery.js.map +0 -1
- package/dist/server.js +0 -113
- package/dist/server.js.map +0 -1
- package/dist/types/engine.d.ts +0 -55
- package/dist/types/engine.d.ts.map +0 -1
- package/dist/types/engine.js +0 -3
- package/dist/types/engine.js.map +0 -1
- package/dist/types.d.ts +0 -6
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/engine.ts +0 -947
- package/examples.ts +0 -717
- package/index.ts +0 -106
- package/learning.ts +0 -779
- package/llm-service.ts +0 -120
- package/planning.ts +0 -1028
- package/reasoning.ts +0 -1079
- package/search-discovery.ts +0 -700
- package/server.ts +0 -115
- package/types/analysis.ts +0 -69
- package/types/core.ts +0 -90
- package/types/creativity.ts +0 -72
- package/types/engine.ts +0 -60
- package/types/index.ts +0 -9
- package/types/learning.ts +0 -69
- package/types/planning.ts +0 -85
- package/types/reasoning.ts +0 -92
- package/types/search.ts +0 -58
- package/types/synthesis.ts +0 -42
- package/types.ts +0 -6
- /package/dist/{server.d.ts → mcp/server.d.ts} +0 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger Utility for gthinking v2.0.0
|
|
3
|
+
* Structured logging with Winston
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import winston from 'winston';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Log levels
|
|
10
|
+
*/
|
|
11
|
+
export const LogLevel = {
|
|
12
|
+
ERROR: 'error',
|
|
13
|
+
WARN: 'warn',
|
|
14
|
+
INFO: 'info',
|
|
15
|
+
DEBUG: 'debug',
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
export type LogLevelType = typeof LogLevel[keyof typeof LogLevel];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Logger configuration interface
|
|
22
|
+
*/
|
|
23
|
+
export interface LoggerConfig {
|
|
24
|
+
level: LogLevelType;
|
|
25
|
+
enableConsole: boolean;
|
|
26
|
+
enableFile: boolean;
|
|
27
|
+
logDir?: string;
|
|
28
|
+
maxFiles?: number;
|
|
29
|
+
maxSize?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Default logger configuration
|
|
34
|
+
*/
|
|
35
|
+
const defaultConfig: LoggerConfig = {
|
|
36
|
+
level: 'info',
|
|
37
|
+
enableConsole: true,
|
|
38
|
+
enableFile: false,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Create a Winston logger instance
|
|
43
|
+
* @param config - Logger configuration
|
|
44
|
+
* @returns Winston logger instance
|
|
45
|
+
*/
|
|
46
|
+
export function createLogger(config: Partial<LoggerConfig> = {}): winston.Logger {
|
|
47
|
+
const mergedConfig = { ...defaultConfig, ...config };
|
|
48
|
+
|
|
49
|
+
const transports: winston.transport[] = [];
|
|
50
|
+
|
|
51
|
+
if (mergedConfig.enableConsole) {
|
|
52
|
+
transports.push(new winston.transports.Console({
|
|
53
|
+
format: winston.format.combine(
|
|
54
|
+
winston.format.colorize(),
|
|
55
|
+
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
|
|
56
|
+
winston.format.printf(({ level, message, timestamp, ...metadata }) => {
|
|
57
|
+
let msg = `${timestamp} [${level}]: ${message}`;
|
|
58
|
+
if (Object.keys(metadata).length > 0) {
|
|
59
|
+
msg += ` ${JSON.stringify(metadata)}`;
|
|
60
|
+
}
|
|
61
|
+
return msg;
|
|
62
|
+
})
|
|
63
|
+
),
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (mergedConfig.enableFile && mergedConfig.logDir) {
|
|
68
|
+
transports.push(new winston.transports.File({
|
|
69
|
+
filename: `${mergedConfig.logDir}/error.log`,
|
|
70
|
+
level: 'error',
|
|
71
|
+
maxFiles: mergedConfig.maxFiles || 5,
|
|
72
|
+
maxsize: (mergedConfig.maxSize || '10m') as any,
|
|
73
|
+
format: winston.format.combine(
|
|
74
|
+
winston.format.timestamp(),
|
|
75
|
+
winston.format.json()
|
|
76
|
+
),
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
transports.push(new winston.transports.File({
|
|
80
|
+
filename: `${mergedConfig.logDir}/combined.log`,
|
|
81
|
+
maxFiles: mergedConfig.maxFiles || 5,
|
|
82
|
+
maxsize: (mergedConfig.maxSize || '10m') as any,
|
|
83
|
+
format: winston.format.combine(
|
|
84
|
+
winston.format.timestamp(),
|
|
85
|
+
winston.format.json()
|
|
86
|
+
),
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return winston.createLogger({
|
|
91
|
+
level: mergedConfig.level,
|
|
92
|
+
defaultMeta: { service: 'gthinking' },
|
|
93
|
+
transports,
|
|
94
|
+
exitOnError: false,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Create default logger instance
|
|
99
|
+
let defaultLogger: winston.Logger = createLogger();
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Set the default logger
|
|
103
|
+
* @param logger - The logger to set as default
|
|
104
|
+
*/
|
|
105
|
+
export function setDefaultLogger(logger: winston.Logger): void {
|
|
106
|
+
defaultLogger = logger;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get the default logger
|
|
111
|
+
* @returns The default logger instance
|
|
112
|
+
*/
|
|
113
|
+
export function getDefaultLogger(): winston.Logger {
|
|
114
|
+
return defaultLogger;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Log an error message
|
|
119
|
+
* @param message - The error message
|
|
120
|
+
* @param metadata - Additional metadata
|
|
121
|
+
*/
|
|
122
|
+
export function error(message: string, metadata: Record<string, unknown> = {}): void {
|
|
123
|
+
defaultLogger.error(message, metadata);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Log a warning message
|
|
128
|
+
* @param message - The warning message
|
|
129
|
+
* @param metadata - Additional metadata
|
|
130
|
+
*/
|
|
131
|
+
export function warn(message: string, metadata: Record<string, unknown> = {}): void {
|
|
132
|
+
defaultLogger.warn(message, metadata);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Log an info message
|
|
137
|
+
* @param message - The info message
|
|
138
|
+
* @param metadata - Additional metadata
|
|
139
|
+
*/
|
|
140
|
+
export function info(message: string, metadata: Record<string, unknown> = {}): void {
|
|
141
|
+
defaultLogger.info(message, metadata);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Log a debug message
|
|
146
|
+
* @param message - The debug message
|
|
147
|
+
* @param metadata - Additional metadata
|
|
148
|
+
*/
|
|
149
|
+
export function debug(message: string, metadata: Record<string, unknown> = {}): void {
|
|
150
|
+
defaultLogger.debug(message, metadata);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Create a child logger with default metadata
|
|
155
|
+
* @param defaultMetadata - Default metadata to include
|
|
156
|
+
* @returns Child logger
|
|
157
|
+
*/
|
|
158
|
+
export function createChildLogger(defaultMetadata: Record<string, unknown>): winston.Logger {
|
|
159
|
+
return defaultLogger.child(defaultMetadata);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Logger class for component-specific logging
|
|
164
|
+
*/
|
|
165
|
+
export class Logger {
|
|
166
|
+
private logger: winston.Logger;
|
|
167
|
+
private component: string;
|
|
168
|
+
|
|
169
|
+
constructor(component: string, config?: Partial<LoggerConfig>) {
|
|
170
|
+
this.component = component;
|
|
171
|
+
this.logger = createLogger(config);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Log an error message
|
|
176
|
+
*/
|
|
177
|
+
public error(message: string, metadata: Record<string, unknown> = {}): void {
|
|
178
|
+
this.logger.error(message, { component: this.component, ...metadata });
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Log a warning message
|
|
183
|
+
*/
|
|
184
|
+
public warn(message: string, metadata: Record<string, unknown> = {}): void {
|
|
185
|
+
this.logger.warn(message, { component: this.component, ...metadata });
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Log an info message
|
|
190
|
+
*/
|
|
191
|
+
public info(message: string, metadata: Record<string, unknown> = {}): void {
|
|
192
|
+
this.logger.info(message, { component: this.component, ...metadata });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Log a debug message
|
|
197
|
+
*/
|
|
198
|
+
public debug(message: string, metadata: Record<string, unknown> = {}): void {
|
|
199
|
+
this.logger.debug(message, { component: this.component, ...metadata });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Create a child logger
|
|
204
|
+
*/
|
|
205
|
+
public child(metadata: Record<string, unknown>): winston.Logger {
|
|
206
|
+
return this.logger.child({ component: this.component, ...metadata });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Performance logger for tracking operation performance
|
|
212
|
+
*/
|
|
213
|
+
export class PerformanceLogger {
|
|
214
|
+
private logger: winston.Logger;
|
|
215
|
+
private timings: Map<string, number> = new Map();
|
|
216
|
+
|
|
217
|
+
constructor() {
|
|
218
|
+
this.logger = createLogger({ level: 'debug' });
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Start timing an operation
|
|
223
|
+
* @param operation - The operation name
|
|
224
|
+
*/
|
|
225
|
+
public start(operation: string): void {
|
|
226
|
+
this.timings.set(operation, Date.now());
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* End timing an operation and log the duration
|
|
231
|
+
* @param operation - The operation name
|
|
232
|
+
* @param metadata - Additional metadata
|
|
233
|
+
*/
|
|
234
|
+
public end(operation: string, metadata: Record<string, unknown> = {}): number {
|
|
235
|
+
const startTime = this.timings.get(operation);
|
|
236
|
+
if (!startTime) {
|
|
237
|
+
this.logger.warn(`No start time found for operation: ${operation}`);
|
|
238
|
+
return 0;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const duration = Date.now() - startTime;
|
|
242
|
+
this.timings.delete(operation);
|
|
243
|
+
|
|
244
|
+
this.logger.debug(`Operation ${operation} completed`, {
|
|
245
|
+
operation,
|
|
246
|
+
duration,
|
|
247
|
+
...metadata,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
return duration;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Log a performance metric
|
|
255
|
+
* @param metric - The metric name
|
|
256
|
+
* @param value - The metric value
|
|
257
|
+
* @param metadata - Additional metadata
|
|
258
|
+
*/
|
|
259
|
+
public logMetric(metric: string, value: number, metadata: Record<string, unknown> = {}): void {
|
|
260
|
+
this.logger.debug(`Performance metric: ${metric}`, {
|
|
261
|
+
metric,
|
|
262
|
+
value,
|
|
263
|
+
...metadata,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Clear all timings
|
|
269
|
+
*/
|
|
270
|
+
public clear(): void {
|
|
271
|
+
this.timings.clear();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Export a singleton performance logger
|
|
276
|
+
export const performanceLogger = new PerformanceLogger();
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Default logger instance
|
|
280
|
+
*/
|
|
281
|
+
export const logger = defaultLogger;
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Create a component logger
|
|
285
|
+
* @param component - Component name
|
|
286
|
+
* @returns Logger instance
|
|
287
|
+
*/
|
|
288
|
+
export function createComponentLogger(component: string): Logger {
|
|
289
|
+
return new Logger(component);
|
|
290
|
+
}
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metrics Utility for gthinking v2.0.0
|
|
3
|
+
* Performance metrics and monitoring
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { EventEmitter } from 'events';
|
|
7
|
+
import { Logger } from './logger';
|
|
8
|
+
|
|
9
|
+
const logger = new Logger('Metrics');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Metric types
|
|
13
|
+
*/
|
|
14
|
+
export type MetricType = 'counter' | 'gauge' | 'histogram' | 'timer';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Metric value type
|
|
18
|
+
*/
|
|
19
|
+
export type MetricValue = number | Record<string, number>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Metric interface
|
|
23
|
+
*/
|
|
24
|
+
export interface Metric {
|
|
25
|
+
name: string;
|
|
26
|
+
type: MetricType;
|
|
27
|
+
value: MetricValue;
|
|
28
|
+
labels: Record<string, string>;
|
|
29
|
+
timestamp: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Timer metric interface
|
|
34
|
+
*/
|
|
35
|
+
export interface TimerMetric {
|
|
36
|
+
startTime: number;
|
|
37
|
+
endTime?: number;
|
|
38
|
+
duration?: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Histogram bucket interface
|
|
43
|
+
*/
|
|
44
|
+
export interface HistogramBucket {
|
|
45
|
+
upperBound: number;
|
|
46
|
+
count: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Metrics collector class
|
|
51
|
+
*/
|
|
52
|
+
export class MetricsCollector extends EventEmitter {
|
|
53
|
+
private metrics: Map<string, Metric> = new Map();
|
|
54
|
+
private timers: Map<string, TimerMetric> = new Map();
|
|
55
|
+
private histograms: Map<string, number[]> = new Map();
|
|
56
|
+
private enabled = true;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Enable or disable metrics collection
|
|
60
|
+
* @param enabled - Whether to enable metrics
|
|
61
|
+
*/
|
|
62
|
+
public setEnabled(enabled: boolean): void {
|
|
63
|
+
this.enabled = enabled;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Record a counter metric
|
|
68
|
+
* @param name - Metric name
|
|
69
|
+
* @param value - Counter value (default: 1)
|
|
70
|
+
* @param labels - Metric labels
|
|
71
|
+
*/
|
|
72
|
+
public counter(name: string, value = 1, labels: Record<string, string> = {}): void {
|
|
73
|
+
if (!this.enabled) return;
|
|
74
|
+
|
|
75
|
+
const key = this.getMetricKey(name, labels);
|
|
76
|
+
const existing = this.metrics.get(key);
|
|
77
|
+
|
|
78
|
+
if (existing && existing.type === 'counter') {
|
|
79
|
+
existing.value = (existing.value as number) + value;
|
|
80
|
+
existing.timestamp = Date.now();
|
|
81
|
+
} else {
|
|
82
|
+
this.metrics.set(key, {
|
|
83
|
+
name,
|
|
84
|
+
type: 'counter',
|
|
85
|
+
value,
|
|
86
|
+
labels,
|
|
87
|
+
timestamp: Date.now(),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Record a gauge metric
|
|
94
|
+
* @param name - Metric name
|
|
95
|
+
* @param value - Gauge value
|
|
96
|
+
* @param labels - Metric labels
|
|
97
|
+
*/
|
|
98
|
+
public gauge(name: string, value: number, labels: Record<string, string> = {}): void {
|
|
99
|
+
if (!this.enabled) return;
|
|
100
|
+
|
|
101
|
+
const key = this.getMetricKey(name, labels);
|
|
102
|
+
this.metrics.set(key, {
|
|
103
|
+
name,
|
|
104
|
+
type: 'gauge',
|
|
105
|
+
value,
|
|
106
|
+
labels,
|
|
107
|
+
timestamp: Date.now(),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Start a timer
|
|
113
|
+
* @param name - Timer name
|
|
114
|
+
* @param labels - Timer labels
|
|
115
|
+
*/
|
|
116
|
+
public startTimer(name: string, labels: Record<string, string> = {}): string {
|
|
117
|
+
const key = this.getMetricKey(name, labels);
|
|
118
|
+
this.timers.set(key, { startTime: Date.now() });
|
|
119
|
+
return key;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* End a timer and record the duration
|
|
124
|
+
* @param key - Timer key returned by startTimer
|
|
125
|
+
* @returns Duration in milliseconds
|
|
126
|
+
*/
|
|
127
|
+
public endTimer(key: string): number {
|
|
128
|
+
const timer = this.timers.get(key);
|
|
129
|
+
if (!timer) {
|
|
130
|
+
logger.warn(`Timer not found: ${key}`);
|
|
131
|
+
return 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
timer.endTime = Date.now();
|
|
135
|
+
timer.duration = timer.endTime - timer.startTime;
|
|
136
|
+
this.timers.delete(key);
|
|
137
|
+
|
|
138
|
+
// Record as histogram
|
|
139
|
+
this.histogram(key, timer.duration);
|
|
140
|
+
|
|
141
|
+
return timer.duration;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Record a histogram value
|
|
146
|
+
* @param name - Histogram name
|
|
147
|
+
* @param value - Value to record
|
|
148
|
+
* @param labels - Histogram labels
|
|
149
|
+
*/
|
|
150
|
+
public histogram(name: string, value: number, labels: Record<string, string> = {}): void {
|
|
151
|
+
if (!this.enabled) return;
|
|
152
|
+
|
|
153
|
+
const key = this.getMetricKey(name, labels);
|
|
154
|
+
const existing = this.histograms.get(key);
|
|
155
|
+
|
|
156
|
+
if (existing) {
|
|
157
|
+
existing.push(value);
|
|
158
|
+
} else {
|
|
159
|
+
this.histograms.set(key, [value]);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Get all metrics
|
|
165
|
+
* @returns Array of all metrics
|
|
166
|
+
*/
|
|
167
|
+
public getMetrics(): Metric[] {
|
|
168
|
+
return Array.from(this.metrics.values());
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Get metrics by name
|
|
173
|
+
* @param name - Metric name
|
|
174
|
+
* @returns Array of metrics with the given name
|
|
175
|
+
*/
|
|
176
|
+
public getMetricsByName(name: string): Metric[] {
|
|
177
|
+
return this.getMetrics().filter(m => m.name === name);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Get histogram statistics
|
|
182
|
+
* @param name - Histogram name
|
|
183
|
+
* @param labels - Histogram labels
|
|
184
|
+
* @returns Histogram statistics
|
|
185
|
+
*/
|
|
186
|
+
public getHistogramStats(name: string, labels: Record<string, string> = {}): {
|
|
187
|
+
count: number;
|
|
188
|
+
sum: number;
|
|
189
|
+
min: number;
|
|
190
|
+
max: number;
|
|
191
|
+
mean: number;
|
|
192
|
+
p50: number;
|
|
193
|
+
p95: number;
|
|
194
|
+
p99: number;
|
|
195
|
+
} | null {
|
|
196
|
+
const key = this.getMetricKey(name, labels);
|
|
197
|
+
const values = this.histograms.get(key);
|
|
198
|
+
|
|
199
|
+
if (!values || values.length === 0) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
204
|
+
const count = sorted.length;
|
|
205
|
+
const sum = sorted.reduce((a, b) => a + b, 0);
|
|
206
|
+
const min = sorted[0];
|
|
207
|
+
const max = sorted[count - 1];
|
|
208
|
+
const mean = sum / count;
|
|
209
|
+
|
|
210
|
+
const percentile = (p: number): number => {
|
|
211
|
+
const index = Math.ceil((p / 100) * count) - 1;
|
|
212
|
+
return sorted[Math.max(0, index)];
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
count,
|
|
217
|
+
sum,
|
|
218
|
+
min,
|
|
219
|
+
max,
|
|
220
|
+
mean,
|
|
221
|
+
p50: percentile(50),
|
|
222
|
+
p95: percentile(95),
|
|
223
|
+
p99: percentile(99),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Clear all metrics
|
|
229
|
+
*/
|
|
230
|
+
public clear(): void {
|
|
231
|
+
this.metrics.clear();
|
|
232
|
+
this.timers.clear();
|
|
233
|
+
this.histograms.clear();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Export metrics in Prometheus format
|
|
238
|
+
* @returns Metrics in Prometheus format
|
|
239
|
+
*/
|
|
240
|
+
public exportPrometheus(): string {
|
|
241
|
+
const lines: string[] = [];
|
|
242
|
+
|
|
243
|
+
for (const metric of this.metrics.values()) {
|
|
244
|
+
const labels = Object.entries(metric.labels)
|
|
245
|
+
.map(([k, v]) => `${k}="${v}"`)
|
|
246
|
+
.join(',');
|
|
247
|
+
|
|
248
|
+
const labelStr = labels ? `{${labels}}` : '';
|
|
249
|
+
lines.push(`${metric.name}${labelStr} ${metric.value}`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
for (const [key, values] of this.histograms.entries()) {
|
|
253
|
+
const [name, ...labelParts] = key.split(':');
|
|
254
|
+
const labels = labelParts.join(':');
|
|
255
|
+
const stats = this.getHistogramStats(name, labels ? JSON.parse(labels) : {});
|
|
256
|
+
|
|
257
|
+
if (stats) {
|
|
258
|
+
lines.push(`${name}_count${labels ? `{${labels}}` : ''} ${stats.count}`);
|
|
259
|
+
lines.push(`${name}_sum${labels ? `{${labels}}` : ''} ${stats.sum}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return lines.join('\n');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Export metrics as JSON
|
|
268
|
+
* @returns Metrics as JSON object
|
|
269
|
+
*/
|
|
270
|
+
public exportJSON(): Record<string, unknown> {
|
|
271
|
+
const result: Record<string, unknown> = {
|
|
272
|
+
counters: {},
|
|
273
|
+
gauges: {},
|
|
274
|
+
histograms: {},
|
|
275
|
+
timestamps: {},
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
for (const metric of this.metrics.values()) {
|
|
279
|
+
const key = this.getMetricKey(metric.name, metric.labels);
|
|
280
|
+
|
|
281
|
+
if (metric.type === 'counter') {
|
|
282
|
+
(result.counters as Record<string, number>)[key] = metric.value as number;
|
|
283
|
+
} else if (metric.type === 'gauge') {
|
|
284
|
+
(result.gauges as Record<string, number>)[key] = metric.value as number;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
(result.timestamps as Record<string, number>)[key] = metric.timestamp;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
for (const [key, values] of this.histograms.entries()) {
|
|
291
|
+
const [name, ...labelParts] = key.split(':');
|
|
292
|
+
const labels = labelParts.join(':');
|
|
293
|
+
const stats = this.getHistogramStats(name, labels ? JSON.parse(labels) : {});
|
|
294
|
+
|
|
295
|
+
if (stats) {
|
|
296
|
+
(result.histograms as Record<string, unknown>)[key] = stats;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return result;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Generate a metric key from name and labels
|
|
305
|
+
* @param name - Metric name
|
|
306
|
+
* @param labels - Metric labels
|
|
307
|
+
* @returns Metric key
|
|
308
|
+
*/
|
|
309
|
+
private getMetricKey(name: string, labels: Record<string, string>): string {
|
|
310
|
+
if (Object.keys(labels).length === 0) {
|
|
311
|
+
return name;
|
|
312
|
+
}
|
|
313
|
+
return `${name}:${JSON.stringify(labels)}`;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Export a singleton instance
|
|
318
|
+
export const metricsCollector = new MetricsCollector();
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Decorator for timing function execution
|
|
322
|
+
* @param metricName - The metric name to use
|
|
323
|
+
* @returns Method decorator
|
|
324
|
+
*/
|
|
325
|
+
export function timed(metricName?: string) {
|
|
326
|
+
return function (
|
|
327
|
+
target: unknown,
|
|
328
|
+
propertyKey: string,
|
|
329
|
+
descriptor: PropertyDescriptor
|
|
330
|
+
): void {
|
|
331
|
+
const originalMethod = descriptor.value;
|
|
332
|
+
const name = metricName || `${target.constructor.name}_${propertyKey}`;
|
|
333
|
+
|
|
334
|
+
descriptor.value = function (...args: unknown[]) {
|
|
335
|
+
const timerKey = metricsCollector.startTimer(name);
|
|
336
|
+
|
|
337
|
+
try {
|
|
338
|
+
const result = originalMethod.apply(this, args);
|
|
339
|
+
|
|
340
|
+
if (result instanceof Promise) {
|
|
341
|
+
return result.finally(() => {
|
|
342
|
+
metricsCollector.endTimer(timerKey);
|
|
343
|
+
});
|
|
344
|
+
} else {
|
|
345
|
+
metricsCollector.endTimer(timerKey);
|
|
346
|
+
return result;
|
|
347
|
+
}
|
|
348
|
+
} catch (error) {
|
|
349
|
+
metricsCollector.endTimer(timerKey);
|
|
350
|
+
throw error;
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Track operation metrics
|
|
358
|
+
* @param operation - Operation name
|
|
359
|
+
* @param stage - Operation stage
|
|
360
|
+
* @param fn - Function to track
|
|
361
|
+
* @returns Function result
|
|
362
|
+
*/
|
|
363
|
+
export async function trackOperation<T>(
|
|
364
|
+
operation: string,
|
|
365
|
+
stage: string,
|
|
366
|
+
fn: () => Promise<T>
|
|
367
|
+
): Promise<T> {
|
|
368
|
+
const timerKey = metricsCollector.startTimer(`operation_${operation}`, { stage });
|
|
369
|
+
|
|
370
|
+
try {
|
|
371
|
+
const result = await fn();
|
|
372
|
+
metricsCollector.counter(`operation_${operation}_success`, 1, { stage });
|
|
373
|
+
return result;
|
|
374
|
+
} catch (error) {
|
|
375
|
+
metricsCollector.counter(`operation_${operation}_error`, 1, { stage });
|
|
376
|
+
throw error;
|
|
377
|
+
} finally {
|
|
378
|
+
metricsCollector.endTimer(timerKey);
|
|
379
|
+
}
|
|
380
|
+
}
|