func (l *loggingMixin) Tracef(format string, args ...interface{}) {
	l.logf(LevelTrace, l.label(), format, args...)
}
Tracef logs a message at trace level.
package ui
import (
	"bytes"
	"io"
	"regexp"
	"strings"
	"sync"
	"time"
	"github.com/pkg/errors"
)
type Level int
const (
	LevelTrace Level = iota  
	LevelDebug               
	LevelInfo                
	LevelWarn                
	LevelError               
	LevelFatal               
)
var (
	ansiStripRe = regexp.MustCompile("[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))|[\n\r]")
	levelColor  = map[Level]string{
		LevelTrace: "\033[37m",
		LevelDebug: "\033[36m",
		LevelInfo:  "\033[32m",
		LevelWarn:  "\033[33m",
		LevelError: "\033[31m",
		LevelFatal: "\033[31m",
	}
)
func (l Level) Visible(other Level) bool {
	return other >= l
}
func (l *Level) UnmarshalText(text []byte) error {
	var err error
	*l, err = LevelFromString(string(text))
	return err
}
func LevelFromString(s string) (Level, error) {
	switch s {
	case "trace":
		return LevelTrace, nil
	case "debug":
		return LevelDebug, nil
	case "info":
		return LevelInfo, nil
	case "warn", "warning":
		return LevelWarn, nil
	case "error":
		return LevelError, nil
	case "fatal":
		return LevelFatal, nil
	default:
		return 0, errors.Errorf("invalid log level %q", s)
	}
}
type Logger interface {
	io.Writer
	Tracef(format string, args ...interface{})
	Debugf(format string, args ...interface{})
	Infof(format string, args ...interface{})
	Warnf(format string, args ...interface{})
	Errorf(format string, args ...interface{})
	Fatalf(format string, args ...interface{})
	WriterAt(level Level) SyncWriter
}
func LogElapsed(log Logger, message string, args ...interface{}) func() {
	start := time.Now()
	return func() {
		args = append(args, time.Since(start))
		log.Tracef(message+" (%s elapsed)", args...)
	}
}
type logWriter struct {
	lock  sync.Mutex
	level Level
	buf   []byte
	logf  func(level Level, format string, args ...interface{})
}
func (l *logWriter) Sync() error {
	l.lock.Lock()
	if len(l.buf) > 0 {
		line := string(l.buf)
		l.buf = nil
		l.logf(l.level, "%s", ansiStripRe.ReplaceAllString(line, ""))
	}
	l.lock.Unlock()
	return nil
}
func (l *logWriter) Write(b []byte) (int, error) {
	l.lock.Lock()
	l.buf = append(l.buf, b...)
	var lines []string
	for i := bytes.IndexByte(l.buf, '\n'); i != -1; i = bytes.IndexByte(l.buf, '\n') {
		lines = append(lines, string(l.buf[:i]))
		if i >= len(l.buf) {
			l.buf = nil
			break
		}
		l.buf = l.buf[i+1:]
	}
	l.lock.Unlock()
	for _, line := range lines {
		l.logf(l.level, "%s", ansiStripRe.ReplaceAllString(line, ""))
	}
	return len(b), nil
}
type loggingMixin struct {
	logWriter
	task    string
	subtask string
	logf    func(level Level, label string, format string, args ...interface{})
}
func (l *loggingMixin) WriterAt(level Level) SyncWriter {
	return &logWriter{
		level: level,
		logf: func(level Level, format string, args ...interface{}) {
			l.logf(level, l.label(), format, args...)
		},
	}
}
func (l *loggingMixin) label() string {
	parts := make([]string, 0, 2)
	if l.task != "" {
		parts = append(parts, l.task)
	}
	if l.subtask != "" {
		parts = append(parts, l.subtask)
	}
	return strings.Join(parts, ":")
}