76bc979cfe
- 从生产服务器下载 emotion-single.log 到本地 logs/server/ - 自动创建目录、备份旧日志、显示文件大小信息 - 使用 scp 命令,依赖已配置的 SSH 免密登录
168 lines
4.9 KiB
Python
168 lines
4.9 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
服务器日志下载脚本
|
||
|
||
从生产服务器下载后端日志到本地 logs/server/ 目录
|
||
|
||
使用方法:
|
||
python tools/download-server-log.py
|
||
|
||
Author: Peanut
|
||
Created: 2026-06-03
|
||
Purpose: 从服务器下载 emotion-single.log 到本地,方便排查问题
|
||
"""
|
||
|
||
import shutil
|
||
import subprocess
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
# ============================================================================
|
||
# 配置
|
||
# ============================================================================
|
||
SERVER_IP = "101.200.208.45"
|
||
USERNAME = "root"
|
||
REMOTE_LOG = "/data/logs/emotion-museum/emotion-single.log"
|
||
|
||
# 本地路径(项目根目录下的 logs/server/)
|
||
PROJECT_DIR = Path(__file__).parent.parent.absolute()
|
||
LOCAL_LOG_DIR = PROJECT_DIR / "logs" / "server"
|
||
LOCAL_LOG = LOCAL_LOG_DIR / "emotion-single.log"
|
||
BACKUP_LOG = LOCAL_LOG_DIR / "emotion-single.log.bak"
|
||
|
||
|
||
# ============================================================================
|
||
# 日志输出
|
||
# ============================================================================
|
||
def log_info(msg):
|
||
print(f"\033[32m[INFO]\033[0m {msg}")
|
||
|
||
|
||
def log_warn(msg):
|
||
print(f"\033[33m[WARN]\033[0m {msg}")
|
||
|
||
|
||
def log_error(msg):
|
||
print(f"\033[31m[ERROR]\033[0m {msg}")
|
||
|
||
|
||
# ============================================================================
|
||
# 核心逻辑
|
||
# ============================================================================
|
||
def check_ssh():
|
||
"""检查 SSH 连接是否正常"""
|
||
try:
|
||
result = subprocess.run(
|
||
["ssh", "-o", "BatchMode=yes", "-o", "ConnectTimeout=10",
|
||
f"{USERNAME}@{SERVER_IP}", "echo ok"],
|
||
capture_output=True, text=True, timeout=15
|
||
)
|
||
if result.returncode == 0:
|
||
log_info("SSH 连接正常")
|
||
return True
|
||
else:
|
||
log_error(f"SSH 连接失败: {result.stderr.strip()}")
|
||
log_error(f"请确认已配置免密登录: ssh {USERNAME}@{SERVER_IP}")
|
||
return False
|
||
except subprocess.TimeoutExpired:
|
||
log_error("SSH 连接超时")
|
||
return False
|
||
except FileNotFoundError:
|
||
log_error("未找到 ssh 命令,请确认已安装 OpenSSH 客户端")
|
||
return False
|
||
|
||
|
||
def get_remote_file_size():
|
||
"""获取远程日志文件大小(字节)"""
|
||
try:
|
||
result = subprocess.run(
|
||
["ssh", "-o", "BatchMode=yes", "-o", "ConnectTimeout=10",
|
||
f"{USERNAME}@{SERVER_IP}", f"stat -c %s {REMOTE_LOG} 2>/dev/null || echo -1"],
|
||
capture_output=True, text=True, timeout=15
|
||
)
|
||
size = int(result.stdout.strip())
|
||
return size if size > 0 else -1
|
||
except (subprocess.TimeoutExpired, ValueError, OSError) as e:
|
||
log_error(f"获取远程文件大小失败: {e}")
|
||
return -1
|
||
|
||
|
||
def format_size(size_bytes):
|
||
"""格式化文件大小"""
|
||
if size_bytes < 0:
|
||
return "未知"
|
||
for unit in ["B", "KB", "MB", "GB"]:
|
||
if size_bytes < 1024:
|
||
return f"{size_bytes:.1f} {unit}"
|
||
size_bytes /= 1024
|
||
return f"{size_bytes:.1f} TB"
|
||
|
||
|
||
def backup_local_log():
|
||
"""备份本地旧日志文件"""
|
||
if LOCAL_LOG.exists():
|
||
shutil.copy2(LOCAL_LOG, BACKUP_LOG)
|
||
log_info(f"已备份旧日志: {BACKUP_LOG}")
|
||
else:
|
||
log_info("本地无旧日志,跳过备份")
|
||
|
||
|
||
def download_log():
|
||
"""下载远程日志文件"""
|
||
LOCAL_LOG_DIR.mkdir(parents=True, exist_ok=True)
|
||
log_info(f"本地目录: {LOCAL_LOG_DIR}")
|
||
|
||
backup_local_log()
|
||
|
||
log_info(f"正在下载: {USERNAME}@{SERVER_IP}:{REMOTE_LOG}")
|
||
log_info(f" -> {LOCAL_LOG}")
|
||
|
||
try:
|
||
result = subprocess.run(
|
||
["scp", "-o", "BatchMode=yes", "-o", "ConnectTimeout=10",
|
||
f"{USERNAME}@{SERVER_IP}:{REMOTE_LOG}", str(LOCAL_LOG)],
|
||
capture_output=True, text=True, timeout=300
|
||
)
|
||
if result.returncode == 0:
|
||
local_size = LOCAL_LOG.stat().st_size
|
||
log_info(f"下载成功: {format_size(local_size)}")
|
||
return True
|
||
else:
|
||
log_error(f"下载失败: {result.stderr.strip()}")
|
||
return False
|
||
except subprocess.TimeoutExpired:
|
||
log_error("下载超时(300 秒)")
|
||
return False
|
||
except Exception as e:
|
||
log_error(f"下载异常: {e}")
|
||
return False
|
||
|
||
|
||
def main():
|
||
log_info("=== 情绪博物馆 - 日志下载工具 ===")
|
||
log_info(f"服务器: {USERNAME}@{SERVER_IP}")
|
||
log_info(f"远程日志: {REMOTE_LOG}")
|
||
|
||
# 检查 SSH
|
||
if not check_ssh():
|
||
sys.exit(1)
|
||
|
||
# 检查远程文件
|
||
remote_size = get_remote_file_size()
|
||
if remote_size < 0:
|
||
log_error(f"远程日志文件不存在或无法访问: {REMOTE_LOG}")
|
||
sys.exit(1)
|
||
log_info(f"远程日志大小: {format_size(remote_size)}")
|
||
|
||
# 下载
|
||
if download_log():
|
||
log_info("=== 下载完成 ===")
|
||
else:
|
||
log_error("下载失败")
|
||
sys.exit(1)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|