diff --git a/README.md b/README.md
index 5b4f804..9866e10 100644
--- a/README.md
+++ b/README.md
@@ -41,7 +41,11 @@ Create and configure your `~/.claude-code-router/config.json` file. For more det
The `config.json` file has several key sections:
- **`PROXY_URL`** (optional): You can set a proxy for API requests, for example: `"PROXY_URL": "http://127.0.0.1:7890"`.
-- **`LOG`** (optional): You can enable logging by setting it to `true`. The log file will be located at `$HOME/.claude-code-router.log`.
+- **`LOG`** (optional): You can enable logging by setting it to `true`. When set to `false`, no log files will be created. Default is `true`.
+- **`LOG_LEVEL`** (optional): Set the logging level. Available options are: `"fatal"`, `"error"`, `"warn"`, `"info"`, `"debug"`, `"trace"`. Default is `"info"`.
+- **Logging Systems**: The Claude Code Router uses two separate logging systems:
+ - **Server-level logs**: HTTP requests, API calls, and server events are logged using pino in the `~/.claude-code-router/logs/` directory with filenames like `ccr-*.log`
+ - **Application-level logs**: Routing decisions and business logic events are logged in `~/.claude-code-router/claude-code-router.log`
- **`APIKEY`** (optional): You can set a secret key to authenticate requests. When set, clients must provide this key in the `Authorization` header (e.g., `Bearer your-secret-key`) or the `x-api-key` header. Example: `"APIKEY": "your-secret-key"`.
- **`HOST`** (optional): You can set the host address for the server. If `APIKEY` is not set, the host will be forced to `127.0.0.1` for security reasons to prevent unauthorized access. Example: `"HOST": "0.0.0.0"`.
- **`NON_INTERACTIVE_MODE`** (optional): When set to `true`, enables compatibility with non-interactive environments like GitHub Actions, Docker containers, or other CI/CD systems. This sets appropriate environment variables (`CI=true`, `FORCE_COLOR=0`, etc.) and configures stdin handling to prevent the process from hanging in automated environments. Example: `"NON_INTERACTIVE_MODE": true`.
diff --git a/README_zh.md b/README_zh.md
index 708589f..15d1710 100644
--- a/README_zh.md
+++ b/README_zh.md
@@ -37,7 +37,11 @@ npm install -g @musistudio/claude-code-router
`config.json` 文件有几个关键部分:
- **`PROXY_URL`** (可选): 您可以为 API 请求设置代理,例如:`"PROXY_URL": "http://127.0.0.1:7890"`。
-- **`LOG`** (可选): 您可以通过将其设置为 `true` 来启用日志记录。日志文件将位于 `$HOME/.claude-code-router.log`。
+- **`LOG`** (可选): 您可以通过将其设置为 `true` 来启用日志记录。当设置为 `false` 时,将不会创建日志文件。默认值为 `true`。
+- **`LOG_LEVEL`** (可选): 设置日志级别。可用选项包括:`"fatal"`、`"error"`、`"warn"`、`"info"`、`"debug"`、`"trace"`。默认值为 `"info"`。
+- **日志系统**: Claude Code Router 使用两个独立的日志系统:
+ - **服务器级别日志**: HTTP 请求、API 调用和服务器事件使用 pino 记录在 `~/.claude-code-router/logs/` 目录中,文件名类似于 `ccr-*.log`
+ - **应用程序级别日志**: 路由决策和业务逻辑事件记录在 `~/.claude-code-router/claude-code-router.log` 文件中
- **`APIKEY`** (可选): 您可以设置一个密钥来进行身份验证。设置后,客户端请求必须在 `Authorization` 请求头 (例如, `Bearer your-secret-key`) 或 `x-api-key` 请求头中提供此密钥。例如:`"APIKEY": "your-secret-key"`。
- **`HOST`** (可选): 您可以设置服务的主机地址。如果未设置 `APIKEY`,出于安全考虑,主机地址将强制设置为 `127.0.0.1`,以防止未经授权的访问。例如:`"HOST": "0.0.0.0"`。
- **`NON_INTERACTIVE_MODE`** (可选): 当设置为 `true` 时,启用与非交互式环境(如 GitHub Actions、Docker 容器或其他 CI/CD 系统)的兼容性。这会设置适当的环境变量(`CI=true`、`FORCE_COLOR=0` 等)并配置 stdin 处理,以防止进程在自动化环境中挂起。例如:`"NON_INTERACTIVE_MODE": true`。
diff --git a/config.example.json b/config.example.json
index bc4d660..a6d592a 100644
--- a/config.example.json
+++ b/config.example.json
@@ -115,5 +115,7 @@
"APIKEY": "your-secret-key",
"HOST": "0.0.0.0",
"API_TIMEOUT_MS": 600000,
- "NON_INTERACTIVE_MODE": false
+ "NON_INTERACTIVE_MODE": false,
+ "LOG": true,
+ "LOG_LEVEL": "info"
}
diff --git a/src/index.ts b/src/index.ts
index bf94cc8..ecb7e11 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -14,6 +14,7 @@ import {
import { CONFIG_FILE } from "./constants";
import createWriteStream from "pino-rotating-file-stream";
import { HOME_DIR } from "./constants";
+import { configureLogging } from "./utils/log";
async function initializeClaudeConfig() {
const homeDir = homedir();
@@ -51,6 +52,10 @@ async function run(options: RunOptions = {}) {
// Clean up old log files, keeping only the 10 most recent ones
await cleanupLogFiles();
const config = await initConfig();
+
+ // Configure logging based on config
+ configureLogging(config);
+
let HOST = config.HOST;
if (config.HOST && !config.APIKEY) {
@@ -82,6 +87,17 @@ async function run(options: RunOptions = {}) {
? parseInt(process.env.SERVICE_PORT)
: port;
+ // Configure logger based on config settings
+ const loggerConfig = config.LOG !== false ? {
+ level: config.LOG_LEVEL || "info",
+ stream: createWriteStream({
+ path: HOME_DIR,
+ filename: config.LOGNAME || `./logs/ccr-${+new Date()}.log`,
+ maxFiles: 3,
+ interval: "1d",
+ }),
+ } : false;
+
const server = createServer({
jsonPath: CONFIG_FILE,
initialConfig: {
@@ -95,15 +111,7 @@ async function run(options: RunOptions = {}) {
"claude-code-router.log"
),
},
- logger: {
- level: "debug",
- stream: createWriteStream({
- path: HOME_DIR,
- filename: config.LOGNAME || `./logs/ccr-${+new Date()}.log`,
- maxFiles: 3,
- interval: "1d",
- }),
- },
+ logger: loggerConfig,
});
// Add async preHandler hook for authentication
server.addHook("preHandler", async (req, reply) => {
diff --git a/src/utils/log.ts b/src/utils/log.ts
index 6999726..111cc8d 100644
--- a/src/utils/log.ts
+++ b/src/utils/log.ts
@@ -9,9 +9,21 @@ if (!fs.existsSync(HOME_DIR)) {
fs.mkdirSync(HOME_DIR, { recursive: true });
}
+// Global variable to store the logging configuration
+let isLogEnabled: boolean | null = null;
+let logLevel: string = "info";
+
+// Function to configure logging
+export function configureLogging(config: { LOG?: boolean; LOG_LEVEL?: string }) {
+ isLogEnabled = config.LOG !== false; // Default to true if not explicitly set to false
+ logLevel = config.LOG_LEVEL || "info";
+}
+
export function log(...args: any[]) {
- // Check if logging is enabled via environment variable
- const isLogEnabled = process.env.LOG === "true";
+ // If logging configuration hasn't been set, default to enabled
+ if (isLogEnabled === null) {
+ isLogEnabled = true;
+ }
if (!isLogEnabled) {
return;
diff --git a/ui/src/components/ConfigProvider.tsx b/ui/src/components/ConfigProvider.tsx
index aae4d34..f679cee 100644
--- a/ui/src/components/ConfigProvider.tsx
+++ b/ui/src/components/ConfigProvider.tsx
@@ -69,6 +69,7 @@ export function ConfigProvider({ children }: ConfigProviderProps) {
// Validate the received data to ensure it has the expected structure
const validConfig = {
LOG: typeof data.LOG === 'boolean' ? data.LOG : false,
+ LOG_LEVEL: typeof data.LOG_LEVEL === 'string' ? data.LOG_LEVEL : 'info',
CLAUDE_PATH: typeof data.CLAUDE_PATH === 'string' ? data.CLAUDE_PATH : '',
HOST: typeof data.HOST === 'string' ? data.HOST : '127.0.0.1',
PORT: typeof data.PORT === 'number' ? data.PORT : 3456,
@@ -103,6 +104,7 @@ export function ConfigProvider({ children }: ConfigProviderProps) {
// Set default empty config when fetch fails
setConfig({
LOG: false,
+ LOG_LEVEL: 'info',
CLAUDE_PATH: '',
HOST: '127.0.0.1',
PORT: 3456,
diff --git a/ui/src/components/SettingsDialog.tsx b/ui/src/components/SettingsDialog.tsx
index c966435..9da6ffc 100644
--- a/ui/src/components/SettingsDialog.tsx
+++ b/ui/src/components/SettingsDialog.tsx
@@ -11,6 +11,7 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
+import { Combobox } from "@/components/ui/combobox";
import { useConfig } from "./ConfigProvider";
interface SettingsDialogProps {
@@ -45,6 +46,21 @@ export function SettingsDialog({ isOpen, onOpenChange }: SettingsDialogProps) {