From 2b867f815268b7cf95cdb5c33f45f4e7bed29bda Mon Sep 17 00:00:00 2001 From: Faissal Elamraoui Date: Wed, 27 Jul 2016 17:42:28 +0200 Subject: [PATCH] Removed external dependency --- .travis.yml | 1 - logs/color_windows.go | 461 +++++++++++++++++++++++++++++++++++++ logs/color_windows_test.go | 294 +++++++++++++++++++++++ logs/logger.go | 5 +- 4 files changed, 757 insertions(+), 4 deletions(-) create mode 100644 logs/color_windows.go create mode 100644 logs/color_windows_test.go diff --git a/.travis.yml b/.travis.yml index fbba71eb..93536488 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,7 +31,6 @@ install: - go get github.com/siddontang/ledisdb/config - go get github.com/siddontang/ledisdb/ledis - go get github.com/ssdb/gossdb/ssdb - - go get github.com/shiena/ansicolor before_script: - psql --version - sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi" diff --git a/logs/color_windows.go b/logs/color_windows.go new file mode 100644 index 00000000..f6c6cad3 --- /dev/null +++ b/logs/color_windows.go @@ -0,0 +1,461 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build windows + +package logs + +import ( + "bytes" + "io" + "strings" + "syscall" + "unsafe" +) + +type ( + outputMode int + csiState int + parseResult int +) + +const ( + outsideCsiCode csiState = iota + firstCsiCode + secondCsiCode +) + +const ( + noConsole parseResult = iota + changedColor + unknown +) + +type ansiColorWriter struct { + w io.Writer + mode outputMode + state csiState + paramStartBuf bytes.Buffer + paramBuf bytes.Buffer +} + +const ( + firstCsiChar byte = '\x1b' + secondeCsiChar byte = '[' + separatorChar byte = ';' + sgrCode byte = 'm' +) + +const ( + foregroundBlue = uint16(0x0001) + foregroundGreen = uint16(0x0002) + foregroundRed = uint16(0x0004) + foregroundIntensity = uint16(0x0008) + backgroundBlue = uint16(0x0010) + backgroundGreen = uint16(0x0020) + backgroundRed = uint16(0x0040) + backgroundIntensity = uint16(0x0080) + underscore = uint16(0x8000) + + foregroundMask = foregroundBlue | foregroundGreen | foregroundRed | foregroundIntensity + backgroundMask = backgroundBlue | backgroundGreen | backgroundRed | backgroundIntensity +) + +const ( + ansiReset = "0" + ansiIntensityOn = "1" + ansiIntensityOff = "21" + ansiUnderlineOn = "4" + ansiUnderlineOff = "24" + ansiBlinkOn = "5" + ansiBlinkOff = "25" + + ansiForegroundBlack = "30" + ansiForegroundRed = "31" + ansiForegroundGreen = "32" + ansiForegroundYellow = "33" + ansiForegroundBlue = "34" + ansiForegroundMagenta = "35" + ansiForegroundCyan = "36" + ansiForegroundWhite = "37" + ansiForegroundDefault = "39" + + ansiBackgroundBlack = "40" + ansiBackgroundRed = "41" + ansiBackgroundGreen = "42" + ansiBackgroundYellow = "43" + ansiBackgroundBlue = "44" + ansiBackgroundMagenta = "45" + ansiBackgroundCyan = "46" + ansiBackgroundWhite = "47" + ansiBackgroundDefault = "49" + + ansiLightForegroundGray = "90" + ansiLightForegroundRed = "91" + ansiLightForegroundGreen = "92" + ansiLightForegroundYellow = "93" + ansiLightForegroundBlue = "94" + ansiLightForegroundMagenta = "95" + ansiLightForegroundCyan = "96" + ansiLightForegroundWhite = "97" + + ansiLightBackgroundGray = "100" + ansiLightBackgroundRed = "101" + ansiLightBackgroundGreen = "102" + ansiLightBackgroundYellow = "103" + ansiLightBackgroundBlue = "104" + ansiLightBackgroundMagenta = "105" + ansiLightBackgroundCyan = "106" + ansiLightBackgroundWhite = "107" +) + +type drawType int + +const ( + foreground drawType = iota + background +) + +type winColor struct { + code uint16 + drawType drawType +} + +var colorMap = map[string]winColor{ + ansiForegroundBlack: {0, foreground}, + ansiForegroundRed: {foregroundRed, foreground}, + ansiForegroundGreen: {foregroundGreen, foreground}, + ansiForegroundYellow: {foregroundRed | foregroundGreen, foreground}, + ansiForegroundBlue: {foregroundBlue, foreground}, + ansiForegroundMagenta: {foregroundRed | foregroundBlue, foreground}, + ansiForegroundCyan: {foregroundGreen | foregroundBlue, foreground}, + ansiForegroundWhite: {foregroundRed | foregroundGreen | foregroundBlue, foreground}, + ansiForegroundDefault: {foregroundRed | foregroundGreen | foregroundBlue, foreground}, + + ansiBackgroundBlack: {0, background}, + ansiBackgroundRed: {backgroundRed, background}, + ansiBackgroundGreen: {backgroundGreen, background}, + ansiBackgroundYellow: {backgroundRed | backgroundGreen, background}, + ansiBackgroundBlue: {backgroundBlue, background}, + ansiBackgroundMagenta: {backgroundRed | backgroundBlue, background}, + ansiBackgroundCyan: {backgroundGreen | backgroundBlue, background}, + ansiBackgroundWhite: {backgroundRed | backgroundGreen | backgroundBlue, background}, + ansiBackgroundDefault: {0, background}, + + ansiLightForegroundGray: {foregroundIntensity, foreground}, + ansiLightForegroundRed: {foregroundIntensity | foregroundRed, foreground}, + ansiLightForegroundGreen: {foregroundIntensity | foregroundGreen, foreground}, + ansiLightForegroundYellow: {foregroundIntensity | foregroundRed | foregroundGreen, foreground}, + ansiLightForegroundBlue: {foregroundIntensity | foregroundBlue, foreground}, + ansiLightForegroundMagenta: {foregroundIntensity | foregroundRed | foregroundBlue, foreground}, + ansiLightForegroundCyan: {foregroundIntensity | foregroundGreen | foregroundBlue, foreground}, + ansiLightForegroundWhite: {foregroundIntensity | foregroundRed | foregroundGreen | foregroundBlue, foreground}, + + ansiLightBackgroundGray: {backgroundIntensity, background}, + ansiLightBackgroundRed: {backgroundIntensity | backgroundRed, background}, + ansiLightBackgroundGreen: {backgroundIntensity | backgroundGreen, background}, + ansiLightBackgroundYellow: {backgroundIntensity | backgroundRed | backgroundGreen, background}, + ansiLightBackgroundBlue: {backgroundIntensity | backgroundBlue, background}, + ansiLightBackgroundMagenta: {backgroundIntensity | backgroundRed | backgroundBlue, background}, + ansiLightBackgroundCyan: {backgroundIntensity | backgroundGreen | backgroundBlue, background}, + ansiLightBackgroundWhite: {backgroundIntensity | backgroundRed | backgroundGreen | backgroundBlue, background}, +} + +var ( + kernel32 = syscall.NewLazyDLL("kernel32.dll") + procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") + procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") + defaultAttr *textAttributes +) + +func init() { + screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout)) + if screenInfo != nil { + colorMap[ansiForegroundDefault] = winColor{ + screenInfo.WAttributes & (foregroundRed | foregroundGreen | foregroundBlue), + foreground, + } + colorMap[ansiBackgroundDefault] = winColor{ + screenInfo.WAttributes & (backgroundRed | backgroundGreen | backgroundBlue), + background, + } + defaultAttr = convertTextAttr(screenInfo.WAttributes) + } +} + +type coord struct { + X, Y int16 +} + +type smallRect struct { + Left, Top, Right, Bottom int16 +} + +type consoleScreenBufferInfo struct { + DwSize coord + DwCursorPosition coord + WAttributes uint16 + SrWindow smallRect + DwMaximumWindowSize coord +} + +func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *consoleScreenBufferInfo { + var csbi consoleScreenBufferInfo + ret, _, _ := procGetConsoleScreenBufferInfo.Call( + hConsoleOutput, + uintptr(unsafe.Pointer(&csbi))) + if ret == 0 { + return nil + } + return &csbi +} + +func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool { + ret, _, _ := procSetConsoleTextAttribute.Call( + hConsoleOutput, + uintptr(wAttributes)) + return ret != 0 +} + +type textAttributes struct { + foregroundColor uint16 + backgroundColor uint16 + foregroundIntensity uint16 + backgroundIntensity uint16 + underscore uint16 + otherAttributes uint16 +} + +func convertTextAttr(winAttr uint16) *textAttributes { + fgColor := winAttr & (foregroundRed | foregroundGreen | foregroundBlue) + bgColor := winAttr & (backgroundRed | backgroundGreen | backgroundBlue) + fgIntensity := winAttr & foregroundIntensity + bgIntensity := winAttr & backgroundIntensity + underline := winAttr & underscore + otherAttributes := winAttr &^ (foregroundMask | backgroundMask | underscore) + return &textAttributes{fgColor, bgColor, fgIntensity, bgIntensity, underline, otherAttributes} +} + +func convertWinAttr(textAttr *textAttributes) uint16 { + var winAttr uint16 + winAttr |= textAttr.foregroundColor + winAttr |= textAttr.backgroundColor + winAttr |= textAttr.foregroundIntensity + winAttr |= textAttr.backgroundIntensity + winAttr |= textAttr.underscore + winAttr |= textAttr.otherAttributes + return winAttr +} + +func changeColor(param []byte) parseResult { + screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout)) + if screenInfo == nil { + return noConsole + } + + winAttr := convertTextAttr(screenInfo.WAttributes) + strParam := string(param) + if len(strParam) <= 0 { + strParam = "0" + } + csiParam := strings.Split(strParam, string(separatorChar)) + for _, p := range csiParam { + c, ok := colorMap[p] + switch { + case !ok: + switch p { + case ansiReset: + winAttr.foregroundColor = defaultAttr.foregroundColor + winAttr.backgroundColor = defaultAttr.backgroundColor + winAttr.foregroundIntensity = defaultAttr.foregroundIntensity + winAttr.backgroundIntensity = defaultAttr.backgroundIntensity + winAttr.underscore = 0 + winAttr.otherAttributes = 0 + case ansiIntensityOn: + winAttr.foregroundIntensity = foregroundIntensity + case ansiIntensityOff: + winAttr.foregroundIntensity = 0 + case ansiUnderlineOn: + winAttr.underscore = underscore + case ansiUnderlineOff: + winAttr.underscore = 0 + case ansiBlinkOn: + winAttr.backgroundIntensity = backgroundIntensity + case ansiBlinkOff: + winAttr.backgroundIntensity = 0 + default: + // unknown code + } + case c.drawType == foreground: + winAttr.foregroundColor = c.code + case c.drawType == background: + winAttr.backgroundColor = c.code + } + } + winTextAttribute := convertWinAttr(winAttr) + setConsoleTextAttribute(uintptr(syscall.Stdout), winTextAttribute) + + return changedColor +} + +func parseEscapeSequence(command byte, param []byte) parseResult { + if defaultAttr == nil { + return noConsole + } + + switch command { + case sgrCode: + return changeColor(param) + default: + return unknown + } +} + +func (cw *ansiColorWriter) flushBuffer() (int, error) { + return cw.flushTo(cw.w) +} + +func (cw *ansiColorWriter) resetBuffer() (int, error) { + return cw.flushTo(nil) +} + +func (cw *ansiColorWriter) flushTo(w io.Writer) (int, error) { + var n1, n2 int + var err error + + startBytes := cw.paramStartBuf.Bytes() + cw.paramStartBuf.Reset() + if w != nil { + n1, err = cw.w.Write(startBytes) + if err != nil { + return n1, err + } + } else { + n1 = len(startBytes) + } + paramBytes := cw.paramBuf.Bytes() + cw.paramBuf.Reset() + if w != nil { + n2, err = cw.w.Write(paramBytes) + if err != nil { + return n1 + n2, err + } + } else { + n2 = len(paramBytes) + } + return n1 + n2, nil +} + +func isParameterChar(b byte) bool { + return ('0' <= b && b <= '9') || b == separatorChar +} + +func (cw *ansiColorWriter) Write(p []byte) (int, error) { + r, nw, first, last := 0, 0, 0, 0 + if cw.mode != DiscardNonColorEscSeq { + cw.state = outsideCsiCode + cw.resetBuffer() + } + + var err error + for i, ch := range p { + switch cw.state { + case outsideCsiCode: + if ch == firstCsiChar { + cw.paramStartBuf.WriteByte(ch) + cw.state = firstCsiCode + } + case firstCsiCode: + switch ch { + case firstCsiChar: + cw.paramStartBuf.WriteByte(ch) + break + case secondeCsiChar: + cw.paramStartBuf.WriteByte(ch) + cw.state = secondCsiCode + last = i - 1 + default: + cw.resetBuffer() + cw.state = outsideCsiCode + } + case secondCsiCode: + if isParameterChar(ch) { + cw.paramBuf.WriteByte(ch) + } else { + nw, err = cw.w.Write(p[first:last]) + r += nw + if err != nil { + return r, err + } + first = i + 1 + result := parseEscapeSequence(ch, cw.paramBuf.Bytes()) + if result == noConsole || (cw.mode == OutputNonColorEscSeq && result == unknown) { + cw.paramBuf.WriteByte(ch) + nw, err := cw.flushBuffer() + if err != nil { + return r, err + } + r += nw + } else { + n, _ := cw.resetBuffer() + // Add one more to the size of the buffer for the last ch + r += n + 1 + } + + cw.state = outsideCsiCode + } + default: + cw.state = outsideCsiCode + } + } + + if cw.mode != DiscardNonColorEscSeq || cw.state == outsideCsiCode { + nw, err = cw.w.Write(p[first:len(p)]) + r += nw + } + + return r, err +} + +// DiscardNonColorEscSeq supports the divided color escape sequence. +// But non-color escape sequence is not output. +// Please use the OutputNonColorEscSeq If you want to output a non-color +// escape sequences such as ncurses. However, it does not support the divided +// color escape sequence. +const ( + _ outputMode = iota + DiscardNonColorEscSeq + OutputNonColorEscSeq +) + +// NewAnsiColorWriter creates and initializes a new ansiColorWriter +// using io.Writer w as its initial contents. +// In the console of Windows, which change the foreground and background +// colors of the text by the escape sequence. +// In the console of other systems, which writes to w all text. +func NewAnsiColorWriter(w io.Writer) io.Writer { + return NewModeAnsiColorWriter(w, DiscardNonColorEscSeq) +} + +// NewModeAnsiColorWriter create and initializes a new ansiColorWriter +// by specifying the outputMode. +func NewModeAnsiColorWriter(w io.Writer, mode outputMode) io.Writer { + if _, ok := w.(*ansiColorWriter); !ok { + return &ansiColorWriter{ + w: w, + mode: mode, + } + } + return w +} \ No newline at end of file diff --git a/logs/color_windows_test.go b/logs/color_windows_test.go new file mode 100644 index 00000000..d87d9798 --- /dev/null +++ b/logs/color_windows_test.go @@ -0,0 +1,294 @@ +// Copyright 2014 beego Author. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build windows + +package logs + +import ( + "bytes" + "fmt" + "syscall" + "testing" +) + +var GetConsoleScreenBufferInfo = getConsoleScreenBufferInfo + +func ChangeColor(color uint16) { + setConsoleTextAttribute(uintptr(syscall.Stdout), color) +} + +func ResetColor() { + ChangeColor(uint16(0x0007)) +} + +func TestWritePlanText(t *testing.T) { + inner := bytes.NewBufferString("") + w := NewAnsiColorWriter(inner) + expected := "plain text" + fmt.Fprintf(w, expected) + actual := inner.String() + if actual != expected { + t.Errorf("Get %q, want %q", actual, expected) + } +} + +func TestWriteParseText(t *testing.T) { + inner := bytes.NewBufferString("") + w := NewAnsiColorWriter(inner) + + inputTail := "\x1b[0mtail text" + expectedTail := "tail text" + fmt.Fprintf(w, inputTail) + actualTail := inner.String() + inner.Reset() + if actualTail != expectedTail { + t.Errorf("Get %q, want %q", actualTail, expectedTail) + } + + inputHead := "head text\x1b[0m" + expectedHead := "head text" + fmt.Fprintf(w, inputHead) + actualHead := inner.String() + inner.Reset() + if actualHead != expectedHead { + t.Errorf("Get %q, want %q", actualHead, expectedHead) + } + + inputBothEnds := "both ends \x1b[0m text" + expectedBothEnds := "both ends text" + fmt.Fprintf(w, inputBothEnds) + actualBothEnds := inner.String() + inner.Reset() + if actualBothEnds != expectedBothEnds { + t.Errorf("Get %q, want %q", actualBothEnds, expectedBothEnds) + } + + inputManyEsc := "\x1b\x1b\x1b\x1b[0m many esc" + expectedManyEsc := "\x1b\x1b\x1b many esc" + fmt.Fprintf(w, inputManyEsc) + actualManyEsc := inner.String() + inner.Reset() + if actualManyEsc != expectedManyEsc { + t.Errorf("Get %q, want %q", actualManyEsc, expectedManyEsc) + } + + expectedSplit := "split text" + for _, ch := range "split \x1b[0m text" { + fmt.Fprintf(w, string(ch)) + } + actualSplit := inner.String() + inner.Reset() + if actualSplit != expectedSplit { + t.Errorf("Get %q, want %q", actualSplit, expectedSplit) + } +} + +type screenNotFoundError struct { + error +} + +func writeAnsiColor(expectedText, colorCode string) (actualText string, actualAttributes uint16, err error) { + inner := bytes.NewBufferString("") + w := NewAnsiColorWriter(inner) + fmt.Fprintf(w, "\x1b[%sm%s", colorCode, expectedText) + + actualText = inner.String() + screenInfo := GetConsoleScreenBufferInfo(uintptr(syscall.Stdout)) + if screenInfo != nil { + actualAttributes = screenInfo.WAttributes + } else { + err = &screenNotFoundError{} + } + return +} + +type testParam struct { + text string + attributes uint16 + ansiColor string +} + +func TestWriteAnsiColorText(t *testing.T) { + screenInfo := GetConsoleScreenBufferInfo(uintptr(syscall.Stdout)) + if screenInfo == nil { + t.Fatal("Could not get ConsoleScreenBufferInfo") + } + defer ChangeColor(screenInfo.WAttributes) + defaultFgColor := screenInfo.WAttributes & uint16(0x0007) + defaultBgColor := screenInfo.WAttributes & uint16(0x0070) + defaultFgIntensity := screenInfo.WAttributes & uint16(0x0008) + defaultBgIntensity := screenInfo.WAttributes & uint16(0x0080) + + fgParam := []testParam{ + {"foreground black ", uint16(0x0000 | 0x0000), "30"}, + {"foreground red ", uint16(0x0004 | 0x0000), "31"}, + {"foreground green ", uint16(0x0002 | 0x0000), "32"}, + {"foreground yellow ", uint16(0x0006 | 0x0000), "33"}, + {"foreground blue ", uint16(0x0001 | 0x0000), "34"}, + {"foreground magenta", uint16(0x0005 | 0x0000), "35"}, + {"foreground cyan ", uint16(0x0003 | 0x0000), "36"}, + {"foreground white ", uint16(0x0007 | 0x0000), "37"}, + {"foreground default", defaultFgColor | 0x0000, "39"}, + {"foreground light gray ", uint16(0x0000 | 0x0008 | 0x0000), "90"}, + {"foreground light red ", uint16(0x0004 | 0x0008 | 0x0000), "91"}, + {"foreground light green ", uint16(0x0002 | 0x0008 | 0x0000), "92"}, + {"foreground light yellow ", uint16(0x0006 | 0x0008 | 0x0000), "93"}, + {"foreground light blue ", uint16(0x0001 | 0x0008 | 0x0000), "94"}, + {"foreground light magenta", uint16(0x0005 | 0x0008 | 0x0000), "95"}, + {"foreground light cyan ", uint16(0x0003 | 0x0008 | 0x0000), "96"}, + {"foreground light white ", uint16(0x0007 | 0x0008 | 0x0000), "97"}, + } + + bgParam := []testParam{ + {"background black ", uint16(0x0007 | 0x0000), "40"}, + {"background red ", uint16(0x0007 | 0x0040), "41"}, + {"background green ", uint16(0x0007 | 0x0020), "42"}, + {"background yellow ", uint16(0x0007 | 0x0060), "43"}, + {"background blue ", uint16(0x0007 | 0x0010), "44"}, + {"background magenta", uint16(0x0007 | 0x0050), "45"}, + {"background cyan ", uint16(0x0007 | 0x0030), "46"}, + {"background white ", uint16(0x0007 | 0x0070), "47"}, + {"background default", uint16(0x0007) | defaultBgColor, "49"}, + {"background light gray ", uint16(0x0007 | 0x0000 | 0x0080), "100"}, + {"background light red ", uint16(0x0007 | 0x0040 | 0x0080), "101"}, + {"background light green ", uint16(0x0007 | 0x0020 | 0x0080), "102"}, + {"background light yellow ", uint16(0x0007 | 0x0060 | 0x0080), "103"}, + {"background light blue ", uint16(0x0007 | 0x0010 | 0x0080), "104"}, + {"background light magenta", uint16(0x0007 | 0x0050 | 0x0080), "105"}, + {"background light cyan ", uint16(0x0007 | 0x0030 | 0x0080), "106"}, + {"background light white ", uint16(0x0007 | 0x0070 | 0x0080), "107"}, + } + + resetParam := []testParam{ + {"all reset", defaultFgColor | defaultBgColor | defaultFgIntensity | defaultBgIntensity, "0"}, + {"all reset", defaultFgColor | defaultBgColor | defaultFgIntensity | defaultBgIntensity, ""}, + } + + boldParam := []testParam{ + {"bold on", uint16(0x0007 | 0x0008), "1"}, + {"bold off", uint16(0x0007), "21"}, + } + + underscoreParam := []testParam{ + {"underscore on", uint16(0x0007 | 0x8000), "4"}, + {"underscore off", uint16(0x0007), "24"}, + } + + blinkParam := []testParam{ + {"blink on", uint16(0x0007 | 0x0080), "5"}, + {"blink off", uint16(0x0007), "25"}, + } + + mixedParam := []testParam{ + {"both black, bold, underline, blink", uint16(0x0000 | 0x0000 | 0x0008 | 0x8000 | 0x0080), "30;40;1;4;5"}, + {"both red, bold, underline, blink", uint16(0x0004 | 0x0040 | 0x0008 | 0x8000 | 0x0080), "31;41;1;4;5"}, + {"both green, bold, underline, blink", uint16(0x0002 | 0x0020 | 0x0008 | 0x8000 | 0x0080), "32;42;1;4;5"}, + {"both yellow, bold, underline, blink", uint16(0x0006 | 0x0060 | 0x0008 | 0x8000 | 0x0080), "33;43;1;4;5"}, + {"both blue, bold, underline, blink", uint16(0x0001 | 0x0010 | 0x0008 | 0x8000 | 0x0080), "34;44;1;4;5"}, + {"both magenta, bold, underline, blink", uint16(0x0005 | 0x0050 | 0x0008 | 0x8000 | 0x0080), "35;45;1;4;5"}, + {"both cyan, bold, underline, blink", uint16(0x0003 | 0x0030 | 0x0008 | 0x8000 | 0x0080), "36;46;1;4;5"}, + {"both white, bold, underline, blink", uint16(0x0007 | 0x0070 | 0x0008 | 0x8000 | 0x0080), "37;47;1;4;5"}, + {"both default, bold, underline, blink", uint16(defaultFgColor | defaultBgColor | 0x0008 | 0x8000 | 0x0080), "39;49;1;4;5"}, + } + + assertTextAttribute := func(expectedText string, expectedAttributes uint16, ansiColor string) { + actualText, actualAttributes, err := writeAnsiColor(expectedText, ansiColor) + if actualText != expectedText { + t.Errorf("Get %q, want %q", actualText, expectedText) + } + if err != nil { + t.Fatal("Could not get ConsoleScreenBufferInfo") + } + if actualAttributes != expectedAttributes { + t.Errorf("Text: %q, Get 0x%04x, want 0x%04x", expectedText, actualAttributes, expectedAttributes) + } + } + + for _, v := range fgParam { + ResetColor() + assertTextAttribute(v.text, v.attributes, v.ansiColor) + } + + for _, v := range bgParam { + ChangeColor(uint16(0x0070 | 0x0007)) + assertTextAttribute(v.text, v.attributes, v.ansiColor) + } + + for _, v := range resetParam { + ChangeColor(uint16(0x0000 | 0x0070 | 0x0008)) + assertTextAttribute(v.text, v.attributes, v.ansiColor) + } + + ResetColor() + for _, v := range boldParam { + assertTextAttribute(v.text, v.attributes, v.ansiColor) + } + + ResetColor() + for _, v := range underscoreParam { + assertTextAttribute(v.text, v.attributes, v.ansiColor) + } + + ResetColor() + for _, v := range blinkParam { + assertTextAttribute(v.text, v.attributes, v.ansiColor) + } + + for _, v := range mixedParam { + ResetColor() + assertTextAttribute(v.text, v.attributes, v.ansiColor) + } +} + +func TestIgnoreUnknownSequences(t *testing.T) { + inner := bytes.NewBufferString("") + w := NewModeAnsiColorWriter(inner, OutputNonColorEscSeq) + + inputText := "\x1b[=decpath mode" + expectedTail := inputText + fmt.Fprintf(w, inputText) + actualTail := inner.String() + inner.Reset() + if actualTail != expectedTail { + t.Errorf("Get %q, want %q", actualTail, expectedTail) + } + + inputText = "\x1b[=tailing esc and bracket\x1b[" + expectedTail = inputText + fmt.Fprintf(w, inputText) + actualTail = inner.String() + inner.Reset() + if actualTail != expectedTail { + t.Errorf("Get %q, want %q", actualTail, expectedTail) + } + + inputText = "\x1b[?tailing esc\x1b" + expectedTail = inputText + fmt.Fprintf(w, inputText) + actualTail = inner.String() + inner.Reset() + if actualTail != expectedTail { + t.Errorf("Get %q, want %q", actualTail, expectedTail) + } + + inputText = "\x1b[1h;3punended color code invalid\x1b3" + expectedTail = inputText + fmt.Fprintf(w, inputText) + actualTail = inner.String() + inner.Reset() + if actualTail != expectedTail { + t.Errorf("Get %q, want %q", actualTail, expectedTail) + } +} \ No newline at end of file diff --git a/logs/logger.go b/logs/logger.go index 3cdf7237..499a5b39 100644 --- a/logs/logger.go +++ b/logs/logger.go @@ -18,7 +18,6 @@ import ( "io" "sync" "time" - "github.com/shiena/ansicolor" "fmt" "os" ) @@ -138,16 +137,16 @@ func ColorByMethod(cond bool, method string) string { } } +// Guard Mutex to guarantee atomicity of W32Debug(string) function var mu sync.Mutex // Helper method to output colored logs in Windows terminals -// using ansicolor (https://github.com/shiena/ansicolor) func W32Debug(msg string) { mu.Lock() defer mu.Unlock() current := time.Now() - w := ansicolor.NewAnsiColorWriter(os.Stdout) + w := NewAnsiColorWriter(os.Stdout) fmt.Fprintf(w, "[beego] %v %s\n", current.Format("2006/01/02 - 15:04:05"), msg) }