fix(logs): implement color profiling for log output streams, e.g. disable color on redirect

This commit is contained in:
Pasha Sviderski committed 2026-10-07 13:30:25 +10:00
1 parent d04772dacd
commit a1b6b31e9c
2 files changed
+14 -7

No files matched your search

+11 -6
View File
@@ -10,6 +10,7 @@ import (
"time" "time"
"charm.land/lipgloss/v2" "charm.land/lipgloss/v2"
"github.com/charmbracelet/colorprofile"
"github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/stringid"
"github.com/psviderski/uncloud/internal/cli/tui" "github.com/psviderski/uncloud/internal/cli/tui"
"github.com/psviderski/uncloud/pkg/api" "github.com/psviderski/uncloud/pkg/api"
@@ -24,6 +25,10 @@ type Formatter struct {
maxServiceWidth int maxServiceWidth int
utc bool utc bool
// Cache each stream's terminal profile to avoid detection on every log entry.
stdout *colorprofile.Writer
stderr *colorprofile.Writer
} }
func NewFormatter(machineNames, serviceNames []string, utc bool) *Formatter { func NewFormatter(machineNames, serviceNames []string, utc bool) *Formatter {
@@ -50,6 +55,8 @@ func NewFormatter(machineNames, serviceNames []string, utc bool) *Formatter {
maxMachineWidth: maxMachineWidth, maxMachineWidth: maxMachineWidth,
maxServiceWidth: maxServiceWidth, maxServiceWidth: maxServiceWidth,
utc: utc, utc: utc,
stdout: colorprofile.NewWriter(os.Stdout, os.Environ()),
stderr: colorprofile.NewWriter(os.Stderr, os.Environ()),
} }
} }
@@ -138,9 +145,9 @@ func (f *Formatter) PrintEntry(entry api.ServiceLogEntry) {
// Print to appropriate stream. // Print to appropriate stream.
if entry.Stream == api.LogStreamStderr { if entry.Stream == api.LogStreamStderr {
fmt.Fprint(os.Stderr, output.String()) fmt.Fprint(f.stderr, output.String())
} else { } else {
fmt.Print(output.String()) fmt.Fprint(f.stdout, output.String())
} }
} }
@@ -148,8 +155,7 @@ func (f *Formatter) PrintEntry(entry api.ServiceLogEntry) {
func (f *Formatter) printError(entry api.ServiceLogEntry) { func (f *Formatter) printError(entry api.ServiceLogEntry) {
if entry.Metadata.ServiceName == "" { if entry.Metadata.ServiceName == "" {
msg := fmt.Sprintf("ERROR: %v", entry.Err) msg := fmt.Sprintf("ERROR: %v", entry.Err)
style := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.BrightRed) fmt.Fprintln(f.stderr, tui.BoldRed.Render(msg))
fmt.Fprintln(os.Stderr, style.Render(msg))
return return
} }
@@ -171,8 +177,7 @@ func (f *Formatter) printError(entry api.ServiceLogEntry) {
msg += fmt.Sprintf(": %v", entry.Err) msg += fmt.Sprintf(": %v", entry.Err)
} }
style := lipgloss.NewStyle().Bold(true).Foreground(lipgloss.BrightYellow) fmt.Fprintln(f.stderr, tui.BoldYellow.Render(msg))
fmt.Fprintln(os.Stderr, style.Render(msg))
} }
// palette is available colors for machine/service differentiation. // palette is available colors for machine/service differentiation.
+3 -1
View File
@@ -3,9 +3,11 @@ package tui
import ( import (
"fmt" "fmt"
"os" "os"
"charm.land/lipgloss/v2"
) )
func PrintWarning(msg string) { func PrintWarning(msg string) {
styledMsg := BoldYellow.Render(fmt.Sprintf("WARNING: %s", msg)) styledMsg := BoldYellow.Render(fmt.Sprintf("WARNING: %s", msg))
fmt.Fprintln(os.Stderr, styledMsg) lipgloss.Fprintln(os.Stderr, styledMsg)
} }