logo
0
0
WeChat Login

Glive - 实时日志监控工具

Go Version License

Glive 是一个基于 Go 语言开发的实时日志监控工具,支持通过 Web 界面实时查看多个日志文件,具有 WebSocket 实时推送、日志过滤、历史日志查看、Base64解码、消息合并等功能。

功能演示


📋 目录


✨ 功能特性

核心功能

  • 📡 实时监控 - 通过 WebSocket 实时推送日志内容到浏览器
  • 📁 多文件监控 - 同时监控多个指定的日志文件
  • 📂 目录监控 - 自动监控整个目录中匹配的文件(支持通配符)
  • 🔄 日志轮转 - 自动检测日志轮转并重新打开文件
  • 📖 历史日志 - 支持查看历史日志内容(可配置行数)
  • 🎨 主题切换 - 支持暗色(Dark)和亮色(Light)主题

Web 界面功能

  • 🔍 实时过滤 - 支持关键字过滤日志内容(正则表达式)
  • ⏸️ 暂停/恢复 - 可随时暂停或恢复日志接收
  • 🧹 清空显示 - 一键清空当前显示的日志
  • 📦 消息合并 - 自动合并重复的消息,显示重复次数
  • 🔓 Base64解码 - 智能识别并解码 Base64/Hex 编码内容
  • 📜 历史日志 - 加载指定文件的最近 N 行历史日志
  • 📐 行折叠 - 长日志自动折叠,支持展开查看
  • 📊 连接状态 - 实时显示 WebSocket 连接状态
  • 👥 在线人数 - 显示当前连接的客户端数量

高级特性

  • 📝 配置热重载 - 配置文件修改后自动生效(无需重启)
  • 🔒 SSL/HTTPS 支持 - 可选启用 HTTPS 加密传输
  • 🖥️ 跨平台 - 支持 Windows、Linux、macOS
  • 高性能 - 使用 Go 协程处理并发连接
  • 🛡️ 文件安全 - 只允许访问已配置的监控文件

🏗️ 架构设计

┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ Web Browser │◄────►│ WebSocket │ │ Log Files │ │ (Frontend) │ │ Server │ │ │ └─────────────────┘ └────────┬────────┘ └────────┬────────┘ │ │ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ │ Tailer │◄────►│ nxadm/tail │ │ (Core) │ │ (File Monitor) │ └─────────────────┘ └─────────────────┘

技术栈

组件技术说明
后端框架GinHTTP Web 框架
WebSocketgorilla/websocket实时通信
日志监控nxadm/tail跨平台文件 tail
配置管理Viper配置解析与热重载
前端原生 HTML/CSS/JS无需构建工具

🚀 快速开始

1. 环境要求

  • Go 1.25 或更高版本
  • 支持的系统:Windows、Linux、macOS

2. 安装

# 克隆仓库 git clone https://github.com/yourusername/glive.git cd glive # 安装依赖 go mod download # 编译 go build -o glive.exe main.go

3. 运行

# 使用默认配置(config.yaml) ./glive.exe # 指定配置文件 ./glive.exe -c /path/to/config.yaml

4. 访问

打开浏览器访问:http://localhost:8080


⚙️ 配置文件

配置文件采用 YAML 格式,默认文件名为 config.yaml

完整配置示例

# 🚀 Glive 配置文件 # 支持 Windows/Linux/macOS server: port: 8080 # 监听端口 ssl: enabled: false # 是否启用HTTPS cert_file: "" # SSL证书路径 key_file: "" # SSL私钥路径 # 📁 日志监控配置 logs: # 支持多文件监控,数组形式 # Windows 示例: "C:\\logs\\app.log" # Linux 示例: "/var/log/nginx/access.log" files: - "./test.log" # 测试用,默认监控当前目录的 test.log - "/var/log/nginx/access.log" - "C:\\logs\\app.log" # 或监控整个目录(自动监控目录内匹配的文件) watch_dir: "/var/log/app" file_pattern: "*.log" # 文件匹配模式 # 🎨 前端配置 frontend: static_path: "./static" # HTML静态文件路径 title: "Glive 日志监控" # 页面标题 theme: "dark" # 主题: dark 或 light # 🔧 高级配置 advanced: tail_from_end: true # 启动时从文件末尾开始(true)或从头(false) reopen_on_rotate: true # 日志轮转时自动重新打开 buffer_size: 100 # 消息缓冲区大小

配置项说明

Server 配置

