wechat-robot/internal/handler/ws_container_logs.go
李寻欢 b256d82855
All checks were successful
BuildImage / build-image (push) Successful in 7m30s
:feature: 添加WebSocket API以实时获取容器日志,并优化消息结构
2025-04-22 20:44:57 +08:00

124 lines
2.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handler
import (
"context"
"io"
"strings"
"time"
"github.com/docker/docker/api/types/container"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/websocket/v2"
"gitee.ltd/lxh/wechat-robot/internal/docker"
)
// WebSocketContainerLogs 通过WebSocket推送容器日志
func WebSocketContainerLogs(c *websocket.Conn) {
// 获取容器ID
containerID := c.Params("id")
if containerID == "" {
c.Close()
return
}
// 创建上下文,可以通过客户端关闭连接来取消
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 设置客户端关闭时取消上下文
go func() {
defer cancel()
for {
if _, _, err := c.ReadMessage(); err != nil {
// 客户端断开连接
return
}
}
}()
// 获取Docker客户端
cli := docker.GetClient()
// 设置日志选项
options := container.LogsOptions{
ShowStdout: true,
ShowStderr: true,
Follow: true, // 持续跟踪日志
Tail: "100", // 初始返回100行
}
// 获取容器日志流
logStream, err := cli.ContainerLogs(ctx, containerID, options)
if err != nil {
// 发送错误消息并关闭连接
c.WriteMessage(websocket.TextMessage, []byte("获取容器日志失败: "+err.Error()))
c.Close()
return
}
defer logStream.Close()
// 创建缓冲区
buffer := make([]byte, 4096)
// 持续读取日志并发送到WebSocket
for {
select {
case <-ctx.Done():
// 上下文被取消,退出循环
return
default:
// 读取日志
n, err := logStream.Read(buffer)
if err != nil {
if err == io.EOF {
// 日志读取完毕,等待新日志
time.Sleep(500 * time.Millisecond)
continue
}
// 其他错误,关闭连接
return
}
if n > 0 {
// 清理Docker日志头部的8个字节控制信息
cleanedLog := cleanDockerLogBuffer(buffer[:n])
// 发送日志到WebSocket
if err := c.WriteMessage(websocket.TextMessage, cleanedLog); err != nil {
// 发送失败,关闭连接
return
}
}
}
}
}
// ContainerLogsWebSocketMiddleware WebSocket中间件用于升级HTTP连接到WebSocket
func ContainerLogsWebSocketMiddleware() fiber.Handler {
return websocket.New(WebSocketContainerLogs, websocket.Config{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
})
}
// cleanDockerLogBuffer 清理Docker日志缓冲区
func cleanDockerLogBuffer(buffer []byte) []byte {
// 如果缓冲区太小,直接返回
if len(buffer) <= 8 {
return buffer
}
// 分割日志为行
lines := strings.Split(string(buffer), "\n")
for i, line := range lines {
if len(line) > 8 {
// 跳过每行前8个字节的头部信息
lines[i] = line[8:]
}
}
// 合并并返回
return []byte(strings.Join(lines, "\n"))
}