2022-06-19 01:52:04 +00:00
|
|
|
|
package pmuxlib
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"strings"
|
|
|
|
|
"sync"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// pname used by pmux itself for logging.
|
|
|
|
|
const pmuxPName = "pmux"
|
|
|
|
|
|
2024-10-29 11:03:52 +00:00
|
|
|
|
// Characters used to denote different kinds of logs in the default Pmux
|
|
|
|
|
// configuration.
|
2022-06-19 01:52:04 +00:00
|
|
|
|
const (
|
2024-10-29 11:03:52 +00:00
|
|
|
|
LogSepStdout = '›'
|
|
|
|
|
LogSepStderr = '»'
|
|
|
|
|
LogSepSys = '~'
|
2022-06-19 01:52:04 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Logger is used by RunProcess to log process details in realtime. You can use
|
|
|
|
|
// a new(NullLogger) if you don't care.
|
|
|
|
|
type Logger interface {
|
|
|
|
|
Println(string)
|
|
|
|
|
Printf(string, ...interface{})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NullLogger is an implementation of Logger which doesn't do anything.
|
|
|
|
|
type NullLogger struct{}
|
|
|
|
|
|
2024-10-29 11:03:52 +00:00
|
|
|
|
var _ Logger = NullLogger{}
|
|
|
|
|
|
|
|
|
|
func (NullLogger) Println(string) {}
|
|
|
|
|
func (NullLogger) Printf(string, ...interface{}) {}
|
2022-06-19 01:52:04 +00:00
|
|
|
|
|
|
|
|
|
// PlainLogger implements Logger by writing each line directly to the given
|
|
|
|
|
// io.Writer as-is.
|
|
|
|
|
type PlainLogger struct {
|
2024-10-29 11:03:52 +00:00
|
|
|
|
io.WriteCloser
|
2022-06-19 01:52:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-29 11:03:52 +00:00
|
|
|
|
var _ Logger = PlainLogger{}
|
|
|
|
|
|
2022-06-19 01:52:04 +00:00
|
|
|
|
func (l PlainLogger) Println(line string) {
|
|
|
|
|
fmt.Fprintln(l, line)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l PlainLogger) Printf(str string, args ...interface{}) {
|
|
|
|
|
fmt.Fprintf(l, str, args...)
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-29 11:03:52 +00:00
|
|
|
|
// PmuxLogger is used by the pmux process itself for logging. It can prefix log
|
|
|
|
|
// lines with a timestamp, the name of the process being logged, and a custom
|
|
|
|
|
// separator in front of the log line to help delineate one kind of log from
|
|
|
|
|
// another.
|
|
|
|
|
type PmuxLogger struct {
|
2022-06-19 01:52:04 +00:00
|
|
|
|
timeFmt string
|
|
|
|
|
|
|
|
|
|
l *sync.Mutex
|
|
|
|
|
out io.Writer
|
|
|
|
|
outBuf *bufio.Writer
|
|
|
|
|
|
|
|
|
|
// maxPNameLen is a pointer because it changes when WithPrefix is called.
|
|
|
|
|
maxPNameLen *uint64
|
|
|
|
|
|
|
|
|
|
pname string
|
|
|
|
|
sep rune
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-29 11:03:52 +00:00
|
|
|
|
var _ Logger = (*PmuxLogger)(nil)
|
|
|
|
|
|
|
|
|
|
// NewPmuxLogger returns a PmuxLogger which will write lines to the given
|
|
|
|
|
// io.Writer, separating metadata from the line itself using the given
|
|
|
|
|
// separator.
|
|
|
|
|
//
|
|
|
|
|
// If timeFmt is not empty string then the timestamp of each line will be output
|
|
|
|
|
// using that format.
|
|
|
|
|
func NewPmuxLogger(out io.Writer, sep rune, timeFmt string) *PmuxLogger {
|
|
|
|
|
var (
|
|
|
|
|
pname = pmuxPName
|
|
|
|
|
maxPNameLen = uint64(len(pname))
|
|
|
|
|
)
|
2022-06-19 01:52:04 +00:00
|
|
|
|
|
2024-10-29 11:03:52 +00:00
|
|
|
|
return &PmuxLogger{
|
2022-06-19 01:52:04 +00:00
|
|
|
|
timeFmt: timeFmt,
|
|
|
|
|
maxPNameLen: &maxPNameLen,
|
|
|
|
|
l: new(sync.Mutex),
|
|
|
|
|
out: out,
|
|
|
|
|
outBuf: bufio.NewWriter(out),
|
|
|
|
|
pname: pname,
|
|
|
|
|
sep: sep,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-29 11:03:52 +00:00
|
|
|
|
// WithSep returns a copy of the PmuxLogger which uses the given separator. The
|
|
|
|
|
// original PmuxLogger will continue to function normally.
|
|
|
|
|
func (l *PmuxLogger) WithSep(sep rune) *PmuxLogger {
|
2022-06-19 01:52:04 +00:00
|
|
|
|
l2 := *l
|
|
|
|
|
l2.sep = sep
|
|
|
|
|
return &l2
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-29 11:03:52 +00:00
|
|
|
|
// WithProcessName returns a copy of the PmuxLogger which will prefix log lines
|
|
|
|
|
// with the given process name.
|
|
|
|
|
//
|
|
|
|
|
// All PmuxLoggers which are spawned using a With* method, including the
|
|
|
|
|
// original, will left pad their process name to the length of the longest
|
|
|
|
|
// process name in the group.
|
|
|
|
|
func (l *PmuxLogger) WithProcessName(pname string) *PmuxLogger {
|
2022-06-19 01:52:04 +00:00
|
|
|
|
l2 := *l
|
|
|
|
|
l2.pname = pname
|
|
|
|
|
|
|
|
|
|
l2.l.Lock()
|
|
|
|
|
defer l2.l.Unlock()
|
|
|
|
|
|
|
|
|
|
if pnameLen := uint64(len(pname)); pnameLen > *l2.maxPNameLen {
|
|
|
|
|
*l2.maxPNameLen = pnameLen
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &l2
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-29 11:03:52 +00:00
|
|
|
|
// Close flushes all buffered log data, and sets the logger to discard all
|
|
|
|
|
// future print calls.
|
|
|
|
|
//
|
|
|
|
|
// PmuxLoggers created via With* methods should be each closed individually.
|
|
|
|
|
func (l *PmuxLogger) Close() error {
|
2022-06-19 01:52:04 +00:00
|
|
|
|
l.l.Lock()
|
|
|
|
|
defer l.l.Unlock()
|
|
|
|
|
|
2024-10-29 11:03:52 +00:00
|
|
|
|
err := l.outBuf.Flush()
|
2022-06-19 01:52:04 +00:00
|
|
|
|
|
|
|
|
|
// this generally shouldn't be necessary, but we could run into cases (e.g.
|
|
|
|
|
// during a force-kill) where further Prints are called after a Close. These
|
|
|
|
|
// should just do nothing.
|
2024-10-29 11:03:52 +00:00
|
|
|
|
l.out = io.Discard
|
2022-06-19 01:52:04 +00:00
|
|
|
|
l.outBuf = bufio.NewWriter(l.out)
|
2024-10-29 11:03:52 +00:00
|
|
|
|
|
|
|
|
|
return err
|
2022-06-19 01:52:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-10-29 11:03:52 +00:00
|
|
|
|
func (l *PmuxLogger) println(line string) {
|
2022-06-19 01:52:04 +00:00
|
|
|
|
|
|
|
|
|
l.l.Lock()
|
|
|
|
|
defer l.l.Unlock()
|
|
|
|
|
|
|
|
|
|
if l.timeFmt != "" {
|
|
|
|
|
fmt.Fprintf(
|
|
|
|
|
l.outBuf,
|
|
|
|
|
"%s %c ",
|
|
|
|
|
time.Now().Format(l.timeFmt),
|
|
|
|
|
l.sep,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Fprintf(
|
|
|
|
|
l.outBuf,
|
|
|
|
|
"%s%s%c %s\n",
|
|
|
|
|
l.pname,
|
|
|
|
|
strings.Repeat(" ", int(*l.maxPNameLen+1)-len(l.pname)),
|
|
|
|
|
l.sep,
|
|
|
|
|
line,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
l.outBuf.Flush()
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-29 11:03:52 +00:00
|
|
|
|
func (l *PmuxLogger) Println(line string) {
|
2022-06-19 01:52:04 +00:00
|
|
|
|
l.println(line)
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-29 11:03:52 +00:00
|
|
|
|
func (l *PmuxLogger) Printf(msg string, args ...interface{}) {
|
2022-06-19 01:52:04 +00:00
|
|
|
|
l.Println(fmt.Sprintf(msg, args...))
|
|
|
|
|
}
|