diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..228e701 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +go.sum +image_generator/typescript \ No newline at end of file diff --git a/README.md b/README.md index d13b694..6214421 100644 --- a/README.md +++ b/README.md @@ -44,54 +44,54 @@ Marker has very simple and extensible way to get your strings colorful and brill #### MatchAll ```go - aristotleQuote := "The more you know, the more you realize you don't know." - emphasized := marker.Mark(aristotleQuote, marker.MatchAll("know"), color.New(color.FgRed)) - fmt.Println(emphasized) +aristotleQuote := "The more you know, the more you realize you don't know." +emphasized := marker.Mark(aristotleQuote, marker.MatchAll("know"), color.New(color.FgRed)) +fmt.Println(emphasized) ``` #### MatchN ```go - boringLog := "[INFO] Nobody wants to read pale [INFO] tags." - brilliantLog := marker.Mark(boringLog, marker.MatchN("[INFO]", 1), color.New(color.FgBlue)) - fmt.Println(brilliantLog) +boringLog := "[INFO] Nobody wants to read pale [INFO] tags." +brilliantLog := marker.Mark(boringLog, marker.MatchN("[INFO]", 1), color.New(color.FgBlue)) +fmt.Println(brilliantLog) ``` #### MatchRegexp ```go - rhyme := "I scream, you all scream, we all scream for ice cream." - r, _ := regexp.Compile("([a-z]?cream)") - careAboutCream := marker.Mark(rhyme, marker.MatchRegexp(r), color.New(color.FgYellow)) - fmt.Println(careAboutCream) +rhyme := "I scream, you all scream, we all scream for ice cream." +r, _ := regexp.Compile("([a-z]?cream)") +careAboutCream := marker.Mark(rhyme, marker.MatchRegexp(r), color.New(color.FgYellow)) +fmt.Println(careAboutCream) ``` #### MatchSurrounded ```go - sentence := "I pull out things surrounded by abcWHOA COLORSdef" - markedSurrounded := marker.Mark(sentence, marker.MatchSurrounded("abc", "def"), magentaFg) - fmt.Println(markedSurrounded) +sentence := "I pull out things surrounded by abcWHOA COLORSdef" +markedSurrounded := marker.Mark(sentence, marker.MatchSurrounded("abc", "def"), magentaFg) +fmt.Println(markedSurrounded) ``` #### MatchBracketSurrounded ```go - sentence = "[INFO] This is what log lines look like" - markedSurrounded = marker.Mark(sentence, marker.MatchBracketSurrounded(), redFg) - fmt.Println(markedSurrounded) +sentence = "[INFO] This is what log lines look like" +markedSurrounded = marker.Mark(sentence, marker.MatchBracketSurrounded(), redFg) +fmt.Println(markedSurrounded) ``` #### MatchParensSurrounded ```go - sentence = "[ERROR] This is what (parens) lines look like" - markedSurrounded = marker.Mark(sentence, marker.MatchParensSurrounded(), blueFg) - fmt.Println(markedSurrounded) +sentence = "[ERROR] This is what (parens) lines look like" +markedSurrounded = marker.Mark(sentence, marker.MatchParensSurrounded(), blueFg) +fmt.Println(markedSurrounded) ``` @@ -102,22 +102,70 @@ Marker has very simple and extensible way to get your strings colorful and brill If you want to mark different patterns in the same string, marker builder is neater way to do this. ```go - rhyme := "I scream, you all scream, we all scream for ice cream." - b := &marker.MarkBuilder{} - r, _ := regexp.Compile("([a-z]?cream)") +rhyme := "I scream, you all scream, we all scream for ice cream." +b := &marker.MarkBuilder{} +r, _ := regexp.Compile("([a-z]?cream)") - markedWithBuilder := b.SetString(rhyme). - Mark(marker.MatchN("for ice", 1), color.New(color.FgRed)). - Mark(marker.MatchAll("all"), color.New(color.FgMagenta)). - Mark(marker.MatchRegexp(r), color.New(color.FgYellow)). - Build() +markedWithBuilder := b.SetString(rhyme). + Mark(marker.MatchN("for ice", 1), color.New(color.FgRed)). + Mark(marker.MatchAll("all"), color.New(color.FgMagenta)). + Mark(marker.MatchRegexp(r), color.New(color.FgYellow)). + Build() - fmt.Println(markedWithBuilder) +fmt.Println(markedWithBuilder) ``` --- +## Log way + +You may want to instrument a logger such that any output coming from it is colorized in the expected manner. `marker` contains functionality which wraps `stdout` or whatever `io.Writer` you wish. + +```go +stdoutMarker := marker.NewStdoutMarker() +markRules := []marker.MarkRule{ + {marker.MatchBracketSurrounded(), color.New(color.FgBlue)}, + {marker.MatchAll("marker"), color.New(color.FgRed)}, +} + +stdoutMarker.AddRules(markRules) +logger := log.New(stdoutMarker, "", 0) + +logger.Println("[INFO] marker is working as expected") +``` + + + +### Customize io.Writer out for log interface + +`marker` also allows you to specify the `io.Writer` that you want to send output to. This is useful if the logger is writing to somewhere other than `stdout` like a file. + +```go +f, _ := os.Create("/tmp/awesome.log") +w := bufio.NewWriter(f) + +writeMarker := marker.NewWriteMarker(w) + +markRules := []marker.MarkRule{ + {marker.MatchBracketSurrounded(), blueFg}, + {marker.MatchAll("marker"), magentaFg}, +} + +writeMarker.AddRules(markRules) + +logger := log.New(writeMarker, "", 0) +logger.Println("[INFO] colorful logs even in files, marker to mark them all!") + +w.Flush() +f.Close() + +output := catFile("/tmp/awesome.log") // cat /tmp/dat2 +fmt.Print(output) +``` + + + ## Writing your custom Matcher As you see in above examples, **Mark** function takes an **MatcherFunc** to match the patterns in given string and colorize them. diff --git a/assets/builder.jpg b/assets/builder.jpg deleted file mode 100644 index f0f2094..0000000 Binary files a/assets/builder.jpg and /dev/null differ diff --git a/assets/matchall.jpg b/assets/matchall.jpg deleted file mode 100644 index 268cee4..0000000 Binary files a/assets/matchall.jpg and /dev/null differ diff --git a/assets/matchn.jpg b/assets/matchn.jpg deleted file mode 100644 index b829f8b..0000000 Binary files a/assets/matchn.jpg and /dev/null differ diff --git a/assets/matchregexp.jpg b/assets/matchregexp.jpg deleted file mode 100644 index d6ab86a..0000000 Binary files a/assets/matchregexp.jpg and /dev/null differ diff --git a/assets/png/builder.png b/assets/png/builder.png index 4de9c99..4a9ed77 100644 Binary files a/assets/png/builder.png and b/assets/png/builder.png differ diff --git a/assets/png/custom.png b/assets/png/custom.png index c9f1818..f52f715 100644 Binary files a/assets/png/custom.png and b/assets/png/custom.png differ diff --git a/assets/png/email.png b/assets/png/email.png index 2e80f46..9f6eae8 100644 Binary files a/assets/png/email.png and b/assets/png/email.png differ diff --git a/assets/png/log.png b/assets/png/log.png new file mode 100644 index 0000000..2032daf Binary files /dev/null and b/assets/png/log.png differ diff --git a/assets/png/logtofile.png b/assets/png/logtofile.png new file mode 100644 index 0000000..56ef851 Binary files /dev/null and b/assets/png/logtofile.png differ diff --git a/assets/png/matchall.png b/assets/png/matchall.png index e4d2543..b34c7ef 100644 Binary files a/assets/png/matchall.png and b/assets/png/matchall.png differ diff --git a/assets/png/matchn.png b/assets/png/matchn.png index c9f1818..f52f715 100644 Binary files a/assets/png/matchn.png and b/assets/png/matchn.png differ diff --git a/assets/png/matchregex.png b/assets/png/matchregex.png index 5b6d0c6..08e852e 100644 Binary files a/assets/png/matchregex.png and b/assets/png/matchregex.png differ diff --git a/assets/png/matchsurrounded1.png b/assets/png/matchsurrounded1.png index 59e6ce0..55d24bb 100644 Binary files a/assets/png/matchsurrounded1.png and b/assets/png/matchsurrounded1.png differ diff --git a/assets/png/matchsurrounded2.png b/assets/png/matchsurrounded2.png index 06315d6..aa91992 100644 Binary files a/assets/png/matchsurrounded2.png and b/assets/png/matchsurrounded2.png differ diff --git a/assets/png/matchsurrounded3.png b/assets/png/matchsurrounded3.png index f4e75f5..3540acd 100644 Binary files a/assets/png/matchsurrounded3.png and b/assets/png/matchsurrounded3.png differ diff --git a/assets/png/showoff.png b/assets/png/showoff.png index a03e971..f0dd866 100644 Binary files a/assets/png/showoff.png and b/assets/png/showoff.png differ diff --git a/assets/showoff.jpg b/assets/showoff.jpg deleted file mode 100644 index d70f22e..0000000 Binary files a/assets/showoff.jpg and /dev/null differ diff --git a/assets/surrounded1.png b/assets/surrounded1.png deleted file mode 100644 index ed0f49b..0000000 Binary files a/assets/surrounded1.png and /dev/null differ diff --git a/assets/surrounded2.png b/assets/surrounded2.png deleted file mode 100644 index ebfd707..0000000 Binary files a/assets/surrounded2.png and /dev/null differ diff --git a/assets/surrounded3.png b/assets/surrounded3.png deleted file mode 100644 index 5ee3166..0000000 Binary files a/assets/surrounded3.png and /dev/null differ diff --git a/examples/log/main.go b/examples/log/main.go new file mode 100644 index 0000000..2abd497 --- /dev/null +++ b/examples/log/main.go @@ -0,0 +1,27 @@ +package main + +import ( + "log" + + "github.com/cyucelen/marker" + "github.com/fatih/color" +) + +var ( + redFg = color.New(color.FgRed) + blueFg = color.New(color.FgBlue) +) + +func main() { + stdoutMarker := marker.NewStdoutMarker() + + markRules := []marker.MarkRule{ + {marker.MatchBracketSurrounded(), blueFg}, + {marker.MatchAll("marker"), redFg}, + } + + stdoutMarker.AddRules(markRules) + + logger := log.New(stdoutMarker, "", 0) + logger.Println("[INFO] marker is working as expected") +} diff --git a/examples/logtofile/main.go b/examples/logtofile/main.go new file mode 100644 index 0000000..43f14a7 --- /dev/null +++ b/examples/logtofile/main.go @@ -0,0 +1,52 @@ +package main + +import ( + "bufio" + "bytes" + "fmt" + "log" + "os" + "os/exec" + + "github.com/cyucelen/marker" + "github.com/fatih/color" +) + +var ( + magentaFg = color.New(color.FgMagenta) + blueFg = color.New(color.FgBlue) +) + +func main() { + f, _ := os.Create("/tmp/awesome.log") + w := bufio.NewWriter(f) + + writeMarker := marker.NewWriteMarker(w) + + markRules := []marker.MarkRule{ + {marker.MatchBracketSurrounded(), blueFg}, + {marker.MatchAll("marker"), magentaFg}, + } + + writeMarker.AddRules(markRules) + + logger := log.New(writeMarker, "", 0) + logger.Println("[INFO] colorful logs even in files, marker to mark them all!") + + w.Flush() + f.Close() + + output := catFile("/tmp/awesome.log") // cat /tmp/dat2 + fmt.Print(output) +} + +func catFile(file string) string { + cmd := exec.Command("cat", file) + cmdOutput := &bytes.Buffer{} + cmd.Stdout = cmdOutput + err := cmd.Run() + if err != nil { + log.Fatalf("cmd.Run() failed with %s\n", err) + } + return string(cmdOutput.Bytes()) +} diff --git a/examples/showoff/main.go b/examples/showoff/main.go index bccaf33..a84e4c7 100644 --- a/examples/showoff/main.go +++ b/examples/showoff/main.go @@ -2,9 +2,10 @@ package main import ( "fmt" + "regexp" + "github.com/cyucelen/marker" "github.com/fatih/color" - "regexp" ) var magentaFg = color.New(color.FgMagenta) @@ -30,16 +31,16 @@ func main() { r, _ := regexp.Compile("([a-z]?cream)") markedWithRegexp := marker.Mark(rhyme, marker.MatchRegexp(r), whiteFg.Add(color.BgHiBlue)) - regexpExampleHeader := fmt.Sprintf("Mark Regexp \"%s\":\t\t\t\t", whiteFg.Add(color.BgHiBlue).Sprint("([a-z]?cream)")) + regexpExampleHeader := fmt.Sprintf("Mark Regexp \"%s\":\t\t\t", whiteFg.Add(color.BgHiBlue).Sprint("([a-z]?cream)")) fmt.Println(regexpExampleHeader + markedWithRegexp) b := &marker.MarkBuilder{} markedWithBuilder := b.SetString(rhyme). Mark(marker.MatchN("for ice", 1), redFg). Mark(marker.MatchAll("all"), magentaFg). - Mark(marker.MatchRegexp(r), redFg.Add(color.BgHiWhite)). + Mark(marker.MatchRegexp(r), blueFg). Build() - builderExampleHeader := fmt.Sprintf("Mark \"%s\", \"%s\", \"%s\" (w/ builder):\t", - color.New(color.FgRed).Sprint("for ice"), magentaFg.Sprint("all"), redFg.Add(color.BgHiWhite).Sprint("([a-z]?cream)")) + builderExampleHeader := fmt.Sprintf("Mark \"%s\", \"%s\", \"%s\" :\t", + color.New(color.FgRed).Sprint("for ice"), magentaFg.Sprint("all"), blueFg.Sprint("([a-z]?cream)")) fmt.Println(builderExampleHeader + markedWithBuilder) } diff --git a/image_generator/README.md b/image_generator/README.md new file mode 100644 index 0000000..a8ebdc1 --- /dev/null +++ b/image_generator/README.md @@ -0,0 +1,40 @@ + +# Terminal image generator + +`generate.py` runs all the `main.go` files under examples and converts terminal outputs to `html` and then `png` + +## Prerequisites + +* Go +* Python3.7 (preferred) +* Pip + +### terminal-to-html + +``` +$ cd ~/ +$ go get github.com/buildkite/terminal-to-html/cmd/terminal-to-html +``` + +### wkhtmltoimage + +#### Arch + +``` +$ yaourt wkhtmltopdf +``` + +#### Ubuntu + +``` +$ sudo apt-get update +$ sudo apt-get install xvfb libfontconfig wkhtmltopdf +``` + +## Usage + +``` +$ python3.7 generate.py +``` + +After running the script, image files should be created under `assets/png` \ No newline at end of file diff --git a/image_generator/log.txt b/image_generator/log.txt new file mode 100644 index 0000000..f61253f --- /dev/null +++ b/image_generator/log.txt @@ -0,0 +1 @@ +[INFO] marker is working as expected diff --git a/log.go b/log.go new file mode 100644 index 0000000..0d4ea61 --- /dev/null +++ b/log.go @@ -0,0 +1,54 @@ +package marker + +import ( + "io" + "os" + + "github.com/fatih/color" +) + +// WriteMarkerOption is functional option type for WriteMarker +type WriteMarkerOption func(*WriteMarker) + +// MarkRule contains marking information to be applied on log stream +type MarkRule struct { + Matcher MatcherFunc + Color *color.Color +} + +// WriteMarker contains specified rules for applying them on output +type WriteMarker struct { + rules []MarkRule + out io.Writer +} + +// NewWriteMarker creates a Marker that writes out to the given io.Writer +func NewWriteMarker(writer io.Writer) *WriteMarker { + logMarker := &WriteMarker{out: writer} + return logMarker +} + +// NewStdoutMarker creates a WriteMarker with default out as os.Stdout +func NewStdoutMarker() *WriteMarker { + return NewWriteMarker(os.Stdout) +} + +// AddRule appends a rule to WriteMarker and returns itself +func (s *WriteMarker) AddRule(rule MarkRule) *WriteMarker { + s.rules = append(s.rules, rule) + return s +} + +// AddRules appends all rules from given slice to WriteMarker rules +func (s *WriteMarker) AddRules(rules []MarkRule) { + s.rules = append(s.rules, rules...) +} + +// Write marks the text with specified rules and writes the output to specifed out +func (s WriteMarker) Write(p []byte) (n int, err error) { + marked := string(p) + for _, rule := range s.rules { + marked = Mark(marked, rule.Matcher, rule.Color) + } + return s.out.Write([]byte(marked)) +} diff --git a/log_test.go b/log_test.go new file mode 100644 index 0000000..aa8e535 --- /dev/null +++ b/log_test.go @@ -0,0 +1,61 @@ +package marker + +import ( + "fmt" + "log" + "os" + "testing" + + "github.com/fatih/color" + "github.com/stretchr/testify/assert" +) + +type MockLogOut struct { + actualLog string +} + +func (m *MockLogOut) Write(p []byte) (n int, err error) { + m.actualLog = string(p) + return len(p), nil +} + +func Test_New(t *testing.T) { + stdoutMarker := NewStdoutMarker() + assert.Equal(t, stdoutMarker.out, os.Stdout) + + mockLogOut := &MockLogOut{} + writeMarker := NewWriteMarker(mockLogOut) + assert.Equal(t, writeMarker.out, mockLogOut) +} + +func Test_Write(t *testing.T) { + redFg := color.New(color.FgRed) + redFg.EnableColor() + red := redFg.SprintFunc() + blueFg := color.New(color.FgBlue) + blueFg.EnableColor() + blue := blueFg.SprintFunc() + + mockOut := &MockLogOut{} + writeMarker := NewWriteMarker(mockOut) + + writeMarker.AddRule(MarkRule{MatchAll("skydome"), redFg}).AddRule(MarkRule{MatchAll("data"), redFg}) + + logger := log.New(writeMarker, "", 0) + logger.Print("best data company is skydome") + + expectedLog := fmt.Sprintf("best %s company is %s\n", red("data"), red("skydome")) + assert.Equal(t, expectedLog, mockOut.actualLog) + + // Testing the mark order here since we cannot assert function equality (https://golang.org/ref/spec#Comparison_operators) + newRules := []MarkRule{ + {MatchAll("skydome"), blueFg}, // blue should override red because of order + {MatchAll("company"), redFg}, + } + writeMarker.AddRules(newRules) + + expectedLog = fmt.Sprintf("best %s %s is %s\n", red("data"), red("company"), red(blue("skydome"))) + logger.Print("best data company is skydome") + assert.Equal(t, expectedLog, mockOut.actualLog) + +}