diff options
| author | Paul Buetow <pbuetow@mimecast.com> | 2021-10-21 21:28:49 +0300 |
|---|---|---|
| committer | Paul Buetow <pbuetow@mimecast.com> | 2021-10-21 21:28:49 +0300 |
| commit | f4207a55f71bfbcfdc532d5cdd3befaa3474a157 (patch) | |
| tree | ea5e4a2d2a67035f645bdee496ae55a52034178a /internal/color | |
| parent | d80d6070557e3a800e3a54967af9eced518f116b (diff) | |
| parent | 739205206d63bf42f4e843b39d04d4c8cd8207c3 (diff) | |
merge develop
Diffstat (limited to 'internal/color')
| -rw-r--r-- | internal/color/brush/brush.go | 194 | ||||
| -rw-r--r-- | internal/color/color.go | 174 | ||||
| -rw-r--r-- | internal/color/color_test.go | 53 | ||||
| -rw-r--r-- | internal/color/colorfy.go | 56 | ||||
| -rw-r--r-- | internal/color/paint.go | 91 | ||||
| -rw-r--r-- | internal/color/table.go | 53 |
6 files changed, 517 insertions, 104 deletions
diff --git a/internal/color/brush/brush.go b/internal/color/brush/brush.go new file mode 100644 index 0000000..63d63d8 --- /dev/null +++ b/internal/color/brush/brush.go @@ -0,0 +1,194 @@ +package brush + +import ( + "strings" + + "github.com/mimecast/dtail/internal/color" + "github.com/mimecast/dtail/internal/config" + "github.com/mimecast/dtail/internal/io/pool" + "github.com/mimecast/dtail/internal/protocol" +) + +func paintSeverity(sb *strings.Builder, text string) bool { + switch { + case strings.HasPrefix(text, "WARN"): + color.PaintWithAttr(sb, text, + config.Client.TermColors.Common.SeverityWarnFg, + config.Client.TermColors.Common.SeverityWarnBg, + config.Client.TermColors.Common.SeverityWarnAttr) + + case strings.HasPrefix(text, "ERROR"): + color.PaintWithAttr(sb, text, + config.Client.TermColors.Common.SeverityErrorFg, + config.Client.TermColors.Common.SeverityErrorBg, + config.Client.TermColors.Common.SeverityErrorAttr) + + case strings.HasPrefix(text, "FATAL"): + color.PaintWithAttr(sb, text, + config.Client.TermColors.Common.SeverityFatalFg, + config.Client.TermColors.Common.SeverityFatalBg, + config.Client.TermColors.Common.SeverityFatalAttr) + + default: + return false + } + return true +} + +func paintRemote(sb *strings.Builder, line string) { + splitted := strings.SplitN(line, protocol.FieldDelimiter, 6) + + color.PaintWithAttr(sb, splitted[0], + config.Client.TermColors.Remote.RemoteFg, + config.Client.TermColors.Remote.RemoteBg, + config.Client.TermColors.Remote.RemoteAttr) + + color.PaintWithAttr(sb, protocol.FieldDelimiter, + config.Client.TermColors.Remote.DelimiterFg, + config.Client.TermColors.Remote.DelimiterBg, + config.Client.TermColors.Remote.DelimiterAttr) + + color.PaintWithAttr(sb, splitted[1], + config.Client.TermColors.Remote.HostnameFg, + config.Client.TermColors.Remote.HostnameBg, + config.Client.TermColors.Remote.HostnameAttr) + + color.PaintWithAttr(sb, protocol.FieldDelimiter, + config.Client.TermColors.Remote.DelimiterFg, + config.Client.TermColors.Remote.DelimiterBg, + config.Client.TermColors.Remote.DelimiterAttr) + + if splitted[2] == "100" { + color.PaintWithAttr(sb, splitted[2], + config.Client.TermColors.Remote.StatsOkFg, + config.Client.TermColors.Remote.StatsOkBg, + config.Client.TermColors.Remote.StatsOkAttr) + } else { + color.PaintWithAttr(sb, splitted[2], + config.Client.TermColors.Remote.StatsWarnFg, + config.Client.TermColors.Remote.StatsWarnBg, + config.Client.TermColors.Remote.StatsWarnAttr) + } + + color.PaintWithAttr(sb, protocol.FieldDelimiter, + config.Client.TermColors.Remote.DelimiterFg, + config.Client.TermColors.Remote.DelimiterBg, + config.Client.TermColors.Remote.DelimiterAttr) + + color.PaintWithAttr(sb, splitted[3], + config.Client.TermColors.Remote.CountFg, + config.Client.TermColors.Remote.CountBg, + config.Client.TermColors.Remote.CountAttr) + + color.PaintWithAttr(sb, protocol.FieldDelimiter, + config.Client.TermColors.Remote.DelimiterFg, + config.Client.TermColors.Remote.DelimiterBg, + config.Client.TermColors.Remote.DelimiterAttr) + + color.PaintWithAttr(sb, splitted[4], + config.Client.TermColors.Remote.IDFg, + config.Client.TermColors.Remote.IDBg, + config.Client.TermColors.Remote.IDAttr) + color.PaintWithAttr(sb, protocol.FieldDelimiter, + config.Client.TermColors.Remote.DelimiterFg, + config.Client.TermColors.Remote.DelimiterBg, + config.Client.TermColors.Remote.DelimiterAttr) + + if paintSeverity(sb, splitted[5]) { + return + } + color.PaintWithAttr(sb, splitted[5], + config.Client.TermColors.Remote.TextFg, + config.Client.TermColors.Remote.TextBg, + config.Client.TermColors.Remote.TextAttr) +} + +func paintClient(sb *strings.Builder, line string) { + splitted := strings.SplitN(line, protocol.FieldDelimiter, 3) + + color.PaintWithAttr(sb, splitted[0], + config.Client.TermColors.Client.ClientFg, + config.Client.TermColors.Client.ClientBg, + config.Client.TermColors.Client.ClientAttr) + + color.PaintWithAttr(sb, protocol.FieldDelimiter, + config.Client.TermColors.Client.DelimiterFg, + config.Client.TermColors.Client.DelimiterBg, + config.Client.TermColors.Client.DelimiterAttr) + + color.PaintWithAttr(sb, splitted[1], + config.Client.TermColors.Client.HostnameFg, + config.Client.TermColors.Client.HostnameBg, + config.Client.TermColors.Client.HostnameAttr) + + color.PaintWithAttr(sb, protocol.FieldDelimiter, + config.Client.TermColors.Client.DelimiterFg, + config.Client.TermColors.Client.DelimiterBg, + config.Client.TermColors.Client.DelimiterAttr) + + if paintSeverity(sb, splitted[2]) { + return + } + + color.PaintWithAttr(sb, splitted[2], + config.Client.TermColors.Client.TextFg, + config.Client.TermColors.Client.TextBg, + config.Client.TermColors.Client.TextAttr) +} + +func paintServer(sb *strings.Builder, line string) { + splitted := strings.SplitN(line, protocol.FieldDelimiter, 3) + + color.PaintWithAttr(sb, splitted[0], + config.Client.TermColors.Server.ServerFg, + config.Client.TermColors.Server.ServerBg, + config.Client.TermColors.Server.ServerAttr) + + color.PaintWithAttr(sb, protocol.FieldDelimiter, + config.Client.TermColors.Server.DelimiterFg, + config.Client.TermColors.Server.DelimiterBg, + config.Client.TermColors.Server.DelimiterAttr) + + color.PaintWithAttr(sb, splitted[1], + config.Client.TermColors.Server.HostnameFg, + config.Client.TermColors.Server.HostnameBg, + config.Client.TermColors.Server.HostnameAttr) + + color.PaintWithAttr(sb, protocol.FieldDelimiter, + config.Client.TermColors.Server.DelimiterFg, + config.Client.TermColors.Server.DelimiterBg, + config.Client.TermColors.Server.DelimiterAttr) + + if paintSeverity(sb, splitted[2]) { + return + } + + color.PaintWithAttr(sb, splitted[2], + config.Client.TermColors.Server.TextFg, + config.Client.TermColors.Server.TextBg, + config.Client.TermColors.Server.TextAttr) +} + +// Colorfy a given line based on the line's content. +func Colorfy(line string) string { + sb := pool.BuilderBuffer.Get().(*strings.Builder) + defer pool.RecycleBuilderBuffer(sb) + + switch { + case strings.HasPrefix(line, "REMOTE"): + paintRemote(sb, line) + + case strings.HasPrefix(line, "CLIENT"): + paintClient(sb, line) + + case strings.HasPrefix(line, "SERVER"): + paintServer(sb, line) + + default: + color.PaintWithAttr(sb, line, + color.FgDefault, + color.BgDefault, + color.AttrNone) + } + return sb.String() +} diff --git a/internal/color/color.go b/internal/color/color.go index 0736199..9d0bc2e 100644 --- a/internal/color/color.go +++ b/internal/color/color.go @@ -1,70 +1,148 @@ -// Package color is used to prettify console output via ANSII terminal colors. +// Package color contains all terminal color codes we know of. package color import ( "fmt" + "strings" ) -// Color name. -type Color string +// FgColor is the text foreground color. +type FgColor string -// Attribute of a color. +// BgColor is the text background color. +type BgColor string + +// Attribute of text. type Attribute string // The possible color variations. const ( - escape = "\x1b" - reset = escape + "[0m" - seq string = "%s%s%s" + escape = "\x1b" - Gray Color = escape + "[30m" - Red Color = escape + "[31m" - Green Color = escape + "[32m" - Orange Color = escape + "[33m" - Blue Color = escape + "[34m" - Magenta Color = escape + "[35m" - Yellow Color = escape + "[36m" - LightGray Color = escape + "[37m" + FgBlack FgColor = escape + "[30m" + FgRed FgColor = escape + "[31m" + FgGreen FgColor = escape + "[32m" + FgYellow FgColor = escape + "[33m" + FgBlue FgColor = escape + "[34m" + FgMagenta FgColor = escape + "[35m" + FgCyan FgColor = escape + "[36m" + FgWhite FgColor = escape + "[37m" + FgDefault FgColor = escape + "[39m" - BgGray Color = escape + "[40m" - BgRed Color = escape + "[41m" - BgGreen Color = escape + "[42m" - BgOrange Color = escape + "[43m" - BgBlue Color = escape + "[44m" - BgMagenta Color = escape + "[45m" - BgYellow Color = escape + "[46m" - BgLightGray Color = escape + "[47m" + BgBlack BgColor = escape + "[40m" + BgRed BgColor = escape + "[41m" + BgGreen BgColor = escape + "[42m" + BgYellow BgColor = escape + "[43m" + BgBlue BgColor = escape + "[44m" + BgMagenta BgColor = escape + "[45m" + BgCyan BgColor = escape + "[46m" + BgWhite BgColor = escape + "[47m" + BgDefault BgColor = escape + "[49m" - Bold Attribute = escape + "[1m" - Italic Attribute = escape + "[3m" - Underline Attribute = escape + "[4m" - ReverseColor Attribute = escape + "[7m" + AttrNone Attribute = "" + AttrReset Attribute = escape + "[0m" + AttrBold Attribute = escape + "[1m" + AttrDim Attribute = escape + "[2m" + AttrItalic Attribute = escape + "[3m" + AttrUnderline Attribute = escape + "[4m" + AttrBlink Attribute = escape + "[5m" + AttrSlowBlink Attribute = escape + "[5m" + AttrRapidBlink Attribute = escape + "[6m" + AttrReverse Attribute = escape + "[7m" + AttrHidden Attribute = escape + "[8m" +) - resetBold = escape + "[22m" - resetItalic = escape + "[23m" - resetUnderline = escape + "[24m" +// ColorNames is the list of all supported terminal colors. +var ColorNames = []string{ + "Black", "Red", "Green", "Yellow", "Blue", "Magenta", "Cyan", "White", "Default", +} - Test Color = BgYellow - TestAttr Attribute = Bold -) +// AttributeNames is the list of all supported terminal text attributes. +var AttributeNames = []string{ + "Bold", "Dim", "Italic", "Underline", "Blink", "SlowBlink", "RapidBlink", + "Reverse", "Hidden", "None", +} -// Colored DTail client output enabled. -var Colored bool +// ToFgColor converts a given string (e.g. from a config file) into a foreground +// color code. +func ToFgColor(s string) (FgColor, error) { + switch strings.ToLower(s) { + case "black": + return FgBlack, nil + case "red": + return FgRed, nil + case "green": + return FgGreen, nil + case "yellow": + return FgYellow, nil + case "blue": + return FgBlue, nil + case "magenta": + return FgMagenta, nil + case "cyan": + return FgCyan, nil + case "white": + return FgWhite, nil + case "default": + return FgDefault, nil + default: + return FgDefault, fmt.Errorf("unknown foreground text color '" + s + "'") + } +} -// Paint a given string in a given color. -func Paint(c Color, s string) string { - return fmt.Sprintf(seq, c, s, reset) +// ToBgColor converts a given string (e.g. from a config file) into a background +// color code. +func ToBgColor(s string) (BgColor, error) { + switch strings.ToLower(s) { + case "black": + return BgBlack, nil + case "red": + return BgRed, nil + case "green": + return BgGreen, nil + case "yellow": + return BgYellow, nil + case "blue": + return BgBlue, nil + case "magenta": + return BgMagenta, nil + case "cyan": + return BgCyan, nil + case "white": + return BgWhite, nil + case "default": + return BgDefault, nil + default: + return BgDefault, fmt.Errorf("unknown background text color '" + s + "'") + } } -// Attr adds a given attribute to a given string, such as "bold" or "italic". -func Attr(c Attribute, s string) string { - switch c { - case Bold: - return fmt.Sprintf(seq, Bold, s, resetBold) - case Italic: - return fmt.Sprintf(seq, Italic, s, resetItalic) - case Underline: - return fmt.Sprintf(seq, Underline, s, resetUnderline) +// ToAttribute converts a given string (e.g. from a config file) into a text attribute. +func ToAttribute(s string) (Attribute, error) { + switch strings.ToLower(s) { + case "bold": + return AttrBold, nil + case "dim": + return AttrDim, nil + case "italic": + return AttrItalic, nil + case "underline": + return AttrUnderline, nil + case "blink": + return AttrBlink, nil + case "slowblink": + return AttrSlowBlink, nil + case "rapidblink": + return AttrRapidBlink, nil + case "reverse": + return AttrReverse, nil + case "hidden": + return AttrHidden, nil + case "none": + fallthrough + case "": + return AttrNone, nil + default: + return AttrNone, fmt.Errorf("unknown text attribute '" + s + "'") } - panic("Unknown attribute") } diff --git a/internal/color/color_test.go b/internal/color/color_test.go new file mode 100644 index 0000000..7002052 --- /dev/null +++ b/internal/color/color_test.go @@ -0,0 +1,53 @@ +package color + +import ( + "strings" + "testing" +) + +func TestColors(t *testing.T) { + text := " Mimecast " + builder := strings.Builder{} + + for _, color := range ColorNames { + fgColor, err := ToFgColor(color) + if err != nil { + t.Errorf("unable to paint foreground : %s\n%v", text, err) + } + builder.WriteString(PaintStrFg(text, fgColor)) + + bgColor, err := ToBgColor(color) + if err != nil { + t.Errorf("unable to paint background: %s\n%v", text, err) + } + builder.WriteString(PaintStrBg(text, bgColor)) + } + + for _, fg := range ColorNames { + fgColor, _ := ToFgColor(fg) + for _, bg := range ColorNames { + if fg == bg { + continue + } + bgColor, _ := ToBgColor(bg) + builder.WriteString(PaintStr(text, fgColor, bgColor)) + } + } + + t.Log(builder.String()) +} + +func TestAttributes(t *testing.T) { + text := " Mimecast " + builder := strings.Builder{} + + for _, attribute := range AttributeNames { + att, err := ToAttribute(attribute) + if err != nil { + t.Errorf("unable to paint attribute: %s\n%v", text, err) + } + builder.WriteString(PaintStrWithAttr(text, FgWhite, BgBlue, att)) + } + + t.Log(builder.String()) +} diff --git a/internal/color/colorfy.go b/internal/color/colorfy.go deleted file mode 100644 index a2beb7a..0000000 --- a/internal/color/colorfy.go +++ /dev/null @@ -1,56 +0,0 @@ -package color - -import ( - "fmt" - "strings" -) - -// Add some color to log lines received from remote servers. -func paintRemote(line string) string { - splitted := strings.Split(line, "|") - if splitted[2] == "100" { - splitted[2] = Paint(BgGreen, splitted[2]) - } else { - splitted[2] = Paint(BgRed, splitted[2]) - } - info := strings.Join(splitted[0:5], "|") - log := strings.Join(splitted[5:], "|") - - if strings.HasPrefix(log, "WARN") { - log = Paint(BgYellow, log) - } else if strings.HasPrefix(log, "ERROR") { - log = Paint(BgRed, log) - } else if strings.HasPrefix(log, "FATAL") { - log = Attr(Bold, Paint(BgRed, log)) - } else { - log = Paint(Blue, log) - } - - return fmt.Sprintf("%s|%s", info, log) -} - -// Add some color to stats generated by the client. -func paintClientStats(line string) string { - splitted := strings.Split(line, "|") - first := strings.Join(splitted[0:4], "|") - connected := Paint(BgBlue, splitted[4]) - last := strings.Join(splitted[5:], "|") - - return fmt.Sprintf("%s|%s|%s", first, connected, last) -} - -// Colorfy a given line based on the line's content. -func Colorfy(line string) string { - switch { - case strings.HasPrefix(line, "REMOTE"): - return paintRemote(line) - case strings.HasPrefix(line, "CLIENT") && strings.Contains(line, "|stats|"): - return paintClientStats(line) - case strings.Contains(line, "ERROR"): - return Paint(Magenta, line) - case strings.Contains(line, "WARN"): - return Paint(Magenta, line) - } - - return line -} diff --git a/internal/color/paint.go b/internal/color/paint.go new file mode 100644 index 0000000..7735d87 --- /dev/null +++ b/internal/color/paint.go @@ -0,0 +1,91 @@ +package color + +import ( + "fmt" + "strings" +) + +// PaintStr paints a given text in a given foreground/background color combination. +func PaintStr(text string, fg FgColor, bg BgColor) string { + return fmt.Sprintf("%s%s%s%s%s", fg, bg, text, BgDefault, FgDefault) +} + +// PaintStrWithAttr paints a given text in a given foreground/background/attribute +// combination +func PaintStrWithAttr(text string, fg FgColor, bg BgColor, attr Attribute) string { + if attr == AttrNone { + return PaintStr(text, fg, bg) + } + return fmt.Sprintf("%s%s%s%s%s%s%s", fg, bg, attr, text, AttrReset, + BgDefault, FgDefault) +} + +// PaintStrFg paints a given text in a given foreground color. +func PaintStrFg(text string, fg FgColor) string { + return fmt.Sprintf("%s%s%s", fg, text, FgDefault) +} + +// PaintStrBg paints a given text in a given background color. +func PaintStrBg(text string, bg BgColor) string { + return fmt.Sprintf("%s%s%s", bg, text, BgDefault) +} + +// PaintStrAttr adds a given attribute to a given text, such as "bold" or "italic". +func PaintStrAttr(text string, attr Attribute) string { + return fmt.Sprintf("%s%s%s", attr, text, AttrReset) +} + +// Paint paints a given text in a given foreground/background color combination. +func Paint(sb *strings.Builder, text string, fg FgColor, bg BgColor) { + sb.WriteString(string(fg)) + sb.WriteString(string(bg)) + sb.WriteString(text) + sb.WriteString(string(BgDefault)) + sb.WriteString(string(FgDefault)) +} + +// Reset background and foreground colors. +func Reset(sb *strings.Builder) { + sb.WriteString(string(BgDefault)) + sb.WriteString(string(FgDefault)) +} + +// PaintWithAttr starts painting a given text in a given foreground/background/ +// attribute combination. +func PaintWithAttr(sb *strings.Builder, text string, fg FgColor, bg BgColor, + attr Attribute) { + + if attr == AttrNone { + Paint(sb, text, fg, bg) + return + } + sb.WriteString(string(fg)) + sb.WriteString(string(bg)) + sb.WriteString(string(attr)) + sb.WriteString(text) + sb.WriteString(string(AttrReset)) + sb.WriteString(string(BgDefault)) + sb.WriteString(string(FgDefault)) +} + +// PaintWithAttrs is similar to PaintWithAttr, but it takes multiple attributes. +func PaintWithAttrs(sb *strings.Builder, text string, fg FgColor, bg BgColor, + attrs []Attribute) { + + sb.WriteString(string(fg)) + sb.WriteString(string(bg)) + for _, attr := range attrs { + sb.WriteString(string(attr)) + } + sb.WriteString(text) + sb.WriteString(string(AttrReset)) + sb.WriteString(string(BgDefault)) + sb.WriteString(string(FgDefault)) +} + +// ResetWithAttr resets background, foreground and attributes. +func ResetWithAttr(sb *strings.Builder) { + sb.WriteString(string(AttrReset)) + sb.WriteString(string(BgDefault)) + sb.WriteString(string(FgDefault)) +} diff --git a/internal/color/table.go b/internal/color/table.go new file mode 100644 index 0000000..e0e4946 --- /dev/null +++ b/internal/color/table.go @@ -0,0 +1,53 @@ +package color + +import ( + "fmt" + "os" +) + +const sampleParagraph string = "Mimecast is Making Email Safer for Business. " + + "We believe that securely operating a business in the cloud requires new " + + "levels of IT preparedness, centered around cyber resilience. This is why " + + "we unify the delivery and management of security, continuity and data " + + "protection for email via one, simple-to-use cloud platform. Thousands of " + + "organizations trust us to increase their cyber resilience preparedness, " + + "streamline compliance, reduce IT complexity and keep their business running. " + + "We give employees fast and secure access to sensitive business information, " + + "and ensure email keeps flowing in the event of an outage. Mimecast will " + + "remain committed to protecting your IT assets through constant innovation " + + "and focus on your success." + +// TablePrintAndExit prints the color table and then exits the process. +func TablePrintAndExit(displaySampleParagraph bool) { + for _, attr := range AttributeNames { + if attr == "Hidden" || attr == "SlowBlink" { + continue + } + printColorTable(attr, displaySampleParagraph) + } + os.Exit(0) +} + +func printColorTable(attr string, displaySampleParagraph bool) { + for _, fg := range ColorNames { + fgColor, _ := ToFgColor(fg) + for _, bg := range ColorNames { + if fg == bg { + continue + } + + bgColor, _ := ToBgColor(bg) + attribute, _ := ToAttribute(attr) + text := fmt.Sprintf(" Foreground:%10s | Background:%10s | Attribute:%10s ", + fg, bg, attr) + fmt.Print(PaintStrWithAttr(text, fgColor, bgColor, attribute)) + + if displaySampleParagraph { + fmt.Print("\n") + fmt.Print(PaintStrWithAttr(sampleParagraph, fgColor, bgColor, attribute)) + fmt.Print("\n") + } + fmt.Print("\n") + } + } +} |
