diff --git a/go.mod b/go.mod index 7ca29d8..c3b3f56 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/charmbracelet/log/v2 go 1.19 require ( - github.com/charmbracelet/colorprofile v0.2.1 + github.com/charmbracelet/colorprofile v0.2.2 github.com/charmbracelet/lipgloss/v2 v2.0.0-alpha.2.0.20250218201041-aa91bd691cf5 github.com/go-logfmt/logfmt v0.6.0 github.com/stretchr/testify v1.10.0 diff --git a/go.sum b/go.sum index 0ab8b95..e127b24 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/charmbracelet/colorprofile v0.2.1 h1:McnsQTrDVGWyjar35VdXQtDWtx0aVDTMfRcospsvHrk= github.com/charmbracelet/colorprofile v0.2.1/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= +github.com/charmbracelet/colorprofile v0.2.2 h1:Cqkg0GqDd9vCdhRIBsCbeS8kvePcm8PyZLhRP9AwmaU= +github.com/charmbracelet/colorprofile v0.2.2/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= github.com/charmbracelet/lipgloss/v2 v2.0.0-alpha.2.0.20250218201041-aa91bd691cf5 h1:IctsjPxgKr8rEpUzoqwM2TlVhLcxS2TlOe7PFa6U7M4= github.com/charmbracelet/lipgloss/v2 v2.0.0-alpha.2.0.20250218201041-aa91bd691cf5/go.mod h1:wvhSDGHyThg9IW8ehatv9MkG4YH5MHYcdhQYmsaCITM= github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= diff --git a/logger.go b/logger.go index 286a0dc..4a4507e 100644 --- a/logger.go +++ b/logger.go @@ -11,6 +11,8 @@ import ( "sync" "sync/atomic" "time" + + "github.com/charmbracelet/colorprofile" ) // ErrMissingValue is returned when a key is missing a value. @@ -21,7 +23,7 @@ type LoggerOption = func(*Logger) // Logger is a Logger that implements Logger. type Logger struct { - w io.Writer + w colorprofile.Writer b bytes.Buffer mu *sync.RWMutex @@ -131,7 +133,7 @@ func (l *Logger) handle(level Level, ts time.Time, frames []runtime.Frame, msg i } // WriteTo will reset the buffer - if _, err := l.b.WriteTo(l.w); err != nil { + if _, err := l.b.WriteTo(&l.w); err != nil { if errors.Is(err, io.ErrShortWrite) { // Reset the buffer even if the lengths don't match up. If we're // using colorprofile's Writer, it will strip the ansi sequences based on @@ -276,21 +278,20 @@ func (l *Logger) SetOutput(w io.Writer) { if w == nil { w = os.Stderr } - l.w = w + l.w.Forward = w var isDiscard uint32 if w == io.Discard { isDiscard = 1 } atomic.StoreUint32(&l.isDiscard, isDiscard) - // Reuse cached renderers - // TODO is this still relevant? - // if v, ok := registry.Load(w); ok { - // l.re = v.(*colorprofile.Writer) - // } else { - // // TODO calculate background color (termenv.colorcache used to do this) - // l.re = colorprofile.NewWriter(w, os.Environ()) - // registry.Store(w, l.re) - // } +} + +// SetColorProfile force sets the underlying color profile for the +// TextFormatter. +func (l *Logger) SetColorProfile(profile colorprofile.Profile) { + l.mu.Lock() + defer l.mu.Unlock() + l.w.Profile = profile } // SetFormatter sets the formatter. diff --git a/pkg.go b/pkg.go index 837a375..762ccdc 100644 --- a/pkg.go +++ b/pkg.go @@ -9,6 +9,8 @@ import ( "sync" "sync/atomic" "time" + + "github.com/charmbracelet/colorprofile" ) var ( @@ -60,6 +62,8 @@ func NewWithOptions(w io.Writer, o Options) *Logger { } l.SetOutput(w) + // Detect color profile from the writer and environment. + l.SetColorProfile(colorprofile.Detect(w, os.Environ())) l.SetLevel(Level(l.level)) l.SetStyles(DefaultStyles()) @@ -133,12 +137,11 @@ func SetPrefix(prefix string) { Default().SetPrefix(prefix) } -// TODO -// SetColorProfile force sets the underlying Lip Gloss renderer color profile -// for the TextFormatter. -// func SetColorProfile(profile colorprofile.Profile) { -// Default().SetColorProfile(profile) -// } +// SetColorProfile force sets the underlying color profile for the +// TextFormatter. +func SetColorProfile(profile colorprofile.Profile) { + Default().SetColorProfile(profile) +} // SetStyles sets the logger styles for the TextFormatter. func SetStyles(s *Styles) { diff --git a/text_test.go b/text_test.go index 447bb41..69223d9 100644 --- a/text_test.go +++ b/text_test.go @@ -24,14 +24,15 @@ func _zeroTime(time.Time) time.Time { func TestNilStyles(t *testing.T) { st := DefaultStyles() - l := New(colorprofile.NewWriter(io.Discard, os.Environ())) + l := New(io.Discard) + l.SetColorProfile(colorprofile.TrueColor) l.SetStyles(nil) assert.Equal(t, st, l.styles) } func TestTextCaller(t *testing.T) { var buf bytes.Buffer - logger := New(colorprofile.NewWriter(&buf, os.Environ())) + logger := New(&buf) logger.SetReportCaller(true) // We calculate the caller offset based on the caller line number. _, file, line, _ := runtime.Caller(0) @@ -100,7 +101,7 @@ func TestTextCaller(t *testing.T) { func TestTextLogger(t *testing.T) { var buf bytes.Buffer - logger := New(colorprofile.NewWriter(&buf, os.Environ())) + logger := New(&buf) cases := []struct { name string expected string @@ -211,7 +212,7 @@ func TestTextLogger(t *testing.T) { func TestTextHelper(t *testing.T) { var buf bytes.Buffer - logger := New(colorprofile.NewWriter(&buf, os.Environ())) + logger := New(&buf) logger.SetReportCaller(true) helper := func() { logger.Helper() @@ -226,7 +227,8 @@ func TestTextHelper(t *testing.T) { func TestTextFatal(t *testing.T) { var buf bytes.Buffer - logger := New(colorprofile.NewWriter(&buf, os.Environ())) + logger := New(&buf) + logger.SetColorProfile(colorprofile.TrueColor) logger.SetReportCaller(true) if os.Getenv("FATAL") == "1" { logger.Fatal("i'm dead") @@ -244,9 +246,8 @@ func TestTextFatal(t *testing.T) { func TestTextValueStyles(t *testing.T) { var buf bytes.Buffer - w := colorprofile.NewWriter(&buf, os.Environ()) - w.Profile = colorprofile.ANSI256 - logger := New(w) + logger := New(&buf) + logger.SetColorProfile(colorprofile.ANSI256) st := DefaultStyles() st.Value = lipgloss.NewStyle().Bold(true) st.Values["key3"] = st.Value.Underline(true) @@ -411,12 +412,13 @@ func TestTextValueStyles(t *testing.T) { func TestCustomLevelStyle(t *testing.T) { var buf bytes.Buffer - l := New(colorprofile.NewWriter(&buf, os.Environ())) + l := New(&buf) + l.SetColorProfile(colorprofile.TrueColor) st := DefaultStyles() lvl := Level(1234) st.Levels[lvl] = lipgloss.NewStyle().Bold(true).SetString("FUNKY") l.SetStyles(st) l.SetLevel(lvl) l.Log(lvl, "foobar") - assert.Equal(t, "FUNKY foobar\n", buf.String()) + assert.Equal(t, "\x1b[1mFUNKY\x1b[m foobar\n", buf.String()) }