mirror of
https://github.com/containers/podman.git
synced 2025-08-06 19:44:14 +08:00

With the advent of Podman 2.0.0 we crossed the magical barrier of go modules. While we were able to continue importing all packages inside of the project, the project could not be vendored anymore from the outside. Move the go module to new major version and change all imports to `github.com/containers/libpod/v2`. The renaming of the imports was done via `gomove` [1]. [1] https://github.com/KSubedi/gomove Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
150 lines
3.5 KiB
Go
150 lines
3.5 KiB
Go
//+build linux
|
|
//+build systemd
|
|
|
|
package libpod
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/containers/libpod/v2/libpod/logs"
|
|
journal "github.com/coreos/go-systemd/v22/sdjournal"
|
|
"github.com/pkg/errors"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
const (
|
|
// journaldLogOut is the journald priority signifying stdout
|
|
journaldLogOut = "6"
|
|
|
|
// journaldLogErr is the journald priority signifying stderr
|
|
journaldLogErr = "3"
|
|
|
|
// bufLen is the length of the buffer to read from a k8s-file
|
|
// formatted log line
|
|
// let's set it as 2k just to be safe if k8s-file format ever changes
|
|
bufLen = 16384
|
|
)
|
|
|
|
func (c *Container) readFromJournal(options *logs.LogOptions, logChannel chan *logs.LogLine) error {
|
|
var config journal.JournalReaderConfig
|
|
if options.Tail < 0 {
|
|
config.NumFromTail = math.MaxUint64
|
|
} else {
|
|
config.NumFromTail = uint64(options.Tail)
|
|
}
|
|
config.Formatter = journalFormatter
|
|
defaultTime := time.Time{}
|
|
if options.Since != defaultTime {
|
|
// coreos/go-systemd/sdjournal doesn't correctly handle requests for data in the future
|
|
// return nothing instead of falsely printing
|
|
if time.Now().Before(options.Since) {
|
|
return nil
|
|
}
|
|
config.Since = time.Since(options.Since)
|
|
}
|
|
config.Matches = append(config.Matches, journal.Match{
|
|
Field: "CONTAINER_ID_FULL",
|
|
Value: c.ID(),
|
|
})
|
|
options.WaitGroup.Add(1)
|
|
|
|
r, err := journal.NewJournalReader(config)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if r == nil {
|
|
return errors.Errorf("journal reader creation failed")
|
|
}
|
|
if options.Tail == math.MaxInt64 {
|
|
r.Rewind()
|
|
}
|
|
|
|
if options.Follow {
|
|
go func() {
|
|
follower := FollowBuffer{logChannel}
|
|
err := r.Follow(nil, follower)
|
|
if err != nil {
|
|
logrus.Debugf(err.Error())
|
|
}
|
|
r.Close()
|
|
options.WaitGroup.Done()
|
|
return
|
|
}()
|
|
return nil
|
|
}
|
|
|
|
go func() {
|
|
bytes := make([]byte, bufLen)
|
|
// /me complains about no do-while in go
|
|
ec, err := r.Read(bytes)
|
|
for ec != 0 && err == nil {
|
|
// because we are reusing bytes, we need to make
|
|
// sure the old data doesn't get into the new line
|
|
bytestr := string(bytes[:ec])
|
|
logLine, err2 := logs.NewLogLine(bytestr)
|
|
if err2 != nil {
|
|
logrus.Error(err2)
|
|
continue
|
|
}
|
|
logChannel <- logLine
|
|
ec, err = r.Read(bytes)
|
|
}
|
|
if err != nil && err != io.EOF {
|
|
logrus.Error(err)
|
|
}
|
|
r.Close()
|
|
options.WaitGroup.Done()
|
|
}()
|
|
return nil
|
|
}
|
|
|
|
func journalFormatter(entry *journal.JournalEntry) (string, error) {
|
|
usec := entry.RealtimeTimestamp
|
|
tsString := time.Unix(0, int64(usec)*int64(time.Microsecond)).Format(logs.LogTimeFormat)
|
|
output := fmt.Sprintf("%s ", tsString)
|
|
priority, ok := entry.Fields["PRIORITY"]
|
|
if !ok {
|
|
return "", errors.Errorf("no PRIORITY field present in journal entry")
|
|
}
|
|
if priority == journaldLogOut {
|
|
output += "stdout "
|
|
} else if priority == journaldLogErr {
|
|
output += "stderr "
|
|
} else {
|
|
return "", errors.Errorf("unexpected PRIORITY field in journal entry")
|
|
}
|
|
|
|
// if CONTAINER_PARTIAL_MESSAGE is defined, the log type is "P"
|
|
if _, ok := entry.Fields["CONTAINER_PARTIAL_MESSAGE"]; ok {
|
|
output += fmt.Sprintf("%s ", logs.PartialLogType)
|
|
} else {
|
|
output += fmt.Sprintf("%s ", logs.FullLogType)
|
|
}
|
|
|
|
// Finally, append the message
|
|
msg, ok := entry.Fields["MESSAGE"]
|
|
if !ok {
|
|
return "", fmt.Errorf("no MESSAGE field present in journal entry")
|
|
}
|
|
output += strings.TrimSpace(msg)
|
|
return output, nil
|
|
}
|
|
|
|
type FollowBuffer struct {
|
|
logChannel chan *logs.LogLine
|
|
}
|
|
|
|
func (f FollowBuffer) Write(p []byte) (int, error) {
|
|
bytestr := string(p)
|
|
logLine, err := logs.NewLogLine(bytestr)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
f.logChannel <- logLine
|
|
return len(p), nil
|
|
}
|