13 KiB
13 KiB
mcp-proxy 日志配置指南
概述
mcp-proxy 支持灵活的日志配置,可以通过配置文件或环境变量进行配置,非常适合与 Tauri 等客户端集成。
日志功能特性
- ✅ 按日期滚动:每天自动创建新的日志文件
- ✅ 自动清理:保留最近 N 天的日志文件
- ✅ 多级别过滤:支持 trace/debug/info/warn/error
- ✅ 双输出:同时输出到控制台和文件
- ✅ 结构化日志:使用 tracing 框架
- ✅ 环境变量覆盖:支持运行时动态配置
配置方式
方式 1: 环境变量(推荐用于 Tauri 集成)
环境变量具有最高优先级,会覆盖配置文件中的设置。
# 设置日志目录(Tauri 可以传递应用数据目录)
export MCP_PROXY_LOG_DIR="/path/to/logs"
# 设置日志级别
export MCP_PROXY_LOG_LEVEL="info"
# 设置服务器端口
export MCP_PROXY_PORT="18099"
Windows (PowerShell):
$env:MCP_PROXY_LOG_DIR = "C:\Users\YourName\AppData\Roaming\YourApp\logs"
$env:MCP_PROXY_LOG_LEVEL = "info"
$env:MCP_PROXY_PORT = "18099"
Windows (CMD):
set MCP_PROXY_LOG_DIR=C:\Users\YourName\AppData\Roaming\YourApp\logs
set MCP_PROXY_LOG_LEVEL=info
set MCP_PROXY_PORT=18099
方式 2: 配置文件
创建 config.yml 文件:
server:
port: 18099
log:
level: "info"
path: "./logs"
retain_days: 7
配置文件查找顺序:
/app/config.yml(Docker 容器内)./config.yml(当前工作目录)- 环境变量
BOT_SERVER_CONFIG指定的路径
Tauri 集成示例
Rust 端 (nuwax-agent)
use std::process::Command;
use std::path::PathBuf;
use tauri::api::path::app_data_dir;
pub async fn start_mcp_proxy(
tauri_config: &tauri::Config,
port: u16,
) -> Result<Child> {
// 获取 Tauri 应用数据目录
let app_data = app_data_dir(tauri_config)
.ok_or_else(|| anyhow::anyhow!("无法获取应用数据目录"))?;
// 创建日志目录
let log_dir = app_data.join("logs").join("mcp-proxy");
std::fs::create_dir_all(&log_dir)?;
let log_dir_str = log_dir.to_string_lossy().to_string();
tracing::info!("MCP Proxy 日志目录: {}", log_dir_str);
// 查找 mcp-proxy 可执行文件
let mcp_proxy_path = resolve_npm_bin("mcp-proxy").await?;
// 启动进程,传递环境变量
let mut cmd = tokio::process::Command::new(&mcp_proxy_path);
cmd.env("MCP_PROXY_LOG_DIR", &log_dir_str);
cmd.env("MCP_PROXY_LOG_LEVEL", "info");
cmd.env("MCP_PROXY_PORT", port.to_string());
// Windows: 隐藏 CMD 窗口
#[cfg(windows)]
{
use std::os::windows::process::CommandExt;
const CREATE_NO_WINDOW: u32 = 0x08000000;
cmd.creation_flags(CREATE_NO_WINDOW);
}
// 捕获 stdout/stderr 用于调试
cmd.stdout(std::process::Stdio::piped());
cmd.stderr(std::process::Stdio::piped());
let mut child = cmd.spawn()?;
// 读取并记录输出
if let Some(stdout) = child.stdout.take() {
tokio::spawn(async move {
let reader = tokio::io::BufReader::new(stdout);
let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
tracing::info!("[mcp-proxy stdout] {}", line);
}
});
}
if let Some(stderr) = child.stderr.take() {
tokio::spawn(async move {
let reader = tokio::io::BufReader::new(stderr);
let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
tracing::error!("[mcp-proxy stderr] {}", line);
}
});
}
Ok(child)
}
TypeScript/JavaScript 端
import { Command } from '@tauri-apps/api/shell';
import { appDataDir, join } from '@tauri-apps/api/path';
async function startMcpProxy(port: number = 18099) {
// 获取应用数据目录
const appData = await appDataDir();
const logDir = await join(appData, 'logs', 'mcp-proxy');
console.log(`MCP Proxy 日志目录: ${logDir}`);
// 创建命令
const command = new Command('mcp-proxy', [], {
env: {
MCP_PROXY_LOG_DIR: logDir,
MCP_PROXY_LOG_LEVEL: 'info',
MCP_PROXY_PORT: port.toString(),
}
});
// 监听输出
command.on('output', (data) => {
console.log('[mcp-proxy]', data);
});
command.on('error', (error) => {
console.error('[mcp-proxy error]', error);
});
// 执行命令
const child = await command.spawn();
console.log(`MCP Proxy 已启动,PID: ${child.pid}`);
return child;
}
日志文件格式
文件命名规则
日志目录/
├── log.2026-02-12 # 2026年2月12日的日志
├── log.2026-02-13 # 2026年2月13日的日志
└── log.2026-02-14 # 2026年2月14日的日志(当前)
自动清理:默认保留最近 5 天的日志,可通过 retain_days 配置。
日志内容示例
2026-02-12T14:30:15.123456Z INFO mcp_proxy: ========================================
2026-02-12T14:30:15.123789Z INFO mcp_proxy: MCP-Proxy 服务启动
2026-02-12T14:30:15.124012Z INFO mcp_proxy: 命令: proxy (HTTP 服务器模式)
2026-02-12T14:30:15.124234Z INFO mcp_proxy: 版本: 0.1.39
2026-02-12T14:30:15.124456Z INFO mcp_proxy: 配置信息:
2026-02-12T14:30:15.124678Z INFO mcp_proxy: - 监听端口: 18099
2026-02-12T14:30:15.124890Z INFO mcp_proxy: - 日志目录: /path/to/logs
2026-02-12T14:30:15.125012Z INFO mcp_proxy: - 日志级别: info
2026-02-12T14:30:15.125234Z INFO mcp_proxy: - 日志保留: 7 天
2026-02-12T14:30:15.125456Z INFO mcp_proxy: 环境变量覆盖:
2026-02-12T14:30:15.125678Z INFO mcp_proxy: - MCP_PROXY_LOG_DIR: /custom/log/path
2026-02-12T14:30:15.125890Z INFO mcp_proxy: ========================================
2026-02-12T14:30:15.234567Z INFO mcp_proxy: 尝试绑定到地址: 0.0.0.0:18099
2026-02-12T14:30:15.345678Z INFO mcp_proxy: 成功绑定到地址: 0.0.0.0:18099
2026-02-12T14:30:15.456789Z INFO mcp_proxy: 初始化应用状态...
2026-02-12T14:30:15.567890Z INFO mcp_proxy: 应用状态初始化完成
2026-02-12T14:30:15.678901Z INFO mcp_proxy: 初始化路由...
2026-02-12T14:30:15.789012Z INFO mcp_proxy: 路由初始化完成
2026-02-12T14:30:15.890123Z INFO mcp_proxy: ✅ 服务启动成功,监听地址: 0.0.0.0:18099
2026-02-12T14:30:15.901234Z INFO mcp_proxy: ✅ 健康检查端点: http://0.0.0.0:18099/health
2026-02-12T14:30:15.912345Z INFO mcp_proxy: ✅ MCP 服务列表: http://0.0.0.0:18099/mcp
2026-02-12T14:30:16.023456Z INFO mcp_proxy: ✅ MCP服务状态检查定时任务已启动
2026-02-12T14:30:16.134567Z INFO mcp_proxy: 系统信息:
2026-02-12T14:30:16.145678Z INFO mcp_proxy: - 操作系统: windows
2026-02-12T14:30:16.156789Z INFO mcp_proxy: - 架构: x86_64
2026-02-12T14:30:16.167890Z INFO mcp_proxy: - 工作目录: "C:\\Users\\YourName\\AppData\\Local"
2026-02-12T14:30:16.178901Z INFO mcp_proxy: 🚀 HTTP 服务器启动,等待连接...
日志级别说明
| 级别 | 用途 | 建议场景 |
|---|---|---|
trace |
最详细的调试信息 | 开发调试 |
debug |
调试信息 | 开发和测试 |
info |
一般信息 | 生产环境推荐 |
warn |
警告信息 | 最小日志 |
error |
错误信息 | 仅记录错误 |
启动时的 stderr 输出
即使日志写入文件,mcp-proxy 也会在启动时向 stderr 输出关键信息:
========================================
MCP-Proxy 启动中...
版本: 0.1.39
配置加载完成:
- 端口: 18099
- 日志目录: /path/to/logs
- 日志级别: info
- 日志保留天数: 7
========================================
这确保即使日志系统初始化失败,也能看到基本的启动信息。
故障排查
问题 1: 日志文件未创建
可能原因:
- 日志目录路径不存在或无权限
- 环境变量设置错误
解决方案:
# 检查日志目录是否存在
ls -la /path/to/logs
# 检查环境变量
echo $MCP_PROXY_LOG_DIR
# 手动创建目录
mkdir -p /path/to/logs
chmod 755 /path/to/logs
问题 2: 看不到日志输出
可能原因:
- 日志级别设置过高(如
error) - Windows 下 CMD 窗口被隐藏
解决方案:
# 降低日志级别
export MCP_PROXY_LOG_LEVEL="debug"
# 或使用 RUST_LOG 环境变量
export RUST_LOG="mcp_proxy=debug"
问题 3: 日志文件过多
可能原因:
retain_days设置过大
解决方案:
# 设置保留天数(通过配置文件)
# config.yml
log:
retain_days: 3 # 只保留 3 天
# 或手动清理旧日志
find /path/to/logs -name "log.*" -mtime +7 -delete
完整示例:nuwax-agent 集成
// nuwax-agent-core/src/service/mcp_proxy.rs
use anyhow::{Context, Result};
use tokio::process::Child;
use std::path::PathBuf;
pub struct McpProxyConfig {
pub port: u16,
pub log_dir: PathBuf,
pub log_level: String,
}
pub async fn start_mcp_proxy(config: McpProxyConfig) -> Result<Child> {
// 确保日志目录存在
std::fs::create_dir_all(&config.log_dir)
.context("创建 MCP Proxy 日志目录失败")?;
tracing::info!("启动 MCP Proxy:");
tracing::info!(" - 端口: {}", config.port);
tracing::info!(" - 日志目录: {}", config.log_dir.display());
tracing::info!(" - 日志级别: {}", config.log_level);
// 查找可执行文件
let mcp_proxy_path = which::which("mcp-proxy")
.or_else(|_| {
// Fallback: 尝试 npm 全局路径
let home = std::env::var("HOME")
.or_else(|_| std::env::var("USERPROFILE"))?;
#[cfg(windows)]
let npm_bin = PathBuf::from(&home).join(".local/bin/mcp-proxy.cmd");
#[cfg(not(windows))]
let npm_bin = PathBuf::from(&home).join(".local/bin/mcp-proxy");
if npm_bin.exists() {
Ok(npm_bin)
} else {
Err(which::Error::CannotFindBinaryPath)
}
})
.context("找不到 mcp-proxy 可执行文件")?;
tracing::info!("mcp-proxy 路径: {}", mcp_proxy_path.display());
// 构建命令
let mut cmd = tokio::process::Command::new(&mcp_proxy_path);
// 设置环境变量
cmd.env("MCP_PROXY_LOG_DIR", config.log_dir.to_string_lossy().as_ref());
cmd.env("MCP_PROXY_LOG_LEVEL", &config.log_level);
cmd.env("MCP_PROXY_PORT", config.port.to_string());
// 捕获输出
cmd.stdout(std::process::Stdio::piped());
cmd.stderr(std::process::Stdio::piped());
// Windows: 隐藏 CMD 窗口
#[cfg(windows)]
{
use std::os::windows::process::CommandExt;
const CREATE_NO_WINDOW: u32 = 0x08000000;
cmd.creation_flags(CREATE_NO_WINDOW);
}
// 启动进程
let mut child = cmd.spawn()
.context("启动 mcp-proxy 进程失败")?;
// 异步读取 stdout
if let Some(stdout) = child.stdout.take() {
use tokio::io::{AsyncBufReadExt, BufReader};
tokio::spawn(async move {
let reader = BufReader::new(stdout);
let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
tracing::info!("[mcp-proxy] {}", line);
}
});
}
// 异步读取 stderr
if let Some(stderr) = child.stderr.take() {
use tokio::io::{AsyncBufReadExt, BufReader};
tokio::spawn(async move {
let reader = BufReader::new(stderr);
let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
tracing::error!("[mcp-proxy stderr] {}", line);
}
});
}
tracing::info!("MCP Proxy 进程已启动,PID: {:?}", child.id());
Ok(child)
}
环境变量参考
| 环境变量 | 类型 | 默认值 | 说明 |
|---|---|---|---|
MCP_PROXY_PORT |
u16 | 3000 |
HTTP 服务监听端口 |
MCP_PROXY_LOG_DIR |
String | ./logs |
日志文件目录 |
MCP_PROXY_LOG_LEVEL |
String | info |
日志级别 (trace/debug/info/warn/error) |
RUST_LOG |
String | - | Rust 标准日志环境变量(更细粒度控制) |
BOT_SERVER_CONFIG |
String | - | 自定义配置文件路径 |
高级用法:RUST_LOG
RUST_LOG 环境变量提供更细粒度的日志控制:
# 只显示 mcp_proxy 模块的 debug 级别日志
export RUST_LOG="mcp_proxy=debug"
# 显示多个模块的日志
export RUST_LOG="mcp_proxy=debug,tokio=info,hyper=warn"
# 显示所有模块的 trace 级别日志(非常详细)
export RUST_LOG="trace"
# 组合使用
export RUST_LOG="debug,mcp_proxy=trace,hyper::proto=error"
注意:RUST_LOG 优先级高于 MCP_PROXY_LOG_LEVEL。
总结
- ✅ 使用
MCP_PROXY_LOG_DIR环境变量指定日志目录(Tauri 集成推荐) - ✅ 日志文件按日期自动滚动,默认保留 5 天
- ✅ 关键启动信息会输出到 stderr,即使日志系统失败也能看到
- ✅ 支持通过环境变量动态配置,无需修改配置文件
- ✅ Windows 下可以隐藏 CMD 窗口,但仍然捕获日志输出
更多信息请参考: