feat: unified logging module and remove redundant first_run logic
- Add logger.py with TimedRotatingFileHandler (daily rotation, 30-day retention) - Replace all print() in migrate.py with logging calls - Add logging to sync.py for sync status tracking - Refactor watch.py to use logger.py, remove duplicate first_run branch - All logs now go to logs/app.log consistently - Add logging config section to config.yaml, add logs/ to .gitignore Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
108
watch.py
108
watch.py
@@ -5,41 +5,16 @@ Watch daemon - periodically syncs LAN Excel files and migrates changed tables to
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
import yaml
|
||||
|
||||
from logger import setup_logger
|
||||
from migrate import load_config, get_connection, create_schema, migrate_table
|
||||
from sync import sync_all_files, determine_tables_to_migrate
|
||||
|
||||
|
||||
def setup_logging(log_file):
|
||||
"""Configure logging to both console and file."""
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
fmt = logging.Formatter(
|
||||
'%(asctime)s [%(levelname)s] %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S'
|
||||
)
|
||||
|
||||
# Console handler
|
||||
ch = logging.StreamHandler(sys.stdout)
|
||||
ch.setLevel(logging.INFO)
|
||||
ch.setFormatter(fmt)
|
||||
logger.addHandler(ch)
|
||||
|
||||
# File handler
|
||||
fh = logging.FileHandler(log_file, encoding='utf-8')
|
||||
fh.setLevel(logging.INFO)
|
||||
fh.setFormatter(fmt)
|
||||
logger.addHandler(fh)
|
||||
|
||||
return logger
|
||||
logger = logging.getLogger('app')
|
||||
|
||||
|
||||
def run_once(cfg, logger):
|
||||
def run_once(cfg):
|
||||
"""Execute one sync + migrate cycle."""
|
||||
excel_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Excel')
|
||||
|
||||
@@ -47,14 +22,6 @@ def run_once(cfg, logger):
|
||||
logger.info("开始同步 LAN 文件...")
|
||||
sync_results = sync_all_files(cfg, excel_dir)
|
||||
|
||||
for rel_path, (status, error_msg) in sync_results.items():
|
||||
if status == 'error':
|
||||
logger.error(f" 同步失败: {rel_path} - {error_msg}")
|
||||
elif status in ('copied', 'updated'):
|
||||
logger.info(f" {status}: {rel_path}")
|
||||
else:
|
||||
logger.debug(f" 跳过: {rel_path}")
|
||||
|
||||
# 2. Determine which tables need migration
|
||||
tables_to_migrate = determine_tables_to_migrate(cfg, sync_results)
|
||||
|
||||
@@ -78,9 +45,9 @@ def run_once(cfg, logger):
|
||||
|
||||
try:
|
||||
rows = migrate_table(cursor, table_cfg, cfg, use_truncate=True)
|
||||
logger.info(f" 迁移完成: {table_name} ({rows:,} 行)")
|
||||
logger.info(f"迁移完成: {table_name} ({rows:,} 行)")
|
||||
except Exception as e:
|
||||
logger.error(f" 迁移失败: {table_name} - {e}", exc_info=True)
|
||||
logger.error(f"迁移失败: {table_name} - {e}", exc_info=True)
|
||||
# Rollback any uncommitted transaction for this table
|
||||
try:
|
||||
conn.rollback()
|
||||
@@ -106,12 +73,18 @@ def main():
|
||||
help='覆盖配置文件中的 check_interval_minutes')
|
||||
args = parser.parse_args()
|
||||
|
||||
# Setup logging
|
||||
log_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'watch.log')
|
||||
logger = setup_logging(log_file)
|
||||
|
||||
# Load config
|
||||
cfg = load_config(args.config)
|
||||
|
||||
# Setup logging
|
||||
log_cfg = cfg.get('logging', {})
|
||||
setup_logger(
|
||||
'app',
|
||||
log_dir=log_cfg.get('log_dir'),
|
||||
level=log_cfg.get('level', 'INFO'),
|
||||
max_keep_days=log_cfg.get('max_keep_days', 30),
|
||||
)
|
||||
|
||||
interval = args.interval or cfg.get('check_interval_minutes', 30)
|
||||
|
||||
logger.info("=" * 50)
|
||||
@@ -119,58 +92,9 @@ def main():
|
||||
logger.info(f" 同步间隔: {interval} 分钟")
|
||||
logger.info("=" * 50)
|
||||
|
||||
first_run = True
|
||||
|
||||
while True:
|
||||
try:
|
||||
if first_run:
|
||||
logger.info("首次运行,强制同步并迁移所有表...")
|
||||
# On first run, force all tables to migrate by marking all files as 'updated'
|
||||
excel_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Excel')
|
||||
sync_results = sync_all_files(cfg, excel_dir)
|
||||
|
||||
# Log sync results
|
||||
for rel_path, (status, error_msg) in sync_results.items():
|
||||
if status == 'error':
|
||||
logger.error(f" 同步失败: {rel_path} - {error_msg}")
|
||||
else:
|
||||
logger.info(f" {status}: {rel_path}")
|
||||
|
||||
# Force all tables to migrate on first run
|
||||
all_tables = {t['table'] for t in cfg['tables']}
|
||||
|
||||
conn = None
|
||||
try:
|
||||
conn = get_connection(cfg)
|
||||
cursor = conn.cursor()
|
||||
create_schema(cursor, cfg['schema'])
|
||||
|
||||
for table_cfg in cfg['tables']:
|
||||
table_name = table_cfg['table']
|
||||
try:
|
||||
rows = migrate_table(cursor, table_cfg, cfg, use_truncate=True)
|
||||
logger.info(f" 迁移完成: {table_name} ({rows:,} 行)")
|
||||
except Exception as e:
|
||||
logger.error(f" 迁移失败: {table_name} - {e}", exc_info=True)
|
||||
try:
|
||||
conn.rollback()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
cursor.close()
|
||||
except Exception as e:
|
||||
logger.error(f"数据库连接失败: {e}", exc_info=True)
|
||||
finally:
|
||||
if conn:
|
||||
try:
|
||||
conn.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
first_run = False
|
||||
else:
|
||||
run_once(cfg, logger)
|
||||
|
||||
run_once(cfg)
|
||||
except Exception as e:
|
||||
logger.error(f"运行周期异常: {e}", exc_info=True)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user