mirror of
				https://github.com/containers/podman.git
				synced 2025-10-25 18:25:59 +08:00 
			
		
		
		
	 11750df510
			
		
	
	11750df510
	
	
	
		
			
			change the default to -1, so that we can change the semantic of "--tail 0" to not print any existing log line. Closes: https://github.com/containers/libpod/issues/4396 Signed-off-by: Giuseppe Scrivano <gscrivan@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/libpod/logs"
 | |
| 	journal "github.com/coreos/go-systemd/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 fasely 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
 | |
| }
 |