@anteros/core 0.0.28 → 0.0.31
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/database/decorator.ts +1 -1
- package/package.json +1 -1
- package/server/boot.ts +2 -1
- package/server/hono.ts +2 -1
- package/server/mcp.ts +9 -9
- package/tests/logger.test.ts +23 -0
- package/utils/logger.ts +14 -2
package/database/decorator.ts
CHANGED
|
@@ -22,7 +22,7 @@ export function LogSlowQuery(): any {
|
|
|
22
22
|
const duration = performance.now() - start;
|
|
23
23
|
const slowMs = cfg.server.logging?.slowQueryMs ?? 200;
|
|
24
24
|
if (duration >= slowMs) {
|
|
25
|
-
logger.warn
|
|
25
|
+
logger.file('warn', `Slow ${propertyKey} (${Math.round(duration)}ms)`, {
|
|
26
26
|
collection: args[0],
|
|
27
27
|
tenant: this.tenant_id,
|
|
28
28
|
duration: Math.round(duration),
|
package/package.json
CHANGED
package/server/boot.ts
CHANGED
|
@@ -177,7 +177,8 @@ async function bootApp(options: BootAppOptions = {} as BootAppOptions) {
|
|
|
177
177
|
const remote: any = (server as any).requestIP?.(req);
|
|
178
178
|
const remoteIp = remote?.address ?? null;
|
|
179
179
|
const remotePort = remote?.port ?? null;
|
|
180
|
-
|
|
180
|
+
// File-only: same as the API access log, no console noise.
|
|
181
|
+
logger.file(`${req.method} ${url.pathname}${url.search} → ${res.status}`, {
|
|
181
182
|
logger: 'http.log.access',
|
|
182
183
|
status: res.status,
|
|
183
184
|
duration: Math.round((performance.now() - start) * 10) / 10,
|
package/server/hono.ts
CHANGED
|
@@ -234,7 +234,8 @@ function createApp(): Hono<{ Variables: HonoVariables }> {
|
|
|
234
234
|
const remoteIp = conn?.remote?.address ?? null;
|
|
235
235
|
const remotePort = conn?.remote?.port ?? null;
|
|
236
236
|
|
|
237
|
-
|
|
237
|
+
// File-only: the per-request access log would flood the console.
|
|
238
|
+
logger.file(`${c.req.method} ${uri} → ${c.res.status}`, {
|
|
238
239
|
logger: 'http.log.access',
|
|
239
240
|
status: c.res.status,
|
|
240
241
|
duration: Math.round(duration * 10) / 10,
|
package/server/mcp.ts
CHANGED
|
@@ -124,7 +124,7 @@ function buildServer(tenantId: string, tools: McpTool[], resources: McpResource[
|
|
|
124
124
|
description: t.description,
|
|
125
125
|
inputSchema: joiToJsonSchema(t.inputSchema),
|
|
126
126
|
}));
|
|
127
|
-
logger.
|
|
127
|
+
logger.file('MCP tools/list', { method: 'tools/list', tenant: tenantId, count: toolsList.length, duration: ms(start) });
|
|
128
128
|
return { tools: toolsList };
|
|
129
129
|
});
|
|
130
130
|
|
|
@@ -132,14 +132,14 @@ function buildServer(tenantId: string, tools: McpTool[], resources: McpResource[
|
|
|
132
132
|
const start = performance.now();
|
|
133
133
|
const tool = tools.find((t) => t.name === req.params.name);
|
|
134
134
|
if (!tool) {
|
|
135
|
-
logger.
|
|
135
|
+
logger.file('warn', 'MCP tools/call', { method: 'tools/call', tenant: tenantId, tool: req.params.name, error: 'unknown tool', duration: ms(start) });
|
|
136
136
|
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${req.params.name}`);
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
const raw = req.params.arguments ?? {};
|
|
140
140
|
const { error, value } = tool.inputSchema.validate(raw, { allowUnknown: false });
|
|
141
141
|
if (error) {
|
|
142
|
-
logger.
|
|
142
|
+
logger.file('warn', 'MCP tools/call', { method: 'tools/call', tenant: tenantId, tool: req.params.name, args: raw, error: 'invalid arguments', duration: ms(start) });
|
|
143
143
|
return {
|
|
144
144
|
content: [{ type: 'text' as const, text: `Invalid arguments: ${error.message}` }],
|
|
145
145
|
isError: true,
|
|
@@ -155,7 +155,7 @@ function buildServer(tenantId: string, tools: McpTool[], resources: McpResource[
|
|
|
155
155
|
const text = typeof result === 'string' ? result : JSON.stringify(result);
|
|
156
156
|
return { content: [{ type: 'text' as const, text }] };
|
|
157
157
|
} catch (err: any) {
|
|
158
|
-
logger.
|
|
158
|
+
logger.file('warn', 'MCP tools/call', { method: 'tools/call', tenant: tenantId, tool: req.params.name, args: value, error: err?.message ?? 'unknown', duration: ms(start) });
|
|
159
159
|
return {
|
|
160
160
|
content: [{ type: 'text' as const, text: `Error: ${err?.message ?? 'unknown'}` }],
|
|
161
161
|
isError: true,
|
|
@@ -173,7 +173,7 @@ function buildServer(tenantId: string, tools: McpTool[], resources: McpResource[
|
|
|
173
173
|
description: r.description,
|
|
174
174
|
mimeType: r.mimeType,
|
|
175
175
|
}));
|
|
176
|
-
logger.
|
|
176
|
+
logger.file('MCP resources/list', { method: 'resources/list', tenant: tenantId, count: resourcesList.length, duration: ms(start) });
|
|
177
177
|
return { resources: resourcesList };
|
|
178
178
|
});
|
|
179
179
|
|
|
@@ -187,7 +187,7 @@ function buildServer(tenantId: string, tools: McpTool[], resources: McpResource[
|
|
|
187
187
|
description: r.description,
|
|
188
188
|
mimeType: r.mimeType,
|
|
189
189
|
}));
|
|
190
|
-
logger.
|
|
190
|
+
logger.file('MCP resources/templates/list', { method: 'resources/templates/list', tenant: tenantId, count: templates.length, duration: ms(start) });
|
|
191
191
|
return { resourceTemplates: templates };
|
|
192
192
|
});
|
|
193
193
|
|
|
@@ -196,7 +196,7 @@ function buildServer(tenantId: string, tools: McpTool[], resources: McpResource[
|
|
|
196
196
|
const uri = req.params.uri;
|
|
197
197
|
const matched = matchUri(uri, resources);
|
|
198
198
|
if (!matched) {
|
|
199
|
-
logger.
|
|
199
|
+
logger.file('warn', 'MCP resources/read', { method: 'resources/read', tenant: tenantId, uri, error: 'unknown uri', duration: ms(start) });
|
|
200
200
|
throw new McpError(ErrorCode.InvalidParams, `Unknown resource URI: ${uri}`);
|
|
201
201
|
}
|
|
202
202
|
|
|
@@ -208,10 +208,10 @@ function buildServer(tenantId: string, tools: McpTool[], resources: McpResource[
|
|
|
208
208
|
params: matched.params,
|
|
209
209
|
uri,
|
|
210
210
|
});
|
|
211
|
-
logger.
|
|
211
|
+
logger.file('MCP resources/read', { method: 'resources/read', tenant: tenantId, uri, resource: matched.resource.name, params: matched.params, duration: ms(start) });
|
|
212
212
|
return result;
|
|
213
213
|
} catch (err: any) {
|
|
214
|
-
logger.
|
|
214
|
+
logger.file('warn', 'MCP resources/read', { method: 'resources/read', tenant: tenantId, uri, error: err?.message ?? 'unknown', duration: ms(start) });
|
|
215
215
|
throw new McpError(ErrorCode.InternalError, `Failed to read resource: ${err?.message ?? 'unknown'}`);
|
|
216
216
|
}
|
|
217
217
|
});
|
package/tests/logger.test.ts
CHANGED
|
@@ -121,4 +121,27 @@ describe("logger", () => {
|
|
|
121
121
|
expect(out.some((l) => l.includes("visible info"))).toBe(true);
|
|
122
122
|
expect(out.some((l) => l.includes("Server started"))).toBe(false);
|
|
123
123
|
});
|
|
124
|
+
|
|
125
|
+
it("logger.file keeps the level when passed explicitly (never on console)", async () => {
|
|
126
|
+
const out: string[] = [];
|
|
127
|
+
const orig = console.log;
|
|
128
|
+
console.log = (...a: any[]) => { out.push(a.join(" ")); };
|
|
129
|
+
|
|
130
|
+
logger.configure({ dir: path.join(DIR, "filelevel"), file: true, console: true, level: "info" });
|
|
131
|
+
logger.file("default level", { a: 1 }); // 2-arg form → level 'info'
|
|
132
|
+
logger.file("warn", "explicit warn", { b: 2 }); // explicit level kept
|
|
133
|
+
logger.file("error", "explicit error", { c: 3 });
|
|
134
|
+
await flush();
|
|
135
|
+
console.log = orig;
|
|
136
|
+
|
|
137
|
+
const lines = readLogLines("filelevel/anteros.log");
|
|
138
|
+
expect(lines.find((l) => l.msg === "default level")!.level).toBe("info");
|
|
139
|
+
expect(lines.find((l) => l.msg === "explicit warn")!.level).toBe("warn");
|
|
140
|
+
expect(lines.find((l) => l.msg === "explicit warn")!.b).toBe(2);
|
|
141
|
+
expect(lines.find((l) => l.msg === "explicit error")!.level).toBe("error");
|
|
142
|
+
// never on the console, whatever the level
|
|
143
|
+
expect(out.some((l) => l.includes("default level"))).toBe(false);
|
|
144
|
+
expect(out.some((l) => l.includes("explicit warn"))).toBe(false);
|
|
145
|
+
expect(out.some((l) => l.includes("explicit error"))).toBe(false);
|
|
146
|
+
});
|
|
124
147
|
});
|
package/utils/logger.ts
CHANGED
|
@@ -186,8 +186,20 @@ class Logger {
|
|
|
186
186
|
warn(msg: string, meta?: unknown) { this.#write('warn', msg, meta); }
|
|
187
187
|
error(msg: string, meta?: unknown) { this.#write('error', msg, meta); }
|
|
188
188
|
|
|
189
|
-
/**
|
|
190
|
-
|
|
189
|
+
/**
|
|
190
|
+
* Write to the file only (no console output) — for framework-internal events
|
|
191
|
+
* (access log, MCP operations, slow queries) that would clutter the terminal.
|
|
192
|
+
* Level defaults to 'info'; `file('warn', msg, meta)` keeps the level.
|
|
193
|
+
*/
|
|
194
|
+
file(msg: string, meta?: unknown): void;
|
|
195
|
+
file(level: LogLevel, msg: string, meta?: unknown): void;
|
|
196
|
+
file(levelOrMsg: LogLevel | string, msgOrMeta?: unknown, meta?: unknown): void {
|
|
197
|
+
if (levelOrMsg === 'debug' || levelOrMsg === 'info' || levelOrMsg === 'warn' || levelOrMsg === 'error') {
|
|
198
|
+
this.#write(levelOrMsg, msgOrMeta as string, meta, { console: false });
|
|
199
|
+
} else {
|
|
200
|
+
this.#write('info', levelOrMsg, msgOrMeta, { console: false });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
191
203
|
|
|
192
204
|
/** Current log file path (empty when file logging is disabled) */
|
|
193
205
|
get filePath(): string { return this.#filePath; }
|