简介
在上篇中我们实现了查看正在运行中的容器列表,本章节我们来实现 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
复制代码
评论