Communication encoding bug fixed, all converted to UTF-8.

This commit is contained in:
戒酒的李白
2025-08-24 15:41:58 +08:00
parent 29dbc76044
commit 5d49063b26
5 changed files with 170 additions and 14 deletions
@@ -8,6 +8,20 @@ import sys
import streamlit as st import streamlit as st
from datetime import datetime from datetime import datetime
import json import json
import locale
# 设置UTF-8编码环境
os.environ['PYTHONIOENCODING'] = 'utf-8'
os.environ['PYTHONUTF8'] = '1'
# 设置系统编码
try:
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
except locale.Error:
try:
locale.setlocale(locale.LC_ALL, 'C.UTF-8')
except locale.Error:
pass
# 添加src目录到Python路径 # 添加src目录到Python路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
@@ -8,6 +8,20 @@ import sys
import streamlit as st import streamlit as st
from datetime import datetime from datetime import datetime
import json import json
import locale
# 设置UTF-8编码环境
os.environ['PYTHONIOENCODING'] = 'utf-8'
os.environ['PYTHONUTF8'] = '1'
# 设置系统编码
try:
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
except locale.Error:
try:
locale.setlocale(locale.LC_ALL, 'C.UTF-8')
except locale.Error:
pass
# 添加src目录到Python路径 # 添加src目录到Python路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
@@ -8,6 +8,20 @@ import sys
import streamlit as st import streamlit as st
from datetime import datetime from datetime import datetime
import json import json
import locale
# 设置UTF-8编码环境
os.environ['PYTHONIOENCODING'] = 'utf-8'
os.environ['PYTHONUTF8'] = '1'
# 设置系统编码
try:
locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')
except locale.Error:
try:
locale.setlocale(locale.LC_ALL, 'C.UTF-8')
except locale.Error:
pass
# 添加src目录到Python路径 # 添加src目录到Python路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
+77 -13
View File
@@ -15,16 +15,26 @@ from flask_socketio import SocketIO, emit
import signal import signal
import atexit import atexit
import requests import requests
import logging
from pathlib import Path
app = Flask(__name__) app = Flask(__name__)
app.config['SECRET_KEY'] = 'weibo_analysis_system_2024' app.config['SECRET_KEY'] = 'weibo_analysis_system_2024'
socketio = SocketIO(app, cors_allowed_origins="*") socketio = SocketIO(app, cors_allowed_origins="*")
# 设置UTF-8编码环境
os.environ['PYTHONIOENCODING'] = 'utf-8'
os.environ['PYTHONUTF8'] = '1'
# 创建日志目录
LOG_DIR = Path('logs')
LOG_DIR.mkdir(exist_ok=True)
# 全局变量存储进程信息 # 全局变量存储进程信息
processes = { processes = {
'insight': {'process': None, 'port': 8501, 'status': 'stopped', 'output': []}, 'insight': {'process': None, 'port': 8501, 'status': 'stopped', 'output': [], 'log_file': None},
'media': {'process': None, 'port': 8502, 'status': 'stopped', 'output': []}, 'media': {'process': None, 'port': 8502, 'status': 'stopped', 'output': [], 'log_file': None},
'query': {'process': None, 'port': 8503, 'status': 'stopped', 'output': []} 'query': {'process': None, 'port': 8503, 'status': 'stopped', 'output': [], 'log_file': None}
} }
# 输出队列 # 输出队列
@@ -34,8 +44,36 @@ output_queues = {
'query': Queue() 'query': Queue()
} }
def write_log_to_file(app_name, line):
"""将日志写入文件"""
try:
log_file_path = LOG_DIR / f"{app_name}.log"
with open(log_file_path, 'a', encoding='utf-8') as f:
f.write(line + '\n')
f.flush()
except Exception as e:
print(f"Error writing log for {app_name}: {e}")
def read_log_from_file(app_name, tail_lines=None):
"""从文件读取日志"""
try:
log_file_path = LOG_DIR / f"{app_name}.log"
if not log_file_path.exists():
return []
with open(log_file_path, 'r', encoding='utf-8') as f:
lines = f.readlines()
lines = [line.rstrip('\n\r') for line in lines if line.strip()]
if tail_lines:
return lines[-tail_lines:]
return lines
except Exception as e:
print(f"Error reading log for {app_name}: {e}")
return []
def read_process_output(process, app_name): def read_process_output(process, app_name):
"""读取进程输出并放入队列""" """读取进程输出并写入文件"""
while True: while True:
try: try:
if process.poll() is not None: if process.poll() is not None:
@@ -43,15 +81,14 @@ def read_process_output(process, app_name):
output = process.stdout.readline() output = process.stdout.readline()
if output: if output:
line = output.decode('utf-8', errors='ignore').strip() # 使用UTF-8解码,忽略错误字符
line = output.decode('utf-8', errors='replace').strip()
if line: if line:
timestamp = datetime.now().strftime('%H:%M:%S') timestamp = datetime.now().strftime('%H:%M:%S')
formatted_line = f"[{timestamp}] {line}" formatted_line = f"[{timestamp}] {line}"
# 添加到输出列表(保持最近100行) # 写入日志文件
processes[app_name]['output'].append(formatted_line) write_log_to_file(app_name, formatted_line)
if len(processes[app_name]['output']) > 100:
processes[app_name]['output'].pop(0)
# 发送到前端 # 发送到前端
socketio.emit('console_output', { socketio.emit('console_output', {
@@ -59,7 +96,9 @@ def read_process_output(process, app_name):
'line': formatted_line 'line': formatted_line
}) })
except Exception as e: except Exception as e:
print(f"Error reading output for {app_name}: {e}") error_msg = f"Error reading output for {app_name}: {e}"
print(error_msg)
write_log_to_file(app_name, f"[{datetime.now().strftime('%H:%M:%S')}] {error_msg}")
break break
def start_streamlit_app(app_name, script_path, port): def start_streamlit_app(app_name, script_path, port):
@@ -72,6 +111,15 @@ def start_streamlit_app(app_name, script_path, port):
if not os.path.exists(script_path): if not os.path.exists(script_path):
return False, f"文件不存在: {script_path}" return False, f"文件不存在: {script_path}"
# 清空之前的日志文件
log_file_path = LOG_DIR / f"{app_name}.log"
if log_file_path.exists():
log_file_path.unlink()
# 创建启动日志
start_msg = f"[{datetime.now().strftime('%H:%M:%S')}] 启动 {app_name} 应用..."
write_log_to_file(app_name, start_msg)
cmd = [ cmd = [
sys.executable, '-m', 'streamlit', 'run', sys.executable, '-m', 'streamlit', 'run',
script_path, script_path,
@@ -81,6 +129,15 @@ def start_streamlit_app(app_name, script_path, port):
'--logger.level', 'info' '--logger.level', 'info'
] ]
# 设置环境变量确保UTF-8编码
env = os.environ.copy()
env.update({
'PYTHONIOENCODING': 'utf-8',
'PYTHONUTF8': '1',
'LANG': 'en_US.UTF-8',
'LC_ALL': 'en_US.UTF-8'
})
# 使用当前工作目录而不是脚本目录 # 使用当前工作目录而不是脚本目录
process = subprocess.Popen( process = subprocess.Popen(
cmd, cmd,
@@ -88,7 +145,9 @@ def start_streamlit_app(app_name, script_path, port):
stderr=subprocess.STDOUT, stderr=subprocess.STDOUT,
bufsize=1, bufsize=1,
universal_newlines=False, universal_newlines=False,
cwd=os.getcwd() cwd=os.getcwd(),
env=env,
encoding=None # 让我们手动处理编码
) )
processes[app_name]['process'] = process processes[app_name]['process'] = process
@@ -106,7 +165,9 @@ def start_streamlit_app(app_name, script_path, port):
return True, f"{app_name} 应用启动中..." return True, f"{app_name} 应用启动中..."
except Exception as e: except Exception as e:
return False, f"启动失败: {str(e)}" error_msg = f"启动失败: {str(e)}"
write_log_to_file(app_name, f"[{datetime.now().strftime('%H:%M:%S')}] {error_msg}")
return False, error_msg
def stop_streamlit_app(app_name): def stop_streamlit_app(app_name):
"""停止Streamlit应用""" """停止Streamlit应用"""
@@ -245,9 +306,12 @@ def get_output(app_name):
if app_name not in processes: if app_name not in processes:
return jsonify({'success': False, 'message': '未知应用'}) return jsonify({'success': False, 'message': '未知应用'})
# 从文件读取完整日志
output_lines = read_log_from_file(app_name)
return jsonify({ return jsonify({
'success': True, 'success': True,
'output': processes[app_name]['output'] 'output': output_lines
}) })
@app.route('/api/search', methods=['POST']) @app.route('/api/search', methods=['POST'])
+51 -1
View File
@@ -338,6 +338,11 @@
checkStatus(); checkStatus();
setInterval(checkStatus, 5000); setInterval(checkStatus, 5000);
// 定期刷新控制台输出
setInterval(() => {
refreshConsoleOutput();
}, 2000);
// 延迟预加载iframe以确保应用启动完成 // 延迟预加载iframe以确保应用启动完成
setTimeout(() => { setTimeout(() => {
preloadIframes(); preloadIframes();
@@ -446,12 +451,18 @@
// 清空并加载新的控制台输出 // 清空并加载新的控制台输出
document.getElementById('consoleOutput').innerHTML = '<div class="console-line">[系统] 切换到 ' + app + ' 应用</div>'; document.getElementById('consoleOutput').innerHTML = '<div class="console-line">[系统] 切换到 ' + app + ' 应用</div>';
// 重置行计数
lastLineCount[app] = 0;
loadConsoleOutput(app); loadConsoleOutput(app);
// 更新嵌入页面 // 更新嵌入页面
updateEmbeddedPage(app); updateEmbeddedPage(app);
} }
// 存储最后显示的行数,避免重复加载
let lastLineCount = {};
// 加载控制台输出 // 加载控制台输出
function loadConsoleOutput(app) { function loadConsoleOutput(app) {
fetch(`/api/output/${app}`) fetch(`/api/output/${app}`)
@@ -459,12 +470,19 @@
.then(data => { .then(data => {
if (data.success && data.output.length > 0) { if (data.success && data.output.length > 0) {
const consoleOutput = document.getElementById('consoleOutput'); const consoleOutput = document.getElementById('consoleOutput');
data.output.forEach(line => {
// 只添加新的行
const lastCount = lastLineCount[app] || 0;
const newLines = data.output.slice(lastCount);
newLines.forEach(line => {
const div = document.createElement('div'); const div = document.createElement('div');
div.className = 'console-line'; div.className = 'console-line';
div.textContent = line; div.textContent = line;
consoleOutput.appendChild(div); consoleOutput.appendChild(div);
}); });
lastLineCount[app] = data.output.length;
consoleOutput.scrollTop = consoleOutput.scrollHeight; consoleOutput.scrollTop = consoleOutput.scrollHeight;
} }
}) })
@@ -472,6 +490,38 @@
console.error('加载输出失败:', error); console.error('加载输出失败:', error);
}); });
} }
// 刷新当前应用的控制台输出
function refreshConsoleOutput() {
if (appStatus[currentApp] === 'running' || appStatus[currentApp] === 'starting') {
fetch(`/api/output/${currentApp}`)
.then(response => response.json())
.then(data => {
if (data.success && data.output.length > 0) {
const consoleOutput = document.getElementById('consoleOutput');
// 只添加新的行
const lastCount = lastLineCount[currentApp] || 0;
const newLines = data.output.slice(lastCount);
if (newLines.length > 0) {
newLines.forEach(line => {
const div = document.createElement('div');
div.className = 'console-line';
div.textContent = line;
consoleOutput.appendChild(div);
});
lastLineCount[currentApp] = data.output.length;
consoleOutput.scrollTop = consoleOutput.scrollHeight;
}
}
})
.catch(error => {
console.error('刷新输出失败:', error);
});
}
}
// 添加控制台输出 // 添加控制台输出
function addConsoleOutput(line) { function addConsoleOutput(line) {