Skip to content

Instantly share code, notes, and snippets.

@seebs
Created June 16, 2018 15:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seebs/33ef10936e5610e5a65b0b7d42d6b4f5 to your computer and use it in GitHub Desktop.
Save seebs/33ef10936e5610e5a65b0b7d42d6b4f5 to your computer and use it in GitHub Desktop.
package main
import (
"bytes"
"fmt"
"sync"
"time"
"github.com/daviddengcn/go-colortext"
)
// derived from mattn/goreman/log.go, experiment in "fixing" line rendering.
type clogger struct {
idx int
proc string
writes chan []byte
done chan struct{}
timeout time.Duration
}
var colors = []ct.Color{
ct.Green,
ct.Cyan,
ct.Magenta,
ct.Yellow,
ct.Blue,
ct.Red,
}
var ci int
var mutex = new(sync.Mutex)
func (l *clogger) writeLine(leftover []byte, s []byte) {
now := time.Now().Format("15:04:05")
format := fmt.Sprintf("%%s %%%ds | ", maxProcNameLength)
mutex.Lock()
ct.ChangeColor(colors[l.idx], false, ct.None, false)
fmt.Printf(format, now, l.proc)
ct.ResetColor()
if leftover != nil {
fmt.Print(string(leftover))
}
fmt.Print(string(s))
mutex.Unlock()
}
// bundle writes into lines, waiting briefly for completion of lines
func (l *clogger) writeLines() {
var tick <-chan time.Time
var leftover []byte
for {
select {
case w, ok := <-l.writes:
if !ok {
if leftover != nil {
l.writeLine(leftover, []byte("\n"))
leftover = nil
}
return
}
buf := bytes.NewBuffer(w)
for {
line, err := buf.ReadBytes('\n')
if len(line) > 0 {
if line[len(line)-1] == '\n' {
l.writeLine(leftover, line)
leftover = nil
tick = nil
} else {
if leftover == nil {
// there is a thing in our system which sends an ANSI
// reset sequence *after* each line, without its own
// newline, but also without any reason to do so at all.
if string(line) != "\x1b[0m" {
leftover = line
}
} else {
leftover = append(leftover, line...)
}
tick = time.After(l.timeout)
}
}
if err != nil {
break
}
}
l.done <- struct{}{}
case <-tick:
if leftover != nil {
l.writeLine(leftover, []byte("\n"))
leftover = nil
}
tick = nil
}
}
}
// write handler of logger.
func (l *clogger) Write(p []byte) (int, error) {
l.writes <- p
<-l.done
return len(p), nil
}
// create logger instance.
func createLogger(proc string) *clogger {
mutex.Lock()
defer mutex.Unlock()
l := &clogger{idx: ci, proc: proc, writes: make(chan []byte), done: make(chan struct{}), timeout: 1 * time.Millisecond}
go l.writeLines()
ci++
if ci >= len(colors) {
ci = 0
}
return l
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment