diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 000000000..35410cacd
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 000000000..c4379ba70
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 000000000..105ce2da2
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 000000000..6a4ddcc51
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 000000000..e623d0283
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/supervisor.iml b/.idea/supervisor.iml
new file mode 100644
index 000000000..f0e2c75c0
--- /dev/null
+++ b/.idea/supervisor.iml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 000000000..35eb1ddfb
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/INSTALL_CN.md b/INSTALL_CN.md
new file mode 100644
index 000000000..3f66bcfe4
--- /dev/null
+++ b/INSTALL_CN.md
@@ -0,0 +1,73 @@
+# Supervisor 安装指南
+
+## 安装方法
+
+### 1. 使用 pip 安装(推荐)
+
+```bash
+pip install supervisor
+```
+
+### 2. 从源码安装
+
+```bash
+# 克隆仓库
+git clone https://github.com/你的用户名/supervisor.git
+cd supervisor
+
+# 安装
+pip install -e .
+```
+
+## 基本配置
+
+1. 生成默认配置文件:
+
+```bash
+echo_supervisord_conf > supervisord.conf
+```
+
+2. 编辑配置文件,根据需要修改:
+
+```bash
+# 设置HTTP服务器端口
+[inet_http_server]
+port=0.0.0.0:9001
+username=admin
+password=123456
+
+# 添加您的程序
+[program:yourapp]
+command=/path/to/your/program
+```
+
+## 启动 Supervisor
+
+```bash
+# 启动 Supervisor
+supervisord -c supervisord.conf
+
+# 使用 supervisorctl 控制进程
+supervisorctl status
+supervisorctl start all
+supervisorctl stop all
+supervisorctl restart all
+```
+
+## Web 界面
+
+安装成功后,访问 http://localhost:9001 即可打开 Supervisor 的 Web 界面。
+
+用户名:admin
+密码:123456 (根据您的配置)
+
+## 组操作
+
+本版本支持对进程组进行操作:
+
+- 重启组:点击组名旁边的"重启组"按钮
+- 停止组:点击组名旁边的"停止组"按钮
+
+## 日志查看
+
+点击进程名后的"查看日志"按钮,可以查看该进程的日志输出。
\ No newline at end of file
diff --git a/PACKAGING_GUIDE_CN.md b/PACKAGING_GUIDE_CN.md
new file mode 100644
index 000000000..d5061697c
--- /dev/null
+++ b/PACKAGING_GUIDE_CN.md
@@ -0,0 +1,83 @@
+# Supervisor 打包与发布指南
+
+## 打包过程
+
+### 1. 环境准备
+
+确保安装了必要的打包工具:
+
+```bash
+pip install setuptools wheel twine
+```
+
+### 2. 修改版本号
+
+1. 编辑 `supervisor/version.txt` 文件,设置正确的版本号
+2. 修改配置文件中的端口设置(如需要)
+
+### 3. 构建包
+
+运行打包脚本生成分发包:
+
+```bash
+./build_package.sh
+```
+
+打包完成后,会在 `dist/` 目录下生成以下文件:
+- `supervisor-4.3.0.tar.gz` - 源码分发包
+- `supervisor-4.3.0-py2.py3-none-any.whl` - Python wheel 包
+
+### 4. 测试安装包
+
+在测试环境中测试生成的包:
+
+```bash
+# 从源码包安装
+pip install dist/supervisor-4.3.0.tar.gz
+
+# 或从 wheel 包安装
+pip install dist/supervisor-4.3.0-py2.py3-none-any.whl
+```
+
+### 5. 上传到 PyPI(可选)
+
+如果你有 PyPI 帐号并想公开发布,可以使用:
+
+```bash
+python -m twine upload dist/*
+```
+
+## 文档结构
+
+发布版本应包含以下文档:
+
+- `README_CN.md` - 中文项目说明
+- `INSTALL_CN.md` - 中文安装指南
+- `RELEASE_NOTES_CN.md` - 中文发布说明
+- `PACKAGING_GUIDE_CN.md` - 中文打包指南(本文档)
+
+## 本地部署
+
+如果只需要本地部署,可以将打包好的分发包复制到目标机器并安装:
+
+```bash
+# 在目标机器上
+pip install supervisor-4.3.0.tar.gz
+```
+
+## 打包脚本说明
+
+`build_package.sh` 脚本执行以下操作:
+
+1. 清理之前的构建文件
+2. 构建源码分发包和 wheel 包
+3. 显示生成的包文件列表
+4. 提供上传到 PyPI 的命令提示
+
+## 故障排除
+
+如果在打包过程中遇到问题:
+
+1. 检查 Python 版本是否兼容
+2. 确认所有依赖已正确安装
+3. 检查文件权限和路径是否正确
\ No newline at end of file
diff --git a/README_CN.md b/README_CN.md
new file mode 100644
index 000000000..ff0eab824
--- /dev/null
+++ b/README_CN.md
@@ -0,0 +1,40 @@
+# Supervisor - 进程控制系统
+
+Supervisor 是一个客户端/服务器系统,允许用户在类 UNIX 操作系统上控制多个进程。
+
+## 特性
+
+- **进程管理**:启动、停止、重启进程
+- **自动重启**:进程崩溃时自动重启
+- **状态监控**:监控进程状态
+- **日志管理**:收集和管理进程输出
+- **Web界面**:通过Web界面管理进程
+- **组操作**:对进程组进行批量操作
+
+## 增强功能
+
+本版本在原始 Supervisor 基础上增加了以下功能:
+
+1. **组操作按钮**:在Web界面中直接操作进程组
+ - 重启组:一键重启整个组中的所有进程
+ - 停止组:一键停止整个组中的所有进程
+
+2. **日志查看增强**:美化了日志查看页面
+ - 语法高亮
+ - 行号显示
+ - 自动滚动
+ - 搜索功能
+ - 复制功能
+
+## 安装
+
+详细安装说明请参考 [INSTALL_CN.md](INSTALL_CN.md)。
+
+## 许可证
+
+Supervisor 是根据类 BSD 许可证发布的,详见 [LICENSE.txt](LICENSES.txt)。
+
+## 更多信息
+
+- 官方文档:http://supervisord.org/
+- 源代码:https://github.com/Supervisor/supervisor
\ No newline at end of file
diff --git a/RELEASE_NOTES_CN.md b/RELEASE_NOTES_CN.md
new file mode 100644
index 000000000..0c96cb2f1
--- /dev/null
+++ b/RELEASE_NOTES_CN.md
@@ -0,0 +1,48 @@
+# Supervisor 4.3.0 发布说明
+
+## 版本亮点
+
+Supervisor 4.3.0 版本是一个重要的增强版本,在标准 Supervisor 功能基础上增加了更多实用功能。
+
+### 主要新特性
+
+1. **进程组操作按钮**
+ - 在 Web 界面中直接添加了"重启组"和"停止组"按钮
+ - 可以一键操作整个进程组,提高管理效率
+ - 优化了按钮布局和交互体验
+
+2. **增强的日志查看界面**
+ - 全新设计的日志查看页面
+ - 添加了语法高亮显示
+ - 支持行号显示
+ - 提供自动滚动功能
+ - 增加日志搜索功能
+ - 一键复制日志内容
+
+3. **用户界面改进**
+ - 优化了组和进程的显示层次结构
+ - 改进了按钮布局和样式
+ - 增强了整体视觉体验
+
+### 错误修复
+
+- 修复了组操作功能中的异步回调处理问题
+- 解决了多个界面显示和布局问题
+- 修复了 RPC 接口调用相关的错误
+
+## 安装指南
+
+详细安装说明请参阅 [INSTALL_CN.md](INSTALL_CN.md)。
+
+## 升级说明
+
+如果您正在从之前的版本升级,只需按照安装指南重新安装即可。配置文件格式保持兼容。
+
+## 兼容性
+
+- 支持 Python 2.7 及 Python 3.4+
+- 兼容所有主流 UNIX/Linux 系统及 macOS
+
+## 致谢
+
+特别感谢所有为此版本贡献代码、测试和反馈的开发者。
\ No newline at end of file
diff --git a/SUPERVISOR_USAGE_CN.md b/SUPERVISOR_USAGE_CN.md
new file mode 100644
index 000000000..5e616258f
--- /dev/null
+++ b/SUPERVISOR_USAGE_CN.md
@@ -0,0 +1,163 @@
+# Supervisor 使用说明
+
+## 配置文件位置
+
+当前项目使用的配置文件位于:
+```
+/Users/feiwentao/tianhei_projects/python_projests/supervisor/supervisord.conf
+```
+
+## 常用命令
+
+为了更方便地操作 Supervisor,我们创建了一个命令辅助脚本 `supervisor_cmd.sh`。使用此脚本可以避免认证错误和路径问题。
+
+### 基本命令
+
+```bash
+# 查看所有进程状态
+./supervisor_cmd.sh status
+
+# 启动特定进程
+./supervisor_cmd.sh start <进程名>
+
+# 停止特定进程
+./supervisor_cmd.sh stop <进程名>
+
+# 重启特定进程
+./supervisor_cmd.sh restart <进程名>
+
+# 关闭 Supervisor
+./supervisor_cmd.sh shutdown
+```
+
+### 组操作命令
+
+```bash
+# 启动整个组
+./supervisor_cmd.sh start <组名>:*
+
+# 停止整个组
+./supervisor_cmd.sh stop <组名>:*
+
+# 重启整个组
+./supervisor_cmd.sh restart <组名>:*
+```
+
+### 配置管理命令
+
+```bash
+# 重新读取配置文件
+./supervisor_cmd.sh reread
+
+# 更新配置(应用新的配置)
+./supervisor_cmd.sh update
+
+# 显示所有命令帮助
+./supervisor_cmd.sh help
+```
+
+## 修改配置文件
+
+使用文本编辑器打开配置文件:
+
+```bash
+vim supervisord.conf
+# 或者
+open -a TextEdit supervisord.conf
+```
+
+### 配置文件主要部分
+
+1. **HTTP 服务器设置**:
+ ```ini
+ [inet_http_server]
+ port=0.0.0.0:9001 # 端口设置
+ username=admin # 登录用户名
+ password=123456 # 登录密码
+ ```
+
+2. **进程配置**:
+ ```ini
+ [program:程序名称]
+ command=要执行的命令 # 必填,程序启动命令
+ directory=工作目录 # 程序的工作目录
+ autostart=true # 是否自动启动
+ autorestart=true # 是否自动重启
+ redirect_stderr=true # 是否重定向错误输出
+ stdout_logfile=日志路径 # 日志文件位置
+ ```
+
+3. **进程组配置**:
+ ```ini
+ [group:组名]
+ programs=程序1,程序2 # 组内的程序列表
+ priority=999 # 启动优先级
+ ```
+
+### 添加新程序
+
+1. 在配置文件末尾添加新的程序段:
+ ```ini
+ [program:新程序名]
+ command=python /path/to/your/script.py
+ directory=/path/to/working/dir
+ autostart=true
+ autorestart=true
+ redirect_stderr=true
+ stdout_logfile=./logs/新程序名.log
+ environment=变量1="值1",变量2="值2"
+ ```
+
+2. 如果要将程序添加到组中,先添加程序配置,然后添加或修改组配置:
+ ```ini
+ [group:组名]
+ programs=现有程序1,现有程序2,新程序名
+ priority=999
+ ```
+
+### 修改现有程序配置
+
+找到对应的 `[program:xxx]` 部分,修改相应的参数。常用参数包括:
+
+- **command**: 启动命令
+- **directory**: 工作目录
+- **autostart**: 是否自动启动(true/false)
+- **autorestart**: 自动重启(true/false/unexpected)
+- **redirect_stderr**: 是否将错误输出重定向到标准输出(true/false)
+- **stdout_logfile**: 标准输出日志文件路径
+- **environment**: 环境变量设置
+
+## Web 界面
+
+Supervisor 提供了一个 Web 界面来管理您的进程:
+
+- 地址:http://localhost:9001
+- 用户名:admin
+- 密码:123456 (根据配置文件设置)
+
+通过 Web 界面,您可以:
+- 查看所有进程的状态
+- 启动/停止/重启单个进程
+- 启动/停止/重启整个进程组
+- 查看进程日志
+
+## 常见问题解决
+
+### 1. 认证错误
+问题:`Server requires authentication: error: 401 Unauthorized`
+解决:使用 `./supervisor_cmd.sh` 脚本执行命令,或指定配置文件路径:
+```bash
+supervisorctl -c $(pwd)/supervisord.conf -u admin -p 123456 status
+```
+
+### 2. 无法连接到 Supervisor
+问题:`unix:///tmp/supervisor.sock no such file`
+解决:确保 Supervisor 已启动,并且 socket 文件路径正确。
+
+### 3. 进程启动后立即退出
+问题:进程状态显示为 `FATAL` 或 `BACKOFF`
+解决:
+- 检查命令是否正确
+- 查看进程日志了解详细错误信息
+- 确保工作目录正确
+- 检查环境变量设置
\ No newline at end of file
diff --git a/build_package.sh b/build_package.sh
new file mode 100755
index 000000000..66a83606d
--- /dev/null
+++ b/build_package.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+# 打包和发布Supervisor
+set -e
+
+# 清理之前的构建
+echo "清理之前的构建..."
+rm -rf build/ dist/ *.egg-info/
+
+# 构建源码包和wheel包
+echo "构建源码包和wheel包..."
+python setup.py sdist bdist_wheel
+
+echo "构建完成!"
+echo "生成的包在dist/目录下"
+ls -la dist/
+
+echo ""
+echo "如需上传到PyPI,请运行:"
+echo "python -m twine upload dist/*"
\ No newline at end of file
diff --git a/scripts/counter.py b/scripts/counter.py
new file mode 100644
index 000000000..356bbd1b5
--- /dev/null
+++ b/scripts/counter.py
@@ -0,0 +1,9 @@
+import time
+
+print("计数器脚本已启动")
+
+count = 0
+while True:
+ count += 1
+ print(f"当前计数: {count}")
+ time.sleep(2)
\ No newline at end of file
diff --git a/scripts/cpu_monitor.py b/scripts/cpu_monitor.py
new file mode 100644
index 000000000..28d5938f9
--- /dev/null
+++ b/scripts/cpu_monitor.py
@@ -0,0 +1,10 @@
+import time
+import psutil
+
+print("CPU监控脚本已启动")
+
+while True:
+ print(f"CPU使用率: {psutil.cpu_percent(interval=1)}%")
+ for i, percentage in enumerate(psutil.cpu_percent(interval=1, percpu=True)):
+ print(f"CPU {i}: {percentage}%")
+ time.sleep(8)
\ No newline at end of file
diff --git a/scripts/memory_monitor.py b/scripts/memory_monitor.py
new file mode 100644
index 000000000..c283c07f7
--- /dev/null
+++ b/scripts/memory_monitor.py
@@ -0,0 +1,10 @@
+import time
+import psutil
+
+print("内存监控脚本已启动")
+
+while True:
+ memory = psutil.virtual_memory()
+ print(f"内存使用率: {memory.percent}%")
+ print(f"可用内存: {memory.available / (1024 * 1024):.2f} MB")
+ time.sleep(10)
\ No newline at end of file
diff --git a/scripts/time_printer.py b/scripts/time_printer.py
new file mode 100644
index 000000000..c3fc2e15f
--- /dev/null
+++ b/scripts/time_printer.py
@@ -0,0 +1,9 @@
+import time
+import datetime
+
+print("时间打印脚本已启动")
+
+while True:
+ now = datetime.datetime.now()
+ print(f"当前时间: {now.strftime('%Y-%m-%d %H:%M:%S')}")
+ time.sleep(5)
\ No newline at end of file
diff --git a/supervisor/http.py b/supervisor/http.py
index af3e3da87..9a4e370c8 100644
--- a/supervisor/http.py
+++ b/supervisor/http.py
@@ -1,3 +1,9 @@
+# -*- coding: utf-8 -*-
+"""Medusa HTTP server implementation
+
+This module contains the HTTP server implementation used by supervisor.
+"""
+
import os
import stat
import time
@@ -6,6 +12,7 @@
import errno
import weakref
import traceback
+import supervisor.options
try:
import pwd
@@ -635,10 +642,11 @@ def checkused(self, socketname):
return True
class tail_f_producer:
- def __init__(self, request, filename, head):
+ def __init__(self, request, filename, head, is_html=False):
self.request = weakref.ref(request)
self.filename = filename
self.delay = 0.1
+ self.is_html = is_html
self._open()
sz = self._fsize()
@@ -658,14 +666,45 @@ def more(self):
bytes_added = newsz - self.sz
if bytes_added < 0:
self.sz = 0
- return "==> File truncated <==\n"
+ return "==> File truncated <==\n" if not self.is_html else "==> File truncated <==\n"
if bytes_added > 0:
self.file.seek(-bytes_added, 2)
- bytes = self.file.read(bytes_added)
+ data = self.file.read(bytes_added)
self.sz = newsz
- return bytes
+
+ if self.is_html and data:
+ # HTML escape to prevent breaking HTML structure
+ data = data.replace(b'&', b'&').replace(b'<', b'<').replace(b'>', b'>')
+ # Highlight log levels
+ data = self._highlight_log_levels(data)
+
+ return data
return NOT_DONE_YET
+ def _highlight_log_levels(self, data):
+ """Highlight different log levels"""
+ import re
+
+ # Convert byte data to string for regular expression
+ if isinstance(data, bytes):
+ data_str = data.decode('utf-8', errors='replace')
+ else:
+ data_str = data
+
+ # Define regex patterns for log levels and corresponding CSS classes
+ patterns = [
+ (r'\b(ERROR|CRITICAL|FATAL)\b', 'log-error'),
+ (r'\b(WARN|WARNING)\b', 'log-warn'),
+ (r'\b(INFO|NOTICE)\b', 'log-info'),
+ (r'\b(DEBUG|TRACE)\b', 'log-debug')
+ ]
+
+ # Add span tags for each match
+ for pattern, css_class in patterns:
+ data_str = re.sub(pattern, r'\1' % css_class, data_str)
+
+ return data_str.encode('utf-8')
+
def _open(self):
self.file = open(self.filename, 'rb')
self.ino = os.fstat(self.file.fileno())[stat.ST_INO]
@@ -680,7 +719,7 @@ def _follow(self):
except (OSError, ValueError):
# file was unlinked
return
-
+
if self.ino != ino: # log rotation occurred
self._close()
self._open()
@@ -744,18 +783,168 @@ def handle_request(self, request):
request.error(404) # not found
return
- mtime = os.stat(logfile)[stat.ST_MTIME]
- request['Last-Modified'] = http_date.build_http_date(mtime)
- request['Content-Type'] = 'text/plain;charset=utf-8'
- # the lack of a Content-Length header makes the outputter
- # send a 'Transfer-Encoding: chunked' response
- request['X-Accel-Buffering'] = 'no'
- # tell reverse proxy server (e.g., nginx) to disable proxy buffering
- # (see also http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffering)
-
- request.push(tail_f_producer(request, logfile, 1024))
-
- request.done()
+ # Get the format parameter from request to decide whether to return HTML or plain text
+ is_html = False
+ if query:
+ import cgi
+ parsed_query = cgi.parse_qs(query)
+ is_html = parsed_query.get('format', ['plain'])[0] == 'html'
+
+ if is_html:
+ # Return a beautified HTML page
+ mtime = os.stat(logfile)[stat.ST_MTIME]
+ request['Last-Modified'] = http_date.build_http_date(mtime)
+ request['Content-Type'] = 'text/html;charset=utf-8'
+ request['X-Accel-Buffering'] = 'no'
+
+ # Create the full process name
+ full_process_name = process_name
+ if group_name != process_name:
+ full_process_name = "%s:%s" % (group_name, process_name)
+
+ # Build HTML header
+ html_head = '''
+
+
+
+ Process %s Log
+
+
+
+
+
+
+
+
''' % (full_process_name, full_process_name, request.args[0])
+
+ html_foot = '''
+
+
+
+
+
+''' % (supervisor.options.VERSION)
+
+ # Send response with beautified page
+ request.push(html_head)
+ request.push(tail_f_producer(request, logfile, 1024, is_html=True))
+ request.push(html_foot)
+ request.done()
+
+ else:
+ # Original plain text response
+ # Set content type and necessary headers
+ request['Content-Type'] = 'text/plain;charset=utf-8'
+ mtime = os.stat(logfile)[stat.ST_MTIME]
+ request['Last-Modified'] = http_date.build_http_date(mtime)
+ request['X-Accel-Buffering'] = 'no'
+
+ # Directly push log content
+ request.push(tail_f_producer(request, logfile, 1024, is_html=False))
+ request.done()
class mainlogtail_handler:
IDENT = 'Main Logtail HTTP Request Handler'
diff --git a/supervisor/ui/status.html b/supervisor/ui/status.html
index 166e3e32f..5dc7e2685 100644
--- a/supervisor/ui/status.html
+++ b/supervisor/ui/status.html
@@ -3,66 +3,81 @@
+
Supervisor Status
-
-
-
-
diff --git a/supervisor/ui/stylesheets/supervisor.css b/supervisor/ui/stylesheets/supervisor.css
index 12aba9288..e553bf90b 100644
--- a/supervisor/ui/stylesheets/supervisor.css
+++ b/supervisor/ui/stylesheets/supervisor.css
@@ -1,215 +1,428 @@
-/* =ORDER
- 1. display
- 2. float and position
- 3. width and height
- 4. Specific element properties
- 5. margin
- 6. border
- 7. padding
- 8. background
- 9. color
-10. font related properties
------------------------------------------------ */
-
-/* =MAIN
------------------------------------------------ */
-body, td, input, select, textarea, a {
- font: 12px/1.5em arial, helvetica, verdana, sans-serif;
- color: #333;
-}
-html, body, form, fieldset, h1, h2, h3, h4, h5, h6,
-p, pre, blockquote, ul, ol, dl, address {
+/* Supervisor Status Page Modern Style */
+/* Reset and Base Styles */
+* {
margin: 0;
padding: 0;
-}
-form label {
- cursor: pointer;
-}
-fieldset {
- border: none;
-}
-img, table {
- border-width: 0;
+ box-sizing: border-box;
}
-/* =COLORS
------------------------------------------------ */
body {
- background-color: #FFFFF3;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+ line-height: 1.6;
color: #333;
+ background: #f5f7fa;
}
-a:link,
-a:visited {
- color: #333;
+
+a {
+ color: #2c7be5;
+ text-decoration: none;
+ transition: color 0.2s, background-color 0.2s;
}
+
a:hover {
- color: #000;
+ color: #1a56a8;
}
-/* =FLOATS
------------------------------------------------ */
-.left {
- float: left;
-}
-.right {
- text-align: right;
- float: right;
-}
-/* clear float */
-.clr:after {
- content: ".";
- display: block;
- height: 0;
- clear: both;
- visibility: hidden;
+/* Layout */
+#wrapper {
+ max-width: 1400px;
+ margin: 0 auto;
+ padding: 20px;
+ min-height: 100vh;
}
-.clr {display: inline-block;}
-/* Hides from IE-mac \*/
-* html .clr {height: 1%;}
-.clr {display: block;}
-/* End hide from IE-mac */
-/* =LAYOUT
------------------------------------------------ */
-html, body {
- height: 100%;
+/* Header */
+#header {
+ display: flex;
+ align-items: center;
+ margin-bottom: 30px;
+ padding: 20px;
+ background: white;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
-#wrapper {
- min-height: 100%;
- height: auto !important;
- height: 100%;
- width: 850px;
- margin: 0 auto -31px;
+
+#header img {
+ height: 40px;
+ margin-right: 15px;
}
-#footer,
-.push {
- height: 30px;
+
+#header h1 {
+ font-size: 24px;
+ color: #2c3e50;
+ margin: 0;
}
.hidden {
display: none;
}
-/* =STATUS
------------------------------------------------ */
-#header {
- margin-bottom: 13px;
- padding: 10px 0 13px 0;
- background: url("../images/rule.gif") left bottom repeat-x;
-}
+/* Status Message */
.status_msg {
- padding: 5px 10px;
- border: 1px solid #919191;
- background-color: #FBFBFB;
- color: #000000;
+ padding: 15px;
+ margin-bottom: 20px;
+ background: #e1f5fe;
+ border-left: 4px solid #03a9f4;
+ border-radius: 4px;
+ transition: opacity 0.5s ease-out;
}
+/* Action Buttons */
#buttons {
- margin: 13px 0;
-}
-#buttons li {
- float: left;
- display: block;
- margin: 0 7px 0 0;
-}
-#buttons a {
- float: left;
- display: block;
- padding: 1px 0 0 0;
-}
-#buttons a, #buttons a:link {
- text-decoration: none;
+ list-style: none;
+ display: flex;
+ gap: 10px;
+ margin-bottom: 20px;
}
-.action-button {
- border: 1px solid #919191;
- text-transform: uppercase;
- padding: 0 5px;
+#buttons li a {
+ display: inline-flex;
+ align-items: center;
+ padding: 8px 16px;
+ background: #1976d2;
+ color: white;
+ text-decoration: none;
border-radius: 4px;
- color: #50504d;
- font-size: 12px;
- background: #fbfbfb;
- font-weight: 600;
+ font-weight: 500;
+ transition: all 0.2s ease;
}
-.action-button:hover {
- border: 1px solid #88b0f2;
- background: #ffffff;
+#buttons li a:hover {
+ background: #1565c0;
+ transform: translateY(-1px);
}
+/* Tables */
table {
width: 100%;
- border: 1px solid #919191;
+ border-collapse: collapse;
+ margin: 0;
+ table-layout: fixed;
}
+
th {
- background-color: #919191;
- color: #fff;
text-align: left;
+ padding: 12px 15px;
+ background-color: #f3f4f6;
+ border-bottom: 1px solid #ddd;
+ font-weight: 600;
+ color: #4b5563;
}
-th.state {
- text-align: center;
- width: 44px;
-}
-th.desc {
- width: 200px;
+
+th.state, td.status {
+ width: 120px;
}
-th.name {
- width: 200px;
+
+th.desc, td:nth-child(2) {
+ width: 250px;
}
-th.action {
+
+th.name, td:nth-child(3) {
+ width: auto;
+ min-width: 250px;
}
-td, th {
- padding: 4px 8px;
- border-bottom: 1px solid #fff;
+
+th.action, td.action {
+ width: 300px;
}
-tr td {
- background-color: #FBFBFB;
+
+td {
+ padding: 10px 15px;
+ border-bottom: 1px solid #eee;
+ vertical-align: middle;
}
-tr.shade td {
- background-color: #F0F0F0;
+
+td:nth-child(3) a {
+ word-break: break-word;
+ display: inline-block;
+ width: 100%;
+ white-space: normal;
}
-.action ul {
- list-style: none;
- display: inline;
+
+tr:last-child td {
+ border-bottom: none;
}
-.action li {
- margin-right: 10px;
- display: inline;
+
+tr.shade {
+ background-color: #f9f9f9;
}
-/* status message */
+/* Status Indicators */
.status span {
- display: block;
- width: 60px;
- height: 16px;
- border: 1px solid #fff;
+ display: inline-block;
+ padding: 4px 12px;
+ border-radius: 12px;
+ font-size: 12px;
+ font-weight: 500;
text-align: center;
- font-size: 95%;
- line-height: 1.4em;
-}
-.statusnominal {
- background-image: url("../images/state0.gif");
+ min-width: 90px;
}
+
.statusrunning {
- background-image: url("../images/state2.gif");
+ background: #e3f2fd;
+ color: #1976d2;
}
+
+.statusnominal {
+ background: #e8f5e9;
+ color: #2e7d32;
+}
+
.statuserror {
- background-image: url("../images/state3.gif");
+ background: #ffebee;
+ color: #c62828;
+}
+
+.statusstopped {
+ background: #fff3e0;
+ color: #e65100;
+}
+
+/* Action Links */
+.action ul {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ display: flex;
+ flex-wrap: nowrap;
+ gap: 10px;
+}
+
+.action ul li {
+ display: inline-block;
+}
+
+.action ul li a {
+ display: inline-block;
+ white-space: nowrap;
+ padding: 5px 10px;
+ font-size: 13px;
+ color: #1976d2;
+ text-decoration: none;
+ border: 1px solid #e0e0e0;
+ border-radius: 4px;
+ background-color: #f5f5f5;
+ transition: all 0.2s ease;
+}
+
+.action ul li a:hover {
+ background: #1976d2;
+ color: white;
+ border-color: #1976d2;
}
+/* 移除分隔符,使用边框样式替代 */
+td.action ul li:not(:last-child):after {
+ content: none !important;
+ margin-left: 0;
+ display: none;
+}
+
+/* Footer */
#footer {
- width: 760px;
- margin: 0 auto;
- padding: 0 10px;
- line-height: 30px;
- border: 1px solid #C8C8C2;
- border-bottom-width: 0;
- background-color: #FBFBFB;
+ margin-top: 40px;
+ padding: 20px;
+ border-top: 1px solid #e9ecef;
+ color: #6c757d;
+ font-size: 14px;
+}
+
+/* Utilities */
+.left {
+ float: left;
+}
+
+.right {
+ float: right;
+}
+
+.clr:after {
+ content: '';
+ display: table;
+ clear: both;
+}
+
+/* 进程组样式 */
+.process-group {
+ margin-bottom: 30px;
+ border: 1px solid #e0e0e0;
+ border-radius: 6px;
overflow: hidden;
- opacity: 0.7;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+}
+
+.group-header {
+ padding: 10px 15px;
+ background-color: #f0f0f0;
+ border-bottom: 1px solid #ddd;
+}
+
+.title-with-actions {
+ display: flex;
+ align-items: center;
+}
+
+.group-title {
+ margin: 0;
+ padding: 0;
+ font-size: 16px;
+ font-weight: 600;
+}
+
+.group-actions {
+ display: flex;
+ margin-left: 20px;
+}
+
+.group-actions ul {
+ display: flex;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ gap: 10px;
+}
+
+.group-actions ul li a {
+ display: inline-block;
+ padding: 4px 12px;
+ background: #f5f5f5;
+ color: #333;
+ text-decoration: none;
+ border: 1px solid #ddd;
+ border-radius: 4px;
+ font-size: 13px;
+ transition: all 0.2s ease;
+}
+
+.group-actions ul li a:hover {
+ background: #e0e0e0;
color: #000;
- font-size: 95%;
}
-#footer a {
- font-size: inherit;
+
+/* 为组操作按钮添加特定颜色 */
+.group-actions ul li:nth-child(1) a {
+ background: #17a2b8;
+ color: white;
+ border-color: #138496;
+}
+
+.group-actions ul li:nth-child(2) a {
+ background: #dc3545;
+ color: white;
+ border-color: #c82333;
+}
+
+.group-actions ul li:nth-child(1) a:hover {
+ background: #138496;
+}
+
+.group-actions ul li:nth-child(2) a:hover {
+ background: #c82333;
+}
+
+.group-summary {
+ display: flex;
+ gap: 10px;
+}
+
+.group-status {
+ font-size: 14px;
+ padding: 4px 10px;
+ border-radius: 20px;
+ background-color: #f5f5f5;
+}
+
+.group-status.running {
+ background-color: #e1f8f0;
+ color: #0abb87;
+}
+
+.group-status.error {
+ background-color: #ffe8ef;
+ color: #fd397a;
+}
+
+.group-status.partial {
+ background-color: #fff4de;
+ color: #ffb822;
+}
+
+.group-icon {
+ margin-right: 5px;
+ transition: transform 0.2s;
+}
+
+.group-content {
+ width: 100%;
+}
+
+/* Responsive Design */
+@media (max-width: 768px) {
+ #wrapper {
+ padding: 10px;
+ }
+
+ #buttons {
+ flex-direction: column;
+ }
+
+ .action-button a {
+ width: 100%;
+ justify-content: center;
+ }
+
+ .action ul {
+ flex-direction: column;
+ }
+
+ .action a {
+ text-align: center;
+ }
+
+ table {
+ display: block;
+ overflow-x: auto;
+ }
+
+ .group-header {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .group-summary {
+ width: 100%;
+ margin-top: 8px;
+ justify-content: space-between;
+ }
+}
+
+/* Animations */
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+.status_msg {
+ animation: fadeIn 0.3s ease-out;
+}
+
+td.action ul {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+ display: flex;
+ gap: 8px;
+}
+
+td.action ul li {
+ display: inline;
+}
+
+td.action a {
+ color: #0066cc;
+ text-decoration: none;
+}
+
+td.action a:hover {
+ text-decoration: underline;
}
diff --git a/supervisor/ui/tail.html b/supervisor/ui/tail.html
index 117cb5f5d..d0a80b9b3 100644
--- a/supervisor/ui/tail.html
+++ b/supervisor/ui/tail.html
@@ -3,25 +3,252 @@
- Supervisor Status
+
+ Process Log
+
+
+
-