Update colors (#2)
Simplify example Update colors Add wrapping Signed-off-by: jolheiser <john.olheiser@gmail.com> Co-authored-by: jolheiser <john.olheiser@gmail.com> Reviewed-on: https://gitea.com/jolheiser/beaver/pulls/2pull/4/head v1.0.2
parent
f3053ba01b
commit
5e1722bd43
|
@ -49,3 +49,5 @@ Beaver allows you to customize the colors of various parts of the message
|
||||||
// Set Trace logging to Magenta instead of Bold-Cyan
|
// Set Trace logging to Magenta instead of Bold-Cyan
|
||||||
color.Trace = color.New(color.FgMagenta)
|
color.Trace = color.New(color.FgMagenta)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
[More info for the `color` package](color/README.md)
|
|
@ -0,0 +1,45 @@
|
||||||
|
# Color
|
||||||
|
|
||||||
|
Beaver comes with the `color` sub-package that can be used even without Beaver calls.
|
||||||
|
|
||||||
|
## Formatting a string with a single attribute
|
||||||
|
```go
|
||||||
|
text := color.FgRed.Format("red")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Formatting a string with a full color
|
||||||
|
```go
|
||||||
|
text := color.New(color.BgGreen, color.FgRed, color.Bold).Format("green background, red text, and bold")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Formatting strings with multiple colors
|
||||||
|
The following are different ways to print `This is a color test!`
|
||||||
|
where the word `color` is red while everything else is `green`.
|
||||||
|
|
||||||
|
### setup
|
||||||
|
Assuming each example is preceded by
|
||||||
|
```go
|
||||||
|
green := color.New(color.FgGreen)
|
||||||
|
red := color.New(color.FgRed)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### pure `fmt.Println`
|
||||||
|
```go
|
||||||
|
fmt.Println(green.Format("This is a"), red.Format("color"), green.Format("test!"))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `color.Wrap`
|
||||||
|
```go
|
||||||
|
fmt.Println(green.Wrap("This is a #{color} test!", red))
|
||||||
|
```
|
||||||
|
|
||||||
|
#### string formatting with `fmt.Printf` and `color.Wrap`
|
||||||
|
```go
|
||||||
|
fmt.Printf(green.Wrap("This is a #{%s} test!\n", red), "color")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### using a different directive prefix
|
||||||
|
```go
|
||||||
|
color.DirectivePrefix('$')
|
||||||
|
fmt.Printf(green.Wrap("This is a ${%s} test!\n", red), "color")
|
||||||
|
```
|
|
@ -0,0 +1,87 @@
|
||||||
|
package color
|
||||||
|
|
||||||
|
// Attribute defines a single SGR Code
|
||||||
|
type Attribute int
|
||||||
|
|
||||||
|
// Logger attributes
|
||||||
|
const (
|
||||||
|
Reset Attribute = iota
|
||||||
|
Bold
|
||||||
|
Faint
|
||||||
|
Italic
|
||||||
|
Underline
|
||||||
|
BlinkSlow
|
||||||
|
BlinkRapid
|
||||||
|
ReverseVideo
|
||||||
|
Concealed
|
||||||
|
CrossedOut
|
||||||
|
)
|
||||||
|
|
||||||
|
// Foreground text colors
|
||||||
|
const (
|
||||||
|
FgBlack Attribute = iota + 30
|
||||||
|
FgRed
|
||||||
|
FgGreen
|
||||||
|
FgYellow
|
||||||
|
FgBlue
|
||||||
|
FgMagenta
|
||||||
|
FgCyan
|
||||||
|
FgWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// Foreground Hi-Intensity text colors
|
||||||
|
const (
|
||||||
|
FgHiBlack Attribute = iota + 90
|
||||||
|
FgHiRed
|
||||||
|
FgHiGreen
|
||||||
|
FgHiYellow
|
||||||
|
FgHiBlue
|
||||||
|
FgHiMagenta
|
||||||
|
FgHiCyan
|
||||||
|
FgHiWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// Background text colors
|
||||||
|
const (
|
||||||
|
BgBlack Attribute = iota + 40
|
||||||
|
BgRed
|
||||||
|
BgGreen
|
||||||
|
BgYellow
|
||||||
|
BgBlue
|
||||||
|
BgMagenta
|
||||||
|
BgCyan
|
||||||
|
BgWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
// Background Hi-Intensity text colors
|
||||||
|
const (
|
||||||
|
BgHiBlack Attribute = iota + 100
|
||||||
|
BgHiRed
|
||||||
|
BgHiGreen
|
||||||
|
BgHiYellow
|
||||||
|
BgHiBlue
|
||||||
|
BgHiMagenta
|
||||||
|
BgHiCyan
|
||||||
|
BgHiWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
var attrCache = make(map[string]*Color)
|
||||||
|
|
||||||
|
func attr(a Attribute) *Color {
|
||||||
|
if c, ok := attrCache[string(a)]; ok {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
c := New(a)
|
||||||
|
attrCache[string(a)] = c
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format is a quick way to format a string using a single attribute
|
||||||
|
func (a Attribute) Format(text string) string {
|
||||||
|
return attr(a).Format(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format is a quick way to format a formatted string using a single attribute
|
||||||
|
func (a Attribute) Formatf(text string, v ...interface{}) string {
|
||||||
|
return attr(a).Formatf(text, v...)
|
||||||
|
}
|
107
color/color.go
107
color/color.go
|
@ -2,82 +2,28 @@ package color
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const escape = "\x1b"
|
||||||
|
|
||||||
|
var (
|
||||||
|
directive = `{[^}]*}`
|
||||||
|
directiveRe = regexp.MustCompile("[#]" + directive)
|
||||||
|
)
|
||||||
|
|
||||||
|
// DirectivePrefix changes the directive prefix for parsing color wrap directives
|
||||||
|
func DirectivePrefix(p rune) {
|
||||||
|
directiveRe = regexp.MustCompile("[" + string(p) + "]" + directive)
|
||||||
|
}
|
||||||
|
|
||||||
// Color defines a custom color object which is defined by SGR parameters.
|
// Color defines a custom color object which is defined by SGR parameters.
|
||||||
type Color struct {
|
type Color struct {
|
||||||
Attrs []Attribute
|
Attrs []Attribute
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attribute defines a single SGR Code
|
|
||||||
type Attribute int
|
|
||||||
|
|
||||||
const escape = "\x1b"
|
|
||||||
|
|
||||||
// Logger attributes
|
|
||||||
const (
|
|
||||||
Reset Attribute = iota
|
|
||||||
Bold
|
|
||||||
Faint
|
|
||||||
Italic
|
|
||||||
Underline
|
|
||||||
BlinkSlow
|
|
||||||
BlinkRapid
|
|
||||||
ReverseVideo
|
|
||||||
Concealed
|
|
||||||
CrossedOut
|
|
||||||
)
|
|
||||||
|
|
||||||
// Foreground text colors
|
|
||||||
const (
|
|
||||||
FgBlack Attribute = iota + 30
|
|
||||||
FgRed
|
|
||||||
FgGreen
|
|
||||||
FgYellow
|
|
||||||
FgBlue
|
|
||||||
FgMagenta
|
|
||||||
FgCyan
|
|
||||||
FgWhite
|
|
||||||
)
|
|
||||||
|
|
||||||
// Foreground Hi-Intensity text colors
|
|
||||||
const (
|
|
||||||
FgHiBlack Attribute = iota + 90
|
|
||||||
FgHiRed
|
|
||||||
FgHiGreen
|
|
||||||
FgHiYellow
|
|
||||||
FgHiBlue
|
|
||||||
FgHiMagenta
|
|
||||||
FgHiCyan
|
|
||||||
FgHiWhite
|
|
||||||
)
|
|
||||||
|
|
||||||
// Background text colors
|
|
||||||
const (
|
|
||||||
BgBlack Attribute = iota + 40
|
|
||||||
BgRed
|
|
||||||
BgGreen
|
|
||||||
BgYellow
|
|
||||||
BgBlue
|
|
||||||
BgMagenta
|
|
||||||
BgCyan
|
|
||||||
BgWhite
|
|
||||||
)
|
|
||||||
|
|
||||||
// Background Hi-Intensity text colors
|
|
||||||
const (
|
|
||||||
BgHiBlack Attribute = iota + 100
|
|
||||||
BgHiRed
|
|
||||||
BgHiGreen
|
|
||||||
BgHiYellow
|
|
||||||
BgHiBlue
|
|
||||||
BgHiMagenta
|
|
||||||
BgHiCyan
|
|
||||||
BgHiWhite
|
|
||||||
)
|
|
||||||
|
|
||||||
// New returns a new Color with attrs Attributes
|
// New returns a new Color with attrs Attributes
|
||||||
func New(attrs ...Attribute) *Color {
|
func New(attrs ...Attribute) *Color {
|
||||||
return &Color{Attrs: attrs}
|
return &Color{Attrs: attrs}
|
||||||
|
@ -118,17 +64,18 @@ func (c *Color) sequence() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Color) wrap(s string) string {
|
func (c *Color) wrap(s string) string {
|
||||||
return c.format() + s + c.unformat()
|
return c.format() + s + unformat()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Color) format() string {
|
func (c *Color) format() string {
|
||||||
return fmt.Sprintf("%s[%sm", escape, c.sequence())
|
return fmt.Sprintf("%s[%sm", escape, c.sequence())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Color) unformat() string {
|
func unformat() string {
|
||||||
return fmt.Sprintf("%s[%dm", escape, Reset)
|
return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Format returns a string wrapped in the color
|
||||||
func (c *Color) Format(text string) string {
|
func (c *Color) Format(text string) string {
|
||||||
if len(c.Attrs) > 0 {
|
if len(c.Attrs) > 0 {
|
||||||
return c.wrap(text)
|
return c.wrap(text)
|
||||||
|
@ -136,6 +83,28 @@ func (c *Color) Format(text string) string {
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Formatf returns a formatted string wrapped in the color
|
||||||
|
func (c *Color) Formatf(format string, v ...interface{}) string {
|
||||||
|
return c.Format(fmt.Sprintf(format, v...))
|
||||||
|
}
|
||||||
|
|
||||||
|
func parse(text string, primary, secondary *Color) string {
|
||||||
|
for {
|
||||||
|
loc := directiveRe.FindStringIndex(text)
|
||||||
|
if loc == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
text = text[:loc[0]] + unformat() + secondary.Format(text[loc[0]+2:loc[1]-1]) + primary.format() + text[loc[1]:]
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap returns a string wrapped in a primary color, with #{} directives wrapped in a secondary color
|
||||||
|
func (c *Color) Wrap(text string, secondary *Color) string {
|
||||||
|
text = parse(text, c, secondary)
|
||||||
|
return c.Format(text)
|
||||||
|
}
|
||||||
|
|
||||||
// ParseLevel parses a string and returns a Beaver Level's Color, defaulting to Info
|
// ParseLevel parses a string and returns a Beaver Level's Color, defaulting to Info
|
||||||
func ParseLevel(level string) *Color {
|
func ParseLevel(level string) *Color {
|
||||||
switch strings.ToUpper(level) {
|
switch strings.ToUpper(level) {
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.jolheiser.com/beaver/color"
|
"go.jolheiser.com/beaver/color"
|
||||||
|
@ -41,12 +40,12 @@ func New(writer io.Writer, level Level, opts FormatOptions) *Logger {
|
||||||
|
|
||||||
func (l *Logger) write(level Level, text string) {
|
func (l *Logger) write(level Level, text string) {
|
||||||
if l.Level <= level {
|
if l.Level <= level {
|
||||||
_, file, line, _ := runtime.Caller(l.skip)
|
|
||||||
var message string
|
var message string
|
||||||
if l.Format.TimePrefix {
|
if l.Format.TimePrefix {
|
||||||
message += color.Time.Format(timePrefix(time.Now())) + " "
|
message += color.Time.Format(timePrefix(time.Now())) + " "
|
||||||
}
|
}
|
||||||
if l.Format.StackPrefix {
|
if l.Format.StackPrefix {
|
||||||
|
_, file, line, _ := runtime.Caller(l.skip)
|
||||||
idx := len(file) - l.Format.StackLimit
|
idx := len(file) - l.Format.StackLimit
|
||||||
if l.Format.StackLimit <= 0 {
|
if l.Format.StackLimit <= 0 {
|
||||||
idx = 0
|
idx = 0
|
||||||
|
@ -75,11 +74,7 @@ func (l *Logger) write(level Level, text string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Logger) log(level Level, v ...interface{}) {
|
func (l *Logger) log(level Level, v ...interface{}) {
|
||||||
var args = make([]string, len(v))
|
text := fmt.Sprint(v...)
|
||||||
for i := 0; i < len(v); i++ {
|
|
||||||
args[i] = fmt.Sprintf("%v", v[i])
|
|
||||||
}
|
|
||||||
text := strings.Join(args, " ")
|
|
||||||
l.write(level, text)
|
l.write(level, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in New Issue