feat: 增加log 配置
This commit is contained in:
parent
85619c0d97
commit
30fc1779f4
|
|
@ -1,7 +1,7 @@
|
|||
# 配置文件
|
||||
app:
|
||||
secret_key: ${APP_SECRET_KEY}
|
||||
debug: true
|
||||
debug: false
|
||||
host: 0.0.0.0
|
||||
port: 8000
|
||||
|
||||
|
|
@ -19,3 +19,7 @@ tools:
|
|||
cache_ttl: 300
|
||||
max_workers: 4
|
||||
max_iterations: 10
|
||||
|
||||
|
||||
logging:
|
||||
level: INFO
|
||||
|
|
|
|||
|
|
@ -323,7 +323,8 @@ body {
|
|||
.message-bubble.assistant .message-container {
|
||||
align-items: flex-start;
|
||||
flex: 1 1 auto;
|
||||
width: 100%;
|
||||
width: 80%;
|
||||
max-width: 80%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -99,10 +99,15 @@ export function createSSEStream(url, body, { onProcessStep, onDone, onError }) {
|
|||
onError(data.content)
|
||||
}
|
||||
} catch (e) {
|
||||
// 忽略解析错误
|
||||
console.error('SSE parse error:', e, 'line:', line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有收到 done 事件,触发错误
|
||||
if (!completed && onError) {
|
||||
onError('stream ended without done event')
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""FastAPI application factory"""
|
||||
import logging
|
||||
from contextlib import asynccontextmanager
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
|
@ -7,6 +8,8 @@ from luxx.config import config
|
|||
from luxx.database import init_db
|
||||
from luxx.routes import api_router
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
|
|
@ -31,7 +34,7 @@ async def lifespan(app: FastAPI):
|
|||
)
|
||||
db.add(default_user)
|
||||
db.commit()
|
||||
print("Default admin user created: admin / admin123")
|
||||
logger.info("Default admin user created: admin / admin123")
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
|
|
|||
|
|
@ -112,6 +112,19 @@ class Config:
|
|||
def tools_max_iterations(self) -> int:
|
||||
return self.get("tools.max_iterations", 10)
|
||||
|
||||
# Logging configuration
|
||||
@property
|
||||
def log_level(self) -> str:
|
||||
return self.get("logging.level", "INFO")
|
||||
|
||||
@property
|
||||
def log_format(self) -> str:
|
||||
return self.get("logging.format", "%(asctime)s | %(levelname)-8s | %(message)s")
|
||||
|
||||
@property
|
||||
def log_date_format(self) -> str:
|
||||
return self.get("logging.date_format", "%Y-%m-%d %H:%M:%S")
|
||||
|
||||
|
||||
# Global configuration instance
|
||||
config = Config()
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
"""LLM API client"""
|
||||
import json
|
||||
import httpx
|
||||
import logging
|
||||
from typing import Dict, Any, Optional, List, AsyncGenerator
|
||||
|
||||
from luxx.config import config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LLMResponse:
|
||||
"""LLM response"""
|
||||
|
|
@ -147,19 +150,18 @@ class LLMClient:
|
|||
"""
|
||||
body = self._build_body(model, messages, tools, stream=True, **kwargs)
|
||||
|
||||
print(f"[LLM] Starting stream_call for model: {model}")
|
||||
print(f"[LLM] Messages count: {len(messages)}")
|
||||
logger.info(f"Starting stream_call for model: {model}, messages count: {len(messages)}")
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=120.0) as client:
|
||||
print(f"[LLM] Sending request to {self.api_url}")
|
||||
logger.info(f"Sending request to {self.api_url}")
|
||||
async with client.stream(
|
||||
"POST",
|
||||
self.api_url,
|
||||
headers=self._build_headers(),
|
||||
json=body
|
||||
) as response:
|
||||
print(f"[LLM] Response status: {response.status_code}")
|
||||
logger.info(f"Response status: {response.status_code}")
|
||||
response.raise_for_status()
|
||||
|
||||
async for line in response.aiter_lines():
|
||||
|
|
@ -167,10 +169,10 @@ class LLMClient:
|
|||
yield line + "\n"
|
||||
except httpx.HTTPStatusError as e:
|
||||
status_code = e.response.status_code if e.response else "?"
|
||||
print(f"[LLM] HTTP error: {status_code}")
|
||||
logger.error(f"HTTP error: {status_code}")
|
||||
yield f"event: error\ndata: {json.dumps({'content': f'HTTP {status_code}: Request failed'})}\n\n"
|
||||
except Exception as e:
|
||||
print(f"[LLM] Exception: {type(e).__name__}: {str(e)}")
|
||||
logger.error(f"Exception: {type(e).__name__}: {str(e)}")
|
||||
yield f"event: error\ndata: {json.dumps({'content': str(e)})}\n\n"
|
||||
|
||||
|
||||
|
|
|
|||
75
run.py
75
run.py
|
|
@ -1,17 +1,90 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Application entry point"""
|
||||
import logging
|
||||
import logging.config
|
||||
from copy import copy
|
||||
import uvicorn
|
||||
from uvicorn.logging import ColourizedFormatter
|
||||
from luxx.config import config
|
||||
|
||||
|
||||
# Custom formatter extending uvicorn's ColourizedFormatter
|
||||
class ModuleFormatter(ColourizedFormatter):
|
||||
"""Add module name after level prefix for non-uvicorn loggers"""
|
||||
|
||||
def formatMessage(self, record: logging.LogRecord) -> str:
|
||||
# Copy record to avoid modifying the original
|
||||
recordcopy = copy(record)
|
||||
|
||||
# Get level name with color
|
||||
levelname = recordcopy.levelname
|
||||
separator = " " * (8 - len(recordcopy.levelname))
|
||||
if self.use_colors:
|
||||
levelname = self.color_level_name(levelname, recordcopy.levelno)
|
||||
if "color_message" in recordcopy.__dict__:
|
||||
recordcopy.msg = recordcopy.__dict__["color_message"]
|
||||
recordcopy.__dict__["message"] = recordcopy.getMessage()
|
||||
|
||||
# Add module name for all loggers
|
||||
name = record.name
|
||||
# For uvicorn.error, show "uvicorn" not "error"
|
||||
if name.startswith('uvicorn.'):
|
||||
module = 'uvicorn'
|
||||
else:
|
||||
module = name
|
||||
levelprefix = levelname + ":" + separator
|
||||
recordcopy.__dict__["levelprefix"] = f"{levelprefix} [{module}]"
|
||||
|
||||
return super(ColourizedFormatter, self).formatMessage(recordcopy)
|
||||
|
||||
|
||||
def get_log_config() -> dict:
|
||||
"""Get log configuration from config.yaml"""
|
||||
log_level = getattr(config, 'log_level', 'INFO')
|
||||
|
||||
return {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"default": {
|
||||
"()": ModuleFormatter,
|
||||
"fmt": "%(levelprefix)s %(message)s",
|
||||
},
|
||||
},
|
||||
"handlers": {
|
||||
"default": {
|
||||
"formatter": "default",
|
||||
"class": "logging.StreamHandler",
|
||||
"stream": "ext://sys.stderr",
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"uvicorn": {"handlers": ["default"], "level": log_level, "propagate": False},
|
||||
"uvicorn.error": {"handlers": ["default"], "level": log_level, "propagate": False},
|
||||
"uvicorn.access": {"handlers": [], "level": "WARNING", "propagate": False},
|
||||
"uvicorn.asgi": {"handlers": [], "level": "WARNING", "propagate": False},
|
||||
},
|
||||
"root": {
|
||||
"handlers": ["default"],
|
||||
"level": log_level,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
LOG_CONFIG = get_log_config()
|
||||
|
||||
|
||||
def main():
|
||||
"""Start the application"""
|
||||
logging.config.dictConfig(LOG_CONFIG)
|
||||
|
||||
uvicorn.run(
|
||||
"luxx:app",
|
||||
host=config.app_host,
|
||||
port=config.app_port,
|
||||
reload=config.debug,
|
||||
log_level="debug" if config.debug else "info"
|
||||
log_level=config.log_level.lower(),
|
||||
log_config=LOG_CONFIG
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue