go实现对容器日志的读取并通过api展示

场景

工作环境中,用容器部署服务是很常见的操作,而新上线的业务,测试人员需要对服务进行测试

但是一下几种可能,使得测试人员并不能方便的查看日志:

  • 有的测试人员docker并不熟
  • 权限比较严格,测试人员没权限操作容器
  • 临时需要查看日志

第一第二中情况咱就不说了,第三种情况,如果只是临时需要,咱们其实没必要特意去接入日志服务

只需要写个服务,将读取到的日志通过api返回出去就好了,又不会占用很多资源,又能随起随停

代码部分

package main

import (
	"bufio"
	"context"
	"fmt"
    _ "embed" // Import the embed package
	"net/http"
	"strconv"
	"time"

	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"github.com/gin-gonic/gin"
)

var (
	host             = "10.0.0.12"
	RemoteDockerHost = "tcp://" + host + ":2376"
	CaPath           = "cert/" + host + "/ca.pem"
	ClientCertPath   = "cert/" + host + "/cert.pem"
	ClientKeyPath    = "cert/" + host + "/key.pem"
)
// 打包将文件打包进去
//go:embed favicon.ico
var favicon []byte

func main() {
	r := gin.Default()
    // 提供 'favicon.ico' 文件
	r.GET("/favicon.ico", func(c *gin.Context) {
		c.Data(http.StatusOK, "image/x-icon", favicon)
	})
	r.GET("/", func(c *gin.Context) {
		containerName := c.DefaultQuery("c", "mysql")
		n := c.DefaultQuery("n", "1000")
		ccc, err := strconv.ParseInt(n, 10, 64)
		if err != nil {
			fmt.Println("参数转化失败:", err)
			c.String(http.StatusBadRequest, "传参有误")
			c.Abort()
			return
		}

		// 创建Docker客户端并指定远程Docker守护进程地址
		// cli, err := client.NewClientWithOpts(
		//	client.WithHost(RemoteDockerHost),
		//	// client.WithVersion("1.41"),
		//	client.WithAPIVersionNegotiation(),
		//	client.WithTLSClientConfig(CaPath, ClientCertPath, ClientKeyPath),
		//)
        // 这里采用本地连接的方式连接
        cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
		if err != nil {
			fmt.Println("创建容器失败:", err)
			c.String(http.StatusBadRequest, "容器访问失败")
			c.Abort()
			return
		}
		_, err = cli.ContainerInspect(c.Request.Context(), containerName)
		if err != nil {
			fmt.Println("容器查询失败:", err)
			c.String(http.StatusBadRequest, "容器不存在")
			c.Abort()
			return
		}
		// fmt.Println(containerInfo)
		option := types.ContainerLogsOptions{
			ShowStdout: true,
			ShowStderr: true,
			Follow:     true,
			// Timestamps: true,
			Tail: n,
		}
		cxt, cancel := context.WithTimeout(c.Request.Context(), 500*time.Millisecond)
		defer cancel()
		out, err := cli.ContainerLogs(cxt, containerName, option)
		if err != nil {
			fmt.Println(err.Error())
			c.String(http.StatusBadRequest, "容器日志访问失败")
			c.Abort()
			return
		}
		defer out.Close()
		scanner := bufio.NewScanner(out)
		// defer cancel()
		num := int64(0)
		var content []byte
		for scanner.Scan() {
			num++
			buf := scanner.Bytes()
			if len(buf) > 8 {
				content = append(content, scanner.Bytes()[8:]...)
			}
			if num != 1 {
				content = append(content, '\n')
			}
			if num >= ccc {
				cancel()
			}
		}
		c.Data(http.StatusOK, "text/plain; charset=utf-8", content)
	})
	r.Run(":50000")
}

因为go语言的特性,对资源消耗比较小,而且非常容易编译成二进制可执行文件,所以能非常好的解决上面的需求

使用

起的服务是50000端口的,然后可以接受两个查询参数

  • c,表示容器ID,可以是容器ID,也可以是容器名,默认是mysql容器
  • n,表示查询的最后的行数,默认是1000行

这样就可以通过查询参数的方式查询主机上的任意容器的日志,当然,觉得不安全,也可以设置黑白名单之类的,这里就不做处理了

posted @ 2023-07-27 19:47  厚礼蝎  阅读(68)  评论(0编辑  收藏  举报