@flink-app/flink 0.14.3 → 2.0.0-alpha.100
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/CHANGELOG.md +1051 -0
- package/SCHEMA_EXTRACTION_ANALYSIS.md +494 -0
- package/SIMPLE_AST_FEASIBILITY.md +570 -0
- package/bin/flink.ts +13 -2
- package/cli/build.ts +24 -44
- package/cli/clean.ts +13 -25
- package/cli/cli-utils.ts +190 -17
- package/cli/dev.ts +252 -0
- package/cli/loadEnvFiles.ts +116 -0
- package/cli/run.ts +45 -62
- package/dist/bin/flink.js +61 -2
- package/dist/cli/build.js +20 -25
- package/dist/cli/clean.js +12 -10
- package/dist/cli/cli-utils.d.ts +34 -3
- package/dist/cli/cli-utils.js +193 -12
- package/dist/cli/dev.d.ts +2 -0
- package/dist/cli/dev.js +279 -0
- package/dist/cli/loadEnvFiles.d.ts +30 -0
- package/dist/cli/loadEnvFiles.js +113 -0
- package/dist/cli/run.js +47 -46
- package/dist/src/DependencyTracker.d.ts +44 -0
- package/dist/src/DependencyTracker.js +239 -0
- package/dist/src/FlinkApp.d.ts +163 -10
- package/dist/src/FlinkApp.js +847 -184
- package/dist/src/FlinkContext.d.ts +41 -0
- package/dist/src/FlinkErrors.d.ts +19 -6
- package/dist/src/FlinkErrors.js +36 -42
- package/dist/src/FlinkHttpHandler.d.ts +219 -26
- package/dist/src/FlinkHttpHandler.js +37 -1
- package/dist/src/FlinkJob.d.ts +10 -0
- package/dist/src/FlinkLog.d.ts +82 -18
- package/dist/src/FlinkLog.js +165 -13
- package/dist/src/FlinkLogFactory.d.ts +288 -0
- package/dist/src/FlinkLogFactory.js +619 -0
- package/dist/src/FlinkRepo.d.ts +10 -2
- package/dist/src/FlinkRepo.js +11 -1
- package/dist/src/FlinkRequestContext.d.ts +63 -0
- package/dist/src/FlinkRequestContext.js +74 -0
- package/dist/src/FlinkResponse.d.ts +6 -0
- package/dist/src/FlinkService.d.ts +38 -0
- package/dist/src/FlinkService.js +46 -0
- package/dist/src/LeaderElection.d.ts +45 -0
- package/dist/src/LeaderElection.js +269 -0
- package/dist/src/SchemaCache.d.ts +84 -0
- package/dist/src/SchemaCache.js +289 -0
- package/dist/src/TypeScriptCompiler.d.ts +161 -51
- package/dist/src/TypeScriptCompiler.js +1253 -617
- package/dist/src/TypeScriptUtils.js +4 -0
- package/dist/src/ai/AgentRunner.d.ts +39 -0
- package/dist/src/ai/AgentRunner.js +760 -0
- package/dist/src/ai/ConversationAgent.d.ts +279 -0
- package/dist/src/ai/ConversationAgent.js +404 -0
- package/dist/src/ai/ConversationFlinkAgent.d.ts +278 -0
- package/dist/src/ai/ConversationFlinkAgent.js +404 -0
- package/dist/src/ai/FlinkAgent.d.ts +690 -0
- package/dist/src/ai/FlinkAgent.js +729 -0
- package/dist/src/ai/FlinkTool.d.ts +135 -0
- package/dist/src/ai/FlinkTool.js +2 -0
- package/dist/src/ai/InMemoryConversationAgent.d.ts +121 -0
- package/dist/src/ai/InMemoryConversationAgent.js +209 -0
- package/dist/src/ai/LLMAdapter.d.ts +148 -0
- package/dist/src/ai/LLMAdapter.js +2 -0
- package/dist/src/ai/PersistentFlinkAgent.d.ts +278 -0
- package/dist/src/ai/PersistentFlinkAgent.js +403 -0
- package/dist/src/ai/SubAgentExecutor.d.ts +38 -0
- package/dist/src/ai/SubAgentExecutor.js +223 -0
- package/dist/src/ai/ToolExecutor.d.ts +64 -0
- package/dist/src/ai/ToolExecutor.js +497 -0
- package/dist/src/ai/agentInstructions.d.ts +68 -0
- package/dist/src/ai/agentInstructions.js +286 -0
- package/dist/src/ai/index.d.ts +8 -0
- package/dist/src/ai/index.js +26 -0
- package/dist/src/ai/instructionFileLoader.d.ts +44 -0
- package/dist/src/ai/instructionFileLoader.js +179 -0
- package/dist/src/auth/FlinkAuthPlugin.d.ts +1 -1
- package/dist/src/handlers/StreamWriterFactory.d.ts +20 -0
- package/dist/src/handlers/StreamWriterFactory.js +83 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.js +17 -0
- package/dist/src/loadPluginSchemas.d.ts +45 -0
- package/dist/src/loadPluginSchemas.js +143 -0
- package/dist/src/schema-extraction/ComplexTypeDetection.d.ts +40 -0
- package/dist/src/schema-extraction/ComplexTypeDetection.js +75 -0
- package/dist/src/schema-extraction/TypeScriptSourceParser.d.ts +321 -0
- package/dist/src/schema-extraction/TypeScriptSourceParser.js +925 -0
- package/dist/src/schema-extraction/TypeScriptSourceParser.spec.d.ts +1 -0
- package/dist/src/schema-extraction/TypeScriptSourceParser.spec.js +233 -0
- package/dist/src/schema-extraction/TypeScriptTokenizer.d.ts +57 -0
- package/dist/src/schema-extraction/TypeScriptTokenizer.js +177 -0
- package/dist/src/schema-extraction/index.d.ts +2 -0
- package/dist/src/schema-extraction/index.js +20 -0
- package/dist/src/schema-extraction/types.d.ts +31 -0
- package/dist/src/schema-extraction/types.js +2 -0
- package/dist/src/utils/loadFlinkConfig.d.ts +53 -0
- package/dist/src/utils/loadFlinkConfig.js +77 -0
- package/dist/src/utils.d.ts +30 -0
- package/dist/src/utils.js +52 -0
- package/dist/src/workers/SchemaGeneratorWorker.d.ts +1 -0
- package/dist/src/workers/SchemaGeneratorWorker.js +49 -0
- package/dist/src/workers/WorkerPool.d.ts +60 -0
- package/dist/src/workers/WorkerPool.js +306 -0
- package/examples/logging-hierarchical-example.ts +125 -0
- package/package.json +29 -4
- package/readme.md +499 -0
- package/spec/AgentDescendantDetection.spec.ts +335 -0
- package/spec/AgentDuplicateDetection.spec.ts +112 -0
- package/spec/AgentObserver.spec.ts +266 -0
- package/spec/AgentRunner.spec.ts +1062 -0
- package/spec/AsyncLocalStorageContext.spec.ts +223 -0
- package/spec/ConversationHooks.spec.ts +257 -0
- package/spec/FlinkAgent.spec.ts +681 -0
- package/spec/FlinkApp.htmlResponse.spec.ts +260 -0
- package/spec/FlinkApp.onError.invocation.spec.ts +151 -0
- package/spec/FlinkApp.onError.spec.ts +1 -2
- package/spec/FlinkApp.query.spec.ts +107 -0
- package/spec/FlinkApp.routeOrdering.spec.ts +61 -0
- package/spec/FlinkApp.undefinedResponse.spec.ts +123 -0
- package/spec/FlinkApp.validationMode.spec.ts +155 -0
- package/spec/FlinkJob.spec.ts +171 -0
- package/spec/FlinkLogFactory.spec.ts +337 -0
- package/spec/FlinkRepo.spec.ts +1 -1
- package/spec/LeaderElection.spec.ts +174 -0
- package/spec/StreamingIntegration.spec.ts +139 -0
- package/spec/ToolExecutor.spec.ts +465 -0
- package/spec/TypeScriptCompiler.spec.ts +1 -1
- package/spec/TypeScriptSourceParser.spec.ts +1215 -0
- package/spec/TypeScriptTokenizer.spec.ts +366 -0
- package/spec/ai/ContextCompaction.spec.ts +405 -0
- package/spec/ai/ConversationAgent.spec.ts +520 -0
- package/spec/ai/InMemoryConversationAgent.spec.ts +144 -0
- package/spec/ai/agentInstructions.spec.ts +358 -0
- package/spec/fixtures/agent-instructions/TestAgent.ts +24 -0
- package/spec/fixtures/agent-instructions/simple.md +3 -0
- package/spec/fixtures/agent-instructions/template.md +18 -0
- package/spec/fixtures/agent-instructions/yaml-format.yaml +9 -0
- package/spec/mock-project/dist/.tsbuildinfo +1 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar.js +56 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCar2.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema.js +52 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema2.js +52 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithArraySchema3.js +52 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithLiteralSchema2.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/GetCarWithSchemaInFile2.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/ManuallyAddedHandler.js +53 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/ManuallyAddedHandler2.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchCar.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOnboardingSession.js +75 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchOrderWithComplexTypes.js +57 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchProductWithIntersection.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PatchUserWithUnion.js +58 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostCar.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogin.js +55 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PostLogout.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/handlers/PutCar.js +54 -0
- package/spec/mock-project/dist/spec/mock-project/src/index.js +83 -0
- package/spec/mock-project/dist/spec/mock-project/src/repos/CarRepo.js +26 -0
- package/spec/mock-project/dist/spec/mock-project/src/schemas/Car.js +2 -0
- package/spec/mock-project/dist/spec/mock-project/src/schemas/DefaultExportSchema.js +2 -0
- package/spec/mock-project/dist/spec/mock-project/src/schemas/FileWithTwoSchemas.js +2 -0
- package/spec/mock-project/dist/src/FlinkApp.js +1000 -0
- package/spec/mock-project/dist/src/FlinkContext.js +2 -0
- package/spec/mock-project/dist/src/FlinkErrors.js +143 -0
- package/spec/mock-project/dist/src/FlinkHttpHandler.js +47 -0
- package/spec/mock-project/dist/src/FlinkJob.js +2 -0
- package/spec/mock-project/dist/src/FlinkLog.js +119 -0
- package/spec/mock-project/dist/src/FlinkLogFactory.js +617 -0
- package/spec/mock-project/dist/src/FlinkPlugin.js +2 -0
- package/spec/mock-project/dist/src/FlinkRepo.js +224 -0
- package/spec/mock-project/dist/src/FlinkRequestContext.js +74 -0
- package/spec/mock-project/dist/src/FlinkResponse.js +2 -0
- package/spec/mock-project/dist/src/ai/AgentExecutor.js +279 -0
- package/spec/mock-project/dist/src/ai/AgentRunner.js +632 -0
- package/spec/mock-project/dist/src/ai/ConversationAgent.js +402 -0
- package/spec/mock-project/dist/src/ai/ConversationFlinkAgent.js +422 -0
- package/spec/mock-project/dist/src/ai/FlinkAgent.js +699 -0
- package/spec/mock-project/dist/src/ai/FlinkTool.js +2 -0
- package/spec/mock-project/dist/src/ai/InMemoryConversationAgent.js +209 -0
- package/spec/mock-project/dist/src/ai/LLMAdapter.js +2 -0
- package/spec/mock-project/dist/src/ai/SubAgentExecutor.js +223 -0
- package/spec/mock-project/dist/src/ai/ToolExecutor.js +412 -0
- package/spec/mock-project/dist/src/ai/agentInstructions.js +246 -0
- package/spec/mock-project/dist/src/auth/FlinkAuthPlugin.js +2 -0
- package/spec/mock-project/dist/src/auth/FlinkAuthUser.js +2 -0
- package/spec/mock-project/dist/src/handlers/GetCar.js +26 -52
- package/spec/mock-project/dist/src/handlers/GetCar.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCar2.js +32 -54
- package/spec/mock-project/dist/src/handlers/GetCar2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema.js +26 -48
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema2.js +28 -48
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema3.js +29 -48
- package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema3.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema.js +26 -50
- package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema2.js +28 -50
- package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile.js +27 -53
- package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile2.js +29 -53
- package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler.js +16 -49
- package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler2.js +25 -50
- package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler2.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchCar.js +27 -53
- package/spec/mock-project/dist/src/handlers/PatchCar.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchOnboardingSession.js +44 -70
- package/spec/mock-project/dist/src/handlers/PatchOnboardingSession.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchOrderWithComplexTypes.js +27 -53
- package/spec/mock-project/dist/src/handlers/PatchOrderWithComplexTypes.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchProductWithIntersection.js +28 -54
- package/spec/mock-project/dist/src/handlers/PatchProductWithIntersection.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PatchUserWithUnion.js +28 -54
- package/spec/mock-project/dist/src/handlers/PatchUserWithUnion.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PostCar.js +24 -50
- package/spec/mock-project/dist/src/handlers/PostCar.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PostLogin.js +25 -51
- package/spec/mock-project/dist/src/handlers/PostLogin.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PostLogout.js +24 -50
- package/spec/mock-project/dist/src/handlers/PostLogout.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/PutCar.js +24 -50
- package/spec/mock-project/dist/src/handlers/PutCar.js.map +1 -0
- package/spec/mock-project/dist/src/handlers/StreamWriterFactory.js +83 -0
- package/spec/mock-project/dist/src/index.js +52 -76
- package/spec/mock-project/dist/src/index.js.map +1 -0
- package/spec/mock-project/dist/src/mock-data-generator.js +9 -0
- package/spec/mock-project/dist/src/repos/CarRepo.js +12 -24
- package/spec/mock-project/dist/src/repos/CarRepo.js.map +1 -0
- package/spec/mock-project/dist/src/schemas/Car.js +3 -1
- package/spec/mock-project/dist/src/schemas/Car.js.map +1 -0
- package/spec/mock-project/dist/src/schemas/DefaultExportSchema.js +3 -1
- package/spec/mock-project/dist/src/schemas/DefaultExportSchema.js.map +1 -0
- package/spec/mock-project/dist/src/schemas/FileWithTwoSchemas.js +3 -1
- package/spec/mock-project/dist/src/schemas/FileWithTwoSchemas.js.map +1 -0
- package/spec/mock-project/dist/src/utils.js +290 -0
- package/spec/mock-project/tsconfig.json +6 -1
- package/spec/schema-generation-nested-objects.spec.ts +97 -0
- package/spec/testHelpers.ts +49 -0
- package/spec/utils.caseConversion.spec.ts +78 -0
- package/spec/utils.spec.ts +13 -13
- package/src/DependencyTracker.ts +166 -0
- package/src/FlinkApp.ts +919 -155
- package/src/FlinkContext.ts +43 -0
- package/src/FlinkErrors.ts +32 -12
- package/src/FlinkHttpHandler.ts +246 -28
- package/src/FlinkJob.ts +11 -0
- package/src/FlinkLog.ts +119 -12
- package/src/FlinkLogFactory.ts +699 -0
- package/src/FlinkRepo.ts +10 -3
- package/src/FlinkRequestContext.ts +95 -0
- package/src/FlinkResponse.ts +6 -0
- package/src/FlinkService.ts +49 -0
- package/src/LeaderElection.ts +203 -0
- package/src/SchemaCache.ts +232 -0
- package/src/TypeScriptCompiler.ts +1347 -610
- package/src/TypeScriptUtils.ts +5 -0
- package/src/ai/AgentRunner.ts +646 -0
- package/src/ai/ConversationAgent.ts +413 -0
- package/src/ai/FlinkAgent.ts +1069 -0
- package/src/ai/FlinkTool.ts +165 -0
- package/src/ai/InMemoryConversationAgent.ts +149 -0
- package/src/ai/LLMAdapter.ts +126 -0
- package/src/ai/ToolExecutor.ts +485 -0
- package/src/ai/agentInstructions.ts +245 -0
- package/src/ai/index.ts +8 -0
- package/src/ai/instructionFileLoader.ts +156 -0
- package/src/auth/FlinkAuthPlugin.ts +2 -1
- package/src/handlers/StreamWriterFactory.ts +84 -0
- package/src/index.ts +14 -0
- package/src/loadPluginSchemas.ts +141 -0
- package/src/schema-extraction/TypeScriptSourceParser.ts +1058 -0
- package/src/schema-extraction/TypeScriptTokenizer.ts +205 -0
- package/src/schema-extraction/index.ts +2 -0
- package/src/schema-extraction/types.ts +34 -0
- package/src/utils/loadFlinkConfig.ts +89 -0
- package/src/utils.ts +52 -0
- package/tsconfig.json +6 -1
|
@@ -0,0 +1,699 @@
|
|
|
1
|
+
import Logger from "node-color-log";
|
|
2
|
+
|
|
3
|
+
type LogLevel = "trace" | "debug" | "info" | "warn" | "error";
|
|
4
|
+
|
|
5
|
+
const LOG_LEVELS: Record<LogLevel, number> = {
|
|
6
|
+
trace: 0,
|
|
7
|
+
debug: 1,
|
|
8
|
+
info: 2,
|
|
9
|
+
warn: 3,
|
|
10
|
+
error: 4,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Component-specific logger with independent log level control
|
|
15
|
+
*/
|
|
16
|
+
export class ComponentLogger {
|
|
17
|
+
private componentName: string;
|
|
18
|
+
private componentLevel: LogLevel | null = null;
|
|
19
|
+
private namedLogger: any;
|
|
20
|
+
|
|
21
|
+
constructor(componentName: string) {
|
|
22
|
+
this.componentName = componentName;
|
|
23
|
+
this.namedLogger = Logger.createNamedLogger(componentName);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Set log level for this specific component.
|
|
28
|
+
* Overrides the global log level.
|
|
29
|
+
*/
|
|
30
|
+
setLevel(level: LogLevel) {
|
|
31
|
+
this.componentLevel = level;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Clear component-specific level and fall back to global level
|
|
36
|
+
*/
|
|
37
|
+
clearLevel() {
|
|
38
|
+
this.componentLevel = null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Check if a message should be logged based on component and global levels
|
|
43
|
+
*/
|
|
44
|
+
private shouldLog(messageLevel: LogLevel): boolean {
|
|
45
|
+
const effectiveLevel = this.componentLevel ?? FlinkLogFactory.getGlobalLevel();
|
|
46
|
+
return LOG_LEVELS[messageLevel] >= LOG_LEVELS[effectiveLevel];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
trace(...args: any[]) {
|
|
50
|
+
if (this.shouldLog("trace")) {
|
|
51
|
+
// node-color-log doesn't support trace, use console.log for stdout
|
|
52
|
+
if (FlinkLogFactory.getShowTimestamps()) {
|
|
53
|
+
const timestamp = new Date().toISOString();
|
|
54
|
+
console.log(`${timestamp} [${this.componentName}] [TRACE]`, ...args);
|
|
55
|
+
} else {
|
|
56
|
+
console.log(`[${this.componentName}] [TRACE]`, ...args);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
debug(...args: any[]) {
|
|
62
|
+
if (this.shouldLog("debug")) {
|
|
63
|
+
this.namedLogger.debug(...args);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
info(...args: any[]) {
|
|
68
|
+
if (this.shouldLog("info")) {
|
|
69
|
+
this.namedLogger.info(...args);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
warn(...args: any[]) {
|
|
74
|
+
if (this.shouldLog("warn")) {
|
|
75
|
+
// Use console.warn to write to stderr (Unix/POSIX best practice)
|
|
76
|
+
if (FlinkLogFactory.getShowTimestamps()) {
|
|
77
|
+
const timestamp = new Date().toISOString();
|
|
78
|
+
console.warn(`${timestamp} [${this.componentName}] [WARN]`, ...args);
|
|
79
|
+
} else {
|
|
80
|
+
console.warn(`[${this.componentName}] [WARN]`, ...args);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
error(...args: any[]) {
|
|
86
|
+
if (this.shouldLog("error")) {
|
|
87
|
+
// Use console.error to write to stderr (Unix/POSIX best practice)
|
|
88
|
+
if (FlinkLogFactory.getShowTimestamps()) {
|
|
89
|
+
const timestamp = new Date().toISOString();
|
|
90
|
+
console.error(`${timestamp} [${this.componentName}] [ERROR]`, ...args);
|
|
91
|
+
} else {
|
|
92
|
+
console.error(`[${this.componentName}] [ERROR]`, ...args);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
json(...args: any[]) {
|
|
98
|
+
if (this.shouldLog("debug")) {
|
|
99
|
+
for (const o of args) {
|
|
100
|
+
console.log(JSON.stringify(o, null, 2));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
bgColorLog(color: string, ...args: any[]) {
|
|
106
|
+
if (this.shouldLog("debug")) {
|
|
107
|
+
this.namedLogger.bgColorLog(color, ...args);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
fontColorLog(color: string, ...args: any[]) {
|
|
112
|
+
if (this.shouldLog("info")) {
|
|
113
|
+
this.namedLogger.fontColorLog(color, ...args);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Configuration for logging system
|
|
120
|
+
*/
|
|
121
|
+
export interface LoggingConfig {
|
|
122
|
+
/**
|
|
123
|
+
* Global log level (default: "info")
|
|
124
|
+
*/
|
|
125
|
+
global?: LogLevel;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Show timestamps in log messages (default: true)
|
|
129
|
+
*/
|
|
130
|
+
showTimestamps?: boolean;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Component-specific log levels with wildcard support
|
|
134
|
+
*
|
|
135
|
+
* Supports:
|
|
136
|
+
* - Exact match: "flink.ai.openai" → matches only "flink.ai.openai"
|
|
137
|
+
* - Prefix match: "flink.ai." → matches "flink.ai.openai", "flink.ai.anthropic", etc.
|
|
138
|
+
* - Single-level wildcard: "flink.ai.*" → matches "flink.ai.openai" but NOT "flink.ai.openai.v4"
|
|
139
|
+
* - Multi-level wildcard: "flink.ai.**" → matches any depth under "flink.ai"
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* {
|
|
143
|
+
* "flink.ai.openai": "trace", // exact match
|
|
144
|
+
* "flink.ai.*": "debug", // single-level wildcard
|
|
145
|
+
* "flink.database.**": "warn", // multi-level wildcard
|
|
146
|
+
* "flink.handlers.": "info" // prefix match (trailing dot)
|
|
147
|
+
* }
|
|
148
|
+
*/
|
|
149
|
+
components?: Record<string, LogLevel>;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @internal
|
|
153
|
+
* Internal use only - hierarchical configurations parsed from components map
|
|
154
|
+
*/
|
|
155
|
+
hierarchical?: Array<{
|
|
156
|
+
prefix: string;
|
|
157
|
+
level: LogLevel;
|
|
158
|
+
specificity: number;
|
|
159
|
+
}>;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* @internal
|
|
163
|
+
* Internal use only - wildcard configurations parsed from components map
|
|
164
|
+
*/
|
|
165
|
+
wildcards?: Array<{
|
|
166
|
+
pattern: string;
|
|
167
|
+
level: LogLevel;
|
|
168
|
+
specificity: number;
|
|
169
|
+
}>;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Factory for creating component-specific loggers with hierarchical level control
|
|
174
|
+
*/
|
|
175
|
+
export class FlinkLogFactory {
|
|
176
|
+
private static globalLevel: LogLevel = "info";
|
|
177
|
+
private static loggers: Map<string, ComponentLogger> = new Map();
|
|
178
|
+
private static initialized = false;
|
|
179
|
+
private static showTimestamps: boolean = true;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Store component configurations from environment/config for lazy application
|
|
183
|
+
*/
|
|
184
|
+
private static componentConfigs: Record<string, LogLevel> = {};
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Hierarchical prefix configurations (Java-style)
|
|
188
|
+
* Sorted by specificity (most specific first) for efficient matching
|
|
189
|
+
*/
|
|
190
|
+
private static hierarchicalConfigs: Array<{
|
|
191
|
+
prefix: string;
|
|
192
|
+
level: LogLevel;
|
|
193
|
+
specificity: number;
|
|
194
|
+
}> = [];
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Wildcard pattern configurations (optional, advanced)
|
|
198
|
+
* Sorted by specificity (most specific first) for efficient matching
|
|
199
|
+
*/
|
|
200
|
+
private static wildcardConfigs: Array<{
|
|
201
|
+
pattern: string;
|
|
202
|
+
level: LogLevel;
|
|
203
|
+
specificity: number;
|
|
204
|
+
}> = [];
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Set the global log level (affects all loggers without component-specific levels)
|
|
208
|
+
*/
|
|
209
|
+
static setGlobalLevel(level: LogLevel) {
|
|
210
|
+
FlinkLogFactory.globalLevel = level;
|
|
211
|
+
// node-color-log doesn't support trace, so use debug as the effective level
|
|
212
|
+
const effectiveLevel = level === "trace" ? "debug" : level;
|
|
213
|
+
Logger.setLevel(effectiveLevel);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Get the current global log level
|
|
218
|
+
*/
|
|
219
|
+
static getGlobalLevel(): LogLevel {
|
|
220
|
+
return FlinkLogFactory.globalLevel;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Get all registered loggers (useful for debugging)
|
|
225
|
+
*/
|
|
226
|
+
static getLoggers(): Map<string, ComponentLogger> {
|
|
227
|
+
return FlinkLogFactory.loggers;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Enable or disable timestamps in log messages
|
|
232
|
+
*/
|
|
233
|
+
static setShowTimestamps(show: boolean) {
|
|
234
|
+
FlinkLogFactory.showTimestamps = show;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Check if timestamps are enabled
|
|
239
|
+
*/
|
|
240
|
+
static getShowTimestamps(): boolean {
|
|
241
|
+
return FlinkLogFactory.showTimestamps;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Clear all component-specific log levels (fall back to global)
|
|
246
|
+
*/
|
|
247
|
+
static resetComponentLevels() {
|
|
248
|
+
FlinkLogFactory.componentConfigs = {};
|
|
249
|
+
for (const logger of Array.from(FlinkLogFactory.loggers.values())) {
|
|
250
|
+
logger.clearLevel();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Clear all hierarchical prefix configurations
|
|
256
|
+
*/
|
|
257
|
+
static resetHierarchicalLevels() {
|
|
258
|
+
FlinkLogFactory.hierarchicalConfigs = [];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Clear all wildcard pattern configurations
|
|
263
|
+
*/
|
|
264
|
+
static resetWildcardLevels() {
|
|
265
|
+
FlinkLogFactory.wildcardConfigs = [];
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Set log level for a specific component by name (exact match)
|
|
270
|
+
*/
|
|
271
|
+
static setComponentLevel(componentName: string, level: LogLevel | null) {
|
|
272
|
+
const normalized = FlinkLogFactory.normalize(componentName);
|
|
273
|
+
|
|
274
|
+
if (level === null) {
|
|
275
|
+
// Remove exact match configuration
|
|
276
|
+
delete FlinkLogFactory.componentConfigs[normalized];
|
|
277
|
+
const logger = FlinkLogFactory.loggers.get(normalized);
|
|
278
|
+
if (logger) {
|
|
279
|
+
logger.clearLevel();
|
|
280
|
+
}
|
|
281
|
+
} else {
|
|
282
|
+
// Set exact match configuration
|
|
283
|
+
FlinkLogFactory.componentConfigs[normalized] = level;
|
|
284
|
+
const logger = FlinkLogFactory.loggers.get(normalized);
|
|
285
|
+
if (logger) {
|
|
286
|
+
logger.setLevel(level);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Set hierarchical prefix-based log level (Java-style)
|
|
293
|
+
* @param prefix Logger name prefix (e.g., "flink.ai" matches all "flink.ai.*")
|
|
294
|
+
* @param level Log level to apply
|
|
295
|
+
*
|
|
296
|
+
* @example
|
|
297
|
+
* FlinkLogFactory.setHierarchicalLevel("flink.ai", "debug");
|
|
298
|
+
* // Now all loggers starting with "flink.ai." will use debug level
|
|
299
|
+
* // This includes the exact match "flink.ai" AND all children "flink.ai.*"
|
|
300
|
+
*/
|
|
301
|
+
static setHierarchicalLevel(prefix: string, level: LogLevel) {
|
|
302
|
+
const normalized = FlinkLogFactory.normalize(prefix);
|
|
303
|
+
// Ensure prefix ends with dot for consistent matching
|
|
304
|
+
const prefixWithDot = normalized.endsWith(".") ? normalized : normalized + ".";
|
|
305
|
+
const specificity = FlinkLogFactory.calculatePrefixSpecificity(normalized);
|
|
306
|
+
|
|
307
|
+
// Remove existing config for this prefix if it exists
|
|
308
|
+
FlinkLogFactory.hierarchicalConfigs = FlinkLogFactory.hierarchicalConfigs.filter(
|
|
309
|
+
(c) => c.prefix !== prefixWithDot
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
// Add new config
|
|
313
|
+
FlinkLogFactory.hierarchicalConfigs.push({
|
|
314
|
+
prefix: prefixWithDot,
|
|
315
|
+
level,
|
|
316
|
+
specificity,
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// Also set exact match for the prefix itself (without trailing dot)
|
|
320
|
+
// This ensures "flink.ai.openai" matches when using setHierarchicalLevel("flink.ai.openai", "debug")
|
|
321
|
+
FlinkLogFactory.componentConfigs[normalized] = level;
|
|
322
|
+
|
|
323
|
+
// Sort by specificity (most specific first) for efficient matching
|
|
324
|
+
FlinkLogFactory.hierarchicalConfigs.sort((a, b) => b.specificity - a.specificity);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Set wildcard pattern-based log level (advanced)
|
|
329
|
+
* @param pattern Wildcard pattern (e.g., "flink.ai.*" or "flink.ai.**")
|
|
330
|
+
* @param level Log level to apply
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* FlinkLogFactory.setWildcardLevel("flink.ai.*", "debug");
|
|
334
|
+
* // Matches flink.ai.openai but NOT flink.ai.openai.v4
|
|
335
|
+
*
|
|
336
|
+
* FlinkLogFactory.setWildcardLevel("flink.ai.**", "trace");
|
|
337
|
+
* // Matches any depth under flink.ai.
|
|
338
|
+
*/
|
|
339
|
+
static setWildcardLevel(pattern: string, level: LogLevel) {
|
|
340
|
+
const normalized = FlinkLogFactory.normalize(pattern);
|
|
341
|
+
const specificity = FlinkLogFactory.calculateWildcardSpecificity(normalized);
|
|
342
|
+
|
|
343
|
+
// Remove existing config for this pattern if it exists
|
|
344
|
+
FlinkLogFactory.wildcardConfigs = FlinkLogFactory.wildcardConfigs.filter(
|
|
345
|
+
(c) => c.pattern !== normalized
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
// Add new config
|
|
349
|
+
FlinkLogFactory.wildcardConfigs.push({
|
|
350
|
+
pattern: normalized,
|
|
351
|
+
level,
|
|
352
|
+
specificity,
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
// Sort by specificity (most specific first) for efficient matching
|
|
356
|
+
FlinkLogFactory.wildcardConfigs.sort((a, b) => b.specificity - a.specificity);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Get the effective log level for a component (resolved through hierarchy)
|
|
361
|
+
* @param componentName Component name to check
|
|
362
|
+
* @returns Resolved log level or null if only global level applies
|
|
363
|
+
*/
|
|
364
|
+
static getEffectiveLevel(componentName: string): LogLevel | null {
|
|
365
|
+
return FlinkLogFactory.resolveComponentLevel(componentName);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Initialize logging from environment variables and/or configuration file
|
|
370
|
+
*
|
|
371
|
+
* Priority (highest to lowest):
|
|
372
|
+
* 1. Config file (flink.config.js)
|
|
373
|
+
* 2. Environment variable (LOG_LEVEL=debug)
|
|
374
|
+
*
|
|
375
|
+
* @param fileConfig Optional logging configuration from flink.config.js
|
|
376
|
+
*
|
|
377
|
+
* @example Environment Variable
|
|
378
|
+
* ```bash
|
|
379
|
+
* LOG_LEVEL=debug pnpm run dev
|
|
380
|
+
* ```
|
|
381
|
+
*
|
|
382
|
+
* @example Config File (flink.config.js)
|
|
383
|
+
* ```javascript
|
|
384
|
+
* module.exports = {
|
|
385
|
+
* logging: {
|
|
386
|
+
* global: "info",
|
|
387
|
+
* showTimestamps: true,
|
|
388
|
+
* components: {
|
|
389
|
+
* "flink.ai.openai": "trace", // exact match
|
|
390
|
+
* "flink.ai.*": "debug", // single-level wildcard
|
|
391
|
+
* "flink.database.**": "warn", // multi-level wildcard
|
|
392
|
+
* "flink.handlers.": "info" // prefix match
|
|
393
|
+
* }
|
|
394
|
+
* }
|
|
395
|
+
* };
|
|
396
|
+
* ```
|
|
397
|
+
*/
|
|
398
|
+
static configure(fileConfig?: LoggingConfig) {
|
|
399
|
+
if (FlinkLogFactory.initialized) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// 1. Parse environment variables (lowest priority)
|
|
404
|
+
const envConfig = FlinkLogFactory.parseEnvironment();
|
|
405
|
+
|
|
406
|
+
// 2. Parse file config components map (highest priority)
|
|
407
|
+
let fileComponentsParsed: {
|
|
408
|
+
exact: Record<string, LogLevel>;
|
|
409
|
+
hierarchical: Array<{ prefix: string; level: LogLevel; specificity: number }>;
|
|
410
|
+
wildcards: Array<{ pattern: string; level: LogLevel; specificity: number }>;
|
|
411
|
+
} = { exact: {}, hierarchical: [], wildcards: [] };
|
|
412
|
+
if (fileConfig?.components) {
|
|
413
|
+
fileComponentsParsed = FlinkLogFactory.parseComponentsMap(fileConfig.components);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// 3. Merge configurations
|
|
417
|
+
const finalConfig = {
|
|
418
|
+
global: fileConfig?.global || envConfig.global || "info",
|
|
419
|
+
showTimestamps: fileConfig?.showTimestamps ?? true, // default: true
|
|
420
|
+
exact: {
|
|
421
|
+
...fileComponentsParsed.exact,
|
|
422
|
+
},
|
|
423
|
+
hierarchical: [...fileComponentsParsed.hierarchical],
|
|
424
|
+
wildcards: [...fileComponentsParsed.wildcards],
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
// 4. Apply global level
|
|
428
|
+
FlinkLogFactory.setGlobalLevel(finalConfig.global);
|
|
429
|
+
|
|
430
|
+
// 5. Apply timestamp setting
|
|
431
|
+
FlinkLogFactory.setShowTimestamps(finalConfig.showTimestamps);
|
|
432
|
+
|
|
433
|
+
// 6. Store exact component configs
|
|
434
|
+
FlinkLogFactory.componentConfigs = finalConfig.exact;
|
|
435
|
+
|
|
436
|
+
// 7. Store hierarchical configs (sorted by specificity)
|
|
437
|
+
FlinkLogFactory.hierarchicalConfigs = finalConfig.hierarchical.sort(
|
|
438
|
+
(a, b) => b.specificity - a.specificity
|
|
439
|
+
);
|
|
440
|
+
|
|
441
|
+
// 8. Store wildcard configs (sorted by specificity)
|
|
442
|
+
FlinkLogFactory.wildcardConfigs = finalConfig.wildcards.sort(
|
|
443
|
+
(a, b) => b.specificity - a.specificity
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
FlinkLogFactory.initialized = true;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Parse logging configuration from environment variables
|
|
451
|
+
* Supports only LOG_LEVEL for global level setting
|
|
452
|
+
*/
|
|
453
|
+
private static parseEnvironment(): LoggingConfig {
|
|
454
|
+
const config: LoggingConfig = {};
|
|
455
|
+
|
|
456
|
+
if (process.env.LOG_LEVEL) {
|
|
457
|
+
const level = process.env.LOG_LEVEL.toLowerCase();
|
|
458
|
+
if (FlinkLogFactory.isValidLogLevel(level)) {
|
|
459
|
+
config.global = level as LogLevel;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return config;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Normalize logger name to lowercase (Java-style convention)
|
|
468
|
+
* @param name Logger name or pattern
|
|
469
|
+
* @returns Normalized lowercase name
|
|
470
|
+
*/
|
|
471
|
+
private static normalize(name: string): string {
|
|
472
|
+
return name.toLowerCase();
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Determine if a name should be treated as a hierarchical prefix
|
|
477
|
+
* @param name Normalized logger name
|
|
478
|
+
* @returns True if it should be treated as a prefix
|
|
479
|
+
*/
|
|
480
|
+
private static shouldTreatAsPrefix(name: string): boolean {
|
|
481
|
+
// Default: if name contains dots, treat as hierarchical prefix
|
|
482
|
+
// This covers the common case where "flink.ai" should match "flink.ai.*"
|
|
483
|
+
return name.includes(".");
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Calculate specificity for prefix matching
|
|
488
|
+
* More specific prefixes (more segments) have higher specificity
|
|
489
|
+
* @param prefix Prefix string (may or may not end with dot)
|
|
490
|
+
* @returns Specificity score (number of segments)
|
|
491
|
+
*/
|
|
492
|
+
private static calculatePrefixSpecificity(prefix: string): number {
|
|
493
|
+
// Remove trailing dot if present
|
|
494
|
+
const normalized = prefix.endsWith(".") ? prefix.slice(0, -1) : prefix;
|
|
495
|
+
// Count dot-separated segments
|
|
496
|
+
return normalized.split(".").filter((s) => s.length > 0).length;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Calculate specificity for wildcard patterns
|
|
501
|
+
* More specific patterns (more non-wildcard segments) have higher specificity
|
|
502
|
+
* For same segment count, * (single-level) is more specific than ** (multi-level)
|
|
503
|
+
* @param pattern Wildcard pattern
|
|
504
|
+
* @returns Specificity score (base + decimal for wildcard type)
|
|
505
|
+
*/
|
|
506
|
+
private static calculateWildcardSpecificity(pattern: string): number {
|
|
507
|
+
const segments = pattern.split(".");
|
|
508
|
+
const nonWildcardCount = segments.filter((seg) => seg !== "*" && seg !== "**" && seg.length > 0).length;
|
|
509
|
+
|
|
510
|
+
// Base specificity: number of non-wildcard segments
|
|
511
|
+
let specificity = nonWildcardCount * 10;
|
|
512
|
+
|
|
513
|
+
// Add fractional specificity based on wildcard type
|
|
514
|
+
// * (single-level) = more specific than ** (multi-level)
|
|
515
|
+
if (pattern.includes("*")) {
|
|
516
|
+
if (pattern.includes("**")) {
|
|
517
|
+
specificity += 1; // ** is less specific
|
|
518
|
+
} else {
|
|
519
|
+
specificity += 5; // * is more specific
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return specificity;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Resolve component log level through hierarchical matching
|
|
528
|
+
* Order of precedence (highest to lowest):
|
|
529
|
+
* 1. Exact match
|
|
530
|
+
* 2. Most specific prefix match
|
|
531
|
+
* 3. Less specific prefix match
|
|
532
|
+
* 4. Wildcard pattern match
|
|
533
|
+
* 5. null (falls back to global level)
|
|
534
|
+
*
|
|
535
|
+
* @param componentName Component name to resolve
|
|
536
|
+
* @returns Resolved log level or null
|
|
537
|
+
*/
|
|
538
|
+
private static resolveComponentLevel(componentName: string): LogLevel | null {
|
|
539
|
+
const normalized = FlinkLogFactory.normalize(componentName);
|
|
540
|
+
|
|
541
|
+
// 1. Try exact match first (O(1) - fastest, highest priority)
|
|
542
|
+
if (FlinkLogFactory.componentConfigs[normalized]) {
|
|
543
|
+
return FlinkLogFactory.componentConfigs[normalized];
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// 2. Try hierarchical prefix matches (O(n) where n = number of prefixes)
|
|
547
|
+
// Already sorted by specificity (most specific first)
|
|
548
|
+
for (const config of FlinkLogFactory.hierarchicalConfigs) {
|
|
549
|
+
if (normalized.startsWith(config.prefix)) {
|
|
550
|
+
return config.level;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// 3. Try wildcard patterns (O(n) where n = number of patterns)
|
|
555
|
+
// Already sorted by specificity (most specific first)
|
|
556
|
+
for (const config of FlinkLogFactory.wildcardConfigs) {
|
|
557
|
+
if (FlinkLogFactory.matchWildcard(normalized, config.pattern)) {
|
|
558
|
+
return config.level;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// 4. No match - fall back to global level
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Simple wildcard matching for logger patterns
|
|
568
|
+
* Supports:
|
|
569
|
+
* - * matches single segment (flink.ai.* matches flink.ai.openai)
|
|
570
|
+
* - ** matches any depth (flink.ai.** matches flink.ai.openai.v4)
|
|
571
|
+
* - Partial segment match (flink.ai.open* matches flink.ai.openai)
|
|
572
|
+
*
|
|
573
|
+
* @param name Logger name to match
|
|
574
|
+
* @param pattern Wildcard pattern
|
|
575
|
+
* @returns True if pattern matches
|
|
576
|
+
*/
|
|
577
|
+
private static matchWildcard(name: string, pattern: string): boolean {
|
|
578
|
+
// Special handling for ** (multi-level wildcard)
|
|
579
|
+
// Need to process dots BEFORE replacing **
|
|
580
|
+
|
|
581
|
+
// Step 1: Escape dots first (before any other processing)
|
|
582
|
+
let regexPattern = pattern.replace(/\./g, "\\.");
|
|
583
|
+
|
|
584
|
+
// Step 2: Replace ** with placeholder (matches anything including dots)
|
|
585
|
+
regexPattern = regexPattern.replace(/\*\*/g, "___DOUBLE_STAR___");
|
|
586
|
+
|
|
587
|
+
// Step 3: Replace single * with single-segment match (non-dot chars)
|
|
588
|
+
regexPattern = regexPattern.replace(/\*/g, "[^\\.]+");
|
|
589
|
+
|
|
590
|
+
// Step 4: Replace ** placeholder with multi-segment match
|
|
591
|
+
regexPattern = regexPattern.replace(/___DOUBLE_STAR___/g, ".*");
|
|
592
|
+
|
|
593
|
+
const regex = new RegExp(`^${regexPattern}$`);
|
|
594
|
+
return regex.test(name);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Parse components map and classify entries into exact, prefix, or wildcard matches
|
|
599
|
+
* @param components Components map from config
|
|
600
|
+
* @returns Classified configuration
|
|
601
|
+
*/
|
|
602
|
+
private static parseComponentsMap(components: Record<string, LogLevel>): {
|
|
603
|
+
exact: Record<string, LogLevel>;
|
|
604
|
+
hierarchical: Array<{ prefix: string; level: LogLevel; specificity: number }>;
|
|
605
|
+
wildcards: Array<{ pattern: string; level: LogLevel; specificity: number }>;
|
|
606
|
+
} {
|
|
607
|
+
const exact: Record<string, LogLevel> = {};
|
|
608
|
+
const hierarchical: Array<{ prefix: string; level: LogLevel; specificity: number }> = [];
|
|
609
|
+
const wildcards: Array<{ pattern: string; level: LogLevel; specificity: number }> = [];
|
|
610
|
+
|
|
611
|
+
for (const [key, level] of Object.entries(components)) {
|
|
612
|
+
const normalized = FlinkLogFactory.normalize(key);
|
|
613
|
+
|
|
614
|
+
if (normalized.includes("*")) {
|
|
615
|
+
// Wildcard pattern (e.g., "flink.ai.*" or "flink.ai.**")
|
|
616
|
+
wildcards.push({
|
|
617
|
+
pattern: normalized,
|
|
618
|
+
level,
|
|
619
|
+
specificity: FlinkLogFactory.calculateWildcardSpecificity(normalized),
|
|
620
|
+
});
|
|
621
|
+
} else if (normalized.endsWith(".")) {
|
|
622
|
+
// Prefix match (e.g., "flink.ai.")
|
|
623
|
+
hierarchical.push({
|
|
624
|
+
prefix: normalized,
|
|
625
|
+
level,
|
|
626
|
+
specificity: FlinkLogFactory.calculatePrefixSpecificity(normalized),
|
|
627
|
+
});
|
|
628
|
+
} else if (normalized.includes(".")) {
|
|
629
|
+
// Ambiguous: could be exact or prefix
|
|
630
|
+
// Store as BOTH exact and hierarchical
|
|
631
|
+
exact[normalized] = level;
|
|
632
|
+
hierarchical.push({
|
|
633
|
+
prefix: normalized + ".",
|
|
634
|
+
level,
|
|
635
|
+
specificity: FlinkLogFactory.calculatePrefixSpecificity(normalized),
|
|
636
|
+
});
|
|
637
|
+
} else {
|
|
638
|
+
// Simple name (no dots) - exact match only
|
|
639
|
+
exact[normalized] = level;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Sort by specificity (most specific first)
|
|
644
|
+
hierarchical.sort((a, b) => b.specificity - a.specificity);
|
|
645
|
+
wildcards.sort((a, b) => b.specificity - a.specificity);
|
|
646
|
+
|
|
647
|
+
return { exact, hierarchical, wildcards };
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Check if a string is a valid log level
|
|
652
|
+
*/
|
|
653
|
+
private static isValidLogLevel(level: string): boolean {
|
|
654
|
+
return ["trace", "debug", "info", "warn", "error"].includes(level);
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Create or retrieve a component-specific logger
|
|
659
|
+
* Automatically applies environment/config settings when logger is created
|
|
660
|
+
* Supports hierarchical prefix matching (Java-style)
|
|
661
|
+
*
|
|
662
|
+
* @param componentName Logger name (lowercase dot notation recommended, e.g., "flink.ai.openai")
|
|
663
|
+
* @returns ComponentLogger instance
|
|
664
|
+
*
|
|
665
|
+
* @example
|
|
666
|
+
* ```typescript
|
|
667
|
+
* // Java-style hierarchical naming (recommended)
|
|
668
|
+
* const log = FlinkLogFactory.createLogger("flink.ai.openai");
|
|
669
|
+
* const dbLog = FlinkLogFactory.createLogger("flink.database.mongodb");
|
|
670
|
+
*
|
|
671
|
+
* // Case insensitive - these create the same logger
|
|
672
|
+
* const log1 = FlinkLogFactory.createLogger("flink.ai.openai");
|
|
673
|
+
* const log2 = FlinkLogFactory.createLogger("Flink.AI.OpenAI"); // Same instance
|
|
674
|
+
* ```
|
|
675
|
+
*/
|
|
676
|
+
static createLogger(componentName: string): ComponentLogger {
|
|
677
|
+
// Initialize from environment and config file if not already done
|
|
678
|
+
if (!FlinkLogFactory.initialized) {
|
|
679
|
+
const { loadFlinkConfig } = require("./utils/loadFlinkConfig");
|
|
680
|
+
const flinkConfig = loadFlinkConfig();
|
|
681
|
+
FlinkLogFactory.configure(flinkConfig?.logging);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// Normalize to lowercase for consistent lookup
|
|
685
|
+
const normalized = FlinkLogFactory.normalize(componentName);
|
|
686
|
+
|
|
687
|
+
if (!FlinkLogFactory.loggers.has(normalized)) {
|
|
688
|
+
const logger = new ComponentLogger(normalized);
|
|
689
|
+
FlinkLogFactory.loggers.set(normalized, logger);
|
|
690
|
+
|
|
691
|
+
// Resolve level through hierarchical matching
|
|
692
|
+
const resolvedLevel = FlinkLogFactory.resolveComponentLevel(normalized);
|
|
693
|
+
if (resolvedLevel) {
|
|
694
|
+
logger.setLevel(resolvedLevel);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
return FlinkLogFactory.loggers.get(normalized)!;
|
|
698
|
+
}
|
|
699
|
+
}
|