参数类型默认值说明
server.portint8080HTTP 服务监听端口
server.ssl.enabledboolfalse是否启用 HTTPS
server.ssl.cert_filestring""SSL 证书文件路径
server.ssl.key_filestring""SSL 私钥文件路径

Logs 配置

参数类型默认值说明
logs.filesarray[]要监控的日志文件列表
logs.watch_dirstring""要监控的目录路径
logs.file_patternstring"*.log"目录监控时的文件匹配模式

Frontend 配置

参数类型默认值说明
frontend.static_pathstring"./static"静态文件目录
frontend.titlestring"Glive 日志监控"页面标题
frontend.themestring"dark"主题样式: darklight

Advanced 配置

参数类型默认值说明
advanced.tail_from_endbooltrue启动时是否从文件末尾开始读取
advanced.reopen_on_rotatebooltrue日志轮转时是否自动重新打开
advanced.buffer_sizeint100WebSocket 消息缓冲区大小

配置热重载

修改 config.yaml 后,系统会自动检测变更并重新加载配置(无需重启服务)。控制台会输出:

📝 配置文件已变更: config.yaml ✅ 配置已热重载

📖 使用说明

命令行参数

参数简写默认值说明
--config-c"config.yaml"指定配置文件路径

界面操作

1. 实时日志查看

  • 打开浏览器访问服务地址
  • 日志会实时显示在页面上
  • 新日志自动滚动到底部
  • 超过 5000 行自动删除最早的历史行

2. 过滤功能 (正则表达式)

  • 在过滤输入框中输入关键字或正则表达式
  • 只显示匹配关键字的日志行
  • 支持实时过滤已接收的日志
  • 使用 i 标志表示忽略大小写

3. 暂停/恢复

  • 点击 暂停 按钮停止接收新日志
  • 点击 继续 按钮恢复接收
  • 暂停时仍可查看历史日志
  • 快捷键: Ctrl+Space

4. 清空显示

  • 点击 清空 按钮清除当前显示的日志
  • 不会删除实际日志文件内容
  • 快捷键: Ctrl+K

5. 消息合并

  • 点击 合并:开/关 按钮切换合并状态
  • 5 秒内相同的消息会自动合并,显示重复次数
  • 合并后只在行尾显示计数徽章,如 5x

6. Base64/Hex 解码

  • 当检测到可解码内容时,行右侧显示 解码 按钮
  • 点击弹出对话框,显示原始内容和解码结果
  • 支持 \xHH 格式、Base64、JSON 等多种编码

7. 行折叠

  • 超过 200 字符的日志行自动折叠
  • 点击 展开 按钮查看完整内容
  • 再次点击 折叠 收起

8. 历史日志

  • 点击 历史 按钮打开历史日志对话框
  • 选择文件和行数(100/500/1000/5000/10000/全部)
  • 点击查询加载历史日志,加载后自动滚动到顶部

9. 重连

  • 点击 重连 按钮手动重新建立 WebSocket 连接
  • 连接断开时自动重连,退避策略:3s × 1.5ⁿ(最大30s)

🎨 前端功能详解

🔓 智能解码功能 (Base64/Hex)

前端自动识别日志中的编码内容,提供一键解码能力。

核心函数: tryBase64Decode(str)

