package main import ( "bytes" "io" "strings" ) const ( colorReset = "\033[0m" colorDim = "\033[2m" colorCyan = "\033[36m" colorBlue = "\033[94m" colorGreen = "\033[32m" colorYellow = "\033[33m" colorRed = "\033[31m" colorMagenta = "\033[35m" ) type colorLineWriter struct { w io.Writer buf []byte } func newColorLineWriter(w io.Writer) *colorLineWriter { return &colorLineWriter{w: w, buf: make([]byte, 0, 1024)} } func (c *colorLineWriter) Write(p []byte) (int, error) { c.buf = append(c.buf, p...) for { i := bytes.IndexByte(c.buf, '\n') if i < 0 { break } line := c.buf[:i] if _, err := c.w.Write([]byte(colorizeLine(string(line)) + "\n")); err != nil { return 0, err } c.buf = c.buf[i+1:] } return len(p), nil } func colorizeLine(line string) string { lower := strings.ToLower(line) lineColor := colorCyan switch { case strings.Contains(lower, "error") || strings.Contains(lower, "failed") || strings.Contains(lower, "disconnected"): lineColor = colorRed case strings.Contains(lower, "warn"): lineColor = colorYellow case strings.Contains(lower, "connected") || strings.Contains(lower, "listening") || strings.Contains(lower, "started"): lineColor = colorGreen case strings.Contains(lower, "public key") || strings.Contains(lower, "ipv6") || strings.Contains(lower, "interface"): lineColor = colorBlue case strings.Contains(lower, "sandbox"): lineColor = colorMagenta } if len(line) > 20 && line[4] == '/' && line[7] == '/' && line[10] == ' ' && line[13] == ':' && line[16] == ':' { timestamp := line[:19] rest := strings.TrimLeft(line[19:], " ") return colorDim + timestamp + colorReset + " " + lineColor + rest + colorReset } return lineColor + line + colorReset }