From 4ebff5064251d24c7d80873074c9a614311b027d Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Fri, 30 Jul 2021 08:51:05 +0300 Subject: initial color config support --- internal/color/color.go | 3 +-- internal/config/client.go | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'internal') diff --git a/internal/color/color.go b/internal/color/color.go index 0736199..7309544 100644 --- a/internal/color/color.go +++ b/internal/color/color.go @@ -26,8 +26,7 @@ const ( Yellow Color = escape + "[36m" LightGray Color = escape + "[37m" - BgGray Color = escape + "[40m" - BgRed Color = escape + "[41m" + BgGray Color = escape + "[40m" BgRed Color = escape + "[41m" BgGreen Color = escape + "[42m" BgOrange Color = escape + "[43m" BgBlue Color = escape + "[44m" diff --git a/internal/config/client.go b/internal/config/client.go index 1515aae..3d8c45a 100644 --- a/internal/config/client.go +++ b/internal/config/client.go @@ -1,8 +1,25 @@ package config +// ClientColorConfig allows to override the default terminal color codes. +type ClientColorConfig struct { + OkBgColor Color +} + +/* + 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:], "|") +*/ + // ClientConfig represents a DTail client configuration (empty as of now as there // are no available config options yet, but that may changes in the future). type ClientConfig struct { + TerminalColors ClientColorConfig `json:",omitempty"` } // Create a new default client configuration. -- cgit v1.2.3 From f46abf36e80a67d13d12dbe4ba8c899026e53961 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 31 Jul 2021 18:20:03 +0300 Subject: more on configurable colors --- internal/color/color.go | 69 -------------------------------------------- internal/color/colorfy.go | 56 ----------------------------------- internal/config/client.go | 51 ++++++++++++++++++++++---------- internal/io/logger/logger.go | 10 +++---- internal/version/version.go | 3 +- 5 files changed, 42 insertions(+), 147 deletions(-) delete mode 100644 internal/color/color.go delete mode 100644 internal/color/colorfy.go (limited to 'internal') diff --git a/internal/color/color.go b/internal/color/color.go deleted file mode 100644 index 7309544..0000000 --- a/internal/color/color.go +++ /dev/null @@ -1,69 +0,0 @@ -// Package color is used to prettify console output via ANSII terminal colors. -package color - -import ( - "fmt" -) - -// Color name. -type Color string - -// Attribute of a color. -type Attribute string - -// The possible color variations. -const ( - escape = "\x1b" - reset = escape + "[0m" - seq string = "%s%s%s" - - 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" - - 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" - - Bold Attribute = escape + "[1m" - Italic Attribute = escape + "[3m" - Underline Attribute = escape + "[4m" - ReverseColor Attribute = escape + "[7m" - - resetBold = escape + "[22m" - resetItalic = escape + "[23m" - resetUnderline = escape + "[24m" - - Test Color = BgYellow - TestAttr Attribute = Bold -) - -// Colored DTail client output enabled. -var Colored bool - -// Paint a given string in a given color. -func Paint(c Color, s string) string { - return fmt.Sprintf(seq, c, s, reset) -} - -// 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) - } - panic("Unknown attribute") -} 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/config/client.go b/internal/config/client.go index 3d8c45a..d6d106f 100644 --- a/internal/config/client.go +++ b/internal/config/client.go @@ -1,28 +1,47 @@ package config -// ClientColorConfig allows to override the default terminal color codes. -type ClientColorConfig struct { - OkBgColor Color -} +import "github.com/mimecast/dtail/internal/color" -/* - 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:], "|") -*/ +// ClientColorConfig allows to override the default terminal color color. +type termColors struct { + RemoteTextFg color.Color + RemoteStatsOkBg color.Color + RemoteStatsWarnBg color.Color + RemoteTraceBg color.Color + RemoteDebugBg color.Color + RemoteWarnBg color.Color + RemoteErrorBg color.Color + RemoteFatalBg color.Color + RemoteFatalAttr color.Attribute + ClientStatsBg color.Color + ClientWarnFg color.Color + ClientErrorFg color.Color +} // ClientConfig represents a DTail client configuration (empty as of now as there // are no available config options yet, but that may changes in the future). type ClientConfig struct { - TerminalColors ClientColorConfig `json:",omitempty"` + TermColorsEnabled bool + TermColors termColors `json:",omitempty"` } // Create a new default client configuration. func newDefaultClientConfig() *ClientConfig { - return &ClientConfig{} + return &ClientConfig{ + TermColorsEnabled: true, + TermColors: termColors{ + RemoteTextFg: color.LightGray, + RemoteStatsOkBg: color.BgGreen, + RemoteStatsWarnBg: color.BgRed, + RemoteTraceBg: color.BgYellow, + RemoteDebugBg: color.BgYellow, + RemoteWarnBg: color.BgYellow, + RemoteErrorBg: color.BgRed, + RemoteFatalBg: color.BgRed, + RemoteFatalAttr: color.Bold, + ClientStatsBg: color.BgBlue, + ClientWarnFg: color.Purple, + ClientErrorFg: color.BgRed, + }, + } } diff --git a/internal/io/logger/logger.go b/internal/io/logger/logger.go index 4254eef..bef5293 100644 --- a/internal/io/logger/logger.go +++ b/internal/io/logger/logger.go @@ -12,7 +12,7 @@ import ( "syscall" "time" - "github.com/mimecast/dtail/internal/color" + "github.com/mimecast/dtail/internal/color/brush" "github.com/mimecast/dtail/internal/config" ) @@ -207,8 +207,8 @@ func write(what, severity, message string) { if Mode.logToStdout { line := fmt.Sprintf("%s|%s|%s|%s\n", what, hostname, severity, message) - if color.Colored { - line = color.Colorfy(line) + if config.Client.TermColorsEnabled { + line = brush.Colorfy(line) } stdoutBufCh <- line @@ -262,8 +262,8 @@ func Raw(message string) { } if Mode.logToStdout { - if color.Colored { - message = color.Colorfy(message) + if config.Client.TermColorsEnabled { + message = brush.Colorfy(message) } stdoutBufCh <- message } diff --git a/internal/version/version.go b/internal/version/version.go index c0f349c..7c206b4 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -5,6 +5,7 @@ import ( "os" "github.com/mimecast/dtail/internal/color" + "github.com/mimecast/dtail/internal/config" ) const ( @@ -25,7 +26,7 @@ func String() string { // PaintedString is a prettier string representation of the DTail version. func PaintedString() string { - if !color.Colored { + if !config.Client.TermColorsEnabled { return String() } name := color.Paint(color.Yellow, Name) -- cgit v1.2.3 From eefb05ae411edef4de293099c5f6611a2ec7e9fb Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Mon, 9 Aug 2021 10:22:54 +0300 Subject: we have all basic ansi text color codes and attributes now. we can also convert a string to a corresponding code (e.g. from a config file). --- internal/color/color.go | 164 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 internal/color/color.go (limited to 'internal') diff --git a/internal/color/color.go b/internal/color/color.go new file mode 100644 index 0000000..c6760d5 --- /dev/null +++ b/internal/color/color.go @@ -0,0 +1,164 @@ +// Package color contains all terminal color codes we know of. +package color + +import ( + "fmt" + "strings" +) + +// FgColor is the text foreground color. +type FgColor string + +// BgColor is the text background color. +type BgColor string + +// Attribute of text. +type Attribute string + +// The possible color variations. +const ( + escape = "\x1b" + seq string = "%s%s%s" + + 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" + + 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" + + AttReset Attribute = escape + "[0m" + AttBold Attribute = escape + "[1m" + AttDim Attribute = escape + "[2m" + AttItalic Attribute = escape + "[3m" + AttUnderline Attribute = escape + "[4m" + AttBlink Attribute = escape + "[5m" + AttSlowBlink Attribute = escape + "[5m" + AttRapidBlink Attribute = escape + "[6m" + AttReverse Attribute = escape + "[7m" + AttHidden Attribute = escape + "[8m" + + // Internal (manual) testing. + FgTest FgColor = FgBlue + BgTest BgColor = BgYellow + AttTest Attribute = AttBold +) + +// Colored DTail client output enabled. +var Colored bool + +// Paint paints a given text in a given foreground/background color combination. +func Paint(fg FgColor, bg FgColor, text string) string { + return fmt.Sprintf(seq, fg, bg, text, BgDefault, FgDefault) +} + +// PaintWithAttr paints a given text in a given foreground/background/attribute combination +func PaintWithAtt(fg FgColor, bg FgColor, att Attribute, text string) string { + return fmt.Sprintf(seq, fg, bg, att, text, AttReset, BgDefault, FgDefault) +} + +// PaintFg paints a given text in a given foreground color. +func PaintFg(fg FgColor, text string) string { + return fmt.Sprintf(seq, fg, text, FgDefault) +} + +// PaintBg paints a given text in a given background color. +func PaintBg(bg BgColor, text string) string { + return fmt.Sprintf(seq, bg, text, BgDefault) +} + +// PaintAtt adds a given attribute to a given text, such as "bold" or "italic". +func PaintAtt(att Attribute, text string) string { + return fmt.Sprintf(seq, att, text, AttReset) +} + +// 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 + "'") + } +} + +// 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 + "'") + } +} + +// ToAttribute converts a given string (e.g. from a config file) into a text attribute. +func ToAttColor(s string) (Attribute, error) { + switch strings.ToLower(s) { + case "bold": + return AttBold, nil + case "dim": + return AttDim, nil + case "italic": + return AttItalic, nil + case "underline": + return AttUnderline, nil + case "blink": + return AttBlink, nil + case "slowblink": + return AttSlowBlink, nil + case "rapidblink": + return AttRapidBlink, nil + case "reverse": + return AttReverse, nil + case "hidden": + return AttHidden, nil + default: + return AttReset, fmt.Errorf("unknown text attribute '" + s + "'") + } +} -- cgit v1.2.3 From 4a1fbb7ed3143e38e10884955839a87966c3255e Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Mon, 9 Aug 2021 10:33:57 +0300 Subject: change paint API --- internal/color/color.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'internal') diff --git a/internal/color/color.go b/internal/color/color.go index c6760d5..52a5752 100644 --- a/internal/color/color.go +++ b/internal/color/color.go @@ -61,27 +61,27 @@ const ( var Colored bool // Paint paints a given text in a given foreground/background color combination. -func Paint(fg FgColor, bg FgColor, text string) string { +func Paint(text string, fg FgColor, bg FgColor) string { return fmt.Sprintf(seq, fg, bg, text, BgDefault, FgDefault) } // PaintWithAttr paints a given text in a given foreground/background/attribute combination -func PaintWithAtt(fg FgColor, bg FgColor, att Attribute, text string) string { +func PaintWithAtt(text string, fg FgColor, bg FgColor, att Attribute) string { return fmt.Sprintf(seq, fg, bg, att, text, AttReset, BgDefault, FgDefault) } // PaintFg paints a given text in a given foreground color. -func PaintFg(fg FgColor, text string) string { +func PaintFg(text string, fg FgColor) string { return fmt.Sprintf(seq, fg, text, FgDefault) } // PaintBg paints a given text in a given background color. -func PaintBg(bg BgColor, text string) string { +func PaintBg(text string, bg BgColor) string { return fmt.Sprintf(seq, bg, text, BgDefault) } // PaintAtt adds a given attribute to a given text, such as "bold" or "italic". -func PaintAtt(att Attribute, text string) string { +func PaintAtt(text string, att Attribute) string { return fmt.Sprintf(seq, att, text, AttReset) } -- cgit v1.2.3 From 03ec9f8fc1b30d0efb60584c90a027ff5816d153 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Tue, 10 Aug 2021 11:23:07 +0300 Subject: add color unit test --- internal/color/color.go | 68 ++++++++++++++++++++------------------------ internal/color/color_test.go | 57 +++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 37 deletions(-) create mode 100644 internal/color/color_test.go (limited to 'internal') diff --git a/internal/color/color.go b/internal/color/color.go index 52a5752..965675f 100644 --- a/internal/color/color.go +++ b/internal/color/color.go @@ -17,8 +17,7 @@ type Attribute string // The possible color variations. const ( - escape = "\x1b" - seq string = "%s%s%s" + escape = "\x1b" FgBlack FgColor = escape + "[30m" FgRed FgColor = escape + "[31m" @@ -40,49 +39,44 @@ const ( BgWhite BgColor = escape + "[47m" BgDefault BgColor = escape + "[49m" - AttReset Attribute = escape + "[0m" - AttBold Attribute = escape + "[1m" - AttDim Attribute = escape + "[2m" - AttItalic Attribute = escape + "[3m" - AttUnderline Attribute = escape + "[4m" - AttBlink Attribute = escape + "[5m" - AttSlowBlink Attribute = escape + "[5m" - AttRapidBlink Attribute = escape + "[6m" - AttReverse Attribute = escape + "[7m" - AttHidden Attribute = escape + "[8m" - - // Internal (manual) testing. - FgTest FgColor = FgBlue - BgTest BgColor = BgYellow - AttTest Attribute = AttBold + 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" ) // Colored DTail client output enabled. var Colored bool // Paint paints a given text in a given foreground/background color combination. -func Paint(text string, fg FgColor, bg FgColor) string { - return fmt.Sprintf(seq, fg, bg, text, BgDefault, FgDefault) +func Paint(text string, fg FgColor, bg BgColor) string { + return fmt.Sprintf("%s%s%s%s%s", fg, bg, text, BgDefault, FgDefault) } // PaintWithAttr paints a given text in a given foreground/background/attribute combination -func PaintWithAtt(text string, fg FgColor, bg FgColor, att Attribute) string { - return fmt.Sprintf(seq, fg, bg, att, text, AttReset, BgDefault, FgDefault) +func PaintWithAttr(text string, fg FgColor, bg BgColor, attr Attribute) string { + return fmt.Sprintf("%s%s%s%s%s%s%s", fg, bg, attr, text, AttrReset, BgDefault, FgDefault) } // PaintFg paints a given text in a given foreground color. func PaintFg(text string, fg FgColor) string { - return fmt.Sprintf(seq, fg, text, FgDefault) + return fmt.Sprintf("%s%s%s", fg, text, FgDefault) } // PaintBg paints a given text in a given background color. func PaintBg(text string, bg BgColor) string { - return fmt.Sprintf(seq, bg, text, BgDefault) + return fmt.Sprintf("%s%s%s", bg, text, BgDefault) } -// PaintAtt adds a given attribute to a given text, such as "bold" or "italic". -func PaintAtt(text string, att Attribute) string { - return fmt.Sprintf(seq, att, text, AttReset) +// PaintAttr adds a given attribute to a given text, such as "bold" or "italic". +func PaintAttr(text string, attr Attribute) string { + return fmt.Sprintf("%s%s%s", attr, text, AttrReset) } // ToFgColor converts a given string (e.g. from a config file) into a foreground color code. @@ -138,27 +132,27 @@ func ToBgColor(s string) (BgColor, error) { } // ToAttribute converts a given string (e.g. from a config file) into a text attribute. -func ToAttColor(s string) (Attribute, error) { +func ToAttribute(s string) (Attribute, error) { switch strings.ToLower(s) { case "bold": - return AttBold, nil + return AttrBold, nil case "dim": - return AttDim, nil + return AttrDim, nil case "italic": - return AttItalic, nil + return AttrItalic, nil case "underline": - return AttUnderline, nil + return AttrUnderline, nil case "blink": - return AttBlink, nil + return AttrBlink, nil case "slowblink": - return AttSlowBlink, nil + return AttrSlowBlink, nil case "rapidblink": - return AttRapidBlink, nil + return AttrRapidBlink, nil case "reverse": - return AttReverse, nil + return AttrReverse, nil case "hidden": - return AttHidden, nil + return AttrHidden, nil default: - return AttReset, fmt.Errorf("unknown text attribute '" + s + "'") + return AttrReset, fmt.Errorf("unknown text attribute '" + s + "'") } } diff --git a/internal/color/color_test.go b/internal/color/color_test.go new file mode 100644 index 0000000..0024b7f --- /dev/null +++ b/internal/color/color_test.go @@ -0,0 +1,57 @@ +package color + +import ( + "strings" + "testing" +) + +func TestColors(t *testing.T) { + colors := []string{ + "Black", "Red", "Green", "Yellow", "Blue", "Magenta", "Cyan", "White", "Default", + } + + text := " Mimecast " + builder := strings.Builder{} + + for _, color := range colors { + fgColor, err := ToFgColor(color) + if err != nil { + t.Errorf("unable to paint foreground : %s\n%v", text, err) + } + builder.WriteString(PaintFg(text, fgColor)) + + bgColor, err := ToBgColor(color) + if err != nil { + t.Errorf("unable to paint background: %s\n%v", text, err) + } + builder.WriteString(PaintBg(text, bgColor)) + } + + for _, color := range colors { + fgColor, _ := ToFgColor(color) + for _, color := range colors { + bgColor, _ := ToBgColor(color) + builder.WriteString(Paint(text, fgColor, bgColor)) + } + } + + t.Log(builder.String()) +} +func TestAttributes(t *testing.T) { + attributes := []string{ + "Bold", "Dim", "Italic", "Underline", "Blink", "SlowBlink", "RapidBlink", "Reverse", "hidden", + } + + text := " Mimecast " + builder := strings.Builder{} + + for _, attribute := range attributes { + att, err := ToAttribute(attribute) + if err != nil { + t.Errorf("unable to paint attribute: %s\n%v", text, err) + } + builder.WriteString(PaintWithAttr(text, FgWhite, BgBlue, att)) + } + + t.Log(builder.String()) +} -- cgit v1.2.3 From f3e08046badcbc852a6875b5553ee0a648021552 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Tue, 10 Aug 2021 12:05:40 +0300 Subject: can compile with new color codes --- internal/color/color.go | 4 ++ internal/config/client.go | 108 ++++++++++++++++++++++++++++++++++---------- internal/version/version.go | 21 ++++++--- 3 files changed, 104 insertions(+), 29 deletions(-) (limited to 'internal') diff --git a/internal/color/color.go b/internal/color/color.go index 965675f..f444294 100644 --- a/internal/color/color.go +++ b/internal/color/color.go @@ -39,6 +39,7 @@ const ( BgWhite BgColor = escape + "[47m" BgDefault BgColor = escape + "[49m" + AttrNone Attribute = "" AttrReset Attribute = escape + "[0m" AttrBold Attribute = escape + "[1m" AttrDim Attribute = escape + "[2m" @@ -61,6 +62,9 @@ func Paint(text string, fg FgColor, bg BgColor) string { // PaintWithAttr paints a given text in a given foreground/background/attribute combination func PaintWithAttr(text string, fg FgColor, bg BgColor, attr Attribute) string { + if attr == AttrNone { + return Paint(text, fg, bg) + } return fmt.Sprintf("%s%s%s%s%s%s%s", fg, bg, attr, text, AttrReset, BgDefault, FgDefault) } diff --git a/internal/config/client.go b/internal/config/client.go index d6d106f..84ad037 100644 --- a/internal/config/client.go +++ b/internal/config/client.go @@ -4,18 +4,49 @@ import "github.com/mimecast/dtail/internal/color" // ClientColorConfig allows to override the default terminal color color. type termColors struct { - RemoteTextFg color.Color - RemoteStatsOkBg color.Color - RemoteStatsWarnBg color.Color - RemoteTraceBg color.Color - RemoteDebugBg color.Color - RemoteWarnBg color.Color - RemoteErrorBg color.Color - RemoteFatalBg color.Color - RemoteFatalAttr color.Attribute - ClientStatsBg color.Color - ClientWarnFg color.Color - ClientErrorFg color.Color + ClientErrorAttr color.Attribute + ClientErrorBg color.BgColor + ClientErrorFg color.FgColor + + ClientStatsAttr color.Attribute + ClientStatsBg color.BgColor + ClientStatsFg color.FgColor + + ClientWarnAttr color.Attribute + ClientWarnBg color.BgColor + ClientWarnFg color.FgColor + + RemoteDebugAttr color.Attribute + RemoteDebugBg color.BgColor + RemoteDebugFg color.FgColor + + RemoteErrorAttr color.Attribute + RemoteErrorBg color.BgColor + RemoteErrorFg color.FgColor + + RemoteFatalAttr color.Attribute + RemoteFatalBg color.BgColor + RemoteFatalFg color.FgColor + + RemoteStatsOkAttr color.Attribute + RemoteStatsOkBg color.BgColor + RemoteStatsOkFg color.FgColor + + RemoteStatsWarnAttr color.Attribute + RemoteStatsWarnBg color.BgColor + RemoteStatsWarnFg color.FgColor + + RemoteTextAttr color.Attribute + RemoteTextBg color.BgColor + RemoteTextFg color.FgColor + + RemoteTraceAttr color.Attribute + RemoteTraceBg color.BgColor + RemoteTraceFg color.FgColor + + RemoteWarnAttr color.Attribute + RemoteWarnBg color.BgColor + RemoteWarnFg color.FgColor } // ClientConfig represents a DTail client configuration (empty as of now as there @@ -30,18 +61,49 @@ func newDefaultClientConfig() *ClientConfig { return &ClientConfig{ TermColorsEnabled: true, TermColors: termColors{ - RemoteTextFg: color.LightGray, + ClientErrorAttr: color.AttrBold, + ClientErrorBg: color.BgBlack, + ClientErrorFg: color.FgRed, + + ClientStatsAttr: color.AttrDim, + ClientStatsBg: color.BgBlue, + ClientStatsFg: color.FgWhite, + + ClientWarnAttr: color.AttrNone, + ClientWarnBg: color.BgBlack, + ClientWarnFg: color.FgMagenta, + + RemoteDebugAttr: color.AttrNone, + RemoteDebugBg: color.BgGreen, + RemoteDebugFg: color.FgWhite, + + RemoteErrorAttr: color.AttrBold, + RemoteErrorBg: color.BgRed, + RemoteErrorFg: color.FgWhite, + + RemoteFatalAttr: color.AttrBlink, + RemoteFatalBg: color.BgRed, + RemoteFatalFg: color.FgBlue, + + RemoteStatsOkAttr: color.AttrNone, RemoteStatsOkBg: color.BgGreen, - RemoteStatsWarnBg: color.BgRed, - RemoteTraceBg: color.BgYellow, - RemoteDebugBg: color.BgYellow, - RemoteWarnBg: color.BgYellow, - RemoteErrorBg: color.BgRed, - RemoteFatalBg: color.BgRed, - RemoteFatalAttr: color.Bold, - ClientStatsBg: color.BgBlue, - ClientWarnFg: color.Purple, - ClientErrorFg: color.BgRed, + RemoteStatsOkFg: color.FgWhite, + + RemoteStatsWarnAttr: color.AttrNone, + RemoteStatsWarnBg: color.BgRed, + RemoteStatsWarnFg: color.FgWhite, + + RemoteTextAttr: color.AttrNone, + RemoteTextBg: color.BgBlack, + RemoteTextFg: color.FgWhite, + + RemoteTraceAttr: color.AttrBold, + RemoteTraceBg: color.BgGreen, + RemoteTraceFg: color.FgWhite, + + RemoteWarnAttr: color.AttrBold, + RemoteWarnBg: color.BgYellow, + RemoteWarnFg: color.FgWhite, }, } } diff --git a/internal/version/version.go b/internal/version/version.go index 7c206b4..a513fdc 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -13,10 +13,10 @@ const ( Name string = "DTail" // Version of DTail. Version string = "3.2.0" - // Additional information for DTail - Additional string = "" // ProtocolCompat -ibility version. ProtocolCompat string = "3" + // Additional information for DTail + Additional string = "Have a lot of fun!" ) // String representation of the DTail version. @@ -29,11 +29,20 @@ func PaintedString() string { if !config.Client.TermColorsEnabled { return String() } - name := color.Paint(color.Yellow, Name) - version := color.Paint(color.Blue, Version) - descr := color.Paint(color.Green, Additional) - return fmt.Sprintf("%s %v Protocol %s %s", name, version, ProtocolCompat, descr) + name := color.PaintWithAttr(Name, + color.FgYellow, color.BgBlue, color.AttrBold) + + version := color.PaintWithAttr(fmt.Sprintf(" %s ", Version), + color.FgBlue, color.BgYellow, color.AttrBold) + + protocol := color.Paint(fmt.Sprintf(" Protocol %s ", ProtocolCompat), + color.FgBlack, color.BgGreen) + + additional := color.PaintWithAttr(fmt.Sprintf(" %s ", Additional), + color.FgWhite, color.BgMagenta, color.AttrBlink) + + return fmt.Sprintf("%s%v%s%s", name, version, protocol, additional) } // PrintAndExit prints the program version and exists. -- cgit v1.2.3 From 9bb96433be61d89f8e86f6f9ded8e35f74156a63 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Wed, 11 Aug 2021 09:32:13 +0300 Subject: add colorTable option --- internal/color/color.go | 4 ++++ internal/color/color_test.go | 23 +++++++++-------------- internal/color/table.go | 41 +++++++++++++++++++++++++++++++++++++++++ internal/config/client.go | 6 +++--- internal/version/version.go | 2 +- 5 files changed, 58 insertions(+), 18 deletions(-) create mode 100644 internal/color/table.go (limited to 'internal') diff --git a/internal/color/color.go b/internal/color/color.go index f444294..a2490be 100644 --- a/internal/color/color.go +++ b/internal/color/color.go @@ -156,6 +156,10 @@ func ToAttribute(s string) (Attribute, error) { return AttrReverse, nil case "hidden": return AttrHidden, nil + case "none": + fallthrough + case "": + return AttrNone, nil default: return AttrReset, fmt.Errorf("unknown text attribute '" + s + "'") } diff --git a/internal/color/color_test.go b/internal/color/color_test.go index 0024b7f..16b2f90 100644 --- a/internal/color/color_test.go +++ b/internal/color/color_test.go @@ -6,14 +6,10 @@ import ( ) func TestColors(t *testing.T) { - colors := []string{ - "Black", "Red", "Green", "Yellow", "Blue", "Magenta", "Cyan", "White", "Default", - } - text := " Mimecast " builder := strings.Builder{} - for _, color := range colors { + for _, color := range ColorNames { fgColor, err := ToFgColor(color) if err != nil { t.Errorf("unable to paint foreground : %s\n%v", text, err) @@ -27,10 +23,13 @@ func TestColors(t *testing.T) { builder.WriteString(PaintBg(text, bgColor)) } - for _, color := range colors { - fgColor, _ := ToFgColor(color) - for _, color := range colors { - bgColor, _ := ToBgColor(color) + for _, fg := range ColorNames { + fgColor, _ := ToFgColor(fg) + for _, bg := range ColorNames { + if fg == bg { + continue + } + bgColor, _ := ToBgColor(bg) builder.WriteString(Paint(text, fgColor, bgColor)) } } @@ -38,14 +37,10 @@ func TestColors(t *testing.T) { t.Log(builder.String()) } func TestAttributes(t *testing.T) { - attributes := []string{ - "Bold", "Dim", "Italic", "Underline", "Blink", "SlowBlink", "RapidBlink", "Reverse", "hidden", - } - text := " Mimecast " builder := strings.Builder{} - for _, attribute := range attributes { + for _, attribute := range AttributeNames { att, err := ToAttribute(attribute) if err != nil { t.Errorf("unable to paint attribute: %s\n%v", text, err) diff --git a/internal/color/table.go b/internal/color/table.go new file mode 100644 index 0000000..8e40a78 --- /dev/null +++ b/internal/color/table.go @@ -0,0 +1,41 @@ +package color + +import ( + "fmt" + "os" +) + +var ColorNames = []string{ + "Black", "Red", "Green", "Yellow", "Blue", "Magenta", "Cyan", "White", "Default", +} + +var AttributeNames = []string{ + "Bold", "Dim", "Italic", "Underline", "Blink", "SlowBlink", "RapidBlink", "Reverse", "Hidden", "None", +} + +func TablePrintAndExit() { + for _, attr := range AttributeNames { + if attr == "Hidden" || attr == "SlowBlink" { + continue + } + printColorTable(attr) + } + os.Exit(0) +} + +func printColorTable(attr string) { + 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(PaintWithAttr(text, fgColor, bgColor, attribute)) + fmt.Print("\n") + } + } +} diff --git a/internal/config/client.go b/internal/config/client.go index 84ad037..5386576 100644 --- a/internal/config/client.go +++ b/internal/config/client.go @@ -75,7 +75,7 @@ func newDefaultClientConfig() *ClientConfig { RemoteDebugAttr: color.AttrNone, RemoteDebugBg: color.BgGreen, - RemoteDebugFg: color.FgWhite, + RemoteDebugFg: color.FgBlack, RemoteErrorAttr: color.AttrBold, RemoteErrorBg: color.BgRed, @@ -83,11 +83,11 @@ func newDefaultClientConfig() *ClientConfig { RemoteFatalAttr: color.AttrBlink, RemoteFatalBg: color.BgRed, - RemoteFatalFg: color.FgBlue, + RemoteFatalFg: color.FgWhite, RemoteStatsOkAttr: color.AttrNone, RemoteStatsOkBg: color.BgGreen, - RemoteStatsOkFg: color.FgWhite, + RemoteStatsOkFg: color.FgBlack, RemoteStatsWarnAttr: color.AttrNone, RemoteStatsWarnBg: color.BgRed, diff --git a/internal/version/version.go b/internal/version/version.go index a513fdc..7cfe12c 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -30,7 +30,7 @@ func PaintedString() string { return String() } - name := color.PaintWithAttr(Name, + name := color.PaintWithAttr(fmt.Sprintf(" %s ", Name), color.FgYellow, color.BgBlue, color.AttrBold) version := color.PaintWithAttr(fmt.Sprintf(" %s ", Version), -- cgit v1.2.3 From 1e00c256842e125a865a6cc3f9aa70a1a498f6dc Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Wed, 11 Aug 2021 09:42:51 +0300 Subject: add paint.go --- internal/color/color.go | 31 ++++--------------------------- internal/color/paint.go | 31 +++++++++++++++++++++++++++++++ internal/color/table.go | 8 -------- 3 files changed, 35 insertions(+), 35 deletions(-) create mode 100644 internal/color/paint.go (limited to 'internal') diff --git a/internal/color/color.go b/internal/color/color.go index a2490be..692de51 100644 --- a/internal/color/color.go +++ b/internal/color/color.go @@ -52,35 +52,12 @@ const ( AttrHidden Attribute = escape + "[8m" ) -// Colored DTail client output enabled. -var Colored bool - -// Paint paints a given text in a given foreground/background color combination. -func Paint(text string, fg FgColor, bg BgColor) string { - return fmt.Sprintf("%s%s%s%s%s", fg, bg, text, BgDefault, FgDefault) -} - -// PaintWithAttr paints a given text in a given foreground/background/attribute combination -func PaintWithAttr(text string, fg FgColor, bg BgColor, attr Attribute) string { - if attr == AttrNone { - return Paint(text, fg, bg) - } - return fmt.Sprintf("%s%s%s%s%s%s%s", fg, bg, attr, text, AttrReset, BgDefault, FgDefault) -} - -// PaintFg paints a given text in a given foreground color. -func PaintFg(text string, fg FgColor) string { - return fmt.Sprintf("%s%s%s", fg, text, FgDefault) -} - -// PaintBg paints a given text in a given background color. -func PaintBg(text string, bg BgColor) string { - return fmt.Sprintf("%s%s%s", bg, text, BgDefault) +var ColorNames = []string{ + "Black", "Red", "Green", "Yellow", "Blue", "Magenta", "Cyan", "White", "Default", } -// PaintAttr adds a given attribute to a given text, such as "bold" or "italic". -func PaintAttr(text string, attr Attribute) string { - return fmt.Sprintf("%s%s%s", attr, text, AttrReset) +var AttributeNames = []string{ + "Bold", "Dim", "Italic", "Underline", "Blink", "SlowBlink", "RapidBlink", "Reverse", "Hidden", "None", } // ToFgColor converts a given string (e.g. from a config file) into a foreground color code. diff --git a/internal/color/paint.go b/internal/color/paint.go new file mode 100644 index 0000000..7862467 --- /dev/null +++ b/internal/color/paint.go @@ -0,0 +1,31 @@ +package color + +import "fmt" + +// Paint paints a given text in a given foreground/background color combination. +func Paint(text string, fg FgColor, bg BgColor) string { + return fmt.Sprintf("%s%s%s%s%s", fg, bg, text, BgDefault, FgDefault) +} + +// PaintWithAttr paints a given text in a given foreground/background/attribute combination +func PaintWithAttr(text string, fg FgColor, bg BgColor, attr Attribute) string { + if attr == AttrNone { + return Paint(text, fg, bg) + } + return fmt.Sprintf("%s%s%s%s%s%s%s", fg, bg, attr, text, AttrReset, BgDefault, FgDefault) +} + +// PaintFg paints a given text in a given foreground color. +func PaintFg(text string, fg FgColor) string { + return fmt.Sprintf("%s%s%s", fg, text, FgDefault) +} + +// PaintBg paints a given text in a given background color. +func PaintBg(text string, bg BgColor) string { + return fmt.Sprintf("%s%s%s", bg, text, BgDefault) +} + +// PaintAttr adds a given attribute to a given text, such as "bold" or "italic". +func PaintAttr(text string, attr Attribute) string { + return fmt.Sprintf("%s%s%s", attr, text, AttrReset) +} diff --git a/internal/color/table.go b/internal/color/table.go index 8e40a78..8c36047 100644 --- a/internal/color/table.go +++ b/internal/color/table.go @@ -5,14 +5,6 @@ import ( "os" ) -var ColorNames = []string{ - "Black", "Red", "Green", "Yellow", "Blue", "Magenta", "Cyan", "White", "Default", -} - -var AttributeNames = []string{ - "Bold", "Dim", "Italic", "Underline", "Blink", "SlowBlink", "RapidBlink", "Reverse", "Hidden", "None", -} - func TablePrintAndExit() { for _, attr := range AttributeNames { if attr == "Hidden" || attr == "SlowBlink" { -- cgit v1.2.3 From 3cc8887885f24a3f0d607af24197bc364ab16b8d Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Thu, 12 Aug 2021 10:56:36 +0300 Subject: add missing brush and also add color client configs plus jsonschema --- internal/color/brush/brush.go | 104 ++++++++++++++++++++++++++++++++++++++++++ internal/color/color.go | 2 +- internal/color/color_test.go | 1 + internal/config/client.go | 6 +-- internal/io/logger/logger.go | 4 +- internal/version/version.go | 9 +++- 6 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 internal/color/brush/brush.go (limited to 'internal') diff --git a/internal/color/brush/brush.go b/internal/color/brush/brush.go new file mode 100644 index 0000000..c5efff6 --- /dev/null +++ b/internal/color/brush/brush.go @@ -0,0 +1,104 @@ +package brush + +import ( + "fmt" + "strings" + + "github.com/mimecast/dtail/internal/color" + "github.com/mimecast/dtail/internal/config" +) + +// 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] = color.Paint(splitted[2], + config.Client.TermColors.RemoteStatsOkFg, + config.Client.TermColors.RemoteStatsOkBg) + } else { + splitted[2] = color.Paint(splitted[2], + config.Client.TermColors.RemoteStatsWarnFg, + config.Client.TermColors.RemoteStatsWarnBg) + } + + info := strings.Join(splitted[0:5], "|") + log := strings.Join(splitted[5:], "|") + + switch { + case strings.HasPrefix(log, "WARN"): + log = color.PaintWithAttr(log, + config.Client.TermColors.RemoteWarnFg, + config.Client.TermColors.RemoteWarnBg, + config.Client.TermColors.RemoteWarnAttr) + + case strings.HasPrefix(log, "ERROR"): + log = color.PaintWithAttr(log, + config.Client.TermColors.RemoteErrorFg, + config.Client.TermColors.RemoteErrorBg, + config.Client.TermColors.RemoteErrorAttr) + + case strings.HasPrefix(log, "FATAL"): + log = color.PaintWithAttr(log, + config.Client.TermColors.RemoteFatalFg, + config.Client.TermColors.RemoteFatalBg, + config.Client.TermColors.RemoteFatalAttr) + + case strings.HasPrefix(log, "DEBUG"): + log = color.PaintWithAttr(log, + config.Client.TermColors.RemoteDebugFg, + config.Client.TermColors.RemoteDebugBg, + config.Client.TermColors.RemoteDebugAttr) + + case strings.HasPrefix(log, "TRACE"): + log = color.PaintWithAttr(log, + config.Client.TermColors.RemoteTraceFg, + config.Client.TermColors.RemoteTraceBg, + config.Client.TermColors.RemoteTraceAttr) + + default: + log = color.PaintWithAttr(log, + config.Client.TermColors.RemoteTextFg, + config.Client.TermColors.RemoteTextBg, + config.Client.TermColors.RemoteTextAttr) + } + + 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 := color.PaintWithAttr(splitted[4], + config.Client.TermColors.ClientStatsFg, + config.Client.TermColors.ClientStatsBg, + config.Client.TermColors.ClientStatsAttr) + 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 color.PaintWithAttr(line, + config.Client.TermColors.ClientErrorFg, + config.Client.TermColors.ClientErrorBg, + config.Client.TermColors.ClientErrorAttr) + + case strings.Contains(line, "WARN"): + return color.PaintWithAttr(line, + config.Client.TermColors.ClientWarnFg, + config.Client.TermColors.ClientWarnBg, + config.Client.TermColors.ClientWarnAttr) + } + + return line +} diff --git a/internal/color/color.go b/internal/color/color.go index 692de51..5c25855 100644 --- a/internal/color/color.go +++ b/internal/color/color.go @@ -138,6 +138,6 @@ func ToAttribute(s string) (Attribute, error) { case "": return AttrNone, nil default: - return AttrReset, fmt.Errorf("unknown text attribute '" + s + "'") + return AttrNone, fmt.Errorf("unknown text attribute '" + s + "'") } } diff --git a/internal/color/color_test.go b/internal/color/color_test.go index 16b2f90..bfc7c54 100644 --- a/internal/color/color_test.go +++ b/internal/color/color_test.go @@ -36,6 +36,7 @@ func TestColors(t *testing.T) { t.Log(builder.String()) } + func TestAttributes(t *testing.T) { text := " Mimecast " builder := strings.Builder{} diff --git a/internal/config/client.go b/internal/config/client.go index 5386576..c1b488c 100644 --- a/internal/config/client.go +++ b/internal/config/client.go @@ -52,14 +52,14 @@ type termColors struct { // ClientConfig represents a DTail client configuration (empty as of now as there // are no available config options yet, but that may changes in the future). type ClientConfig struct { - TermColorsEnabled bool - TermColors termColors `json:",omitempty"` + TermColorsEnable bool + TermColors termColors `json:",omitempty"` } // Create a new default client configuration. func newDefaultClientConfig() *ClientConfig { return &ClientConfig{ - TermColorsEnabled: true, + TermColorsEnable: true, TermColors: termColors{ ClientErrorAttr: color.AttrBold, ClientErrorBg: color.BgBlack, diff --git a/internal/io/logger/logger.go b/internal/io/logger/logger.go index bef5293..bb9dc02 100644 --- a/internal/io/logger/logger.go +++ b/internal/io/logger/logger.go @@ -207,7 +207,7 @@ func write(what, severity, message string) { if Mode.logToStdout { line := fmt.Sprintf("%s|%s|%s|%s\n", what, hostname, severity, message) - if config.Client.TermColorsEnabled { + if config.Client.TermColorsEnable { line = brush.Colorfy(line) } @@ -262,7 +262,7 @@ func Raw(message string) { } if Mode.logToStdout { - if config.Client.TermColorsEnabled { + if config.Client.TermColorsEnable { message = brush.Colorfy(message) } stdoutBufCh <- message diff --git a/internal/version/version.go b/internal/version/version.go index 7cfe12c..3e683bd 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -26,7 +26,7 @@ func String() string { // PaintedString is a prettier string representation of the DTail version. func PaintedString() string { - if !config.Client.TermColorsEnabled { + if !config.Client.TermColorsEnable { return String() } @@ -45,8 +45,13 @@ func PaintedString() string { return fmt.Sprintf("%s%v%s%s", name, version, protocol, additional) } +// Print the version. +func Print() { + fmt.Println(PaintedString()) +} + // PrintAndExit prints the program version and exists. func PrintAndExit() { - fmt.Println(PaintedString()) + Print() os.Exit(0) } -- cgit v1.2.3 From c2522ffb59514443816a96386c16bb7527cbe57c Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 21 Aug 2021 14:54:24 +0300 Subject: read files bytewise for more control of whats happening - change transport protocol for more control over newlines --- internal/clients/handlers/basehandler.go | 6 +-- internal/clients/handlers/maprhandler.go | 3 +- internal/io/fs/readfile.go | 71 ++++++++++++------------------- internal/io/logger/logger.go | 7 ++- internal/mapr/aggregateset.go | 10 +++-- internal/protocol/protocol.go | 10 +++++ internal/server/handlers/serverhandler.go | 15 ++++--- internal/version/version.go | 7 ++- 8 files changed, 66 insertions(+), 63 deletions(-) create mode 100644 internal/protocol/protocol.go (limited to 'internal') diff --git a/internal/clients/handlers/basehandler.go b/internal/clients/handlers/basehandler.go index f07fd90..acafe0e 100644 --- a/internal/clients/handlers/basehandler.go +++ b/internal/clients/handlers/basehandler.go @@ -9,7 +9,7 @@ import ( "github.com/mimecast/dtail/internal" "github.com/mimecast/dtail/internal/io/logger" - "github.com/mimecast/dtail/internal/version" + "github.com/mimecast/dtail/internal/protocol" ) type baseHandler struct { @@ -43,7 +43,7 @@ func (h *baseHandler) SendMessage(command string) error { logger.Debug("Sending command", h.server, command, encoded) select { - case h.commands <- fmt.Sprintf("protocol %s base64 %v;", version.ProtocolCompat, encoded): + case h.commands <- fmt.Sprintf("protocol %s base64 %v;", protocol.ProtocolCompat, encoded): case <-time.After(time.Second * 5): return fmt.Errorf("Timed out sending command '%s' (base64: '%s')", command, encoded) case <-h.Done(): @@ -57,7 +57,7 @@ func (h *baseHandler) SendMessage(command string) error { func (h *baseHandler) Write(p []byte) (n int, err error) { for _, b := range p { h.receiveBuf = append(h.receiveBuf, b) - if b == '\n' { + if b == protocol.MessageDelimiter { if len(h.receiveBuf) == 0 { continue } diff --git a/internal/clients/handlers/maprhandler.go b/internal/clients/handlers/maprhandler.go index fb71c8f..7ac5895 100644 --- a/internal/clients/handlers/maprhandler.go +++ b/internal/clients/handlers/maprhandler.go @@ -7,6 +7,7 @@ import ( "github.com/mimecast/dtail/internal/io/logger" "github.com/mimecast/dtail/internal/mapr" "github.com/mimecast/dtail/internal/mapr/client" + "github.com/mimecast/dtail/internal/protocol" ) // MaprHandler is the handler used on the client side for running mapreduce aggregations. @@ -58,7 +59,7 @@ func (h *MaprHandler) Write(p []byte) (n int, err error) { // related data. func (h *MaprHandler) handleAggregateMessage(message string) { h.count++ - parts := strings.Split(message, "➔") + parts := strings.Split(message, protocol.AggregateDelimiter) // Index 0 contains 'AGGREGATE', 1 contains server host. // Aggregation data begins from index 2. diff --git a/internal/io/fs/readfile.go b/internal/io/fs/readfile.go index 6757bd6..8a365a1 100644 --- a/internal/io/fs/readfile.go +++ b/internal/io/fs/readfile.go @@ -14,6 +14,7 @@ import ( "github.com/mimecast/dtail/internal/io/line" "github.com/mimecast/dtail/internal/io/logger" + "github.com/mimecast/dtail/internal/protocol" "github.com/mimecast/dtail/internal/regex" "github.com/DataDog/zstd" @@ -148,80 +149,64 @@ func (f readFile) read(ctx context.Context, fd *os.File, rawLines chan []byte, t if err != nil { return err } - rawLine := make([]byte, 0, 512) lineLengthThreshold := 1024 * 1024 // 1mb - longLineWarning := false + warnedAboutLongLine := false + message := make([]byte, 0, 512) for { select { case <-ctx.Done(): return nil - default: - } - - select { case <-truncate: if isTruncated, err := f.truncated(fd); isTruncated { return err } - logger.Info(f.filePath, "Current offset", offset) default: } - // Read some bytes (max 4k at once as of go 1.12). isPrefix will - // be set if line does not fit into 4k buffer. - bytes, isPrefix, err := reader.ReadLine() + b, err := reader.ReadByte() if err != nil { - // If EOF, sleep a couple of ms and return with nil error. - // If other error, return with non-nil error. if err != io.EOF { return err } if !f.seekEOF { - logger.Debug(f.FilePath(), "End of file reached") + logger.Info(f.FilePath(), "End of file reached") return nil } time.Sleep(time.Millisecond * 100) continue } + offset++ - rawLine = append(rawLine, bytes...) - offset += uint64(len(bytes)) - - if !isPrefix { - // last LineRead call returned contend until end of line. - rawLine = append(rawLine, '\n') - select { - case rawLines <- rawLine: - case <-ctx.Done(): - return nil + switch b { + case '\n': + if len(message) == 0 { + time.Sleep(time.Millisecond * 100) + continue } - rawLine = make([]byte, 0, 512) - if longLineWarning { - longLineWarning = false - } - continue - } - - // Last LineRead call could not read content until end of line, buffer - // was too small. Determine whether we exceed the max line length we - // want dtail to send to the client at once. Possibly split up log line - // into multiple log lines. - if len(rawLine) >= lineLengthThreshold { - if !longLineWarning { - f.serverMessages <- logger.Warn(f.filePath, "Long log line, splitting into multiple lines") - // Only print out one warning per long log line. - longLineWarning = true - } - rawLine = append(rawLine, '\n') select { - case rawLines <- rawLine: + case rawLines <- append(message, protocol.MessageDelimiter): + message = make([]byte, 0, 512) + warnedAboutLongLine = false case <-ctx.Done(): return nil } - rawLine = make([]byte, 0, 512) + default: + if len(message) >= lineLengthThreshold { + if !warnedAboutLongLine { + f.serverMessages <- logger.Warn(f.filePath, "Long log line, splitting into multiple lines") + warnedAboutLongLine = true + } + select { + case <-ctx.Done(): + return nil + case rawLines <- append(message, protocol.MessageDelimiter): + message = make([]byte, 0, 512) + } + } + message = append(message, b) } } } diff --git a/internal/io/logger/logger.go b/internal/io/logger/logger.go index bb9dc02..3a3935d 100644 --- a/internal/io/logger/logger.go +++ b/internal/io/logger/logger.go @@ -205,7 +205,7 @@ func Trace(args ...interface{}) string { // Write log line to buffer and/or log file. func write(what, severity, message string) { if Mode.logToStdout { - line := fmt.Sprintf("%s|%s|%s|%s\n", what, hostname, severity, message) + line := fmt.Sprintf("%s|%s|%s|%s", what, hostname, severity, message) if config.Client.TermColorsEnable { line = brush.Colorfy(line) @@ -219,7 +219,7 @@ func write(what, severity, message string) { timeStr := t.Format("20060102-150405") fileLogBufCh <- buf{ time: t, - message: fmt.Sprintf("%s|%s|%s|%s\n", severity, timeStr, what, message), + message: fmt.Sprintf("%s|%s|%s|%s", severity, timeStr, what, message), } } } @@ -326,6 +326,7 @@ func Flush() { select { case message := <-stdoutBufCh: stdoutWriter.WriteString(message) + stdoutWriter.WriteString("\n") default: stdoutWriter.Flush() return @@ -338,6 +339,7 @@ func writeToStdout(ctx context.Context) { select { case message := <-stdoutBufCh: stdoutWriter.WriteString(message) + stdoutWriter.WriteString("\n") case <-time.After(time.Millisecond * 100): stdoutWriter.Flush() case <-pauseCh: @@ -365,6 +367,7 @@ func writeToFile(ctx context.Context) { dateStr := buf.time.Format("20060102") w := fileWriter(dateStr) w.WriteString(buf.message) + w.WriteString("\n") case <-pauseCh: PAUSE: for { diff --git a/internal/mapr/aggregateset.go b/internal/mapr/aggregateset.go index a6cc6eb..029d87b 100644 --- a/internal/mapr/aggregateset.go +++ b/internal/mapr/aggregateset.go @@ -5,6 +5,8 @@ import ( "fmt" "strconv" "strings" + + "github.com/mimecast/dtail/internal/protocol" ) // AggregateSet represents aggregated key/value pairs from the @@ -70,20 +72,20 @@ func (s *AggregateSet) Serialize(ctx context.Context, groupKey string, ch chan<- var sb strings.Builder sb.WriteString(groupKey) - sb.WriteString("➔") - sb.WriteString(fmt.Sprintf("%d➔", s.Samples)) + sb.WriteString(protocol.AggregateDelimiter) + sb.WriteString(fmt.Sprintf("%d%s", s.Samples, protocol.AggregateDelimiter)) for k, v := range s.FValues { sb.WriteString(k) sb.WriteString("=") - sb.WriteString(fmt.Sprintf("%v➔", v)) + sb.WriteString(fmt.Sprintf("%v%s", v, protocol.AggregateDelimiter)) } for k, v := range s.SValues { sb.WriteString(k) sb.WriteString("=") sb.WriteString(v) - sb.WriteString("➔") + sb.WriteString(protocol.AggregateDelimiter) } select { diff --git a/internal/protocol/protocol.go b/internal/protocol/protocol.go new file mode 100644 index 0000000..2a570cd --- /dev/null +++ b/internal/protocol/protocol.go @@ -0,0 +1,10 @@ +package protocol + +const ( + // ProtocolCompat -ibility version + ProtocolCompat string = "4" + // MessageDelimiter delimits separate messages. + MessageDelimiter byte = '¬' + // AggregateDelimiter delimits parts of an aggregation message. + AggregateDelimiter string = "➔" +) diff --git a/internal/server/handlers/serverhandler.go b/internal/server/handlers/serverhandler.go index 185e7c2..23e3aeb 100644 --- a/internal/server/handlers/serverhandler.go +++ b/internal/server/handlers/serverhandler.go @@ -17,8 +17,8 @@ import ( "github.com/mimecast/dtail/internal/io/logger" "github.com/mimecast/dtail/internal/mapr/server" "github.com/mimecast/dtail/internal/omode" + "github.com/mimecast/dtail/internal/protocol" user "github.com/mimecast/dtail/internal/user/server" - "github.com/mimecast/dtail/internal/version" ) const ( @@ -92,24 +92,27 @@ func (h *ServerHandler) Read(p []byte) (n int, err error) { } if message[0] == '.' { // Handle hidden message (don't display to the user, interpreted by dtail client) - wholePayload := []byte(fmt.Sprintf("%s\n", message)) + wholePayload := []byte(fmt.Sprintf("%s%b", message, protocol.MessageDelimiter)) n = copy(p, wholePayload) return } // Handle normal server message (display to the user) - wholePayload := []byte(fmt.Sprintf("SERVER|%s|%s\n", h.hostname, message)) + wholePayload := []byte(fmt.Sprintf("SERVER|%s|%s%b", h.hostname, message, protocol.MessageDelimiter)) n = copy(p, wholePayload) return case message := <-h.aggregatedMessages: // Send mapreduce-aggregated data as a message. - data := fmt.Sprintf("AGGREGATE➔%s➔%s\n", h.hostname, message) + data := fmt.Sprintf("AGGREGATE%s%s%s%s%b", + protocol.AggregateDelimiter, h.hostname, + protocol.AggregateDelimiter, message, protocol.MessageDelimiter) wholePayload := []byte(data) n = copy(p, wholePayload) return case line := <-h.lines: + //fmt.Printf("<<<%d,%s>>>\n", len(line.Content), line.Content) // Send normal file content data as a message. serverInfo := []byte(fmt.Sprintf("REMOTE|%s|%3d|%v|%s|", h.hostname, line.TransmittedPerc, line.Count, line.SourceID)) @@ -182,8 +185,8 @@ func (h *ServerHandler) handleProtocolVersion(args []string) ([]string, int, err return args, argc, errors.New("unable to determine protocol version") } - if args[1] != version.ProtocolCompat { - err := fmt.Errorf("server with protocol version '%s' but client with '%s', please update DTail", version.ProtocolCompat, args[1]) + if args[1] != protocol.ProtocolCompat { + err := fmt.Errorf("server with protocol version '%s' but client with '%s', please update DTail", protocol.ProtocolCompat, args[1]) return args, argc, err } diff --git a/internal/version/version.go b/internal/version/version.go index 3e683bd..7f07c83 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -6,6 +6,7 @@ import ( "github.com/mimecast/dtail/internal/color" "github.com/mimecast/dtail/internal/config" + "github.com/mimecast/dtail/internal/protocol" ) const ( @@ -13,15 +14,13 @@ const ( Name string = "DTail" // Version of DTail. Version string = "3.2.0" - // ProtocolCompat -ibility version. - ProtocolCompat string = "3" // Additional information for DTail Additional string = "Have a lot of fun!" ) // String representation of the DTail version. func String() string { - return fmt.Sprintf("%s %v Protocol %s %s", Name, Version, ProtocolCompat, Additional) + return fmt.Sprintf("%s %v Protocol %s %s", Name, Version, protocol.ProtocolCompat, Additional) } // PaintedString is a prettier string representation of the DTail version. @@ -36,7 +35,7 @@ func PaintedString() string { version := color.PaintWithAttr(fmt.Sprintf(" %s ", Version), color.FgBlue, color.BgYellow, color.AttrBold) - protocol := color.Paint(fmt.Sprintf(" Protocol %s ", ProtocolCompat), + protocol := color.Paint(fmt.Sprintf(" Protocol %s ", protocol.ProtocolCompat), color.FgBlack, color.BgGreen) additional := color.PaintWithAttr(fmt.Sprintf(" %s ", Additional), -- cgit v1.2.3 From 9883a190109623b64e6d311dc2b462a6eae68003 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sun, 22 Aug 2021 10:07:00 +0300 Subject: introduces the protocol package --- internal/clients/handlers/basehandler.go | 3 ++- internal/clients/handlers/healthhandler.go | 3 ++- internal/clients/handlers/maprhandler.go | 2 +- internal/clients/maprclient.go | 3 ++- internal/server/handlers/controlhandler.go | 3 ++- internal/server/handlers/serverhandler.go | 6 +++--- internal/ssh/ssh.go | 3 ++- 7 files changed, 14 insertions(+), 9 deletions(-) (limited to 'internal') diff --git a/internal/clients/handlers/basehandler.go b/internal/clients/handlers/basehandler.go index acafe0e..fe83faa 100644 --- a/internal/clients/handlers/basehandler.go +++ b/internal/clients/handlers/basehandler.go @@ -56,13 +56,14 @@ func (h *baseHandler) SendMessage(command string) error { // Read data from the dtail server via Writer interface. func (h *baseHandler) Write(p []byte) (n int, err error) { for _, b := range p { - h.receiveBuf = append(h.receiveBuf, b) if b == protocol.MessageDelimiter { if len(h.receiveBuf) == 0 { continue } message := string(h.receiveBuf) h.handleMessageType(message) + } else { + h.receiveBuf = append(h.receiveBuf, b) } } diff --git a/internal/clients/handlers/healthhandler.go b/internal/clients/handlers/healthhandler.go index 0440706..213748c 100644 --- a/internal/clients/handlers/healthhandler.go +++ b/internal/clients/handlers/healthhandler.go @@ -6,6 +6,7 @@ import ( "time" "github.com/mimecast/dtail/internal" + "github.com/mimecast/dtail/internal/protocol" ) // HealthHandler implements the handler required for health checks. @@ -72,7 +73,7 @@ func (h *HealthHandler) SendMessage(command string) error { func (h *HealthHandler) Write(p []byte) (n int, err error) { for _, b := range p { h.receiveBuf = append(h.receiveBuf, b) - if b == '\n' { + if b == protocol.MessageDelimiter { // '\n' { h.receive <- string(h.receiveBuf) h.receiveBuf = h.receiveBuf[:0] } diff --git a/internal/clients/handlers/maprhandler.go b/internal/clients/handlers/maprhandler.go index 7ac5895..afad507 100644 --- a/internal/clients/handlers/maprhandler.go +++ b/internal/clients/handlers/maprhandler.go @@ -37,7 +37,7 @@ func NewMaprHandler(server string, query *mapr.Query, globalGroup *mapr.GlobalGr func (h *MaprHandler) Write(p []byte) (n int, err error) { for _, b := range p { h.baseHandler.receiveBuf = append(h.baseHandler.receiveBuf, b) - if b == '\n' { + if b == protocol.MessageDelimiter { // '\n' { if len(h.baseHandler.receiveBuf) == 0 { continue } diff --git a/internal/clients/maprclient.go b/internal/clients/maprclient.go index 1c0c2cc..77b674b 100644 --- a/internal/clients/maprclient.go +++ b/internal/clients/maprclient.go @@ -170,7 +170,8 @@ func (c *MaprClient) printResults() { return } - logger.Raw(fmt.Sprintf("%s\n", c.query.RawQuery)) + //logger.Raw(fmt.Sprintf("%s\n", c.query.RawQuery)) + logger.Raw(c.query.RawQuery) logger.Raw(result) } diff --git a/internal/server/handlers/controlhandler.go b/internal/server/handlers/controlhandler.go index 1e17c78..a217b40 100644 --- a/internal/server/handlers/controlhandler.go +++ b/internal/server/handlers/controlhandler.go @@ -8,6 +8,7 @@ import ( "github.com/mimecast/dtail/internal" "github.com/mimecast/dtail/internal/io/logger" + "github.com/mimecast/dtail/internal/protocol" user "github.com/mimecast/dtail/internal/user/server" ) @@ -56,7 +57,7 @@ func (h *ControlHandler) Read(p []byte) (n int, err error) { for { select { case message := <-h.serverMessages: - wholePayload := []byte(fmt.Sprintf("SERVER|%s|%s\n", h.hostname, message)) + wholePayload := []byte(fmt.Sprintf("SERVER|%s|%s%b", h.hostname, message, protocol.MessageDelimiter)) n = copy(p, wholePayload) return case <-h.done.Done(): diff --git a/internal/server/handlers/serverhandler.go b/internal/server/handlers/serverhandler.go index 23e3aeb..9541a34 100644 --- a/internal/server/handlers/serverhandler.go +++ b/internal/server/handlers/serverhandler.go @@ -92,13 +92,13 @@ func (h *ServerHandler) Read(p []byte) (n int, err error) { } if message[0] == '.' { // Handle hidden message (don't display to the user, interpreted by dtail client) - wholePayload := []byte(fmt.Sprintf("%s%b", message, protocol.MessageDelimiter)) + wholePayload := []byte(fmt.Sprintf("%s%s", message, string(protocol.MessageDelimiter))) n = copy(p, wholePayload) return } // Handle normal server message (display to the user) - wholePayload := []byte(fmt.Sprintf("SERVER|%s|%s%b", h.hostname, message, protocol.MessageDelimiter)) + wholePayload := []byte(fmt.Sprintf("SERVER|%s|%s%s", h.hostname, message, string(protocol.MessageDelimiter))) n = copy(p, wholePayload) return @@ -112,7 +112,7 @@ func (h *ServerHandler) Read(p []byte) (n int, err error) { return case line := <-h.lines: - //fmt.Printf("<<<%d,%s>>>\n", len(line.Content), line.Content) + //fmt.Printf("\t<<<%d,%s>>>\n", len(line.Content), line.Content) // Send normal file content data as a message. serverInfo := []byte(fmt.Sprintf("REMOTE|%s|%3d|%v|%s|", h.hostname, line.TransmittedPerc, line.Count, line.SourceID)) diff --git a/internal/ssh/ssh.go b/internal/ssh/ssh.go index 3a2e416..78bf99e 100644 --- a/internal/ssh/ssh.go +++ b/internal/ssh/ssh.go @@ -6,12 +6,13 @@ import ( "crypto/x509" "encoding/pem" "fmt" - "github.com/mimecast/dtail/internal/io/logger" "io/ioutil" "net" "os" "syscall" + "github.com/mimecast/dtail/internal/io/logger" + gossh "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/agent" "golang.org/x/crypto/ssh/terminal" -- cgit v1.2.3 From 6d727b9bdbc387c8a5c34406a2c4de9140face38 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 28 Aug 2021 19:36:46 +0100 Subject: use a byte.Buffer in the file reader --- internal/io/fs/readfile.go | 32 ++++++++++++++++++------------- internal/io/line/line.go | 5 +++-- internal/io/pool/bytesbuffer.go | 19 ++++++++++++++++++ internal/mapr/server/aggregate.go | 4