自己动手写Docker系列 -- 5.3实现logs命令查看容器日志

415 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情

简介

在上篇中我们实现了查看正在运行中的容器列表,本章节我们来实现logs命令,来查看正在运行中的容器的运行日志

源码说明

同时放到了Gitee和Github上,都可进行获取

本章节对应的版本标签是:5.3,防止后面代码过多,不好查看,可切换到标签版本进行查看

代码实现

实现该功能的主要思路如下:

1 日志的保存:在使用-d后台运行的时候,我们将文件的输出重定向到文件中,这样就将日志保存到了文件中,提供给后面查看

2 日志的查看:在日志运行过程中,文件已保存到约定的目录,我们只需要读取日志文件内容进行显示即可

运行日志的保存

我们约定将日志文件保存到指定位置

如下,在容器配置中,新增日志文件名称,这样日志文件对应的路径就是:/var/run/mydocker/{容器名}/container.log

var (
	RUNNING             = "running"
	STOP                = "stop"
	EXIT                = "exited"
	DefaultInfoLocation = "/var/run/mydocker/%s/"
	ConfigName          = "config.json"
	ContainerLogFile    = "container.log"
)

然后将后台运行的容器的输出定向输入到文件中

在启动的时候,将容器名称传递进去,如果没有的话,随机生成(在以前章节中已实现)

func Run(tty, detach bool, cmdArray []string, config *subsystem.ResourceConfig, volume, containerName string) {
	pwd, err := os.Getwd()
	if err != nil {
		log.Errorf("Run get pwd err: %v", err)
		return
	}
	mntUrl := pwd + "/mnt/"
	rootUrl := pwd + "/"
	// 传入容器名
	parent, writePipe := container.NewParentProcess(tty, containerName, rootUrl, mntUrl, volume)
	if err := parent.Start(); err != nil {
		log.Error(err)
		// 如果fork进程出现异常,但有相关的文件已经进行了挂载,需要进行清理,避免后面运行报错时,需要手工清理
		deleteWorkSpace(rootUrl, mntUrl, volume)
		return
	}
	......
}

然后在fork进程的时候,生成相关的文件,将标准输出重定向到文件中,如下

func NewParentProcess(tty bool, containerName, rootUrl, mntUrl, volume string) (*exec.Cmd, *os.File) {
	......
	if tty {
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
	} else {
		// 创建日志保存文件夹
		dirUrl := fmt.Sprintf(DefaultInfoLocation, containerName)
		if err := os.MkdirAll(dirUrl, 0622); err != nil {
			log.Errorf("mkdir dir %s, err: %v", dirUrl, err)
			return nil, nil
		}

		// 生成日志文件
		stdLogFilePath := dirUrl + ContainerLogFile
		stdLogFile, err := os.Create(stdLogFilePath)
		if err != nil {
			log.Errorf("create file %s, err: %v", stdLogFilePath, err)
			return nil, nil
		}

		// 将输出定向输出到文件
		cmd.Stdout = stdLogFile
	}
	......
}

这样,我们就将容器的日志进行了保存

查看容器日志

查看日志就比较简单了,根据容器配置信息,找到日志存放文件,读取进行查看即可

新增logs命令:

func main() {
	......
	app.Commands = []cli.Command{
		command.InitCommand,
		command.RunCommand,
		command.CommitCommand,
		command.ListCommand,
		command.LogCommand,
	}
	.....
}

新增logs命令解析

var LogCommand = cli.Command{
	Name:  "logs",
	Usage: "print logs of a container",
	Action: func(context *cli.Context) error {
		if len(context.Args()) < 1 {
			return fmt.Errorf("Missing container name")
		}
		containerName := context.Args().Get(0)
		return run.LogContainer(containerName)
	},
}

logs查看的具体实现,读取容器日志文件,进行查看

func LogContainer(containerName string) error {
	dirUrl := fmt.Sprintf(container.DefaultInfoLocation, containerName)
	logFilePath := dirUrl + container.ContainerLogFile
	file, err := os.Open(logFilePath)
	defer file.Close()
	if err != nil {
		return fmt.Errorf("open file %s, err: %v", logFilePath, err)
	}

	content, err := ioutil.ReadAll(file)
	if err != nil {
		return fmt.Errorf("read file %s, err: %v", logFilePath, err)
	}
	fmt.Fprint(os.Stdout, string(content))
	return nil
}

运行测试

我们运行一个后台的top命令容器,然后确认查看相关的信息是否正确:

 root@lw-Code-01-Series-PF5NU1G  ~/code/go/dockerDemo   main  ./main run -d -name bird top                                                                                          ✔  ⚡  374  04:58:58
{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-04-08T04:59:03+08:00"}
{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-04-08T04:59:03+08:00"}
{"level":"info","msg":"all command is : top","time":"2022-04-08T04:59:03+08:00"}
{"level":"info","msg":"parent process run","time":"2022-04-08T04:59:03+08:00"}

 root@lw-Code-01-Series-PF5NU1G  ~/code/go/dockerDemo   main  ./main ps                                                                                                    SIG(127) ↵  ⚡  375  04:59:03
ID           NAME        PID         STATUS      COMMAND     CREATED
3391689383   bird        28013       running     top         8000-04-04 00:00:00

 root@lw-Code-01-Series-PF5NU1G  ~/code/go/dockerDemo   main  tree /var/run/mydocker                                                                                                ✔  ⚡  376  04:59:07
/var/run/mydocker
└── bird
    ├── config.json
    └── container.log

1 directory, 2 files

root@lw-Code-01-Series-PF5NU1G  ~/code/go/dockerDemo   main  ./main logs bird                                                                                                SIG(127) ↵  ⚡  375  04:59:03
Mem: 10193564K used, 22080872K free, 55104K shrd, 106496K buff, 3969012K cached
CPU:  0.0% usr  1.3% sys  0.9% nic 97.5% idle  0.0% io  0.0% irq  0.0% sirq
Load average: 0.19 0.34 0.47 2/1222 6
  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND