简介
在上篇中我们实现了查看正在运行中的容器列表,本章节我们来实现 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:03ID NAME PID STATUS COMMAND CREATED3391689383 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:03Mem: 10193564K used, 22080872K free, 55104K shrd, 106496K buff, 3969012K cachedCPU: 0.0% usr 1.3% sys 0.9% nic 97.5% idle 0.0% io 0.0% irq 0.0% sirqLoad average: 0.19 0.34 0.47 2/1222 6 PID PPID USER STAT VSZ %VSZ CPU %CPU COMMAND
复制代码
评论