mirror of
https://github.com/containers/podman
synced 2024-10-22 02:03:38 +00:00
ee4051db61
Signed-off-by: Urvashi Mohnani <umohnani@redhat.com> Closes: #62 Approved by: rhatdan
154 lines
3.9 KiB
Go
154 lines
3.9 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/hpcloud/tail"
|
|
"github.com/pkg/errors"
|
|
"github.com/projectatomic/libpod/libpod"
|
|
"github.com/urfave/cli"
|
|
)
|
|
|
|
type logOptions struct {
|
|
details bool
|
|
follow bool
|
|
sinceTime time.Time
|
|
tail uint64
|
|
}
|
|
|
|
var (
|
|
logsFlags = []cli.Flag{
|
|
cli.BoolFlag{
|
|
Name: "details",
|
|
Usage: "Show extra details provided to the logs",
|
|
Hidden: true,
|
|
},
|
|
cli.BoolFlag{
|
|
Name: "follow, f",
|
|
Usage: "Follow log output. The default is false",
|
|
},
|
|
cli.StringFlag{
|
|
Name: "since",
|
|
Usage: "Show logs since TIMESTAMP",
|
|
},
|
|
cli.Uint64Flag{
|
|
Name: "tail",
|
|
Usage: "Output the specified number of LINES at the end of the logs. Defaults to 0, which prints all lines",
|
|
},
|
|
}
|
|
logsDescription = "The kpod logs command batch-retrieves whatever logs are present for a container at the time of execution. This does not guarantee execution" +
|
|
"order when combined with kpod run (i.e. your run may not have generated any logs at the time you execute kpod logs"
|
|
logsCommand = cli.Command{
|
|
Name: "logs",
|
|
Usage: "Fetch the logs of a container",
|
|
Description: logsDescription,
|
|
Flags: logsFlags,
|
|
Action: logsCmd,
|
|
ArgsUsage: "CONTAINER",
|
|
}
|
|
)
|
|
|
|
func logsCmd(c *cli.Context) error {
|
|
if err := validateFlags(c, logsFlags); err != nil {
|
|
return err
|
|
}
|
|
|
|
runtime, err := getRuntime(c)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "could not get runtime")
|
|
}
|
|
defer runtime.Shutdown(false)
|
|
|
|
args := c.Args()
|
|
if len(args) != 1 {
|
|
return errors.Errorf("'kpod logs' requires exactly one container name/ID")
|
|
}
|
|
|
|
sinceTime := time.Time{}
|
|
if c.IsSet("since") {
|
|
// parse time, error out if something is wrong
|
|
since, err := time.Parse("2006-01-02T15:04:05.999999999-07:00", c.String("since"))
|
|
if err != nil {
|
|
return errors.Wrapf(err, "could not parse time: %q", c.String("since"))
|
|
}
|
|
sinceTime = since
|
|
}
|
|
|
|
opts := logOptions{
|
|
details: c.Bool("details"),
|
|
follow: c.Bool("follow"),
|
|
sinceTime: sinceTime,
|
|
tail: c.Uint64("tail"),
|
|
}
|
|
|
|
ctr, err := runtime.LookupContainer(args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
logs := make(chan string)
|
|
go func() {
|
|
err = getLogs(ctr, logs, opts)
|
|
}()
|
|
printLogs(logs)
|
|
return err
|
|
}
|
|
|
|
// getLogs returns the logs of a container from the log file
|
|
// log file is created when the container is started/ran
|
|
func getLogs(container *libpod.Container, logChan chan string, opts logOptions) error {
|
|
defer close(logChan)
|
|
|
|
seekInfo := &tail.SeekInfo{Offset: 0, Whence: 0}
|
|
if opts.tail > 0 {
|
|
// seek to correct position in log files
|
|
seekInfo.Offset = int64(opts.tail)
|
|
seekInfo.Whence = 2
|
|
}
|
|
|
|
t, err := tail.TailFile(container.LogPath(), tail.Config{Follow: opts.follow, ReOpen: false, Location: seekInfo})
|
|
for line := range t.Lines {
|
|
if since, err := logSinceTime(opts.sinceTime, line.Text); err != nil || !since {
|
|
continue
|
|
}
|
|
logMessage := line.Text[secondSpaceIndex(line.Text):]
|
|
logChan <- logMessage
|
|
}
|
|
return err
|
|
}
|
|
|
|
func printLogs(logs chan string) {
|
|
for line := range logs {
|
|
fmt.Println(line)
|
|
}
|
|
}
|
|
|
|
// returns true if the time stamps of the logs are equal to or after the
|
|
// timestamp comparing to
|
|
func logSinceTime(sinceTime time.Time, logStr string) (bool, error) {
|
|
timestamp := strings.Split(logStr, " ")[0]
|
|
logTime, err := time.Parse("2006-01-02T15:04:05.999999999-07:00", timestamp)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return logTime.After(sinceTime) || logTime.Equal(sinceTime), nil
|
|
}
|
|
|
|
// secondSpaceIndex returns the index of the second space in a string
|
|
// In a line of the logs, the first two tokens are a timestamp and stdout/stderr,
|
|
// followed by the message itself. This allows us to get the index of the message
|
|
// and avoid sending the other information back to the caller of GetLogs()
|
|
func secondSpaceIndex(line string) int {
|
|
index := strings.Index(line, " ")
|
|
if index == -1 {
|
|
return 0
|
|
}
|
|
index = strings.Index(line[index:], " ")
|
|
if index == -1 {
|
|
return 0
|
|
}
|
|
return index
|
|
}
|