mcp-log-query-server 3.6.0 → 3.7.0
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/config.js +25 -1
- package/index.js +910 -910
- package/logger.js +27 -17
- package/loki-client.js +13 -5
- package/package.json +3 -3
package/logger.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 轻量日志模块:本地文件追加(默认不走 stderr)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Windsurf
|
|
6
|
-
*
|
|
4
|
+
* ⚠️ 重要背景(3.6.1):
|
|
5
|
+
* Windsurf 根本没有 MCP 专属的 Output 频道,从不读取子进程的 stderr。
|
|
6
|
+
* Node 默认 stderr pipe buffer ~64KB,满了后 `console.error` 会**同步阻塞 event loop**
|
|
7
|
+
* → 整个 MCP 进程冻结,新请求来了不处理,已发出的 fetch 不推进,cancel signal 也接收不到。
|
|
8
|
+
*
|
|
9
|
+
* 因此默认只写文件,stderr 仅在显式要求时开启(用于单独调试,MCP server 场景禁用)。
|
|
7
10
|
*
|
|
8
11
|
* 用法:
|
|
9
12
|
* import { log } from './logger.js';
|
|
@@ -16,7 +19,8 @@
|
|
|
16
19
|
* 环境变量:
|
|
17
20
|
* - MCP_LOG_FILE: 自定义日志文件路径(默认 <tmpdir>/mcp-log-query.log)
|
|
18
21
|
* - MCP_LOG_MAX_BYTES: 单文件最大字节数(默认 10MB,超过则轮转到 .1)
|
|
19
|
-
* - MCP_LOG_DISABLE: 设为 '1'
|
|
22
|
+
* - MCP_LOG_DISABLE: 设为 '1' 则完全禁用日志(文件+stderr 都不写)
|
|
23
|
+
* - MCP_LOG_STDERR: 设为 '1' 时**同时**写 stderr(独立调试用,MCP 子进程模式下不要开)
|
|
20
24
|
*/
|
|
21
25
|
|
|
22
26
|
import fs from 'node:fs';
|
|
@@ -26,6 +30,8 @@ import path from 'node:path';
|
|
|
26
30
|
const LOG_FILE = process.env.MCP_LOG_FILE || path.join(os.tmpdir(), 'mcp-log-query.log');
|
|
27
31
|
const MAX_BYTES = parseInt(process.env.MCP_LOG_MAX_BYTES || `${10 * 1024 * 1024}`, 10);
|
|
28
32
|
const DISABLED = process.env.MCP_LOG_DISABLE === '1';
|
|
33
|
+
// 默认不走 stderr(MCP 子进程下会被 host 无限 backpressure 阻塞 event loop)
|
|
34
|
+
const WRITE_STDERR = process.env.MCP_LOG_STDERR === '1';
|
|
29
35
|
|
|
30
36
|
// 懒初始化:首次写入时再检查目录
|
|
31
37
|
let initialized = false;
|
|
@@ -41,8 +47,8 @@ function ensureInit() {
|
|
|
41
47
|
LOG_FILE,
|
|
42
48
|
`\n========== [${new Date().toISOString()}] MCP log-query 进程启动 pid=${process.pid} ==========\n`
|
|
43
49
|
);
|
|
44
|
-
} catch
|
|
45
|
-
|
|
50
|
+
} catch {
|
|
51
|
+
// 文件初始化失败,静默(同 log 函数的考虑)
|
|
46
52
|
}
|
|
47
53
|
}
|
|
48
54
|
|
|
@@ -59,13 +65,14 @@ function rotateIfNeeded() {
|
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
/**
|
|
62
|
-
*
|
|
68
|
+
* 写一条日志:默认仅本地文件;MCP_LOG_STDERR=1 时同时写 stderr
|
|
69
|
+
*
|
|
70
|
+
* ⚠️ 重要:MCP 子进程下 stderr 管道会被 host 无限 backpressure 阻塞 event loop,
|
|
71
|
+
* 所以默认禁用 stderr 输出。只在独立调试(非 MCP 模式)时开启。
|
|
72
|
+
*
|
|
63
73
|
* @param {string} msg - 日志内容(不需要带时间戳,本函数自动加)
|
|
64
74
|
*/
|
|
65
75
|
export function log(msg) {
|
|
66
|
-
// stderr 永远写,兼容 Windsurf Output 面板实时查看
|
|
67
|
-
console.error(msg);
|
|
68
|
-
|
|
69
76
|
if (DISABLED) return;
|
|
70
77
|
|
|
71
78
|
ensureInit();
|
|
@@ -74,12 +81,15 @@ export function log(msg) {
|
|
|
74
81
|
try {
|
|
75
82
|
rotateIfNeeded();
|
|
76
83
|
fs.appendFileSync(LOG_FILE, line);
|
|
77
|
-
} catch
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
84
|
+
} catch {
|
|
85
|
+
// 文件写失败静默忽略:不能为了日志去打 stderr(会阻塞 event loop)
|
|
86
|
+
// 也不缓存 warning;调试请检查磁盘 / 权限
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 可选 stderr 输出(独立调试用)
|
|
90
|
+
if (WRITE_STDERR) {
|
|
91
|
+
// 直接 write,失败忽略;不用 console.error 以免同步阻塞
|
|
92
|
+
try { process.stderr.write(msg + '\n'); } catch {}
|
|
83
93
|
}
|
|
84
94
|
}
|
|
85
95
|
|
package/loki-client.js
CHANGED
|
@@ -362,21 +362,29 @@ export function groupLogsByService(lokiResult) {
|
|
|
362
362
|
/**
|
|
363
363
|
* 构建按服务查询的 LogQL 表达式
|
|
364
364
|
* 根据环境是否有 project 标签,自动选择不同的 filename 路径格式:
|
|
365
|
-
* - 有 project 标签(CMS
|
|
365
|
+
* - 有 project 标签(CMS / 预发布): /data/services/logs/senior/clife-senior-health-app/normal_logs/normal.log
|
|
366
366
|
* - 无 project 标签(私有化): /data/services/logs/clife-senior-health-app/normal_logs/normal.log
|
|
367
367
|
*
|
|
368
|
-
*
|
|
368
|
+
* 注意 path 与 label 的差异:
|
|
369
|
+
* - 预发布 label 是 'pre-senior'(用于 LogQL 标签匹配,不在这里)
|
|
370
|
+
* - 预发布 path 仍然是 'senior'(写在 filename 路径里)
|
|
371
|
+
* 通过环境配置的 pathProject 字段单独控制路径段,避免与 label 混用
|
|
372
|
+
*
|
|
373
|
+
* @param {string} project - 项目 label,如 'senior' / 'pre-senior'(仅当环境无 pathProject 字段时回退使用)
|
|
369
374
|
* @param {string} servicePodPattern - 服务目录名,如 'clife-senior-health-app'
|
|
370
375
|
* @param {string} keyword - 搜索关键词(可选)
|
|
371
|
-
* @param {string} envName - 环境名称,如 'cms'、'chengyang'
|
|
376
|
+
* @param {string} envName - 环境名称,如 'cms'、'pre'、'chengyang'
|
|
372
377
|
*/
|
|
373
378
|
export function buildServiceLogQL(project, servicePodPattern, keyword = '', envName = '') {
|
|
374
379
|
const env = envName ? LOKI_ENVIRONMENTS[envName] : null;
|
|
375
380
|
const hasProject = env ? env.hasProjectLabel !== false : true;
|
|
381
|
+
// 路径段优先使用 env.pathProject(预发布需要 label='pre-senior' 但 path='senior'),回退到 project
|
|
382
|
+
const pathSegment = (env && env.pathProject) || project;
|
|
376
383
|
|
|
377
|
-
//
|
|
384
|
+
// 有 project 标签的环境: /data/services/logs/{pathSegment}/xxx-app/...
|
|
385
|
+
// 无 project 标签的环境: /data/services/logs/xxx-app/...
|
|
378
386
|
const filename = hasProject
|
|
379
|
-
? `/data/services/logs/${
|
|
387
|
+
? `/data/services/logs/${pathSegment}/${servicePodPattern}/normal_logs/normal.log`
|
|
380
388
|
: `/data/services/logs/${servicePodPattern}/normal_logs/normal.log`;
|
|
381
389
|
|
|
382
390
|
let expr = `{filename="${filename}"}`;
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-log-query-server",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.0",
|
|
4
4
|
"description": "MCP Server for querying server logs via SSH jump host and Grafana Loki API",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
-
"mcp-log-query": "
|
|
8
|
+
"mcp-log-query": "index.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"index.js",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"license": "MIT",
|
|
38
38
|
"repository": {
|
|
39
39
|
"type": "git",
|
|
40
|
-
"url": "https://github.com/huawang1258/mcp-services.git"
|
|
40
|
+
"url": "git+https://github.com/huawang1258/mcp-services.git"
|
|
41
41
|
},
|
|
42
42
|
"homepage": "https://github.com/huawang1258/mcp-services#readme"
|
|
43
43
|
}
|