638 lines
26 KiB
Python
638 lines
26 KiB
Python
import os
|
||
from loguru import logger
|
||
from PySide6.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit,
|
||
QPushButton, QComboBox, QMessageBox, QTextEdit,
|
||
QGroupBox, QGridLayout, QProgressBar, QDialog,
|
||
QDialogButtonBox)
|
||
from PySide6.QtCore import Qt
|
||
|
||
from threads import (GunicornInstallThread, GunicornTestThread,
|
||
UploadGunicornServiceThread, ManageGunicornServiceThread)
|
||
|
||
|
||
class PasswordDialog(QDialog):
|
||
def __init__(self, parent=None):
|
||
super().__init__(parent)
|
||
self.setWindowTitle("输入密码")
|
||
self.setMinimumWidth(300)
|
||
|
||
layout = QVBoxLayout()
|
||
|
||
# 密码输入
|
||
password_layout = QHBoxLayout()
|
||
password_layout.addWidget(QLabel("密码:"))
|
||
self.password_input = QLineEdit()
|
||
self.password_input.setEchoMode(QLineEdit.Password)
|
||
password_layout.addWidget(self.password_input)
|
||
layout.addLayout(password_layout)
|
||
|
||
# 按钮
|
||
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||
button_box.accepted.connect(self.accept)
|
||
button_box.rejected.connect(self.reject)
|
||
layout.addWidget(button_box)
|
||
|
||
self.setLayout(layout)
|
||
|
||
def get_password(self):
|
||
return self.password_input.text()
|
||
|
||
|
||
class GunicornTab(QWidget):
|
||
def __init__(self, parent=None):
|
||
super().__init__(parent)
|
||
self.parent = parent
|
||
self.init_ui()
|
||
|
||
# 连接服务器切换信号
|
||
if self.parent and hasattr(self.parent, 'server_changed'):
|
||
self.parent.server_changed.connect(self.on_server_changed)
|
||
logger.info("Gunicorn标签已连接到服务器切换信号")
|
||
def init_ui(self):
|
||
layout = QVBoxLayout()
|
||
|
||
# Gunicorn配置组
|
||
config_group = QGroupBox("Gunicorn配置")
|
||
config_layout = QGridLayout()
|
||
|
||
config_layout.addWidget(QLabel("Django项目路径:"), 0, 0)
|
||
self.django_path_input = QLineEdit()
|
||
self.django_path_input.setPlaceholderText("/home/user/django_project")
|
||
config_layout.addWidget(self.django_path_input, 0, 1)
|
||
|
||
config_layout.addWidget(QLabel("服务名称:"), 1, 0)
|
||
self.service_name_input = QLineEdit("django_gunicorn")
|
||
config_layout.addWidget(self.service_name_input, 1, 1)
|
||
|
||
config_layout.addWidget(QLabel("端口:"), 2, 0)
|
||
self.port_input = QLineEdit("8000")
|
||
config_layout.addWidget(self.port_input, 2, 1)
|
||
|
||
config_layout.addWidget(QLabel("工作进程数:"), 3, 0)
|
||
self.workers_input = QLineEdit("3")
|
||
config_layout.addWidget(self.workers_input, 3, 1)
|
||
|
||
self.load_config_btn = QPushButton("加载配置")
|
||
self.load_config_btn.clicked.connect(self.load_gunicorn_config)
|
||
config_layout.addWidget(self.load_config_btn, 4, 1)
|
||
|
||
config_group.setLayout(config_layout)
|
||
layout.addWidget(config_group)
|
||
|
||
# Gunicorn操作组
|
||
gunicorn_group = QGroupBox("Gunicorn操作")
|
||
gunicorn_layout = QGridLayout()
|
||
|
||
self.install_gunicorn_btn = QPushButton("安装Gunicorn")
|
||
self.install_gunicorn_btn.clicked.connect(self.install_gunicorn)
|
||
gunicorn_layout.addWidget(self.install_gunicorn_btn, 0, 0)
|
||
|
||
self.test_gunicorn_btn = QPushButton("测试Gunicorn")
|
||
self.test_gunicorn_btn.clicked.connect(self.test_gunicorn)
|
||
gunicorn_layout.addWidget(self.test_gunicorn_btn, 0, 1)
|
||
|
||
self.upload_service_btn = QPushButton("上传服务文件")
|
||
self.upload_service_btn.clicked.connect(self.upload_service_file)
|
||
gunicorn_layout.addWidget(self.upload_service_btn, 1, 0)
|
||
|
||
self.start_service_btn = QPushButton("启动服务")
|
||
self.start_service_btn.clicked.connect(lambda: self.manage_service("start"))
|
||
gunicorn_layout.addWidget(self.start_service_btn, 1, 1)
|
||
|
||
self.stop_service_btn = QPushButton("停止服务")
|
||
self.stop_service_btn.clicked.connect(lambda: self.manage_service("stop"))
|
||
gunicorn_layout.addWidget(self.stop_service_btn, 2, 0)
|
||
|
||
self.restart_service_btn = QPushButton("重启服务")
|
||
self.restart_service_btn.clicked.connect(lambda: self.manage_service("restart"))
|
||
gunicorn_layout.addWidget(self.restart_service_btn, 2, 1)
|
||
|
||
self.status_service_btn = QPushButton("查看服务状态")
|
||
self.status_service_btn.clicked.connect(lambda: self.manage_service("status"))
|
||
gunicorn_layout.addWidget(self.status_service_btn, 3, 0)
|
||
|
||
self.enable_service_btn = QPushButton("启用开机自启")
|
||
self.enable_service_btn.clicked.connect(lambda: self.manage_service("enable"))
|
||
gunicorn_layout.addWidget(self.enable_service_btn, 3, 1)
|
||
|
||
gunicorn_group.setLayout(gunicorn_layout)
|
||
layout.addWidget(gunicorn_group)
|
||
|
||
# 服务文件编辑器
|
||
service_group = QGroupBox("Gunicorn服务文件")
|
||
service_layout = QVBoxLayout()
|
||
|
||
self.service_editor = QTextEdit()
|
||
self.service_editor.setPlaceholderText("Gunicorn服务文件内容将在这里显示...")
|
||
service_layout.addWidget(self.service_editor)
|
||
|
||
service_group.setLayout(service_layout)
|
||
layout.addWidget(service_group)
|
||
|
||
# 操作输出
|
||
output_group = QGroupBox("操作输出")
|
||
output_layout = QVBoxLayout()
|
||
|
||
self.output_text = QTextEdit()
|
||
self.output_text.setReadOnly(True)
|
||
self.output_text.setPlaceholderText("操作结果将在这里显示...")
|
||
output_layout.addWidget(self.output_text)
|
||
|
||
# 进度条
|
||
self.progress_bar = QProgressBar()
|
||
self.progress_bar.setVisible(False)
|
||
output_layout.addWidget(self.progress_bar)
|
||
|
||
output_group.setLayout(output_layout)
|
||
layout.addWidget(output_group)
|
||
|
||
# 日志查看组
|
||
log_group = QGroupBox("日志查看")
|
||
log_layout = QGridLayout()
|
||
|
||
self.view_access_log_btn = QPushButton("查看访问日志")
|
||
self.view_access_log_btn.clicked.connect(self.view_access_log)
|
||
log_layout.addWidget(self.view_access_log_btn, 0, 0)
|
||
|
||
self.view_error_log_btn = QPushButton("查看错误日志")
|
||
self.view_error_log_btn.clicked.connect(self.view_error_log)
|
||
log_layout.addWidget(self.view_error_log_btn, 0, 1)
|
||
|
||
log_group.setLayout(log_layout)
|
||
layout.addWidget(log_group)
|
||
|
||
layout.addStretch()
|
||
self.setLayout(layout)
|
||
|
||
# 加载Gunicorn配置
|
||
self.load_gunicorn_config()
|
||
|
||
def load_gunicorn_config(self):
|
||
"""加载Gunicorn配置,使用config.json中的值"""
|
||
try:
|
||
if self.parent and hasattr(self.parent, 'get_current_config'):
|
||
config = self.parent.get_current_config()
|
||
if config:
|
||
# 使用remote_directory而不是django_path
|
||
django_path = config.get('remote_directory', '')
|
||
project_name = config.get('project_name', 'myproject')
|
||
username = config.get('username', 'www-data')
|
||
|
||
# 设置Django项目路径
|
||
self.django_path_input.setText(django_path)
|
||
|
||
# 设置服务名称为项目名
|
||
self.service_name_input.setText(project_name)
|
||
|
||
# 生成服务文件内容
|
||
service_content = self.generate_service_file_from_config(config)
|
||
self.service_editor.setText(service_content)
|
||
|
||
logger.info(f"从当前服务器配置加载Gunicorn配置: remote_directory={django_path}, project_name={project_name}")
|
||
else:
|
||
logger.warning("未找到当前服务器配置")
|
||
else:
|
||
# 兼容旧的加载方式
|
||
config_path = os.path.join(os.path.dirname(__file__), 'config.json')
|
||
with open(config_path, 'r', encoding='utf-8') as f:
|
||
config = json.load(f)
|
||
|
||
if config and 'servers' in config and len(config['servers']) > 0:
|
||
server_config = config['servers'][0]
|
||
# 使用remote_directory
|
||
django_path = server_config.get('remote_directory', '')
|
||
project_name = server_config.get('project_name', 'myproject')
|
||
username = server_config.get('username', 'www-data')
|
||
|
||
# 设置Django项目路径
|
||
self.django_path_input.setText(django_path)
|
||
|
||
# 设置服务名称为项目名
|
||
self.service_name_input.setText(project_name)
|
||
|
||
# 生成服务文件内容
|
||
service_content = self.generate_service_file_from_config(server_config)
|
||
self.service_editor.setText(service_content)
|
||
|
||
logger.info(f"从配置文件加载Gunicorn配置: remote_directory={django_path}, project_name={project_name}")
|
||
except Exception as e:
|
||
logger.error(f"加载Gunicorn配置失败: {str(e)}")
|
||
# 不显示警告,避免影响用户体验
|
||
|
||
def generate_service_file_from_config(self, config):
|
||
"""根据config.json配置生成服务文件内容"""
|
||
username = config.get('username', 'www-data')
|
||
project_name = config.get('project_name', 'myproject')
|
||
remote_directory = config.get('remote_directory', '/home/user')
|
||
django_path = config.get('django_path', remote_directory)
|
||
|
||
# 构建完整的项目路径
|
||
project_path = f"{django_path.rstrip('/')}"
|
||
|
||
return f"""[Unit]
|
||
Description=Gunicorn daemon for {project_name}
|
||
After=network.target
|
||
|
||
[Service]
|
||
User={username}
|
||
Group={username}
|
||
WorkingDirectory={project_path}
|
||
# 所有Gunicorn参数直接在这里配置
|
||
ExecStart=/usr/local/bin/gunicorn \\
|
||
--bind 0.0.0.0:8000 \\
|
||
--workers 4 \\
|
||
--worker-class sync \\
|
||
--name {project_name} \\
|
||
--access-logfile {project_path}/logs/gunicorn_access.log \\
|
||
--error-logfile {project_path}/logs/gunicorn_error.log \\
|
||
--log-level info \\
|
||
{project_name}.wsgi:application
|
||
Restart=on-failure
|
||
RestartSec=5s
|
||
PrivateTmp=true
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target"""
|
||
|
||
def generate_service_file(self, service_name, django_path, port, workers):
|
||
"""保持向后兼容的方法"""
|
||
# 获取config.json中的配置信息
|
||
config = None
|
||
if self.parent and hasattr(self.parent, 'server_connection_tab'):
|
||
config = self.parent.server_connection_tab.get_current_config()
|
||
|
||
if config:
|
||
return self.generate_service_file_from_config(config)
|
||
else:
|
||
# 如果没有config,使用默认值
|
||
username = 'www-data'
|
||
project_name = service_name
|
||
project_path = f"{django_path.rstrip('/')}/{project_name}"
|
||
|
||
return f"""[Unit]
|
||
Description=Gunicorn daemon for {project_name}
|
||
After=network.target
|
||
|
||
[Service]
|
||
User={username}
|
||
Group={username}
|
||
WorkingDirectory={project_path}
|
||
# 所有Gunicorn参数直接在这里配置
|
||
ExecStart=/usr/local/bin/gunicorn \\
|
||
--bind 0.0.0.0:{port} \\
|
||
--workers 4 \\
|
||
--worker-class sync \\
|
||
--name {project_name} \\
|
||
--access-logfile {project_path}/logs/gunicorn_access.log \\
|
||
--error-logfile {project_path}/logs/gunicorn_error.log \\
|
||
--log-level info \\
|
||
{project_name}.wsgi:application
|
||
Restart=on-failure
|
||
RestartSec=5s
|
||
PrivateTmp=true
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target"""
|
||
|
||
def check_ssh_connection(self):
|
||
if not self.parent or not self.parent.ssh_client:
|
||
QMessageBox.warning(self, "警告", "请先连接服务器")
|
||
return False
|
||
return True
|
||
|
||
def get_password(self):
|
||
# 获取密码
|
||
password = None
|
||
if self.parent and hasattr(self.parent, 'server_connection_tab'):
|
||
password = self.parent.server_connection_tab.password_input.text()
|
||
logger.info(f"从server_connection_tab获取密码,长度: {len(password) if password else 0}")
|
||
|
||
# 如果密码为空,弹出密码输入对话框
|
||
if not password:
|
||
logger.info("密码为空,弹出密码输入对话框")
|
||
dialog = PasswordDialog(self)
|
||
if dialog.exec_() == QDialog.Accepted:
|
||
password = dialog.get_password()
|
||
logger.info(f"从对话框获取密码,长度: {len(password) if password else 0}")
|
||
# 保存密码到服务器连接标签页
|
||
if self.parent and hasattr(self.parent, 'server_connection_tab'):
|
||
self.parent.server_connection_tab.password_input.setText(password)
|
||
else:
|
||
logger.warning("用户取消了密码输入")
|
||
return None
|
||
|
||
return password
|
||
|
||
def install_gunicorn(self):
|
||
if not self.check_ssh_connection():
|
||
return
|
||
|
||
self.output_text.append("正在安装Gunicorn...")
|
||
self.install_gunicorn_btn.setEnabled(False)
|
||
self.progress_bar.setVisible(True)
|
||
self.progress_bar.setValue(0)
|
||
|
||
# 获取密码
|
||
password = self.get_password()
|
||
if password is None:
|
||
self.install_gunicorn_btn.setEnabled(True)
|
||
self.progress_bar.setVisible(False)
|
||
return
|
||
|
||
self.gunicorn_install_thread = GunicornInstallThread(self.parent.ssh_client, password)
|
||
self.gunicorn_install_thread.progress_updated.connect(self.update_progress)
|
||
self.gunicorn_install_thread.result_ready.connect(self.on_install_gunicorn_result)
|
||
self.gunicorn_install_thread.start()
|
||
|
||
def update_progress(self, value):
|
||
self.progress_bar.setValue(value)
|
||
|
||
def on_install_gunicorn_result(self, success, message):
|
||
self.install_gunicorn_btn.setEnabled(True)
|
||
self.progress_bar.setVisible(False)
|
||
if success:
|
||
self.output_text.append(f"Gunicorn安装成功: {message}")
|
||
logger.info(f"Gunicorn安装成功: {message}")
|
||
else:
|
||
self.output_text.append(f"Gunicorn安装失败: {message}")
|
||
logger.error(f"Gunicorn安装失败: {message}")
|
||
|
||
def test_gunicorn(self):
|
||
"""测试Gunicorn配置"""
|
||
if not self.check_ssh_connection():
|
||
return
|
||
|
||
django_path = self.django_path_input.text().strip()
|
||
if not django_path:
|
||
QMessageBox.warning(self, "警告", "请输入Django项目路径")
|
||
return
|
||
|
||
# 获取端口配置
|
||
port = self.port_input.text().strip() or "8000"
|
||
|
||
# 记录测试参数
|
||
logger.info(f"开始测试Gunicorn,Django路径: {django_path}, 端口: {port}")
|
||
|
||
self.output_text.append(f"正在测试Gunicorn {django_path} (端口: {port})...")
|
||
self.test_gunicorn_btn.setEnabled(False)
|
||
self.progress_bar.setVisible(True)
|
||
self.progress_bar.setValue(0)
|
||
|
||
logger.info("创建Gunicorn测试线程")
|
||
# 注意:GunicornTestThread构造函数可能需要更新以接受端口参数
|
||
# 确保测试时使用的端口与实际服务启动时一致
|
||
self.gunicorn_test_thread = GunicornTestThread(self.parent.ssh_client, django_path, port)
|
||
self.gunicorn_test_thread.progress_updated.connect(self.update_progress)
|
||
self.gunicorn_test_thread.result_ready.connect(self.on_test_gunicorn_result)
|
||
self.gunicorn_test_thread.start()
|
||
logger.info("Gunicorn测试线程已启动")
|
||
|
||
def on_test_gunicorn_result(self, success, message):
|
||
self.test_gunicorn_btn.setEnabled(True)
|
||
self.progress_bar.setVisible(False)
|
||
if success:
|
||
self.output_text.append(f"Gunicorn测试成功: {message}")
|
||
logger.info(f"Gunicorn测试成功: {message}")
|
||
else:
|
||
self.output_text.append(f"Gunicorn测试失败: {message}")
|
||
logger.error(f"Gunicorn测试失败: {message}")
|
||
|
||
def upload_service_file(self):
|
||
if not self.check_ssh_connection():
|
||
return
|
||
|
||
# 获取config.json中的配置信息
|
||
config = None
|
||
if self.parent and hasattr(self.parent, 'server_connection_tab'):
|
||
config = self.parent.server_connection_tab.get_current_config()
|
||
|
||
if config:
|
||
project_name = config.get('project_name', 'django')
|
||
else:
|
||
project_name = 'django'
|
||
|
||
# 使用gunicorn_[project_name].service格式作为服务名称
|
||
service_name = f"gunicorn_{project_name}"
|
||
service_content = self.service_editor.toPlainText()
|
||
|
||
if not service_name or not service_content:
|
||
QMessageBox.warning(self, "警告", "请编辑服务文件内容")
|
||
return
|
||
|
||
self.output_text.append(f"正在上传服务文件 {service_name}...")
|
||
self.upload_service_btn.setEnabled(False)
|
||
|
||
# 获取密码
|
||
password = self.get_password()
|
||
if password is None:
|
||
self.upload_service_btn.setEnabled(True)
|
||
return
|
||
|
||
self.upload_thread = UploadGunicornServiceThread(self.parent.ssh_client, service_name, service_content, password)
|
||
self.upload_thread.result_ready.connect(self.on_upload_service_result)
|
||
self.upload_thread.start()
|
||
|
||
def on_upload_service_result(self, success, message):
|
||
self.upload_service_btn.setEnabled(True)
|
||
if success:
|
||
self.output_text.append(f"服务文件上传成功: {message}")
|
||
logger.info(f"服务文件上传成功: {message}")
|
||
else:
|
||
self.output_text.append(f"服务文件上传失败: {message}")
|
||
logger.error(f"服务文件上传失败: {message}")
|
||
|
||
def manage_service(self, action):
|
||
if not self.check_ssh_connection():
|
||
return
|
||
|
||
# 获取config.json中的配置信息
|
||
config = None
|
||
if self.parent and hasattr(self.parent, 'server_connection_tab'):
|
||
config = self.parent.server_connection_tab.get_current_config()
|
||
|
||
if config:
|
||
project_name = config.get('project_name', 'django')
|
||
else:
|
||
project_name = 'django'
|
||
|
||
# 使用gunicorn_[project_name].service格式作为服务名称
|
||
service_name = f"gunicorn_{project_name}"
|
||
|
||
logger.info(f"正在执行服务 {action} 操作,服务名称: {service_name}")
|
||
self.output_text.append(f"正在执行服务 {action} 操作...")
|
||
|
||
# 禁用所有服务管理按钮
|
||
buttons = [self.start_service_btn, self.stop_service_btn, self.restart_service_btn,
|
||
self.status_service_btn, self.enable_service_btn]
|
||
for btn in buttons:
|
||
btn.setEnabled(False)
|
||
|
||
# 获取密码
|
||
password = self.get_password()
|
||
if password is None:
|
||
logger.warning("未获取到密码,取消服务操作")
|
||
for btn in buttons:
|
||
btn.setEnabled(True)
|
||
return
|
||
|
||
# 获取端口配置
|
||
port = self.port_input.text().strip() or "8000"
|
||
|
||
logger.info(f"获取到密码,长度: {len(password)},创建ManageGunicornServiceThread线程")
|
||
logger.info(f"开始管理Gunicorn服务 - 服务名: {service_name}, 操作: {action}, 端口: {port}")
|
||
self.manage_thread = ManageGunicornServiceThread(self.parent.ssh_client, service_name, action, password, port)
|
||
self.manage_thread.result_ready.connect(lambda s, m: self.on_manage_service_result(s, m, buttons))
|
||
self.manage_thread.start()
|
||
logger.info(f"Gunicorn服务管理线程已启动 - 操作: {action}")
|
||
|
||
def on_manage_service_result(self, success, message, buttons):
|
||
# 重新启用所有服务管理按钮
|
||
for btn in buttons:
|
||
btn.setEnabled(True)
|
||
|
||
if success:
|
||
self.output_text.append(f"服务操作成功: {message}")
|
||
logger.info(f"服务操作成功: {message}")
|
||
else:
|
||
self.output_text.append(f"服务操作失败: {message}")
|
||
logger.error(f"服务操作失败: {message}")
|
||
# 如果是密码相关错误,提供更详细的提示
|
||
if "password" in message.lower() or "sudo" in message.lower():
|
||
self.output_text.append("提示:请检查sudo密码是否正确,或尝试重新输入密码")
|
||
logger.warning("检测到可能的密码问题,提示用户重新输入密码")
|
||
|
||
def on_server_changed(self):
|
||
self.load_gunicorn_config()
|
||
|
||
def check_service_status(self):
|
||
if not self.check_ssh_connection():
|
||
return
|
||
|
||
# 获取config.json中的配置信息
|
||
config = None
|
||
if self.parent and hasattr(self.parent, 'server_connection_tab'):
|
||
config = self.parent.server_connection_tab.get_current_config()
|
||
|
||
if config:
|
||
project_name = config.get('project_name', 'django')
|
||
else:
|
||
project_name = 'django'
|
||
|
||
# 使用gunicorn_[project_name].service格式作为服务名称
|
||
service_name = f"gunicorn_{project_name}"
|
||
|
||
self.manage_service("status")
|
||
|
||
def view_access_log(self):
|
||
"""查看Gunicorn访问日志"""
|
||
if not self.check_ssh_connection():
|
||
return
|
||
|
||
# 获取config.json中的配置信息
|
||
config = None
|
||
if self.parent and hasattr(self.parent, 'server_connection_tab'):
|
||
config = self.parent.server_connection_tab.get_current_config()
|
||
|
||
if config:
|
||
project_path = config.get('remote_directory', '/home/xiaji')
|
||
else:
|
||
project_path = '/home/xiaji'
|
||
|
||
# 使用动态路径
|
||
access_log_path = f"{project_path.rstrip('/')}/logs/gunicorn_access.log"
|
||
|
||
self.output_text.append(f"正在查看访问日志: {access_log_path}")
|
||
|
||
# 获取密码
|
||
password = self.get_password()
|
||
if password is None:
|
||
return
|
||
|
||
try:
|
||
# 首先检查日志文件是否存在,如果不存在则创建
|
||
log_dir = f"{project_path.rstrip('/')}/logs"
|
||
check_command = f"sudo test -f {access_log_path} || (sudo mkdir -p {log_dir} && sudo touch {access_log_path} && sudo chmod 644 {access_log_path})"
|
||
stdin, stdout, stderr = self.parent.ssh_client.exec_command(check_command)
|
||
stdin.write(password + '\n')
|
||
stdin.flush()
|
||
|
||
# 使用tail命令查看日志的最后50行
|
||
command = f"sudo tail -n 50 {access_log_path}"
|
||
stdin, stdout, stderr = self.parent.ssh_client.exec_command(command)
|
||
stdin.write(password + '\n')
|
||
stdin.flush()
|
||
|
||
output = stdout.read().decode('utf-8')
|
||
error = stderr.read().decode('utf-8')
|
||
|
||
if output:
|
||
self.output_text.append("=== Gunicorn访问日志 ===")
|
||
self.output_text.append(output)
|
||
logger.info(f"成功查看访问日志: {len(output)} 字符")
|
||
else:
|
||
self.output_text.append("访问日志为空")
|
||
|
||
if error and "sudo" not in error.lower():
|
||
self.output_text.append(f"错误信息: {error}")
|
||
logger.error(f"查看访问日志出错: {error}")
|
||
|
||
except Exception as e:
|
||
self.output_text.append(f"查看访问日志失败: {str(e)}")
|
||
logger.error(f"查看访问日志失败: {str(e)}")
|
||
|
||
def view_error_log(self):
|
||
"""查看Gunicorn错误日志"""
|
||
if not self.check_ssh_connection():
|
||
return
|
||
|
||
# 获取config.json中的配置信息
|
||
config = None
|
||
if self.parent and hasattr(self.parent, 'server_connection_tab'):
|
||
config = self.parent.server_connection_tab.get_current_config()
|
||
|
||
if config:
|
||
project_path = config.get('remote_directory', '/home/xiaji')
|
||
else:
|
||
project_path = '/home/xiaji'
|
||
|
||
# 使用动态路径
|
||
error_log_path = f"{project_path.rstrip('/')}/logs/gunicorn_error.log"
|
||
|
||
self.output_text.append(f"正在查看错误日志: {error_log_path}")
|
||
|
||
# 获取密码
|
||
password = self.get_password()
|
||
if password is None:
|
||
return
|
||
|
||
try:
|
||
# 首先检查日志文件是否存在,如果不存在则创建
|
||
log_dir = f"{project_path.rstrip('/')}/logs"
|
||
check_command = f"sudo test -f {error_log_path} || (sudo mkdir -p {log_dir} && sudo touch {error_log_path} && sudo chmod 644 {error_log_path})"
|
||
stdin, stdout, stderr = self.parent.ssh_client.exec_command(check_command)
|
||
stdin.write(password + '\n')
|
||
stdin.flush()
|
||
|
||
# 使用tail命令查看日志的最后50行
|
||
command = f"sudo tail -n 50 {error_log_path}"
|
||
stdin, stdout, stderr = self.parent.ssh_client.exec_command(command)
|
||
stdin.write(password + '\n')
|
||
stdin.flush()
|
||
|
||
output = stdout.read().decode('utf-8')
|
||
error = stderr.read().decode('utf-8')
|
||
|
||
if output:
|
||
self.output_text.append("=== Gunicorn错误日志 ===")
|
||
self.output_text.append(output)
|
||
logger.info(f"成功查看错误日志: {len(output)} 字符")
|
||
else:
|
||
self.output_text.append("错误日志为空")
|
||
|
||
if error and "sudo" not in error.lower():
|
||
self.output_text.append(f"错误信息: {error}")
|
||
logger.error(f"查看错误日志出错: {error}")
|
||
|
||
except Exception as e:
|
||
self.output_text.append(f"查看错误日志失败: {str(e)}")
|
||
logger.error(f"查看错误日志失败: {str(e)}") |