mirror of
				https://github.com/linuxkit/linuxkit.git
				synced 2025-10-31 15:47:46 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			110 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			110 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package main
 | |
| 
 | |
| import (
 | |
| 	"encoding/json"
 | |
| 	"errors"
 | |
| 	"flag"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"net"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	logDump byte = iota
 | |
| 	logFollow
 | |
| 	logDumpFollow
 | |
| )
 | |
| 
 | |
| // LogEntry the structure of a log entry
 | |
| type LogEntry struct {
 | |
| 	Time   time.Time `json:"time"`
 | |
| 	Source string    `json:"source"`
 | |
| 	Msg    string    `json:"msg"`
 | |
| 	Error  error
 | |
| }
 | |
| 
 | |
| func (msg *LogEntry) String() string {
 | |
| 	return fmt.Sprintf("%s;%s;%s", msg.Time.Format(time.RFC3339Nano), strings.ReplaceAll(msg.Source, `;`, `\;`), msg.Msg)
 | |
| }
 | |
| 
 | |
| func main() {
 | |
| 	var err error
 | |
| 
 | |
| 	var socketPath string
 | |
| 	var follow bool
 | |
| 	var dumpFollow bool
 | |
| 
 | |
| 	flag.StringVar(&socketPath, "socket", "/var/run/memlogdq.sock", "memlogd log query socket")
 | |
| 	flag.BoolVar(&dumpFollow, "F", false, "dump log, then follow")
 | |
| 	flag.BoolVar(&follow, "f", false, "follow log buffer")
 | |
| 	flag.Parse()
 | |
| 
 | |
| 	if dumpFollow {
 | |
| 		// StreamLogs() has seperate 'dump' and 'follow' flags, since 'dumpFollow' includes 'follow' we set that too
 | |
| 		follow = true
 | |
| 	}
 | |
| 
 | |
| 	c, err := StreamLogs(socketPath, follow, dumpFollow)
 | |
| 	if err != nil {
 | |
| 		panic(err)
 | |
| 	}
 | |
| 	for entry := range c {
 | |
| 		if entry.Error != nil {
 | |
| 			panic(entry.Error)
 | |
| 		}
 | |
| 		fmt.Println(entry.String())
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // StreamLogs read the memlogd logs from socketPath, convert them to LogEntry struct
 | |
| // and send those on the return channel. If there is an error in parsing, it will be the
 | |
| // Error on the LogEntry struct. When the socket is closed, will close the channel.
 | |
| // If stream is complete, will close, unless follow is true, in which case it will
 | |
| // continue to listen for new logs.
 | |
| func StreamLogs(socketPath string, follow, dump bool) (<-chan LogEntry, error) {
 | |
| 	addr := net.UnixAddr{
 | |
| 		Name: socketPath,
 | |
| 		Net:  "unix",
 | |
| 	}
 | |
| 	conn, err := net.DialUnix("unix", nil, &addr)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	var n int
 | |
| 	switch {
 | |
| 	case follow && dump:
 | |
| 		n, err = conn.Write([]byte{logDumpFollow})
 | |
| 	case follow:
 | |
| 		n, err = conn.Write([]byte{logFollow})
 | |
| 	default:
 | |
| 		n, err = conn.Write([]byte{logDump})
 | |
| 	}
 | |
| 
 | |
| 	if err != nil || n < 1 {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	c := make(chan LogEntry)
 | |
| 	go func(c chan<- LogEntry) {
 | |
| 		defer conn.Close()
 | |
| 		var (
 | |
| 			entry   LogEntry
 | |
| 			decoder = json.NewDecoder(conn)
 | |
| 		)
 | |
| 		for {
 | |
| 			if err := decoder.Decode(&entry); err != nil {
 | |
| 				if errors.Is(err, net.ErrClosed) || errors.Is(err, io.EOF) {
 | |
| 					close(c)
 | |
| 					return
 | |
| 				}
 | |
| 				entry = LogEntry{Error: err}
 | |
| 			}
 | |
| 			c <- entry
 | |
| 		}
 | |
| 	}(c)
 | |
| 	return c, nil
 | |
| }
 |