diff --git a/backend/app/controller/chat_controller.py b/backend/app/controller/chat_controller.py index 45bafd41e..d42c49386 100644 --- a/backend/app/controller/chat_controller.py +++ b/backend/app/controller/chat_controller.py @@ -5,7 +5,6 @@ from pathlib import Path from dotenv import load_dotenv from fastapi import APIRouter, Request, Response from fastapi.responses import StreamingResponse -from loguru import logger from app.utils import traceroot_wrapper as traceroot from app.component import code from app.exception.exception import UserException diff --git a/backend/app/controller/task_controller.py b/backend/app/controller/task_controller.py index f8f104f1b..1b4f60b41 100644 --- a/backend/app/controller/task_controller.py +++ b/backend/app/controller/task_controller.py @@ -1,7 +1,6 @@ from typing import Literal from dotenv import load_dotenv from fastapi import APIRouter, Response -from loguru import logger from pydantic import BaseModel from app.model.chat import NewAgent, UpdateData from app.service.task import ( @@ -16,6 +15,10 @@ from app.service.task import ( ) import asyncio from app.component.environment import set_user_env_path +from app.utils import traceroot_wrapper as traceroot + +# traceroot logger for task controller +logger = traceroot.get_logger("task_controller") router = APIRouter(tags=["task"]) diff --git a/backend/app/controller/tool_controller.py b/backend/app/controller/tool_controller.py index 4a18857fe..0612f7784 100644 --- a/backend/app/controller/tool_controller.py +++ b/backend/app/controller/tool_controller.py @@ -1,7 +1,10 @@ from fastapi import APIRouter, HTTPException -from loguru import logger from app.utils.toolkit.notion_mcp_toolkit import NotionMCPToolkit from app.utils.toolkit.google_calendar_toolkit import GoogleCalendarToolkit +from app.utils import traceroot_wrapper as traceroot + +# traceroot logger for tool controller +logger = traceroot.get_logger("tool_controller") router = APIRouter(tags=["task"]) diff --git a/backend/app/exception/handler.py b/backend/app/exception/handler.py index 3e2bf9c81..4c645d990 100644 --- a/backend/app/exception/handler.py +++ b/backend/app/exception/handler.py @@ -3,12 +3,14 @@ from fastapi import Request from fastapi.encoders import jsonable_encoder from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse -from loguru import logger from app import api from app.component import code from app.exception.exception import NoPermissionException, ProgramException, TokenException from app.component.pydantic.i18n import trans, get_language from app.exception.exception import UserException +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("exception_handler") @api.exception_handler(RequestValidationError) diff --git a/backend/app/model/chat.py b/backend/app/model/chat.py index e04de75e8..1cb53c052 100644 --- a/backend/app/model/chat.py +++ b/backend/app/model/chat.py @@ -3,9 +3,11 @@ import json from pathlib import Path import re from typing import Literal -from loguru import logger from pydantic import BaseModel, field_validator from camel.types import ModelType, RoleType +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("chat_model") class Status(str, Enum): diff --git a/backend/app/service/chat_service.py b/backend/app/service/chat_service.py index a17a3451c..c932f9e7f 100644 --- a/backend/app/service/chat_service.py +++ b/backend/app/service/chat_service.py @@ -19,7 +19,6 @@ from camel.toolkits import AgentCommunicationToolkit, ToolkitMessageIntegration from app.utils.toolkit.human_toolkit import HumanToolkit from app.utils.toolkit.note_taking_toolkit import NoteTakingToolkit from app.utils.workforce import Workforce -from loguru import logger from app.model.chat import Chat, NewAgent, Status, sse_json, TaskContent from camel.tasks import Task from app.utils.agent import ( @@ -40,6 +39,9 @@ from app.service.task import Action, Agents from app.utils.server.sync_step import sync_step from camel.types import ModelPlatformType from camel.models import ModelProcessingError +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("chat_service") @sync_step diff --git a/backend/app/service/task.py b/backend/app/service/task.py index 5e68708ed..865ac7ec9 100644 --- a/backend/app/service/task.py +++ b/backend/app/service/task.py @@ -9,7 +9,9 @@ from contextlib import contextmanager from contextvars import ContextVar from datetime import datetime, timedelta import weakref -from loguru import logger +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("task_service") class Action(str, Enum): diff --git a/backend/app/utils/agent.py b/backend/app/utils/agent.py index 64ac7486f..06241423d 100644 --- a/backend/app/utils/agent.py +++ b/backend/app/utils/agent.py @@ -50,7 +50,6 @@ from camel.types import ModelPlatformType, ModelType from camel.toolkits import MCPToolkit, ToolkitMessageIntegration import datetime from pydantic import BaseModel -from loguru import logger from app.model.chat import Chat, McpServers # Create traceroot logger for agent tracking @@ -171,7 +170,7 @@ class ListenChatAgent(ChatAgent): except Exception as e: res = None error_info = e - logger.exception(e) + traceroot_logger.error(str(e), exc_info=True) traceroot_logger.error(f"Agent {self.agent_name} unexpected error in step: {e}", exc_info=True) message = f"Error processing message: {e!s}" total_tokens = 0 @@ -341,7 +340,7 @@ class ListenChatAgent(ChatAgent): error_msg = f"Error executing tool '{func_name}': {e!s}" result = f"Tool execution failed: {error_msg}" mask_flag = False - logger.debug(error_msg) + traceroot_logger.debug(error_msg) traceroot_logger.error(f"Tool execution failed for {func_name}: {e}") traceback.print_exc() @@ -403,7 +402,7 @@ class ListenChatAgent(ChatAgent): # Capture the error message to prevent framework crash error_msg = f"Error executing async tool '{func_name}': {e!s}" result = {"error": error_msg} - logger.warning(error_msg) + traceroot_logger.warning(error_msg) traceroot_logger.error(f"Async tool execution failed for {func_name}: {e}") traceback.print_exc() diff --git a/backend/app/utils/listen/toolkit_listen.py b/backend/app/utils/listen/toolkit_listen.py index 77079c7c5..76999de29 100644 --- a/backend/app/utils/listen/toolkit_listen.py +++ b/backend/app/utils/listen/toolkit_listen.py @@ -3,8 +3,6 @@ from functools import wraps from inspect import iscoroutinefunction import json from typing import Any, Callable - -from loguru import logger from app.service.task import ( ActionActivateToolkitData, ActionDeactivateToolkitData, @@ -12,6 +10,9 @@ from app.service.task import ( ) from app.utils.toolkit.abstract_toolkit import AbstractToolkit from app.service.task import process_task +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("toolkit_listen") def listen_toolkit( diff --git a/backend/app/utils/server/sync_step.py b/backend/app/utils/server/sync_step.py index c45714e2f..1cef7e93b 100644 --- a/backend/app/utils/server/sync_step.py +++ b/backend/app/utils/server/sync_step.py @@ -3,9 +3,11 @@ import httpx import asyncio import os import json -from loguru import logger from app.service.chat_service import Chat from app.component.environment import env +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("sync_step") def sync_step(func): diff --git a/backend/app/utils/toolkit/human_toolkit.py b/backend/app/utils/toolkit/human_toolkit.py index ba616b5c3..e2ebaa79a 100644 --- a/backend/app/utils/toolkit/human_toolkit.py +++ b/backend/app/utils/toolkit/human_toolkit.py @@ -1,12 +1,13 @@ import asyncio from camel.toolkits.base import BaseToolkit -from loguru import logger from camel.toolkits.function_tool import FunctionTool from app.service.task import Action, ActionAskData, ActionNoticeData, get_task_lock from app.utils.listen.toolkit_listen import listen_toolkit from app.utils.toolkit.abstract_toolkit import AbstractToolkit from app.service.task import process_task -# Rewrite HumanToolkit because the system's user interaction was using console, but in electron we cannot use console. Changed to use SSE response to let frontend show dialog for user interaction +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("human_toolkit") class HumanToolkit(BaseToolkit, AbstractToolkit): diff --git a/backend/app/utils/toolkit/hybrid_browser_python_toolkit.py b/backend/app/utils/toolkit/hybrid_browser_python_toolkit.py index 911a6dd87..e3f9dcb4c 100644 --- a/backend/app/utils/toolkit/hybrid_browser_python_toolkit.py +++ b/backend/app/utils/toolkit/hybrid_browser_python_toolkit.py @@ -12,12 +12,14 @@ from camel.toolkits.hybrid_browser_toolkit_py.actions import ActionExecutor from camel.toolkits.hybrid_browser_toolkit_py.snapshot import PageSnapshot from camel.toolkits.hybrid_browser_toolkit_py.agent import PlaywrightLLMAgent from camel.toolkits.function_tool import FunctionTool -from loguru import logger from app.component.environment import env from app.exception.exception import ProgramException from app.service.task import Agents from app.utils.listen.toolkit_listen import listen_toolkit from app.utils.toolkit.abstract_toolkit import AbstractToolkit +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("hybrid_browser_python_toolkit") class BrowserSession(BaseHybridBrowserSession): diff --git a/backend/app/utils/toolkit/hybrid_browser_toolkit.py b/backend/app/utils/toolkit/hybrid_browser_toolkit.py index adf6c3d0c..a9a2cc2c2 100644 --- a/backend/app/utils/toolkit/hybrid_browser_toolkit.py +++ b/backend/app/utils/toolkit/hybrid_browser_toolkit.py @@ -4,7 +4,6 @@ import time import asyncio import json from typing import Any, Dict, List, Optional -from loguru import logger import websockets import websockets.exceptions @@ -18,6 +17,9 @@ from app.component.environment import env from app.service.task import Agents from app.utils.listen.toolkit_listen import listen_toolkit from app.utils.toolkit.abstract_toolkit import AbstractToolkit +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("hybrid_browser_toolkit") class WebSocketBrowserWrapper(BaseWebSocketBrowserWrapper): diff --git a/backend/app/utils/toolkit/notion_mcp_toolkit.py b/backend/app/utils/toolkit/notion_mcp_toolkit.py index 36928aa0a..44e566dba 100644 --- a/backend/app/utils/toolkit/notion_mcp_toolkit.py +++ b/backend/app/utils/toolkit/notion_mcp_toolkit.py @@ -1,10 +1,12 @@ import os from typing import Any, Dict, List -from loguru import logger from camel.toolkits import FunctionTool from app.component.environment import env from app.utils.toolkit.abstract_toolkit import AbstractToolkit from camel.toolkits.mcp_toolkit import MCPToolkit +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("notion_mcp_toolkit") class NotionMCPToolkit(MCPToolkit, AbstractToolkit): diff --git a/backend/app/utils/toolkit/search_toolkit.py b/backend/app/utils/toolkit/search_toolkit.py index f32d196a5..f08918826 100644 --- a/backend/app/utils/toolkit/search_toolkit.py +++ b/backend/app/utils/toolkit/search_toolkit.py @@ -2,11 +2,13 @@ from typing import Any, Dict, List, Literal from camel.toolkits import SearchToolkit as BaseSearchToolkit from camel.toolkits.function_tool import FunctionTool import httpx -from loguru import logger from app.component.environment import env, env_not_empty from app.service.task import Agents from app.utils.listen.toolkit_listen import listen_toolkit from app.utils.toolkit.abstract_toolkit import AbstractToolkit +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("search_toolkit") class SearchToolkit(BaseSearchToolkit, AbstractToolkit): diff --git a/backend/app/utils/toolkit/slack_toolkit.py b/backend/app/utils/toolkit/slack_toolkit.py index 920047a5f..3b92ee533 100644 --- a/backend/app/utils/toolkit/slack_toolkit.py +++ b/backend/app/utils/toolkit/slack_toolkit.py @@ -1,10 +1,12 @@ from camel.toolkits import SlackToolkit as BaseSlackToolkit from camel.toolkits.function_tool import FunctionTool -from loguru import logger from app.component.environment import env from app.service.task import Agents from app.utils.listen.toolkit_listen import listen_toolkit from app.utils.toolkit.abstract_toolkit import AbstractToolkit +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("slack_toolkit") class SlackToolkit(BaseSlackToolkit, AbstractToolkit): diff --git a/backend/app/utils/workforce.py b/backend/app/utils/workforce.py index 541110253..78d89fa02 100644 --- a/backend/app/utils/workforce.py +++ b/backend/app/utils/workforce.py @@ -9,7 +9,6 @@ from camel.societies.workforce.workforce import ( from camel.societies.workforce.task_channel import TaskChannel from camel.societies.workforce.base import BaseNode from camel.societies.workforce.utils import TaskAssignResult -from loguru import logger from camel.tasks.task import Task, TaskState, validate_task_content from app.component import code from app.exception.exception import UserException @@ -23,25 +22,10 @@ from app.service.task import ( get_task_lock, ) from app.utils.single_agent_worker import SingleAgentWorker +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("workforce") -# === Debug sink === Write detailed dependency debug logs to file (logs/workforce_debug.log) -# Create a new file every day, keep the logs for the last 7 days, and write asynchronously without blocking the main process -logger.add( - "logs/workforce_debug_{time:YYYY-MM-DD}.log", - rotation="00:00", - retention="7 days", - enqueue=True, - level="DEBUG", -) -# Independent sink: only collect the "[WF]" debug lines we insert to quickly view the dependency chain -logger.add( - "logs/wf_trace_{time:YYYY-MM-DD-HH}.log", - rotation="00:00", - retention="7 days", - enqueue=True, - level="DEBUG", - filter=lambda record: record["message"].startswith("[WF]"), -) class Workforce(BaseWorkforce): diff --git a/backend/main.py b/backend/main.py index f76059b92..d2f49ca39 100644 --- a/backend/main.py +++ b/backend/main.py @@ -4,34 +4,25 @@ import signal import asyncio import atexit from app import api -from loguru import logger from app.component.environment import auto_include_routers, env +from app.utils import traceroot_wrapper as traceroot os.environ["PYTHONIOENCODING"] = "utf-8" +app_logger = traceroot.get_logger("main") + # Log application startup -logger.info("Starting Eigent Multi-Agent System API") -logger.info(f"Python encoding: {os.environ.get('PYTHONIOENCODING')}") -logger.info(f"Environment: {os.environ.get('ENVIRONMENT', 'development')}") +app_logger.info("Starting Eigent Multi-Agent System API") +app_logger.info(f"Python encoding: {os.environ.get('PYTHONIOENCODING')}") +app_logger.info(f"Environment: {os.environ.get('ENVIRONMENT', 'development')}") prefix = env("url_prefix", "") -logger.info(f"Loading routers with prefix: '{prefix}'") +app_logger.info(f"Loading routers with prefix: '{prefix}'") auto_include_routers(api, prefix, "app/controller") -logger.info("All routers loaded successfully") +app_logger.info("All routers loaded successfully") -# Configure Loguru -log_path = os.path.expanduser("~/.eigent/runtime/log/app.log") -os.makedirs(os.path.dirname(log_path), exist_ok=True) -logger.add( - log_path, # Log file - rotation="10 MB", # Log rotation: 10MB per file - retention="10 days", # Retain logs for the last 10 days - level="DEBUG", # Log level - encoding="utf-8", -) -logger.info(f"Loguru configured with log file: {log_path}") dir = pathlib.Path(__file__).parent / "runtime" dir.mkdir(parents=True, exist_ok=True) @@ -44,12 +35,12 @@ async def write_pid_file(): async with aiofiles.open(dir / "run.pid", "w") as f: await f.write(str(os.getpid())) - logger.info(f"PID file written: {os.getpid()}") + app_logger.info(f"PID file written: {os.getpid()}") # Create task to write PID pid_task = asyncio.create_task(write_pid_file()) -logger.info("PID write task created") +app_logger.info("PID write task created") # Graceful shutdown handler shutdown_event = asyncio.Event() @@ -57,8 +48,8 @@ shutdown_event = asyncio.Event() async def cleanup_resources(): r"""Cleanup all resources on shutdown""" - logger.info("Starting graceful shutdown...") - logger.info("Starting graceful shutdown process") + app_logger.info("Starting graceful shutdown...") + app_logger.info("Starting graceful shutdown process") from app.service.task import task_locks, _cleanup_task @@ -75,21 +66,21 @@ async def cleanup_resources(): task_lock = task_locks[task_id] await task_lock.cleanup() except Exception as e: - logger.error(f"Error cleaning up task {task_id}: {e}") + app_logger.error(f"Error cleaning up task {task_id}: {e}") # Remove PID file pid_file = dir / "run.pid" if pid_file.exists(): pid_file.unlink() - logger.info("Graceful shutdown completed") - logger.info("All resources cleaned up successfully") + app_logger.info("Graceful shutdown completed") + app_logger.info("All resources cleaned up successfully") def signal_handler(signum, frame): r"""Handle shutdown signals""" - logger.info(f"Received signal {signum}") - logger.warning(f"Received shutdown signal: {signum}") + app_logger.info(f"Received signal {signum}") + app_logger.warning(f"Received shutdown signal: {signum}") asyncio.create_task(cleanup_resources()) shutdown_event.set() @@ -101,4 +92,4 @@ signal.signal(signal.SIGINT, signal_handler) atexit.register(lambda: asyncio.run(cleanup_resources())) # Log successful initialization -logger.info("Application initialization completed successfully") +app_logger.info("Application initialization completed successfully") diff --git a/server/app/controller/mcp/mcp_controller.py b/server/app/controller/mcp/mcp_controller.py index 1a38c7580..8fdf8b2d7 100644 --- a/server/app/controller/mcp/mcp_controller.py +++ b/server/app/controller/mcp/mcp_controller.py @@ -11,9 +11,11 @@ from app.component.database import session from app.model.mcp.mcp import Mcp, McpOut, McpType from app.model.mcp.mcp_env import McpEnv, Status as McpEnvStatus from app.model.mcp.mcp_user import McpImportType, McpUser, Status -from loguru import logger from camel.toolkits.mcp_toolkit import MCPToolkit from app.component.environment import env +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("server_mcp_controller") from app.component.validator.McpServer import ( McpRemoteServer, diff --git a/server/app/controller/mcp/proxy_controller.py b/server/app/controller/mcp/proxy_controller.py index aa008229a..ea0648fd9 100644 --- a/server/app/controller/mcp/proxy_controller.py +++ b/server/app/controller/mcp/proxy_controller.py @@ -1,11 +1,13 @@ from fastapi import APIRouter, Depends from exa_py import Exa -from loguru import logger from app.component.auth import key_must from app.component.environment import env_not_empty from app.model.mcp.proxy import ExaSearch from typing import Any, cast import requests +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("server_proxy_controller") from app.model.user.key import Key diff --git a/server/app/controller/mcp/user_controller.py b/server/app/controller/mcp/user_controller.py index 12b979abb..f3ff04246 100644 --- a/server/app/controller/mcp/user_controller.py +++ b/server/app/controller/mcp/user_controller.py @@ -7,9 +7,11 @@ from app.component.auth import Auth, auth_must from fastapi_babel import _ from app.model.mcp.mcp_user import McpUser, McpUserIn, McpUserOut, McpUserUpdate, Status from app.model.mcp.mcp import Mcp -from loguru import logger from camel.toolkits.mcp_toolkit import MCPToolkit from app.component.environment import env +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("server_mcp_user_controller") router = APIRouter(tags=["McpUser Management"]) diff --git a/server/app/controller/user/login_controller.py b/server/app/controller/user/login_controller.py index 908e63da9..51fcf9df6 100644 --- a/server/app/controller/user/login_controller.py +++ b/server/app/controller/user/login_controller.py @@ -8,8 +8,10 @@ from app.component.encrypt import password_verify from app.component.stack_auth import StackAuth from app.exception.exception import UserException from app.model.user.user import LoginByPasswordIn, LoginResponse, Status, User, RegisterIn -from loguru import logger from app.component.environment import env +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("server_login_controller") router = APIRouter(tags=["Login/Registration"]) diff --git a/server/app/model/user/user_credits_record.py b/server/app/model/user/user_credits_record.py index bcdb7750d..86dfdcea4 100644 --- a/server/app/model/user/user_credits_record.py +++ b/server/app/model/user/user_credits_record.py @@ -8,7 +8,9 @@ from app.model.abstract.model import AbstractModel, DefaultTimes from datetime import date, datetime, timedelta from app.model.user.key import ModelType from app.component.database import session_make -from loguru import logger +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("user_credits_record") class CreditsChannel(IntEnum): diff --git a/server/main.py b/server/main.py index e440a7b13..4bfdfb4a9 100644 --- a/server/main.py +++ b/server/main.py @@ -1,8 +1,10 @@ from app import api from app.component.environment import auto_include_routers, env -from loguru import logger import os from fastapi.staticfiles import StaticFiles +from app.utils import traceroot_wrapper as traceroot + +logger = traceroot.get_logger("server_main") prefix = env("url_prefix", "") auto_include_routers(api, prefix, "app/controller") @@ -20,11 +22,3 @@ if public_dir and os.path.isdir(public_dir): api.mount("/public", StaticFiles(directory=public_dir), name="public") else: logger.warning("Skipping /public mount because public directory is unavailable") - -logger.add( - "runtime/log/app.log", - rotation="10 MB", - retention="10 days", - level="DEBUG", - enqueue=True, -)