Skip to content

Commit

Permalink
feat: set custom styles (#84)
Browse files Browse the repository at this point in the history
Move styles to a per logger instance bases.
  • Loading branch information
aymanbagabas authored Nov 6, 2023
1 parent 7472e59 commit bd09619
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 133 deletions.
14 changes: 14 additions & 0 deletions logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type Logger struct {
fields []interface{}

helpers *sync.Map
styles *Styles
}

func (l *Logger) log(level Level, msg interface{}, keyvals ...interface{}) {
Expand Down Expand Up @@ -290,15 +291,28 @@ func (l *Logger) SetColorProfile(profile termenv.Profile) {
l.re.SetColorProfile(profile)
}

// SetStyles sets the logger styles for the TextFormatter.
func (l *Logger) SetStyles(s *Styles) {
if s == nil {
s = DefaultStyles()
}
l.mu.Lock()
defer l.mu.Unlock()
l.styles = s
}

// With returns a new logger with the given keyvals added.
func (l *Logger) With(keyvals ...interface{}) *Logger {
var st Styles
l.mu.Lock()
sl := *l
st = *l.styles
l.mu.Unlock()
sl.b = bytes.Buffer{}
sl.mu = &sync.RWMutex{}
sl.helpers = &sync.Map{}
sl.fields = append(l.fields, keyvals...)
sl.styles = &st
return &sl
}

Expand Down
6 changes: 6 additions & 0 deletions pkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ func NewWithOptions(w io.Writer, o Options) *Logger {

l.SetOutput(w)
l.SetLevel(Level(l.level))
l.SetStyles(DefaultStyles())

if l.callerFormatter == nil {
l.callerFormatter = ShortCallerFormatter
Expand Down Expand Up @@ -132,6 +133,11 @@ func SetColorProfile(profile termenv.Profile) {
defaultLogger.SetColorProfile(profile)
}

// SetStyles sets the logger styles for the TextFormatter.
func SetStyles(s *Styles) {
defaultLogger.SetStyles(s)
}

// GetPrefix returns the prefix for the default logger.
func GetPrefix() string {
return defaultLogger.GetPrefix()
Expand Down
161 changes: 77 additions & 84 deletions styles.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,99 +6,92 @@ import (
"github.com/charmbracelet/lipgloss"
)

var (
// TimestampStyle is the style for timestamps.
TimestampStyle = lipgloss.NewStyle()
// Styles defines the styles for the text logger.
type Styles struct {
// Timestamp is the style for timestamps.
Timestamp lipgloss.Style

// CallerStyle is the style for caller.
CallerStyle = lipgloss.NewStyle().Faint(true)
// Caller is the style for source caller.
Caller lipgloss.Style

// PrefixStyle is the style for prefix.
PrefixStyle = lipgloss.NewStyle().Bold(true).Faint(true)
// Prefix is the style for prefix.
Prefix lipgloss.Style

// MessageStyle is the style for messages.
MessageStyle = lipgloss.NewStyle()
// Message is the style for messages.
Message lipgloss.Style

// KeyStyle is the style for keys.
KeyStyle = lipgloss.NewStyle().Faint(true)
// Key is the style for keys.
Key lipgloss.Style

// ValueStyle is the style for values.
ValueStyle = lipgloss.NewStyle()
// Value is the style for values.
Value lipgloss.Style

// SeparatorStyle is the style for separators.
SeparatorStyle = lipgloss.NewStyle().Faint(true)
// Separator is the style for separators.
Separator lipgloss.Style

// DebugLevel is the style for debug level.
DebugLevelStyle = lipgloss.NewStyle().
SetString(strings.ToUpper(DebugLevel.String())).
Bold(true).
MaxWidth(4).
Foreground(lipgloss.AdaptiveColor{
Light: "63",
Dark: "63",
})
// Levels are the styles for each level.
Levels map[Level]lipgloss.Style

// InfoLevel is the style for info level.
InfoLevelStyle = lipgloss.NewStyle().
SetString(strings.ToUpper(InfoLevel.String())).
Bold(true).
MaxWidth(4).
Foreground(lipgloss.AdaptiveColor{
Light: "39",
Dark: "86",
})
// Keys overrides styles for specific keys.
Keys map[string]lipgloss.Style

// WarnLevel is the style for warn level.
WarnLevelStyle = lipgloss.NewStyle().
SetString(strings.ToUpper(WarnLevel.String())).
Bold(true).
MaxWidth(4).
Foreground(lipgloss.AdaptiveColor{
Light: "208",
Dark: "192",
})

// ErrorLevel is the style for error level.
ErrorLevelStyle = lipgloss.NewStyle().
SetString(strings.ToUpper(ErrorLevel.String())).
Bold(true).
MaxWidth(4).
Foreground(lipgloss.AdaptiveColor{
Light: "203",
Dark: "204",
})

// FatalLevel is the style for error level.
FatalLevelStyle = lipgloss.NewStyle().
SetString(strings.ToUpper(FatalLevel.String())).
Bold(true).
MaxWidth(4).
Foreground(lipgloss.AdaptiveColor{
Light: "133",
Dark: "134",
})

// KeyStyles overrides styles for specific keys.
KeyStyles = map[string]lipgloss.Style{}

// ValueStyles overrides value styles for specific keys.
ValueStyles = map[string]lipgloss.Style{}
)
// Values overrides value styles for specific keys.
Values map[string]lipgloss.Style
}

// levelStyle is a helper function to get the style for a level.
func levelStyle(level Level) lipgloss.Style {
switch level {
case DebugLevel:
return DebugLevelStyle
case InfoLevel:
return InfoLevelStyle
case WarnLevel:
return WarnLevelStyle
case ErrorLevel:
return ErrorLevelStyle
case FatalLevel:
return FatalLevelStyle
default:
return lipgloss.NewStyle()
// DefaultStyles returns the default styles.
func DefaultStyles() *Styles {
return &Styles{
Timestamp: lipgloss.NewStyle(),
Caller: lipgloss.NewStyle().Faint(true),
Prefix: lipgloss.NewStyle().Bold(true).Faint(true),
Message: lipgloss.NewStyle(),
Key: lipgloss.NewStyle().Faint(true),
Value: lipgloss.NewStyle(),
Separator: lipgloss.NewStyle().Faint(true),
Levels: map[Level]lipgloss.Style{
DebugLevel: lipgloss.NewStyle().
SetString(strings.ToUpper(DebugLevel.String())).
Bold(true).
MaxWidth(4).
Foreground(lipgloss.AdaptiveColor{
Light: "63",
Dark: "63",
}),
InfoLevel: lipgloss.NewStyle().
SetString(strings.ToUpper(InfoLevel.String())).
Bold(true).
MaxWidth(4).
Foreground(lipgloss.AdaptiveColor{
Light: "39",
Dark: "86",
}),
WarnLevel: lipgloss.NewStyle().
SetString(strings.ToUpper(WarnLevel.String())).
Bold(true).
MaxWidth(4).
Foreground(lipgloss.AdaptiveColor{
Light: "208",
Dark: "192",
}),
ErrorLevel: lipgloss.NewStyle().
SetString(strings.ToUpper(ErrorLevel.String())).
Bold(true).
MaxWidth(4).
Foreground(lipgloss.AdaptiveColor{
Light: "203",
Dark: "204",
}),
FatalLevel: lipgloss.NewStyle().
SetString(strings.ToUpper(FatalLevel.String())).
Bold(true).
MaxWidth(4).
Foreground(lipgloss.AdaptiveColor{
Light: "133",
Dark: "134",
}),
},
Keys: map[string]lipgloss.Style{},
Values: map[string]lipgloss.Style{},
}
}
40 changes: 24 additions & 16 deletions text.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,19 @@ const (
)

func (l *Logger) writeIndent(w io.Writer, str string, indent string, newline bool, key string) {
st := l.styles

// kindly borrowed from hclog
for {
nl := strings.IndexByte(str, '\n')
if nl == -1 {
if str != "" {
_, _ = w.Write([]byte(indent))
val := escapeStringForOutput(str, false)
if valueStyle, ok := ValueStyles[key]; ok {
if valueStyle, ok := st.Values[key]; ok {
val = valueStyle.Renderer(l.re).Render(val)
} else {
val = ValueStyle.Renderer(l.re).Render(val)
val = st.Value.Renderer(l.re).Render(val)
}
_, _ = w.Write([]byte(val))
if newline {
Expand All @@ -38,7 +40,7 @@ func (l *Logger) writeIndent(w io.Writer, str string, indent string, newline boo

_, _ = w.Write([]byte(indent))
val := escapeStringForOutput(str[:nl], false)
val = ValueStyle.Renderer(l.re).Render(val)
val = st.Value.Renderer(l.re).Render(val)
_, _ = w.Write([]byte(val))
_, _ = w.Write([]byte{'\n'})
str = str[nl+1:]
Expand Down Expand Up @@ -148,6 +150,7 @@ func writeSpace(w io.Writer, first bool) {
}

func (l *Logger) textFormatter(keyvals ...interface{}) {
st := l.styles
lenKeyvals := len(keyvals)

for i := 0; i < lenKeyvals; i += 2 {
Expand All @@ -158,41 +161,46 @@ func (l *Logger) textFormatter(keyvals ...interface{}) {
case TimestampKey:
if t, ok := keyvals[i+1].(time.Time); ok {
ts := t.Format(l.timeFormat)
ts = TimestampStyle.Renderer(l.re).Render(ts)
ts = st.Timestamp.Renderer(l.re).Render(ts)
writeSpace(&l.b, firstKey)
l.b.WriteString(ts)
}
case LevelKey:
if level, ok := keyvals[i+1].(Level); ok {
lvl := levelStyle(level).Renderer(l.re).String()
writeSpace(&l.b, firstKey)
l.b.WriteString(lvl)
var lvl string
if lvlStyle, ok := st.Levels[level]; ok {
lvl = lvlStyle.Renderer(l.re).String()
}
if lvl != "" {
writeSpace(&l.b, firstKey)
l.b.WriteString(lvl)
}
}
case CallerKey:
if caller, ok := keyvals[i+1].(string); ok {
caller = fmt.Sprintf("<%s>", caller)
caller = CallerStyle.Renderer(l.re).Render(caller)
caller = st.Caller.Renderer(l.re).Render(caller)
writeSpace(&l.b, firstKey)
l.b.WriteString(caller)
}
case PrefixKey:
if prefix, ok := keyvals[i+1].(string); ok {
prefix = PrefixStyle.Renderer(l.re).Render(prefix + ":")
prefix = st.Prefix.Renderer(l.re).Render(prefix + ":")
writeSpace(&l.b, firstKey)
l.b.WriteString(prefix)
}
case MessageKey:
if msg := keyvals[i+1]; msg != nil {
m := fmt.Sprint(msg)
m = MessageStyle.Renderer(l.re).Render(m)
m = st.Message.Renderer(l.re).Render(m)
writeSpace(&l.b, firstKey)
l.b.WriteString(m)
}
default:
sep := separator
indentSep := indentSeparator
sep = SeparatorStyle.Renderer(l.re).Render(sep)
indentSep = SeparatorStyle.Renderer(l.re).Render(indentSep)
sep = st.Separator.Renderer(l.re).Render(sep)
indentSep = st.Separator.Renderer(l.re).Render(indentSep)
key := fmt.Sprint(keyvals[i])
val := fmt.Sprintf("%+v", keyvals[i+1])
raw := val == ""
Expand All @@ -203,14 +211,14 @@ func (l *Logger) textFormatter(keyvals ...interface{}) {
continue
}
actualKey := key
valueStyle := ValueStyle
if vs, ok := ValueStyles[actualKey]; ok {
valueStyle := st.Value
if vs, ok := st.Values[actualKey]; ok {
valueStyle = vs
}
if keyStyle, ok := KeyStyles[key]; ok {
if keyStyle, ok := st.Keys[key]; ok {
key = keyStyle.Renderer(l.re).Render(key)
} else {
key = KeyStyle.Renderer(l.re).Render(key)
key = st.Key.Renderer(l.re).Render(key)
}

// Values may contain multiple lines, and that format
Expand Down
Loading

0 comments on commit bd09619

Please sign in to comment.