From 414dca6ee4f4ba0c7e4902135595c5c03b4d5c97 Mon Sep 17 00:00:00 2001 From: zhiyong Date: Wed, 30 Apr 2025 16:30:07 +0800 Subject: [PATCH] refactor: logger --- src/logger_config.py | 191 +++++++++++++++++++++++++++++++ src/simulation_trader.py | 37 +----- src/strategy_position_manager.py | 8 +- src/trade_server.py | 107 ++++++----------- src/xt_trader.py | 20 +--- 5 files changed, 238 insertions(+), 125 deletions(-) create mode 100644 src/logger_config.py diff --git a/src/logger_config.py b/src/logger_config.py new file mode 100644 index 0000000..ae96621 --- /dev/null +++ b/src/logger_config.py @@ -0,0 +1,191 @@ +import logging +from config import Config +import os +from logging.handlers import RotatingFileHandler, TimedRotatingFileHandler + +# 确保日志目录存在 +def ensure_log_dir(log_dir): + if not os.path.exists(log_dir): + os.makedirs(log_dir) + return log_dir + +# 创建通用的日志格式化器 +def create_formatter(format_str=None): + if not format_str: + format_str = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' + return logging.Formatter(format_str) + +# 创建服务器日志记录器 +def setup_server_logger(log_dir="logs", max_bytes=10*1024*1024, backup_count=5, level=logging.INFO, log_format=None): + """ + 创建服务器日志记录器 + + Args: + log_dir: 日志目录 + max_bytes: 单个日志文件最大大小 + backup_count: 保留的备份文件数量 + level: 日志级别 + log_format: 日志格式 + + Returns: + logging.Logger: 日志记录器 + """ + log_dir = ensure_log_dir(log_dir) + logger = logging.getLogger('trade_server') + logger.setLevel(level) + + # 清除已有的处理器 + for handler in logger.handlers[:]: + logger.removeHandler(handler) + + # 创建文件处理器 + file_handler = RotatingFileHandler( + os.path.join(log_dir, 'trade_server.log'), + maxBytes=max_bytes, + backupCount=backup_count + ) + formatter = create_formatter(log_format) + file_handler.setFormatter(formatter) + logger.addHandler(file_handler) + + # 添加控制台处理器 + console_handler = logging.StreamHandler() + console_handler.setFormatter(formatter) + logger.addHandler(console_handler) + + return logger + +# 创建真实交易日志记录器 +def setup_real_trader_logger(log_dir="logs", level=logging.INFO, log_format=None): + """ + 创建真实交易日志记录器 + + Args: + log_dir: 日志目录 + level: 日志级别 + log_format: 日志格式 + + Returns: + logging.Logger: 日志记录器 + """ + log_dir = ensure_log_dir(log_dir) + logger = logging.getLogger('real_trader') + logger.setLevel(level) + + # 清除已有的处理器 + for handler in logger.handlers[:]: + logger.removeHandler(handler) + + # 创建每日滚动文件处理器 + file_handler = TimedRotatingFileHandler( + os.path.join(log_dir, 'real_trader.log'), + when="midnight", + interval=1, + backupCount=7, + encoding="utf-8" + ) + file_handler.suffix = "%Y-%m-%d" + formatter = create_formatter(log_format or '%(asctime)s - %(levelname)s - %(message)s') + file_handler.setFormatter(formatter) + logger.addHandler(file_handler) + + # 添加控制台处理器 + console_handler = logging.StreamHandler() + console_handler.setFormatter(formatter) + logger.addHandler(console_handler) + + return logger + +# 创建模拟交易日志记录器 +def setup_simulation_logger(log_dir="logs", max_bytes=10*1024*1024, backup_count=5, level=logging.INFO, log_format=None): + """ + 创建模拟交易日志记录器 + + Args: + log_dir: 日志目录 + max_bytes: 单个日志文件最大大小 + backup_count: 保留的备份文件数量 + level: 日志级别 + log_format: 日志格式 + + Returns: + logging.Logger: 日志记录器 + """ + log_dir = ensure_log_dir(log_dir) + logger = logging.getLogger('simulation_trader') + logger.setLevel(level) + + # 清除已有的处理器 + for handler in logger.handlers[:]: + logger.removeHandler(handler) + + # 创建文件处理器 + file_handler = RotatingFileHandler( + os.path.join(log_dir, 'simulation_trader.log'), + maxBytes=max_bytes, + backupCount=backup_count + ) + formatter = create_formatter(log_format or '%(asctime)s - %(message)s') + file_handler.setFormatter(formatter) + logger.addHandler(file_handler) + + # 添加控制台处理器 + console_handler = logging.StreamHandler() + console_handler.setFormatter(formatter) + logger.addHandler(console_handler) + + return logger + +# 创建策略持仓管理日志记录器 (使用与服务器相同的日志记录器) +def setup_strategy_logger(): + """ + 创建策略持仓管理日志记录器 + + Returns: + logging.Logger: 日志记录器 + """ + return logging.getLogger('trade_server') + +# 根据配置创建合适的日志记录器 +def get_logger(module_name, log_dir="logs", level=Config.LOG_LEVEL if hasattr(Config, 'LOG_LEVEL') else logging.INFO): + """ + 获取适合模块的日志记录器 + + Args: + module_name: 模块名称,如 'server', 'real_trader', 'simulation_trader', 'strategy' + log_dir: 日志目录 + level: 日志级别 + + Returns: + logging.Logger: 日志记录器 + """ + if module_name == 'server': + return setup_server_logger(log_dir, level=level) + elif module_name == 'real_trader': + return setup_real_trader_logger(log_dir, level=level) + elif module_name == 'simulation_trader': + return setup_simulation_logger(log_dir, level=level) + elif module_name == 'strategy': + return setup_strategy_logger() + else: + # 默认日志记录器 + logger = logging.getLogger(module_name) + logger.setLevel(level) + + # 确保日志记录器至少有一个处理器 + if not logger.handlers: + log_dir = ensure_log_dir(log_dir) + handler = logging.StreamHandler() + handler.setFormatter(create_formatter()) + logger.addHandler(handler) + + # 添加文件处理器 + file_handler = RotatingFileHandler( + os.path.join(log_dir, f'{module_name}.log'), + maxBytes=10*1024*1024, + backupCount=5 + ) + file_handler.setFormatter(create_formatter()) + logger.addHandler(file_handler) + + return logger \ No newline at end of file diff --git a/src/simulation_trader.py b/src/simulation_trader.py index 8699b49..3b5cb3b 100644 --- a/src/simulation_trader.py +++ b/src/simulation_trader.py @@ -1,45 +1,14 @@ -import logging -from logging.handlers import RotatingFileHandler -import os -import time + +from logger_config import get_logger class SimulationTrader: def __init__(self, logger=None): - self.logger = logger or self._setup_default_logger() + self.logger = logger or get_logger('simulation_trader') # 添加模拟持仓字典,用于追踪模拟交易的持仓 self.sim_positions = {} # 模拟资金账户信息 self.sim_balance = {"cash": 1000000.00, "frozen": 0.00, "total": 1000000.00} - def _setup_default_logger(self): - """设置默认日志记录器""" - sim_logger = logging.getLogger('trade_simulation') - sim_logger.setLevel(logging.INFO) - - # 确保没有重复的处理器 - for handler in sim_logger.handlers[:]: - sim_logger.removeHandler(handler) - - # 文件处理器 - log_dir = "logs" - if not os.path.exists(log_dir): - os.makedirs(log_dir) - - file_handler = RotatingFileHandler( - os.path.join(log_dir, 'trade_simulation.log'), - maxBytes=10*1024*1024, # 10MB - backupCount=5 - ) - file_handler.setFormatter(logging.Formatter('%(asctime)s - %(message)s')) - sim_logger.addHandler(file_handler) - - # 控制台处理器 - console_handler = logging.StreamHandler() - console_handler.setFormatter(logging.Formatter('%(asctime)s - %(message)s')) - sim_logger.addHandler(console_handler) - - return sim_logger - def is_logged_in(self): """检查交易系统是否已经登录 diff --git a/src/strategy_position_manager.py b/src/strategy_position_manager.py index 5de0482..cb1226d 100644 --- a/src/strategy_position_manager.py +++ b/src/strategy_position_manager.py @@ -1,9 +1,12 @@ import time import os import json -import logging from simulation_trader import SimulationTrader from xtquant import xtconstant +from logger_config import get_logger + +# 获取日志记录器 +logger = get_logger('strategy') # 策略仓位管理 strategy_positions = { @@ -19,9 +22,6 @@ pending_orders = { 'simulation': {} # 存储模拟交易未完成委托 } -# 获取日志记录器 -logger = logging.getLogger('trade_server') - class StrategyPositionManager: """策略持仓管理器,负责管理不同策略的持仓情况""" diff --git a/src/trade_server.py b/src/trade_server.py index 1c6616f..d99dde2 100644 --- a/src/trade_server.py +++ b/src/trade_server.py @@ -3,26 +3,50 @@ import threading import time from xt_trader import XtTrader from flask import Flask, request, abort, jsonify -from flask_limiter import Limiter -from flask_limiter.util import get_remote_address from config import Config -import logging -import os -from logging.handlers import RotatingFileHandler -import concurrent.futures from concurrent.futures import TimeoutError -import json +import concurrent.futures import atexit from simulation_trader import SimulationTrader import datetime -from xtquant import xtconstant from strategy_position_manager import StrategyPositionManager +from logger_config import get_logger +# 获取日志记录器 +logger = get_logger('server') # 全局交易实例(采用单例模式) _sim_trader_instance = None # 模拟交易实例(单例) _real_trader_instance = None # 实盘交易实例(单例) +# 获取模拟交易实例的辅助函数 +def get_sim_trader(): + """获取模拟交易实例 - 保证单例模式 + + Returns: + 返回模拟交易单例实例 + """ + global _sim_trader_instance + if _sim_trader_instance is None: + _sim_trader_instance = SimulationTrader() + return _sim_trader_instance + +# 获取实盘交易实例的辅助函数 +def get_real_trader(): + """获取实盘交易实例 - 保证单例模式 + + Returns: + 返回实盘交易单例实例 + """ + global _real_trader_instance + if _real_trader_instance is None: + _real_trader_instance = XtTrader() + # 检查交易实例是否已登录,如果未登录则进行登录 + if not _real_trader_instance.is_logged_in(): + logger.info("创建新的XtTrader实例并登录") + _real_trader_instance.login() + return _real_trader_instance + # 获取交易实例 def get_trader(use_sim_trader=False): """获取交易实例 - 采用单例模式 @@ -33,21 +57,13 @@ def get_trader(use_sim_trader=False): Returns: 返回交易实例,根据参数和配置决定是模拟交易还是实盘交易 """ - global _sim_trader_instance, _real_trader_instance - # 如果强制使用模拟交易,返回模拟交易单例 if use_sim_trader: - # 如果模拟交易实例不存在,创建一个 - if _sim_trader_instance is None: - _sim_trader_instance = SimulationTrader() - return _sim_trader_instance + return get_sim_trader() # 如果配置为仅模拟交易,返回模拟交易单例 if Config.SIMULATION_ONLY: - # 如果模拟交易实例不存在,创建一个 - if _sim_trader_instance is None: - _sim_trader_instance = SimulationTrader() - return _sim_trader_instance + return get_sim_trader() # 判断当前是否为交易时间 now = datetime.datetime.now() @@ -72,59 +88,10 @@ def get_trader(use_sim_trader=False): # 如果不是交易日或不在交易时间内,返回模拟交易单例 if not is_trading_day or not is_trading_hour: logger.info(f"当前非交易时段 - 日期: {now.date()}, 时间: {current_time}, 使用模拟交易") - # 如果模拟交易实例不存在,创建一个 - if _sim_trader_instance is None: - _sim_trader_instance = SimulationTrader() - return _sim_trader_instance + return get_sim_trader() - # 否则返回真实交易单例(如果存在)或创建一个新的 - if _real_trader_instance is None: - _real_trader_instance = XtTrader() - # 检查交易实例是否已登录,如果未登录则进行登录 - if not _real_trader_instance.is_logged_in(): - logger.info("创建新的XtTrader实例并登录") - _real_trader_instance.login() - - return _real_trader_instance - -# 配置日志 -def setup_logger(): - log_dir = Config.LOG_DIR - try: - if not os.path.exists(log_dir): - os.makedirs(log_dir) - logger = logging.getLogger('trade_server') - logger.info(f"Created logs directory at: {os.path.abspath(log_dir)}") - except Exception as e: - print(f"Error creating logs directory: {str(e)}") - # 如果无法创建目录,使用当前目录 - log_dir = "." - - logger = logging.getLogger('trade_server') - logger.setLevel(getattr(logging, Config.LOG_LEVEL)) - - # 确保没有重复的处理器 - for handler in logger.handlers[:]: - logger.removeHandler(handler) - - # 文件处理器 - file_handler = RotatingFileHandler( - os.path.join(log_dir, 'trade_server.log'), - maxBytes=Config.LOG_MAX_BYTES, - backupCount=Config.LOG_BACKUP_COUNT - ) - file_handler.setFormatter(logging.Formatter(Config.LOG_FORMAT)) - logger.addHandler(file_handler) - - # 添加控制台处理器 - console_handler = logging.StreamHandler() - console_handler.setFormatter(logging.Formatter(Config.LOG_FORMAT)) - logger.addHandler(console_handler) - - return logger - -logger = setup_logger() - + # 否则返回真实交易单例 + return get_real_trader() def run_daily(time_str, job_func): """设置每天在指定时间运行的任务 diff --git a/src/xt_trader.py b/src/xt_trader.py index acd8bf3..42e6324 100644 --- a/src/xt_trader.py +++ b/src/xt_trader.py @@ -1,7 +1,5 @@ import os import random -import logging -from logging.handlers import TimedRotatingFileHandler from config import Config from xtquant.xttrader import XtQuantTrader from xtquant.xttype import StockAccount @@ -9,22 +7,10 @@ from xtquant import xtconstant from xtquant.xtdata import get_instrument_detail, get_trading_time import datetime as dt from chinese_calendar import is_workday +from logger_config import get_logger -# 日志配置 -LOG_DIR = "log" -if not os.path.exists(LOG_DIR): - os.makedirs(LOG_DIR) -log_path = os.path.join(LOG_DIR, "%Y-%m-%d.log") -logger = logging.getLogger("xt_trader") -logger.setLevel(logging.INFO) -handler = TimedRotatingFileHandler( - os.path.join(LOG_DIR, "xt_trader.log"), when="midnight", interval=1, backupCount=7, encoding="utf-8" -) -handler.suffix = "%Y-%m-%d" -formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') -handler.setFormatter(formatter) -if not logger.handlers: - logger.addHandler(handler) +# 获取日志记录器 +logger = get_logger('real_trader') class MyXtQuantTraderCallback: def on_connected(self):