/** * 智能解码函数 - 支持多种编码格式 * 1. \xHH 格式的 UTF-8 转义序列 * 2. Base64 编码 * 3. 多重兜底验证机制 */ function tryBase64Decode(str) { if (!str || str.length < 10) return null; // 策略1: 解码 \xHH 格式的 UTF-8 转义序列 const hexResult = tryDecodeHexEscapes(str); if (hexResult && isValidDecodedContent(hexResult.decoded)) { return hexResult; } // 策略2: 尝试 Base64 解码 const base64Result = tryDecodeBase64(str); if (base64Result && isValidDecodedContent(base64Result.decoded)) { return base64Result; } // 策略3: 尝试混合解码 (先解 hex,再解 base64) if (hexResult) { const nestedBase64 = tryDecodeBase64(hexResult.decoded); if (nestedBase64 && isValidDecodedContent(nestedBase64.decoded)) { return { encoded: hexResult.encoded, decoded: nestedBase64.decoded }; } } // 策略4: 尝试 JSON 解析 const jsonResult = tryDecodeJSON(str); if (jsonResult) return jsonResult; return null; }

解码策略详解

策略函数名处理思路
Hex 转义解码tryDecodeHexEscapes(str)识别 \xHH 格式,提取十六进制字节数组,使用 UTF-8 解码器转换为字符串
Base64 解码tryDecodeBase64(str)使用正则提取可能的 Base64 片段,验证长度有效性后使用 atob() 解码
混合解码-先解 Hex 转义,再尝试 Base64 解码(处理嵌套编码)
JSON 解码tryDecodeJSON(str)检测 JSON 特征(\x22 : \x22),先解 Hex 再解析 JSON

Hex 解码核心逻辑

function tryDecodeHexEscapes(str) { const hexPattern = /\\x([0-9A-Fa-f]{2})/g; if (!hexPattern.test(str)) return null; // 至少要有3个 \xHH 序列才尝试解码 const matches = str.match(/\\x[0-9A-Fa-f]{2}/g); if (!matches || matches.length < 3) return null; // 提取所有十六进制字节 const hexBytes = []; let match; hexPattern.lastIndex = 0; while ((match = hexPattern.exec(str)) !== null) { hexBytes.push(parseInt(match[1], 16)); } // 使用 UTF-8 解码器解码 const uint8Array = new Uint8Array(hexBytes); const decoded = new TextDecoder('utf-8', { fatal: true }).decode(uint8Array); return { encoded: str, decoded: decoded }; }

Base64 解码核心逻辑

function tryDecodeBase64(str) { // 提取可能的 Base64 片段 const base64Pattern = /[A-Za-z0-9+/]{20,}={0,2}/g; const matches = str.match(base64Pattern); if (!matches) return null; for (let match of matches) { if (match.length < 16) continue; // 验证 Base64 长度有效性 const padding = (match.match(/=/g) || []).length; const baseLen = match.length - padding; if (baseLen % 4 === 1) continue; // 无效长度 try { const decoded = atob(match); if (decoded.length > 5 && hasReadableChars(decoded)) { return { encoded: match, decoded: decoded }; } } catch (e) {} } return null; }

内容验证机制

function isValidDecodedContent(str) { if (!str || str.length < 2) return false; // 检查可读字符比例 >= 60% const readableRatio = getReadableRatio(str); if (readableRatio < 0.6) return false; // 检查控制字符比例 < 10% const controlChars = str.match(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g); if (controlChars && controlChars.length > str.length * 0.1) return false; // 验证 Unicode 有效性 try { encodeURIComponent(str); } catch (e) { return false; } return true; }

使用方式

当检测到可解码内容时,日志行右侧会显示 解码 按钮:

  • 点击按钮弹出解码对话框
  • 显示原始编码内容和解码结果
  • 支持一键复制解码结果

📦 消息合并功能

自动合并连续出现的相同日志消息,减少界面冗余。

核心逻辑

// 重复消息合并配置 const GROUP_TIMEOUT = 5000; // 5秒内相同消息视为重复 let messageGroups = new Map(); // 存储消息指纹和合并信息 function getMessageFingerprint(filename, content) { // 移除时间戳后生成指纹 const cleanContent = content.replace(/\[\d{2}:\d{2}:\d{2}\]/g, '').trim(); return filename + ':' + cleanContent; } function processMessage(data) { if (isMergeEnabled && !data.includes('✅ 已连接')) { const fingerprint = getMessageFingerprint(filename, content); const existingGroup = messageGroups.get(fingerprint); // 如果在超时时间内,增加计数 if (existingGroup && (Date.now() - existingGroup.time) < GROUP_TIMEOUT) { existingGroup.count++; // 更新徽章显示 const badge = existingGroup.element.querySelector('.count-badge'); badge.textContent = existingGroup.count + 'x'; badge.style.display = 'inline-flex'; return; // 不添加新行 } } // 创建新行并记录指纹 const element = addLogLine(html, level, content); messageGroups.set(fingerprint, { element: element, count: 1, time: Date.now() }); }

合并规则

  1. 指纹生成: 文件名 + 内容(移除时间戳)
  2. 时间窗口: 5秒内出现的相同消息
  3. 计数显示: 在行尾显示徽章,如 5x
  4. 系统消息: 系统消息(📢、✅等)也参与合并

控制方式

  • 点击 合并:开/关 按钮切换合并状态
  • 默认开启,实时生效
  • 合并状态改变时不影响已显示的消息

📐 行折叠功能

自动折叠过长的日志行,保持界面整洁。

实现机制

function addLogLine(html, level, rawContent, autoScroll) { const isLongContent = rawContent && rawContent.length > 200; if (isLongContent) { div.classList.add('collapsed'); // 添加折叠样式 } // 添加展开/折叠按钮 if (isLongContent) { actionsHtml += '<button class="line-action-btn" onclick="toggleCollapse(this.parentElement.parentElement)">展开</button>'; } } function toggleCollapse(lineElement) { const isCollapsed = lineElement.classList.contains('collapsed'); if (isCollapsed) { lineElement.classList.remove('collapsed'); btn.textContent = '折叠'; } else { lineElement.classList.add('collapsed'); btn.textContent = '展开'; } }

CSS 折叠效果

.line.collapsed .log-content { max-height: 3.2em; /* 限制高度为3行 */ overflow: hidden; /* 隐藏溢出 */ position: relative; } /* 底部渐变遮罩 */ .line.collapsed .log-content::after { content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 1.5em; background: linear-gradient(transparent, #0d1117); }

🔍 日志级别自动识别

根据日志内容自动识别并标记级别。

function detectLevel(content) { if (/\b(error|err|fail|failed|exception|critical|fatal)\b/i.test(content)) { return 'error'; // 红色 } else if (/\b(warn|warning)\b/i.test(content)) { return 'warn'; // 橙色 } return ''; // 默认颜色 }
级别关键词颜色
Errorerror, err, fail, failed, exception, critical, fatal红色 (#f85149)
Warnwarn, warning橙色 (#f0883e)
Info-蓝色 (#58a6ff)
System系统消息灰色 (#858585)

⌨️ 快捷键支持

快捷键功能
Ctrl+K清空日志
Ctrl+Space暂停/继续
Esc关闭弹窗

🔌 API 文档

REST API

1. 健康检查

GET /health

响应示例:

{ "status": "ok", "clients": 5 }

2. 获取服务器状态

GET /api/status

响应示例:

{ "status": "ok", "clients": 5, "monitored_files": ["./test.log", "/var/log/app.log"], "config": { "port": 8080, "tail_from_end": true, "reopen_on_rotate": true } }

3. 获取历史日志

GET /api/history?file=<文件路径>&lines=<行数>

参数说明:

  • file (必填): 日志文件路径
  • lines (可选): 返回的行数,默认 1000

响应示例:

{ "file": "./test.log", "lines": ["2024-01-01 10:00:00 Log line 1", "2024-01-01 10:00:01 Log line 2"], "count": 2 }

错误响应:

  • 400 Bad Request: 缺少 file 参数
  • 403 Forbidden: 文件不在监控列表中
  • 500 Internal Server Error: 读取文件失败

WebSocket API

连接地址

ws://localhost:8080/ws

消息格式

服务端推送的消息:

{ "type": "log", "file": "./test.log", "content": "2024-01-01 10:00:00 Log message here", "timestamp": "2024-01-01T10:00:00Z" }

系统消息:

{ "type": "system", "content": "📁 开始监控: ./test.log" }

🛠️ 开发指南

项目结构

Glive/ ├── main.go # 程序入口 ├── config.yaml # 默认配置文件 ├── go.mod # Go 模块定义 ├── go.sum # 依赖校验 ├── config/ # 配置管理包 │ └── config.go # 配置解析与热重载 ├── tailer/ # 日志监控核心 │ ├── tailer.go # 跨平台 tailer 实现 │ ├── tailer_unix.go # Unix 系统优化 │ └── tailer_windows.go # Windows 系统优化 ├── static/ # 前端静态文件 │ └── index.html # Web 界面 └── README.md # 项目说明

核心模块

1. Tailer 模块 (tailer/tailer.go)

负责日志文件的实时监控和广播:

type Tailer struct { clients map[*websocket.Conn]bool // WebSocket 客户端 broadcast chan string // 广播通道 tails map[string]*tail.Tail // 文件 tail 实例 mu sync.RWMutex // 互斥锁 wg sync.WaitGroup // 等待组 stopCh chan struct{} // 停止信号 cfg *config.Config // 配置 }

主要方法:

  • New(cfg) - 创建 Tailer 实例
  • Start() - 启动监控
  • Stop() - 停止监控
  • RegisterClient(conn) - 注册 WebSocket 客户端
  • UnregisterClient(conn) - 注销 WebSocket 客户端
  • tailFile(filePath) - 监控单个文件
  • watchDirectory(dir) - 监控整个目录
  • broadcaster() - 广播消息给所有客户端
  • safeBroadcast(msg) - 安全广播(带超时防阻塞)

2. Config 模块 (config/config.go)

负责配置管理和热重载:

func Init(configPath string) error // 初始化配置 func GetConfig() *Config // 获取当前配置

热重载实现:

v.WatchConfig() v.OnConfigChange(func(e fsnotify.Event) { var newConf Config if err := v.Unmarshal(&newConf); err != nil { fmt.Printf("❌ 重载配置失败: %v\n", err) } else { Conf = &newConf fmt.Printf("✅ 配置已热重载\n") } })

3. Web 服务 (main.go)

Gin 框架提供 HTTP 和 WebSocket 服务:

路由功能
GET /首页(Web 界面)
GET /wsWebSocket 连接
GET /static/*静态文件服务
GET /api/status服务状态 API
GET /api/history历史日志 API
GET /health健康检查

WebSocket 升级配置:

var upgrader = websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true // 生产环境建议限制域名 }, ReadBufferSize: 1024, WriteBufferSize: 1024, }

4. 前端模块 (static/index.html)

纯原生 JavaScript 实现,无需构建工具。

核心功能代码位置
功能函数/变量行号
WebSocket 连接connect()460
消息处理processMessage(data)545
智能解码tryBase64Decode(str)630
Hex 解码tryDecodeHexEscapes(str)669
Base64 解码tryDecodeBase64(str)775
JSON 解码tryDecodeJSON(str)803
内容验证isValidDecodedContent(str)830
消息合并getMessageFingerprint()540
行折叠toggleCollapse()930
日志级别识别detectLevel()609
过滤功能applyFilter()448
历史日志fetchHistory()1003

添加新功能

添加新的 API 端点

main.go 中的路由设置部分添加:

r.GET("/api/new-endpoint", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "New endpoint", }) })

添加新的日志处理逻辑

tailer/tailer.go 中修改:

func (t *Tailer) tailFile(filePath string) { // 添加自定义处理逻辑 for line := range tl.Lines { processedLine := customProcess(line.Text) t.safeBroadcast(processedLine) } }

构建脚本

项目包含 build.sh 构建脚本:

# Windows 构建 ./build.sh windows # Linux 构建 ./build.sh linux # macOS 构建 ./build.sh darwin

❓ 常见问题

Q1: 无法监控文件,提示权限不足?

解决方案:

  • Linux/macOS: 确保运行用户有读取权限
    chmod 644 /var/log/your-app.log
  • Windows: 以管理员身份运行程序

Q2: 日志轮转后无法继续监控?

解决方案: 在配置中启用日志轮转检测:

advanced: reopen_on_rotate: true

Q3: 如何监控 Docker 容器内的日志?

解决方案: 使用卷挂载将容器日志映射到主机:

docker run -v /var/lib/docker/containers:/var/log/containers:ro glive

然后在配置中:

logs: files: - "/var/log/containers/container-id/container-name-json.log"

Q4: WebSocket 连接失败?

排查步骤:

  1. 检查防火墙是否放行端口
  2. 确认服务正常运行:curl http://localhost:8080/health
  3. 查看浏览器控制台错误信息
  4. 检查 Nginx/Apache 反向代理 WebSocket 配置

Q5: 如何设置开机自启动?

Linux (systemd): 创建服务文件 /etc/systemd/system/glive.service

[Unit] Description=Glive Log Monitor After=network.target [Service] Type=simple ExecStart=/usr/local/bin/glive -c /etc/glive/config.yaml Restart=always User=glive [Install] WantedBy=multi-user.target

启用服务:

sudo systemctl enable glive sudo systemctl start glive

Windows: 使用 NSSM 创建服务:

nssm install Glive "C:\Glive\glive.exe" nssm set Glive AppDirectory "C:\Glive" nssm start Glive

Q6: 如何配置 HTTPS?

  1. 准备 SSL 证书和私钥
  2. 修改配置:
server: port: 8443 ssl: enabled: true cert_file: "/path/to/cert.pem" key_file: "/path/to/key.pem"

Q7: 配置文件修改后没有生效?

可能原因:

  • 文件路径不是绝对路径时,确保在程序工作目录下
  • 配置格式错误(检查 YAML 语法)
  • 查看控制台输出是否有热重载提示

📄 许可证

本项目采用 MIT 许可证 - 详见 LICENSE 文件


🤝 贡献

欢迎提交 Issue 和 Pull Request!

  1. Fork 本仓库
  2. 创建特性分支 (git checkout -b feature/amazing-feature)
  3. 提交更改 (git commit -m 'Add amazing feature')
  4. 推送到分支 (git push origin feature/amazing-feature)
  5. 创建 Pull Request

📧 联系方式


Made with ❤️ by Glive Team

About

No description, topics, or website provided.
110.98 MiB
0 forks0 stars1 branches0 TagREADMEApache-2.0 license
Language
Go76.6%
Shell17.4%
JavaScript5.7%
Dockerfile0.4%