diff options
Diffstat (limited to 'sd_daemon/log.go')
-rw-r--r-- | sd_daemon/log.go | 91 |
1 files changed, 80 insertions, 11 deletions
diff --git a/sd_daemon/log.go b/sd_daemon/log.go index df82d4b..806f248 100644 --- a/sd_daemon/log.go +++ b/sd_daemon/log.go @@ -17,7 +17,7 @@ package sd_daemon import ( - "fmt" + "bytes" "io" "log/syslog" "os" @@ -36,6 +36,7 @@ import ( type Logger struct { mu sync.Mutex out io.Writer + buf []byte } func NewLogger(w io.Writer) Logger { @@ -50,25 +51,93 @@ func NewLogger(w io.Writer) Logger { // talk to syslog or journald directly. var Log = Logger{out: os.Stderr} +// Cheap version of +// +// *buf = append(*buf, fmt.Sprintf("<%d>", n)...) +func appendPrefix(buf []byte, n syslog.Priority) []byte { + var b [22]byte // 21 = ceil(log_10(2^64))+len("<>") + b[len(b)-1] = '>' + i := len(b) - 2 + for n >= 10 { + b[i] = byte('0' + n%10) + n = n / 10 + i-- + } + b[i] = byte('0' + n) + i-- + b[i] = '<' + return append(buf, b[i:]...) +} + // WriteString writes a message with the specified priority to the // log. -func (l Logger) WriteBytes(level syslog.Priority, msg []byte) (n int, err error) { - return l.WriteString(level, string(msg)) +func (l Logger) WriteString(level syslog.Priority, msg string) (n int, err error) { + l.mu.Lock() + defer l.mu.Unlock() + + msg = strings.TrimSuffix(msg, "\n") + + // The following is a cheap version of: + // + // prefix := fmt.Sprintf("<%d>", level) + // buf := prefix + strings.Replace(msg, "\n", "\n"+prefix, -1) + // return io.WriteString(l.out, buf) + + l.buf = l.buf[:0] + l.buf = appendPrefix(l.buf, level) // possible allocation + prefix := l.buf + nlines := strings.Count(msg, "\n") + 1 + n = len(msg) + len(prefix)*nlines + 1 + if cap(l.buf) < n { + l.buf = make([]byte, len(l.buf), n) // allocation + copy(l.buf, prefix) + } + + for nlines > 1 { + nl := strings.IndexByte(msg, '\n') + l.buf = append(l.buf, msg[:nl+1]...) + l.buf = append(l.buf, prefix...) + msg = msg[nl+1:] + nlines-- + } + l.buf = append(l.buf, msg...) + l.buf = append(l.buf, '\n') + + return l.out.Write(l.buf) } // WriteString writes a message with the specified priority to the // log. -func (l Logger) WriteString(level syslog.Priority, msg string) (n int, err error) { +func (l Logger) WriteBytes(level syslog.Priority, msg []byte) (n int, err error) { + // Copy/pasted from WriteString and + // * `strings.` -> `bytes.` + // * `"\n"` -> `[]byte{'\n'}` l.mu.Lock() defer l.mu.Unlock() - // BUG(lukeshu): Logger uses high-level string functions that - // do many small allocations, making it insuitable for in - // tight loops; it should use a buffer property to be - // essentially zero-allocation. - prefix := fmt.Sprintf("<%d>", level) - buf := prefix + strings.Replace(strings.TrimSuffix(msg, "\n"), "\n", "\n"+prefix, -1) - return io.WriteString(l.out, buf) + msg = bytes.TrimSuffix(msg, []byte{'\n'}) + + l.buf = l.buf[:0] + l.buf = appendPrefix(l.buf, level) // possible allocation + prefix := l.buf + nlines := bytes.Count(msg, []byte{'\n'}) + 1 + n = len(msg) + len(prefix)*nlines + 1 + if cap(l.buf) < n { + l.buf = make([]byte, len(l.buf), n) // allocation + copy(l.buf, prefix) + } + + for nlines > 1 { + nl := bytes.IndexByte(msg, '\n') + l.buf = append(l.buf, msg[:nl+1]...) + l.buf = append(l.buf, prefix...) + msg = msg[nl+1:] + nlines-- + } + l.buf = append(l.buf, msg...) + l.buf = append(l.buf, '\n') + + return l.out.Write(l.buf) } type loggerWriter struct { |