You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by da...@apache.org on 2017/01/24 20:56:18 UTC
[07/13] incubator-trafficcontrol git commit: Vendored
github.com/cihub/seelog.
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/format.go
----------------------------------------------------------------------
diff --git a/traffic_stats/vendor/github.com/cihub/seelog b/traffic_stats/vendor/github.com/cihub/seelog
deleted file mode 160000
index 175e6e3..0000000
--- a/traffic_stats/vendor/github.com/cihub/seelog
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 175e6e3d439fe2e1cee7ab652b12eb546c145a13
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/format.go b/traffic_stats/vendor/github.com/cihub/seelog/format.go
new file mode 100644
index 0000000..ec47b45
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/format.go
@@ -0,0 +1,466 @@
+// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package seelog
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+// FormatterSymbol is a special symbol used in config files to mark special format aliases.
+const (
+ FormatterSymbol = '%'
+)
+
+const (
+ formatterParameterStart = '('
+ formatterParameterEnd = ')'
+)
+
+// Time and date formats used for %Date and %Time aliases.
+const (
+ DateDefaultFormat = "2006-01-02"
+ TimeFormat = "15:04:05"
+)
+
+var DefaultMsgFormat = "%Ns [%Level] %Msg%n"
+
+var (
+ DefaultFormatter *formatter
+ msgonlyformatter *formatter
+)
+
+func init() {
+ var err error
+ if DefaultFormatter, err = NewFormatter(DefaultMsgFormat); err != nil {
+ reportInternalError(fmt.Errorf("error during creating DefaultFormatter: %s", err))
+ }
+ if msgonlyformatter, err = NewFormatter("%Msg"); err != nil {
+ reportInternalError(fmt.Errorf("error during creating msgonlyformatter: %s", err))
+ }
+}
+
+// FormatterFunc represents one formatter object that starts with '%' sign in the 'format' attribute
+// of the 'format' config item. These special symbols are replaced with context values or special
+// strings when message is written to byte receiver.
+//
+// Check https://github.com/cihub/seelog/wiki/Formatting for details.
+// Full list (with descriptions) of formatters: https://github.com/cihub/seelog/wiki/Format-reference
+//
+// FormatterFunc takes raw log message, level, log context and returns a string, number (of any type) or any object
+// that can be evaluated as string.
+type FormatterFunc func(message string, level LogLevel, context LogContextInterface) interface{}
+
+// FormatterFuncCreator is a factory of FormatterFunc objects. It is used to generate parameterized
+// formatters (such as %Date or %EscM) and custom user formatters.
+type FormatterFuncCreator func(param string) FormatterFunc
+
+var formatterFuncs = map[string]FormatterFunc{
+ "Level": formatterLevel,
+ "Lev": formatterLev,
+ "LEVEL": formatterLEVEL,
+ "LEV": formatterLEV,
+ "l": formatterl,
+ "Msg": formatterMsg,
+ "FullPath": formatterFullPath,
+ "File": formatterFile,
+ "RelFile": formatterRelFile,
+ "Func": FormatterFunction,
+ "FuncShort": FormatterFunctionShort,
+ "Line": formatterLine,
+ "Time": formatterTime,
+ "UTCTime": formatterUTCTime,
+ "Ns": formatterNs,
+ "UTCNs": formatterUTCNs,
+ "r": formatterr,
+ "n": formattern,
+ "t": formattert,
+}
+
+var formatterFuncsParameterized = map[string]FormatterFuncCreator{
+ "Date": createDateTimeFormatterFunc,
+ "UTCDate": createUTCDateTimeFormatterFunc,
+ "EscM": createANSIEscapeFunc,
+}
+
+func errorAliasReserved(name string) error {
+ return fmt.Errorf("cannot use '%s' as custom formatter name. Name is reserved", name)
+}
+
+// RegisterCustomFormatter registers a new custom formatter factory with a given name. If returned error is nil,
+// then this name (prepended by '%' symbol) can be used in 'format' attributes in configuration and
+// it will be treated like the standard parameterized formatter identifiers.
+//
+// RegisterCustomFormatter needs to be called before creating a logger for it to take effect. The general recommendation
+// is to call it once in 'init' func of your application or any initializer func.
+//
+// For usage examples, check https://github.com/cihub/seelog/wiki/Custom-formatters.
+//
+// Name must only consist of letters (unicode.IsLetter).
+//
+// Name must not be one of the already registered standard formatter names
+// (https://github.com/cihub/seelog/wiki/Format-reference) and previously registered
+// custom format names. To avoid any potential name conflicts (in future releases), it is recommended
+// to start your custom formatter name with a namespace (e.g. 'MyCompanySomething') or a 'Custom' keyword.
+func RegisterCustomFormatter(name string, creator FormatterFuncCreator) error {
+ if _, ok := formatterFuncs[name]; ok {
+ return errorAliasReserved(name)
+ }
+ if _, ok := formatterFuncsParameterized[name]; ok {
+ return errorAliasReserved(name)
+ }
+ formatterFuncsParameterized[name] = creator
+ return nil
+}
+
+// formatter is used to write messages in a specific format, inserting such additional data
+// as log level, date/time, etc.
+type formatter struct {
+ fmtStringOriginal string
+ fmtString string
+ formatterFuncs []FormatterFunc
+}
+
+// NewFormatter creates a new formatter using a format string
+func NewFormatter(formatString string) (*formatter, error) {
+ fmtr := new(formatter)
+ fmtr.fmtStringOriginal = formatString
+ if err := buildFormatterFuncs(fmtr); err != nil {
+ return nil, err
+ }
+ return fmtr, nil
+}
+
+func buildFormatterFuncs(formatter *formatter) error {
+ var (
+ fsbuf = new(bytes.Buffer)
+ fsolm1 = len(formatter.fmtStringOriginal) - 1
+ )
+ for i := 0; i <= fsolm1; i++ {
+ if char := formatter.fmtStringOriginal[i]; char != FormatterSymbol {
+ fsbuf.WriteByte(char)
+ continue
+ }
+ // Check if the index is at the end of the string.
+ if i == fsolm1 {
+ return fmt.Errorf("format error: %c cannot be last symbol", FormatterSymbol)
+ }
+ // Check if the formatter symbol is doubled and skip it as nonmatching.
+ if formatter.fmtStringOriginal[i+1] == FormatterSymbol {
+ fsbuf.WriteRune(FormatterSymbol)
+ i++
+ continue
+ }
+ function, ni, err := formatter.extractFormatterFunc(i + 1)
+ if err != nil {
+ return err
+ }
+ // Append formatting string "%v".
+ fsbuf.Write([]byte{37, 118})
+ i = ni
+ formatter.formatterFuncs = append(formatter.formatterFuncs, function)
+ }
+ formatter.fmtString = fsbuf.String()
+ return nil
+}
+
+func (formatter *formatter) extractFormatterFunc(index int) (FormatterFunc, int, error) {
+ letterSequence := formatter.extractLetterSequence(index)
+ if len(letterSequence) == 0 {
+ return nil, 0, fmt.Errorf("format error: lack of formatter after %c at %d", FormatterSymbol, index)
+ }
+
+ function, formatterLength, ok := formatter.findFormatterFunc(letterSequence)
+ if ok {
+ return function, index + formatterLength - 1, nil
+ }
+
+ function, formatterLength, ok, err := formatter.findFormatterFuncParametrized(letterSequence, index)
+ if err != nil {
+ return nil, 0, err
+ }
+ if ok {
+ return function, index + formatterLength - 1, nil
+ }
+
+ return nil, 0, errors.New("format error: unrecognized formatter at " + strconv.Itoa(index) + ": " + letterSequence)
+}
+
+func (formatter *formatter) extractLetterSequence(index int) string {
+ letters := ""
+
+ bytesToParse := []byte(formatter.fmtStringOriginal[index:])
+ runeCount := utf8.RuneCount(bytesToParse)
+ for i := 0; i < runeCount; i++ {
+ rune, runeSize := utf8.DecodeRune(bytesToParse)
+ bytesToParse = bytesToParse[runeSize:]
+
+ if unicode.IsLetter(rune) {
+ letters += string(rune)
+ } else {
+ break
+ }
+ }
+ return letters
+}
+
+func (formatter *formatter) findFormatterFunc(letters string) (FormatterFunc, int, bool) {
+ currentVerb := letters
+ for i := 0; i < len(letters); i++ {
+ function, ok := formatterFuncs[currentVerb]
+ if ok {
+ return function, len(currentVerb), ok
+ }
+ currentVerb = currentVerb[:len(currentVerb)-1]
+ }
+
+ return nil, 0, false
+}
+
+func (formatter *formatter) findFormatterFuncParametrized(letters string, lettersStartIndex int) (FormatterFunc, int, bool, error) {
+ currentVerb := letters
+ for i := 0; i < len(letters); i++ {
+ functionCreator, ok := formatterFuncsParameterized[currentVerb]
+ if ok {
+ parameter := ""
+ parameterLen := 0
+ isVerbEqualsLetters := i == 0 // if not, then letter goes after formatter, and formatter is parameterless
+ if isVerbEqualsLetters {
+ userParameter := ""
+ var err error
+ userParameter, parameterLen, ok, err = formatter.findparameter(lettersStartIndex + len(currentVerb))
+ if ok {
+ parameter = userParameter
+ } else if err != nil {
+ return nil, 0, false, err
+ }
+ }
+
+ return functionCreator(parameter), len(currentVerb) + parameterLen, true, nil
+ }
+
+ currentVerb = currentVerb[:len(currentVerb)-1]
+ }
+
+ return nil, 0, false, nil
+}
+
+func (formatter *formatter) findparameter(startIndex int) (string, int, bool, error) {
+ if len(formatter.fmtStringOriginal) == startIndex || formatter.fmtStringOriginal[startIndex] != formatterParameterStart {
+ return "", 0, false, nil
+ }
+
+ endIndex := strings.Index(formatter.fmtStringOriginal[startIndex:], string(formatterParameterEnd))
+ if endIndex == -1 {
+ return "", 0, false, fmt.Errorf("Unmatched parenthesis or invalid parameter at %d: %s",
+ startIndex, formatter.fmtStringOriginal[startIndex:])
+ }
+ endIndex += startIndex
+
+ length := endIndex - startIndex + 1
+
+ return formatter.fmtStringOriginal[startIndex+1 : endIndex], length, true, nil
+}
+
+// Format processes a message with special formatters, log level, and context. Returns formatted string
+// with all formatter identifiers changed to appropriate values.
+func (formatter *formatter) Format(message string, level LogLevel, context LogContextInterface) string {
+ if len(formatter.formatterFuncs) == 0 {
+ return formatter.fmtString
+ }
+
+ params := make([]interface{}, len(formatter.formatterFuncs))
+ for i, function := range formatter.formatterFuncs {
+ params[i] = function(message, level, context)
+ }
+
+ return fmt.Sprintf(formatter.fmtString, params...)
+}
+
+func (formatter *formatter) String() string {
+ return formatter.fmtStringOriginal
+}
+
+//=====================================================
+
+const (
+ wrongLogLevel = "WRONG_LOGLEVEL"
+ wrongEscapeCode = "WRONG_ESCAPE"
+)
+
+var levelToString = map[LogLevel]string{
+ TraceLvl: "Trace",
+ DebugLvl: "Debug",
+ InfoLvl: "Info",
+ WarnLvl: "Warn",
+ ErrorLvl: "Error",
+ CriticalLvl: "Critical",
+ Off: "Off",
+}
+
+var levelToShortString = map[LogLevel]string{
+ TraceLvl: "Trc",
+ DebugLvl: "Dbg",
+ InfoLvl: "Inf",
+ WarnLvl: "Wrn",
+ ErrorLvl: "Err",
+ CriticalLvl: "Crt",
+ Off: "Off",
+}
+
+var levelToShortestString = map[LogLevel]string{
+ TraceLvl: "t",
+ DebugLvl: "d",
+ InfoLvl: "i",
+ WarnLvl: "w",
+ ErrorLvl: "e",
+ CriticalLvl: "c",
+ Off: "o",
+}
+
+func formatterLevel(message string, level LogLevel, context LogContextInterface) interface{} {
+ levelStr, ok := levelToString[level]
+ if !ok {
+ return wrongLogLevel
+ }
+ return levelStr
+}
+
+func formatterLev(message string, level LogLevel, context LogContextInterface) interface{} {
+ levelStr, ok := levelToShortString[level]
+ if !ok {
+ return wrongLogLevel
+ }
+ return levelStr
+}
+
+func formatterLEVEL(message string, level LogLevel, context LogContextInterface) interface{} {
+ return strings.ToTitle(formatterLevel(message, level, context).(string))
+}
+
+func formatterLEV(message string, level LogLevel, context LogContextInterface) interface{} {
+ return strings.ToTitle(formatterLev(message, level, context).(string))
+}
+
+func formatterl(message string, level LogLevel, context LogContextInterface) interface{} {
+ levelStr, ok := levelToShortestString[level]
+ if !ok {
+ return wrongLogLevel
+ }
+ return levelStr
+}
+
+func formatterMsg(message string, level LogLevel, context LogContextInterface) interface{} {
+ return message
+}
+
+func formatterFullPath(message string, level LogLevel, context LogContextInterface) interface{} {
+ return context.FullPath()
+}
+
+func formatterFile(message string, level LogLevel, context LogContextInterface) interface{} {
+ return context.FileName()
+}
+
+func formatterRelFile(message string, level LogLevel, context LogContextInterface) interface{} {
+ return context.ShortPath()
+}
+
+func FormatterFunction(message string, level LogLevel, context LogContextInterface) interface{} {
+ return context.Func()
+}
+
+func FormatterFunctionShort(message string, level LogLevel, context LogContextInterface) interface{} {
+ f := context.Func()
+ spl := strings.Split(f, ".")
+ return spl[len(spl)-1]
+}
+
+func formatterLine(message string, level LogLevel, context LogContextInterface) interface{} {
+ return context.Line()
+}
+
+func formatterTime(message string, level LogLevel, context LogContextInterface) interface{} {
+ return context.CallTime().Format(TimeFormat)
+}
+
+func formatterUTCTime(message string, level LogLevel, context LogContextInterface) interface{} {
+ return context.CallTime().UTC().Format(TimeFormat)
+}
+
+func formatterNs(message string, level LogLevel, context LogContextInterface) interface{} {
+ return context.CallTime().UnixNano()
+}
+
+func formatterUTCNs(message string, level LogLevel, context LogContextInterface) interface{} {
+ return context.CallTime().UTC().UnixNano()
+}
+
+func formatterr(message string, level LogLevel, context LogContextInterface) interface{} {
+ return "\r"
+}
+
+func formattern(message string, level LogLevel, context LogContextInterface) interface{} {
+ return "\n"
+}
+
+func formattert(message string, level LogLevel, context LogContextInterface) interface{} {
+ return "\t"
+}
+
+func createDateTimeFormatterFunc(dateTimeFormat string) FormatterFunc {
+ format := dateTimeFormat
+ if format == "" {
+ format = DateDefaultFormat
+ }
+ return func(message string, level LogLevel, context LogContextInterface) interface{} {
+ return context.CallTime().Format(format)
+ }
+}
+
+func createUTCDateTimeFormatterFunc(dateTimeFormat string) FormatterFunc {
+ format := dateTimeFormat
+ if format == "" {
+ format = DateDefaultFormat
+ }
+ return func(message string, level LogLevel, context LogContextInterface) interface{} {
+ return context.CallTime().UTC().Format(format)
+ }
+}
+
+func createANSIEscapeFunc(escapeCodeString string) FormatterFunc {
+ return func(message string, level LogLevel, context LogContextInterface) interface{} {
+ if len(escapeCodeString) == 0 {
+ return wrongEscapeCode
+ }
+
+ return fmt.Sprintf("%c[%sm", 0x1B, escapeCodeString)
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/format_test.go
----------------------------------------------------------------------
diff --git a/traffic_stats/vendor/github.com/cihub/seelog b/traffic_stats/vendor/github.com/cihub/seelog
deleted file mode 160000
index 175e6e3..0000000
--- a/traffic_stats/vendor/github.com/cihub/seelog
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 175e6e3d439fe2e1cee7ab652b12eb546c145a13
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/format_test.go b/traffic_stats/vendor/github.com/cihub/seelog/format_test.go
new file mode 100644
index 0000000..3cac6b4
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/format_test.go
@@ -0,0 +1,237 @@
+// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package seelog
+
+import (
+ "fmt"
+ "strings"
+ "testing"
+ "time"
+)
+
+const (
+ TestFuncName = "TestFormats"
+)
+
+type formatTest struct {
+ formatString string
+ input string
+ inputLogLevel LogLevel
+ expectedOutput string
+ errorExpected bool
+}
+
+var formatTests = []formatTest{
+ {"test", "abcdef", TraceLvl, "test", false},
+ {"", "abcdef", TraceLvl, "", false},
+ {"%Level", "", TraceLvl, "Trace", false},
+ {"%Level", "", DebugLvl, "Debug", false},
+ {"%Level", "", InfoLvl, "Info", false},
+ {"%Level", "", WarnLvl, "Warn", false},
+ {"%Level", "", ErrorLvl, "Error", false},
+ {"%Level", "", CriticalLvl, "Critical", false},
+ {"[%Level]", "", TraceLvl, "[Trace]", false},
+ {"[%Level]", "abc", DebugLvl, "[Debug]", false},
+ {"%LevelLevel", "", InfoLvl, "InfoLevel", false},
+ {"[%Level][%Level]", "", WarnLvl, "[Warn][Warn]", false},
+ {"[%Level]X[%Level]", "", ErrorLvl, "[Error]X[Error]", false},
+ {"%Levelll", "", CriticalLvl, "Criticalll", false},
+ {"%Lvl", "", TraceLvl, "", true},
+ {"%%Level", "", DebugLvl, "%Level", false},
+ {"%Level%", "", InfoLvl, "", true},
+ {"%sevel", "", WarnLvl, "", true},
+ {"Level", "", ErrorLvl, "Level", false},
+ {"%LevelLevel", "", CriticalLvl, "CriticalLevel", false},
+ {"%Lev", "", TraceLvl, "Trc", false},
+ {"%Lev", "", DebugLvl, "Dbg", false},
+ {"%Lev", "", InfoLvl, "Inf", false},
+ {"%Lev", "", WarnLvl, "Wrn", false},
+ {"%Lev", "", ErrorLvl, "Err", false},
+ {"%Lev", "", CriticalLvl, "Crt", false},
+ {"[%Lev]", "", TraceLvl, "[Trc]", false},
+ {"[%Lev]", "abc", DebugLvl, "[Dbg]", false},
+ {"%LevLevel", "", InfoLvl, "InfLevel", false},
+ {"[%Level][%Lev]", "", WarnLvl, "[Warn][Wrn]", false},
+ {"[%Lev]X[%Lev]", "", ErrorLvl, "[Err]X[Err]", false},
+ {"%Levll", "", CriticalLvl, "Crtll", false},
+ {"%LEVEL", "", TraceLvl, "TRACE", false},
+ {"%LEVEL", "", DebugLvl, "DEBUG", false},
+ {"%LEVEL", "", InfoLvl, "INFO", false},
+ {"%LEVEL", "", WarnLvl, "WARN", false},
+ {"%LEVEL", "", ErrorLvl, "ERROR", false},
+ {"%LEVEL", "", CriticalLvl, "CRITICAL", false},
+ {"[%LEVEL]", "", TraceLvl, "[TRACE]", false},
+ {"[%LEVEL]", "abc", DebugLvl, "[DEBUG]", false},
+ {"%LEVELLEVEL", "", InfoLvl, "INFOLEVEL", false},
+ {"[%LEVEL][%LEVEL]", "", WarnLvl, "[WARN][WARN]", false},
+ {"[%LEVEL]X[%Level]", "", ErrorLvl, "[ERROR]X[Error]", false},
+ {"%LEVELLL", "", CriticalLvl, "CRITICALLL", false},
+ {"%LEV", "", TraceLvl, "TRC", false},
+ {"%LEV", "", DebugLvl, "DBG", false},
+ {"%LEV", "", InfoLvl, "INF", false},
+ {"%LEV", "", WarnLvl, "WRN", false},
+ {"%LEV", "", ErrorLvl, "ERR", false},
+ {"%LEV", "", CriticalLvl, "CRT", false},
+ {"[%LEV]", "", TraceLvl, "[TRC]", false},
+ {"[%LEV]", "abc", DebugLvl, "[DBG]", false},
+ {"%LEVLEVEL", "", InfoLvl, "INFLEVEL", false},
+ {"[%LEVEL][%LEV]", "", WarnLvl, "[WARN][WRN]", false},
+ {"[%LEV]X[%LEV]", "", ErrorLvl, "[ERR]X[ERR]", false},
+ {"%LEVLL", "", CriticalLvl, "CRTLL", false},
+ {"%l", "", TraceLvl, "t", false},
+ {"%l", "", DebugLvl, "d", false},
+ {"%l", "", InfoLvl, "i", false},
+ {"%l", "", WarnLvl, "w", false},
+ {"%l", "", ErrorLvl, "e", false},
+ {"%l", "", CriticalLvl, "c", false},
+ {"[%l]", "", TraceLvl, "[t]", false},
+ {"[%l]", "abc", DebugLvl, "[d]", false},
+ {"%Level%Msg", "", TraceLvl, "Trace", false},
+ {"%Level%Msg", "A", DebugLvl, "DebugA", false},
+ {"%Level%Msg", "", InfoLvl, "Info", false},
+ {"%Level%Msg", "test", WarnLvl, "Warntest", false},
+ {"%Level%Msg", " ", ErrorLvl, "Error ", false},
+ {"%Level%Msg", "", CriticalLvl, "Critical", false},
+ {"[%Level]", "", TraceLvl, "[Trace]", false},
+ {"[%Level]", "abc", DebugLvl, "[Debug]", false},
+ {"%Level%MsgLevel", "A", InfoLvl, "InfoALevel", false},
+ {"[%Level]%Msg[%Level]", "test", WarnLvl, "[Warn]test[Warn]", false},
+ {"[%Level]%MsgX[%Level]", "test", ErrorLvl, "[Error]testX[Error]", false},
+ {"%Levell%Msgl", "Test", CriticalLvl, "CriticallTestl", false},
+ {"%Lev%Msg%LEVEL%LEV%l%Msg", "Test", InfoLvl, "InfTestINFOINFiTest", false},
+ {"%r", "", CriticalLvl, "\r", false},
+ {"%n", "", CriticalLvl, "\n", false},
+ {"%t", "", CriticalLvl, "\t", false},
+}
+
+func TestFormats(t *testing.T) {
+
+ context, conErr := currentContext(nil)
+ if conErr != nil {
+ t.Fatal("Cannot get current context:" + conErr.Error())
+ return
+ }
+
+ for _, test := range formatTests {
+
+ form, err := NewFormatter(test.formatString)
+
+ if (err != nil) != test.errorExpected {
+ t.Errorf("input: %s \nInput LL: %s\n* Expected error:%t Got error: %t\n",
+ test.input, test.inputLogLevel, test.errorExpected, (err != nil))
+ if err != nil {
+ t.Logf("%s\n", err.Error())
+ }
+ continue
+ } else if err != nil {
+ continue
+ }
+
+ msg := form.Format(test.input, test.inputLogLevel, context)
+
+ if err == nil && msg != test.expectedOutput {
+ t.Errorf("format: %s \nInput: %s \nInput LL: %s\n* Expected: %s \n* Got: %s\n",
+ test.formatString, test.input, test.inputLogLevel, test.expectedOutput, msg)
+ }
+ }
+}
+
+func TestDateFormat(t *testing.T) {
+ _, err := NewFormatter("%Date")
+ if err != nil {
+ t.Error("Unexpected error: " + err.Error())
+ }
+}
+
+func TestDateParameterizedFormat(t *testing.T) {
+ testFormat := "Mon Jan 02 2006 15:04:05"
+ preciseForamt := "Mon Jan 02 2006 15:04:05.000"
+
+ context, conErr := currentContext(nil)
+ if conErr != nil {
+ t.Fatal("Cannot get current context:" + conErr.Error())
+ return
+ }
+
+ form, err := NewFormatter("%Date(" + preciseForamt + ")")
+ if err != nil {
+ t.Error("Unexpected error: " + err.Error())
+ }
+
+ dateBefore := time.Now().Format(testFormat)
+ msg := form.Format("", TraceLvl, context)
+ dateAfter := time.Now().Format(testFormat)
+
+ if !strings.HasPrefix(msg, dateBefore) && !strings.HasPrefix(msg, dateAfter) {
+ t.Errorf("incorrect message: %v. Expected %v or %v", msg, dateBefore, dateAfter)
+ }
+
+ _, err = NewFormatter("%Date(" + preciseForamt)
+ if err == nil {
+ t.Error("Expected error for invalid format")
+ }
+}
+
+func createTestFormatter(format string) FormatterFunc {
+ return func(message string, level LogLevel, context LogContextInterface) interface{} {
+ return "TEST " + context.Func() + " TEST"
+ }
+}
+
+func TestCustomFormatterRegistration(t *testing.T) {
+ err := RegisterCustomFormatter("Level", createTestFormatter)
+ if err == nil {
+ t.Errorf("expected an error when trying to register a custom formatter with a reserved alias")
+ }
+ err = RegisterCustomFormatter("EscM", createTestFormatter)
+ if err == nil {
+ t.Errorf("expected an error when trying to register a custom formatter with a reserved parameterized alias")
+ }
+ err = RegisterCustomFormatter("TEST", createTestFormatter)
+ if err != nil {
+ t.Fatalf("Registering custom formatter: unexpected error: %s", err)
+ }
+ err = RegisterCustomFormatter("TEST", createTestFormatter)
+ if err == nil {
+ t.Errorf("expected an error when trying to register a custom formatter with duplicate name")
+ }
+
+ context, conErr := currentContext(nil)
+ if conErr != nil {
+ t.Fatal("Cannot get current context:" + conErr.Error())
+ return
+ }
+
+ form, err := NewFormatter("%Msg %TEST 123")
+ if err != nil {
+ t.Fatalf("%s\n", err.Error())
+ }
+
+ expected := fmt.Sprintf("test TEST %sTestCustomFormatterRegistration TEST 123", commonPrefix)
+ msg := form.Format("test", DebugLvl, context)
+ if msg != expected {
+ t.Fatalf("Custom formatter: invalid output. Expected: '%s'. Got: '%s'", expected, msg)
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/internals_baseerror.go
----------------------------------------------------------------------
diff --git a/traffic_stats/vendor/github.com/cihub/seelog b/traffic_stats/vendor/github.com/cihub/seelog
deleted file mode 160000
index 175e6e3..0000000
--- a/traffic_stats/vendor/github.com/cihub/seelog
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 175e6e3d439fe2e1cee7ab652b12eb546c145a13
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/internals_baseerror.go b/traffic_stats/vendor/github.com/cihub/seelog/internals_baseerror.go
new file mode 100644
index 0000000..c0b271d
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/internals_baseerror.go
@@ -0,0 +1,10 @@
+package seelog
+
+// Base struct for custom errors.
+type baseError struct {
+ message string
+}
+
+func (be baseError) Error() string {
+ return be.message
+}
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/internals_byteverifiers_test.go
----------------------------------------------------------------------
diff --git a/traffic_stats/vendor/github.com/cihub/seelog b/traffic_stats/vendor/github.com/cihub/seelog
deleted file mode 160000
index 175e6e3..0000000
--- a/traffic_stats/vendor/github.com/cihub/seelog
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 175e6e3d439fe2e1cee7ab652b12eb546c145a13
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/internals_byteverifiers_test.go b/traffic_stats/vendor/github.com/cihub/seelog/internals_byteverifiers_test.go
new file mode 100644
index 0000000..0ab6ebc
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/internals_byteverifiers_test.go
@@ -0,0 +1,118 @@
+// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package seelog
+
+import (
+ "errors"
+ "strconv"
+ "testing"
+)
+
+// bytesVerifier is a byte receiver which is used for correct input testing.
+// It allows to compare expected result and actual result in context of received bytes.
+type bytesVerifier struct {
+ expectedBytes []byte // bytes that are expected to be written in next Write call
+ waitingForInput bool // true if verifier is waiting for a Write call
+ writtenData []byte // real bytes that actually were received during the last Write call
+ testEnv *testing.T
+}
+
+func newBytesVerifier(t *testing.T) (*bytesVerifier, error) {
+ if t == nil {
+ return nil, errors.New("testing environment param is nil")
+ }
+
+ verifier := new(bytesVerifier)
+ verifier.testEnv = t
+
+ return verifier, nil
+}
+
+// Write is used to check whether verifier was waiting for input and whether bytes are the same as expectedBytes.
+// After Write call, waitingForInput is set to false.
+func (verifier *bytesVerifier) Write(bytes []byte) (n int, err error) {
+ if !verifier.waitingForInput {
+ verifier.testEnv.Errorf("unexpected input: %v", string(bytes))
+ return
+ }
+
+ verifier.waitingForInput = false
+ verifier.writtenData = bytes
+
+ if verifier.expectedBytes != nil {
+ if bytes == nil {
+ verifier.testEnv.Errorf("incoming 'bytes' is nil")
+ } else {
+ if len(bytes) != len(verifier.expectedBytes) {
+ verifier.testEnv.Errorf("'Bytes' has unexpected len. Expected: %d. Got: %d. . Expected string: %q. Got: %q",
+ len(verifier.expectedBytes), len(bytes), string(verifier.expectedBytes), string(bytes))
+ } else {
+ for i := 0; i < len(bytes); i++ {
+ if verifier.expectedBytes[i] != bytes[i] {
+ verifier.testEnv.Errorf("incorrect data on position %d. Expected: %d. Got: %d. Expected string: %q. Got: %q",
+ i, verifier.expectedBytes[i], bytes[i], string(verifier.expectedBytes), string(bytes))
+ break
+ }
+ }
+ }
+ }
+ }
+
+ return len(bytes), nil
+}
+
+func (verifier *bytesVerifier) ExpectBytes(bytes []byte) {
+ verifier.waitingForInput = true
+ verifier.expectedBytes = bytes
+}
+
+func (verifier *bytesVerifier) MustNotExpect() {
+ if verifier.waitingForInput {
+ errorText := "Unexpected input: "
+
+ if verifier.expectedBytes != nil {
+ errorText += "len = " + strconv.Itoa(len(verifier.expectedBytes))
+ errorText += ". text = " + string(verifier.expectedBytes)
+ }
+
+ verifier.testEnv.Errorf(errorText)
+ }
+}
+
+func (verifier *bytesVerifier) Close() error {
+ return nil
+}
+
+// nullWriter implements io.Writer inteface and does nothing, always returning a successful write result
+type nullWriter struct {
+}
+
+func (writer *nullWriter) Write(bytes []byte) (n int, err error) {
+ return len(bytes), nil
+}
+
+func (writer *nullWriter) Close() error {
+ return nil
+}
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/internals_fsutils.go
----------------------------------------------------------------------
diff --git a/traffic_stats/vendor/github.com/cihub/seelog b/traffic_stats/vendor/github.com/cihub/seelog
deleted file mode 160000
index 175e6e3..0000000
--- a/traffic_stats/vendor/github.com/cihub/seelog
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 175e6e3d439fe2e1cee7ab652b12eb546c145a13
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/internals_fsutils.go b/traffic_stats/vendor/github.com/cihub/seelog/internals_fsutils.go
new file mode 100644
index 0000000..c0a0e0e
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/internals_fsutils.go
@@ -0,0 +1,320 @@
+package seelog
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "sync"
+)
+
+// File and directory permitions.
+const (
+ defaultFilePermissions = 0666
+ defaultDirectoryPermissions = 0767
+)
+
+const (
+ // Max number of directories can be read asynchronously.
+ maxDirNumberReadAsync = 1000
+)
+
+type cannotOpenFileError struct {
+ baseError
+}
+
+func newCannotOpenFileError(fname string) *cannotOpenFileError {
+ return &cannotOpenFileError{baseError{message: "Cannot open file: " + fname}}
+}
+
+type notDirectoryError struct {
+ baseError
+}
+
+func newNotDirectoryError(dname string) *notDirectoryError {
+ return ¬DirectoryError{baseError{message: dname + " is not directory"}}
+}
+
+// fileFilter is a filtering criteria function for '*os.File'.
+// Must return 'false' to set aside the given file.
+type fileFilter func(os.FileInfo, *os.File) bool
+
+// filePathFilter is a filtering creteria function for file path.
+// Must return 'false' to set aside the given file.
+type filePathFilter func(filePath string) bool
+
+// GetSubdirNames returns a list of directories found in
+// the given one with dirPath.
+func getSubdirNames(dirPath string) ([]string, error) {
+ fi, err := os.Stat(dirPath)
+ if err != nil {
+ return nil, err
+ }
+ if !fi.IsDir() {
+ return nil, newNotDirectoryError(dirPath)
+ }
+ dd, err := os.Open(dirPath)
+ // Cannot open file.
+ if err != nil {
+ if dd != nil {
+ dd.Close()
+ }
+ return nil, err
+ }
+ defer dd.Close()
+ // TODO: Improve performance by buffering reading.
+ allEntities, err := dd.Readdir(-1)
+ if err != nil {
+ return nil, err
+ }
+ subDirs := []string{}
+ for _, entity := range allEntities {
+ if entity.IsDir() {
+ subDirs = append(subDirs, entity.Name())
+ }
+ }
+ return subDirs, nil
+}
+
+// getSubdirAbsPaths recursively visit all the subdirectories
+// starting from the given directory and returns absolute paths for them.
+func getAllSubdirAbsPaths(dirPath string) (res []string, err error) {
+ dps, err := getSubdirAbsPaths(dirPath)
+ if err != nil {
+ res = []string{}
+ return
+ }
+ res = append(res, dps...)
+ for _, dp := range dps {
+ sdps, err := getAllSubdirAbsPaths(dp)
+ if err != nil {
+ return []string{}, err
+ }
+ res = append(res, sdps...)
+ }
+ return
+}
+
+// getSubdirAbsPaths supplies absolute paths for all subdirectiries in a given directory.
+// Input: (I1) dirPath - absolute path of a directory in question.
+// Out: (O1) - slice of subdir asbolute paths; (O2) - error of the operation.
+// Remark: If error (O2) is non-nil then (O1) is nil and vice versa.
+func getSubdirAbsPaths(dirPath string) ([]string, error) {
+ sdns, err := getSubdirNames(dirPath)
+ if err != nil {
+ return nil, err
+ }
+ rsdns := []string{}
+ for _, sdn := range sdns {
+ rsdns = append(rsdns, filepath.Join(dirPath, sdn))
+ }
+ return rsdns, nil
+}
+
+// getOpenFilesInDir supplies a slice of os.File pointers to files located in the directory.
+// Remark: Ignores files for which fileFilter returns false
+func getOpenFilesInDir(dirPath string, fFilter fileFilter) ([]*os.File, error) {
+ dfi, err := os.Open(dirPath)
+ if err != nil {
+ return nil, newCannotOpenFileError("Cannot open directory " + dirPath)
+ }
+ defer dfi.Close()
+ // Size of read buffer (i.e. chunk of items read at a time).
+ rbs := 64
+ resFiles := []*os.File{}
+L:
+ for {
+ // Read directory entities by reasonable chuncks
+ // to prevent overflows on big number of files.
+ fis, e := dfi.Readdir(rbs)
+ switch e {
+ // It's OK.
+ case nil:
+ // Do nothing, just continue cycle.
+ case io.EOF:
+ break L
+ // Something went wrong.
+ default:
+ return nil, e
+ }
+ // THINK: Maybe, use async running.
+ for _, fi := range fis {
+ // NB: On Linux this could be a problem as
+ // there are lots of file types available.
+ if !fi.IsDir() {
+ f, e := os.Open(filepath.Join(dirPath, fi.Name()))
+ if e != nil {
+ if f != nil {
+ f.Close()
+ }
+ // THINK: Add nil as indicator that a problem occurred.
+ resFiles = append(resFiles, nil)
+ continue
+ }
+ // Check filter condition.
+ if fFilter != nil && !fFilter(fi, f) {
+ continue
+ }
+ resFiles = append(resFiles, f)
+ }
+ }
+ }
+ return resFiles, nil
+}
+
+func isRegular(m os.FileMode) bool {
+ return m&os.ModeType == 0
+}
+
+// getDirFilePaths return full paths of the files located in the directory.
+// Remark: Ignores files for which fileFilter returns false.
+func getDirFilePaths(dirPath string, fpFilter filePathFilter, pathIsName bool) ([]string, error) {
+ dfi, err := os.Open(dirPath)
+ if err != nil {
+ return nil, newCannotOpenFileError("Cannot open directory " + dirPath)
+ }
+ defer dfi.Close()
+
+ var absDirPath string
+ if !filepath.IsAbs(dirPath) {
+ absDirPath, err = filepath.Abs(dirPath)
+ if err != nil {
+ return nil, fmt.Errorf("cannot get absolute path of directory: %s", err.Error())
+ }
+ } else {
+ absDirPath = dirPath
+ }
+
+ // TODO: check if dirPath is really directory.
+ // Size of read buffer (i.e. chunk of items read at a time).
+ rbs := 2 << 5
+ filePaths := []string{}
+
+ var fp string
+L:
+ for {
+ // Read directory entities by reasonable chuncks
+ // to prevent overflows on big number of files.
+ fis, e := dfi.Readdir(rbs)
+ switch e {
+ // It's OK.
+ case nil:
+ // Do nothing, just continue cycle.
+ case io.EOF:
+ break L
+ // Indicate that something went wrong.
+ default:
+ return nil, e
+ }
+ // THINK: Maybe, use async running.
+ for _, fi := range fis {
+ // NB: Should work on every Windows and non-Windows OS.
+ if isRegular(fi.Mode()) {
+ if pathIsName {
+ fp = fi.Name()
+ } else {
+ // Build full path of a file.
+ fp = filepath.Join(absDirPath, fi.Name())
+ }
+ // Check filter condition.
+ if fpFilter != nil && !fpFilter(fp) {
+ continue
+ }
+ filePaths = append(filePaths, fp)
+ }
+ }
+ }
+ return filePaths, nil
+}
+
+// getOpenFilesByDirectoryAsync runs async reading directories 'dirPaths' and inserts pairs
+// in map 'filesInDirMap': Key - directory name, value - *os.File slice.
+func getOpenFilesByDirectoryAsync(
+ dirPaths []string,
+ fFilter fileFilter,
+ filesInDirMap map[string][]*os.File,
+) error {
+ n := len(dirPaths)
+ if n > maxDirNumberReadAsync {
+ return fmt.Errorf("number of input directories to be read exceeded max value %d", maxDirNumberReadAsync)
+ }
+ type filesInDirResult struct {
+ DirName string
+ Files []*os.File
+ Error error
+ }
+ dirFilesChan := make(chan *filesInDirResult, n)
+ var wg sync.WaitGroup
+ // Register n goroutines which are going to do work.
+ wg.Add(n)
+ for i := 0; i < n; i++ {
+ // Launch asynchronously the piece of work.
+ go func(dirPath string) {
+ fs, e := getOpenFilesInDir(dirPath, fFilter)
+ dirFilesChan <- &filesInDirResult{filepath.Base(dirPath), fs, e}
+ // Mark the current goroutine as finished (work is done).
+ wg.Done()
+ }(dirPaths[i])
+ }
+ // Wait for all goroutines to finish their work.
+ wg.Wait()
+ // Close the error channel to let for-range clause
+ // get all the buffered values without blocking and quit in the end.
+ close(dirFilesChan)
+ for fidr := range dirFilesChan {
+ if fidr.Error == nil {
+ // THINK: What will happen if the key is already present?
+ filesInDirMap[fidr.DirName] = fidr.Files
+ } else {
+ return fidr.Error
+ }
+ }
+ return nil
+}
+
+// fileExists return flag whether a given file exists
+// and operation error if an unclassified failure occurs.
+func fileExists(path string) (bool, error) {
+ _, err := os.Stat(path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return false, nil
+ }
+ return false, err
+ }
+ return true, nil
+}
+
+// createDirectory makes directory with a given name
+// making all parent directories if necessary.
+func createDirectory(dirPath string) error {
+ var dPath string
+ var err error
+ if !filepath.IsAbs(dirPath) {
+ dPath, err = filepath.Abs(dirPath)
+ if err != nil {
+ return err
+ }
+ } else {
+ dPath = dirPath
+ }
+ exists, err := fileExists(dPath)
+ if err != nil {
+ return err
+ }
+ if exists {
+ return nil
+ }
+ return os.MkdirAll(dPath, os.ModeDir)
+}
+
+// tryRemoveFile gives a try removing the file
+// only ignoring an error when the file does not exist.
+func tryRemoveFile(filePath string) (err error) {
+ err = os.Remove(filePath)
+ if os.IsNotExist(err) {
+ err = nil
+ return
+ }
+ return
+}
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/internals_xmlnode.go
----------------------------------------------------------------------
diff --git a/traffic_stats/vendor/github.com/cihub/seelog b/traffic_stats/vendor/github.com/cihub/seelog
deleted file mode 160000
index 175e6e3..0000000
--- a/traffic_stats/vendor/github.com/cihub/seelog
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 175e6e3d439fe2e1cee7ab652b12eb546c145a13
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/internals_xmlnode.go b/traffic_stats/vendor/github.com/cihub/seelog/internals_xmlnode.go
new file mode 100644
index 0000000..9858849
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/internals_xmlnode.go
@@ -0,0 +1,175 @@
+// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package seelog
+
+import (
+ "encoding/xml"
+ "errors"
+ "fmt"
+ "io"
+ "strings"
+)
+
+type xmlNode struct {
+ name string
+ attributes map[string]string
+ children []*xmlNode
+ value string
+}
+
+func newNode() *xmlNode {
+ node := new(xmlNode)
+ node.children = make([]*xmlNode, 0)
+ node.attributes = make(map[string]string)
+ return node
+}
+
+func (node *xmlNode) String() string {
+ str := fmt.Sprintf("<%s", node.name)
+
+ for attrName, attrVal := range node.attributes {
+ str += fmt.Sprintf(" %s=\"%s\"", attrName, attrVal)
+ }
+
+ str += ">"
+ str += node.value
+
+ if len(node.children) != 0 {
+ for _, child := range node.children {
+ str += fmt.Sprintf("%s", child)
+ }
+ }
+
+ str += fmt.Sprintf("</%s>", node.name)
+
+ return str
+}
+
+func (node *xmlNode) unmarshal(startEl xml.StartElement) error {
+ node.name = startEl.Name.Local
+
+ for _, v := range startEl.Attr {
+ _, alreadyExists := node.attributes[v.Name.Local]
+ if alreadyExists {
+ return errors.New("tag '" + node.name + "' has duplicated attribute: '" + v.Name.Local + "'")
+ }
+ node.attributes[v.Name.Local] = v.Value
+ }
+
+ return nil
+}
+
+func (node *xmlNode) add(child *xmlNode) {
+ if node.children == nil {
+ node.children = make([]*xmlNode, 0)
+ }
+
+ node.children = append(node.children, child)
+}
+
+func (node *xmlNode) hasChildren() bool {
+ return node.children != nil && len(node.children) > 0
+}
+
+//=============================================
+
+func unmarshalConfig(reader io.Reader) (*xmlNode, error) {
+ xmlParser := xml.NewDecoder(reader)
+
+ config, err := unmarshalNode(xmlParser, nil)
+ if err != nil {
+ return nil, err
+ }
+ if config == nil {
+ return nil, errors.New("xml has no content")
+ }
+
+ nextConfigEntry, err := unmarshalNode(xmlParser, nil)
+ if nextConfigEntry != nil {
+ return nil, errors.New("xml contains more than one root element")
+ }
+
+ return config, nil
+}
+
+func unmarshalNode(xmlParser *xml.Decoder, curToken xml.Token) (node *xmlNode, err error) {
+ firstLoop := true
+ for {
+ var tok xml.Token
+ if firstLoop && curToken != nil {
+ tok = curToken
+ firstLoop = false
+ } else {
+ tok, err = getNextToken(xmlParser)
+ if err != nil || tok == nil {
+ return
+ }
+ }
+
+ switch tt := tok.(type) {
+ case xml.SyntaxError:
+ err = errors.New(tt.Error())
+ return
+ case xml.CharData:
+ value := strings.TrimSpace(string([]byte(tt)))
+ if node != nil {
+ node.value += value
+ }
+ case xml.StartElement:
+ if node == nil {
+ node = newNode()
+ err := node.unmarshal(tt)
+ if err != nil {
+ return nil, err
+ }
+ } else {
+ childNode, childErr := unmarshalNode(xmlParser, tok)
+ if childErr != nil {
+ return nil, childErr
+ }
+
+ if childNode != nil {
+ node.add(childNode)
+ } else {
+ return
+ }
+ }
+ case xml.EndElement:
+ return
+ }
+ }
+}
+
+func getNextToken(xmlParser *xml.Decoder) (tok xml.Token, err error) {
+ if tok, err = xmlParser.Token(); err != nil {
+ if err == io.EOF {
+ err = nil
+ return
+ }
+ return
+ }
+
+ return
+}
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/internals_xmlnode_test.go
----------------------------------------------------------------------
diff --git a/traffic_stats/vendor/github.com/cihub/seelog b/traffic_stats/vendor/github.com/cihub/seelog
deleted file mode 160000
index 175e6e3..0000000
--- a/traffic_stats/vendor/github.com/cihub/seelog
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 175e6e3d439fe2e1cee7ab652b12eb546c145a13
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/internals_xmlnode_test.go b/traffic_stats/vendor/github.com/cihub/seelog/internals_xmlnode_test.go
new file mode 100644
index 0000000..2655002
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/internals_xmlnode_test.go
@@ -0,0 +1,196 @@
+// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package seelog
+
+import (
+ "strings"
+ "testing"
+ //"fmt"
+ "reflect"
+)
+
+var testEnv *testing.T
+
+/*func TestWrapper(t *testing.T) {
+ testEnv = t
+
+ s := "<a d='a'><g m='a'></g><g h='t' j='kk'></g></a>"
+ reader := strings.NewReader(s)
+ config, err := unmarshalConfig(reader)
+ if err != nil {
+ testEnv.Error(err)
+ return
+ }
+
+ printXML(config, 0)
+}
+
+func printXML(node *xmlNode, level int) {
+ indent := strings.Repeat("\t", level)
+ fmt.Print(indent + node.name)
+ for key, value := range node.attributes {
+ fmt.Print(" " + key + "/" + value)
+ }
+ fmt.Println()
+
+ for _, child := range node.children {
+ printXML(child, level+1)
+ }
+}*/
+
+var xmlNodeTests []xmlNodeTest
+
+type xmlNodeTest struct {
+ testName string
+ inputXML string
+ expected interface{}
+ errorExpected bool
+}
+
+func getXMLTests() []xmlNodeTest {
+ if xmlNodeTests == nil {
+ xmlNodeTests = make([]xmlNodeTest, 0)
+
+ testName := "Simple test"
+ testXML := `<a></a>`
+ testExpected := newNode()
+ testExpected.name = "a"
+ xmlNodeTests = append(xmlNodeTests, xmlNodeTest{testName, testXML, testExpected, false})
+
+ testName = "Multiline test"
+ testXML =
+ `
+<a>
+</a>
+`
+ testExpected = newNode()
+ testExpected.name = "a"
+ xmlNodeTests = append(xmlNodeTests, xmlNodeTest{testName, testXML, testExpected, false})
+
+ testName = "Multiline test #2"
+ testXML =
+ `
+
+
+<a>
+
+</a>
+
+`
+ testExpected = newNode()
+ testExpected.name = "a"
+ xmlNodeTests = append(xmlNodeTests, xmlNodeTest{testName, testXML, testExpected, false})
+
+ testName = "Incorrect names"
+ testXML = `< a >< /a >`
+ xmlNodeTests = append(xmlNodeTests, xmlNodeTest{testName, testXML, nil, true})
+
+ testName = "Comments"
+ testXML =
+ `<!-- <abcdef/> -->
+<a> <!-- 12345-->
+</a>
+`
+ testExpected = newNode()
+ testExpected.name = "a"
+ xmlNodeTests = append(xmlNodeTests, xmlNodeTest{testName, testXML, testExpected, false})
+
+ testName = "Multiple roots"
+ testXML = `<a></a><b></b>`
+ xmlNodeTests = append(xmlNodeTests, xmlNodeTest{testName, testXML, nil, true})
+
+ testName = "Multiple roots + incorrect xml"
+ testXML = `<a></a><b>`
+ xmlNodeTests = append(xmlNodeTests, xmlNodeTest{testName, testXML, nil, true})
+
+ testName = "Some unicode and data"
+ testXML = `<\u4fc4\u8bed>\u0434\u0430\u043d\u043d\u044b\u0435</\u4fc4\u8bed>`
+ testExpected = newNode()
+ testExpected.name = "\u4fc4\u8bed"
+ testExpected.value = "\u0434\u0430\u043d\u043d\u044b\u0435"
+ xmlNodeTests = append(xmlNodeTests, xmlNodeTest{testName, testXML, testExpected, false})
+
+ testName = "Values and children"
+ testXML = `<\u4fc4\u8bed>\u0434\u0430\u043d\u043d\u044b\u0435<and_a_child></and_a_child></\u4fc4\u8bed>`
+ testExpected = newNode()
+ testExpected.name = "\u4fc4\u8bed"
+ testExpected.value = "\u0434\u0430\u043d\u043d\u044b\u0435"
+ child := newNode()
+ child.name = "and_a_child"
+ testExpected.children = append(testExpected.children, child)
+ xmlNodeTests = append(xmlNodeTests, xmlNodeTest{testName, testXML, testExpected, false})
+
+ testName = "Just children"
+ testXML = `<\u4fc4\u8bed><and_a_child></and_a_child></\u4fc4\u8bed>`
+ testExpected = newNode()
+ testExpected.name = "\u4fc4\u8bed"
+ child = newNode()
+ child.name = "and_a_child"
+ testExpected.children = append(testExpected.children, child)
+ xmlNodeTests = append(xmlNodeTests, xmlNodeTest{testName, testXML, testExpected, false})
+
+ testName = "Mixed test"
+ testXML = `<\u4fc4\u8bed a="1" b="2.13" c="abc"><child abc="bca"/><child abc="def"></child></\u4fc4\u8bed>`
+ testExpected = newNode()
+ testExpected.name = "\u4fc4\u8bed"
+ testExpected.attributes["a"] = "1"
+ testExpected.attributes["b"] = "2.13"
+ testExpected.attributes["c"] = "abc"
+ child = newNode()
+ child.name = "child"
+ child.attributes["abc"] = "bca"
+ testExpected.children = append(testExpected.children, child)
+ child = newNode()
+ child.name = "child"
+ child.attributes["abc"] = "def"
+ testExpected.children = append(testExpected.children, child)
+ xmlNodeTests = append(xmlNodeTests, xmlNodeTest{testName, testXML, testExpected, false})
+ }
+
+ return xmlNodeTests
+}
+
+func TestXmlNode(t *testing.T) {
+
+ for _, test := range getXMLTests() {
+
+ reader := strings.NewReader(test.inputXML)
+ parsedXML, err := unmarshalConfig(reader)
+
+ if (err != nil) != test.errorExpected {
+ t.Errorf("\n%s:\nXML input: %s\nExpected error:%t. Got error: %t\n", test.testName,
+ test.inputXML, test.errorExpected, (err != nil))
+ if err != nil {
+ t.Logf("%s\n", err.Error())
+ }
+ continue
+ }
+
+ if err == nil && !reflect.DeepEqual(parsedXML, test.expected) {
+ t.Errorf("\n%s:\nXML input: %s\nExpected: %s. \nGot: %s\n", test.testName,
+ test.inputXML, test.expected, parsedXML)
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/io/iotest/iotest.go
----------------------------------------------------------------------
diff --git a/traffic_stats/vendor/github.com/cihub/seelog b/traffic_stats/vendor/github.com/cihub/seelog
deleted file mode 160000
index 175e6e3..0000000
--- a/traffic_stats/vendor/github.com/cihub/seelog
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 175e6e3d439fe2e1cee7ab652b12eb546c145a13
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/io/iotest/iotest.go b/traffic_stats/vendor/github.com/cihub/seelog/io/iotest/iotest.go
new file mode 100644
index 0000000..c73a567
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/io/iotest/iotest.go
@@ -0,0 +1,38 @@
+package iotest
+
+import (
+ "bytes"
+ "io"
+ "io/ioutil"
+ "os"
+ "testing"
+)
+
+// TempFile creates a new temporary file for testing and returns the file
+// pointer and a cleanup function for closing and removing the file.
+func TempFile(t *testing.T) (*os.File, func()) {
+ f, err := ioutil.TempFile("", "test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ return f, func() {
+ os.Remove(f.Name())
+ f.Close()
+ }
+}
+
+// FileInfo computes the os.FileInfo for a given byte slice.
+func FileInfo(t *testing.T, fbytes []byte) os.FileInfo {
+ // Get FileInfo
+ f, clean := TempFile(t)
+ defer clean()
+ _, err := io.Copy(f, bytes.NewReader(fbytes))
+ if err != nil {
+ t.Fatalf("copy to temp file: %v", err)
+ }
+ fi, err := f.Stat()
+ if err != nil {
+ t.Fatalf("stat temp file: %v", err)
+ }
+ return fi
+}
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/io/iotest/iotest_test.go
----------------------------------------------------------------------
diff --git a/traffic_stats/vendor/github.com/cihub/seelog b/traffic_stats/vendor/github.com/cihub/seelog
deleted file mode 160000
index 175e6e3..0000000
--- a/traffic_stats/vendor/github.com/cihub/seelog
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 175e6e3d439fe2e1cee7ab652b12eb546c145a13
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/io/iotest/iotest_test.go b/traffic_stats/vendor/github.com/cihub/seelog/io/iotest/iotest_test.go
new file mode 100644
index 0000000..a2d1bec
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/io/iotest/iotest_test.go
@@ -0,0 +1,43 @@
+package iotest
+
+import (
+ "os"
+ "syscall"
+ "testing"
+)
+
+func TestTempFile(t *testing.T) {
+ f, cleanup := TempFile(t)
+ if _, err := f.Write([]byte("test")); err != nil {
+ t.Fatalf("temp file not writable: %v", err)
+ }
+ cleanup()
+ // Confirm closed
+
+ if err := f.Close(); err != syscall.EINVAL {
+ t.Errorf("temp file was not closed by cleanup func")
+ }
+ if _, err := os.Stat(f.Name()); !os.IsNotExist(err) {
+ t.Errorf("temp file was not removed by cleanup func")
+ }
+}
+
+var finfoTests = map[string]string{
+ "empty": "",
+ "non-empty": "I am a log file",
+}
+
+func TestFileInfo(t *testing.T) {
+ for name, in := range finfoTests {
+ got := FileInfo(t, []byte(in))
+ testEqual(t, name, "size", got.Size(), int64(len(in)))
+ testEqual(t, name, "mode", got.Mode(), os.FileMode(0600))
+ testEqual(t, name, "isDir", got.IsDir(), false)
+ }
+}
+
+func testEqual(t *testing.T, name, field string, got, want interface{}) {
+ if got != want {
+ t.Errorf("%s: incorrect %v: got %q but want %q", name, field, got, want)
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/log.go
----------------------------------------------------------------------
diff --git a/traffic_stats/vendor/github.com/cihub/seelog b/traffic_stats/vendor/github.com/cihub/seelog
deleted file mode 160000
index 175e6e3..0000000
--- a/traffic_stats/vendor/github.com/cihub/seelog
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 175e6e3d439fe2e1cee7ab652b12eb546c145a13
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/log.go b/traffic_stats/vendor/github.com/cihub/seelog/log.go
new file mode 100644
index 0000000..f775e1f
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/log.go
@@ -0,0 +1,307 @@
+// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package seelog
+
+import (
+ "errors"
+ "fmt"
+ "sync"
+ "time"
+)
+
+const (
+ staticFuncCallDepth = 3 // See 'commonLogger.log' method comments
+ loggerFuncCallDepth = 3
+)
+
+// Current is the logger used in all package level convenience funcs like 'Trace', 'Debug', 'Flush', etc.
+var Current LoggerInterface
+
+// Default logger that is created from an empty config: "<seelog/>". It is not closed by a ReplaceLogger call.
+var Default LoggerInterface
+
+// Disabled logger that doesn't produce any output in any circumstances. It is neither closed nor flushed by a ReplaceLogger call.
+var Disabled LoggerInterface
+
+var pkgOperationsMutex *sync.Mutex
+
+func init() {
+ pkgOperationsMutex = new(sync.Mutex)
+ var err error
+
+ if Default == nil {
+ Default, err = LoggerFromConfigAsBytes([]byte("<seelog />"))
+ }
+
+ if Disabled == nil {
+ Disabled, err = LoggerFromConfigAsBytes([]byte("<seelog levels=\"off\"/>"))
+ }
+
+ if err != nil {
+ panic(fmt.Sprintf("Seelog couldn't start. Error: %s", err.Error()))
+ }
+
+ Current = Default
+}
+
+func createLoggerFromFullConfig(config *configForParsing) (LoggerInterface, error) {
+ if config.LogType == syncloggerTypeFromString {
+ return NewSyncLogger(&config.logConfig), nil
+ } else if config.LogType == asyncLooploggerTypeFromString {
+ return NewAsyncLoopLogger(&config.logConfig), nil
+ } else if config.LogType == asyncTimerloggerTypeFromString {
+ logData := config.LoggerData
+ if logData == nil {
+ return nil, errors.New("async timer data not set")
+ }
+
+ asyncInt, ok := logData.(asyncTimerLoggerData)
+ if !ok {
+ return nil, errors.New("invalid async timer data")
+ }
+
+ logger, err := NewAsyncTimerLogger(&config.logConfig, time.Duration(asyncInt.AsyncInterval))
+ if !ok {
+ return nil, err
+ }
+
+ return logger, nil
+ } else if config.LogType == adaptiveLoggerTypeFromString {
+ logData := config.LoggerData
+ if logData == nil {
+ return nil, errors.New("adaptive logger parameters not set")
+ }
+
+ adaptData, ok := logData.(adaptiveLoggerData)
+ if !ok {
+ return nil, errors.New("invalid adaptive logger parameters")
+ }
+
+ logger, err := NewAsyncAdaptiveLogger(
+ &config.logConfig,
+ time.Duration(adaptData.MinInterval),
+ time.Duration(adaptData.MaxInterval),
+ adaptData.CriticalMsgCount,
+ )
+ if err != nil {
+ return nil, err
+ }
+
+ return logger, nil
+ }
+ return nil, errors.New("invalid config log type/data")
+}
+
+// UseLogger sets the 'Current' package level logger variable to the specified value.
+// This variable is used in all Trace/Debug/... package level convenience funcs.
+//
+// Example:
+//
+// after calling
+// seelog.UseLogger(somelogger)
+// the following:
+// seelog.Debug("abc")
+// will be equal to
+// somelogger.Debug("abc")
+//
+// IMPORTANT: UseLogger do NOT close the previous logger (only flushes it). So if
+// you constantly use it to replace loggers and don't close them in other code, you'll
+// end up having memory leaks.
+//
+// To safely replace loggers, use ReplaceLogger.
+func UseLogger(logger LoggerInterface) error {
+ if logger == nil {
+ return errors.New("logger can not be nil")
+ }
+
+ pkgOperationsMutex.Lock()
+ defer pkgOperationsMutex.Unlock()
+
+ oldLogger := Current
+ Current = logger
+
+ if oldLogger != nil {
+ oldLogger.Flush()
+ }
+
+ return nil
+}
+
+// ReplaceLogger acts as UseLogger but the logger that was previously
+// used is disposed (except Default and Disabled loggers).
+//
+// Example:
+// import log "github.com/cihub/seelog"
+//
+// func main() {
+// logger, err := log.LoggerFromConfigAsFile("seelog.xml")
+//
+// if err != nil {
+// panic(err)
+// }
+//
+// log.ReplaceLogger(logger)
+// defer log.Flush()
+//
+// log.Trace("test")
+// log.Debugf("var = %s", "abc")
+// }
+func ReplaceLogger(logger LoggerInterface) error {
+ if logger == nil {
+ return errors.New("logger can not be nil")
+ }
+
+ pkgOperationsMutex.Lock()
+ defer pkgOperationsMutex.Unlock()
+
+ defer func() {
+ if err := recover(); err != nil {
+ reportInternalError(fmt.Errorf("recovered from panic during ReplaceLogger: %s", err))
+ }
+ }()
+
+ if Current == Default {
+ Current.Flush()
+ } else if Current != nil && !Current.Closed() && Current != Disabled {
+ Current.Flush()
+ Current.Close()
+ }
+
+ Current = logger
+
+ return nil
+}
+
+// Tracef formats message according to format specifier
+// and writes to default logger with log level = Trace.
+func Tracef(format string, params ...interface{}) {
+ pkgOperationsMutex.Lock()
+ defer pkgOperationsMutex.Unlock()
+ Current.traceWithCallDepth(staticFuncCallDepth, newLogFormattedMessage(format, params))
+}
+
+// Debugf formats message according to format specifier
+// and writes to default logger with log level = Debug.
+func Debugf(format string, params ...interface{}) {
+ pkgOperationsMutex.Lock()
+ defer pkgOperationsMutex.Unlock()
+ Current.debugWithCallDepth(staticFuncCallDepth, newLogFormattedMessage(format, params))
+}
+
+// Infof formats message according to format specifier
+// and writes to default logger with log level = Info.
+func Infof(format string, params ...interface{}) {
+ pkgOperationsMutex.Lock()
+ defer pkgOperationsMutex.Unlock()
+ Current.infoWithCallDepth(staticFuncCallDepth, newLogFormattedMessage(format, params))
+}
+
+// Warnf formats message according to format specifier and writes to default logger with log level = Warn
+func Warnf(format string, params ...interface{}) error {
+ pkgOperationsMutex.Lock()
+ defer pkgOperationsMutex.Unlock()
+ message := newLogFormattedMessage(format, params)
+ Current.warnWithCallDepth(staticFuncCallDepth, message)
+ return errors.New(message.String())
+}
+
+// Errorf formats message according to format specifier and writes to default logger with log level = Error
+func Errorf(format string, params ...interface{}) error {
+ pkgOperationsMutex.Lock()
+ defer pkgOperationsMutex.Unlock()
+ message := newLogFormattedMessage(format, params)
+ Current.errorWithCallDepth(staticFuncCallDepth, message)
+ return errors.New(message.String())
+}
+
+// Criticalf formats message according to format specifier and writes to default logger with log level = Critical
+func Criticalf(format string, params ...interface{}) error {
+ pkgOperationsMutex.Lock()
+ defer pkgOperationsMutex.Unlock()
+ message := newLogFormattedMessage(format, params)
+ Current.criticalWithCallDepth(staticFuncCallDepth, message)
+ return errors.New(message.String())
+}
+
+// Trace formats message using the default formats for its operands and writes to default logger with log level = Trace
+func Trace(v ...interface{}) {
+ pkgOperationsMutex.Lock()
+ defer pkgOperationsMutex.Unlock()
+ Current.traceWithCallDepth(staticFuncCallDepth, newLogMessage(v))
+}
+
+// Debug formats message using the default formats for its operands and writes to default logger with log level = Debug
+func Debug(v ...interface{}) {
+ pkgOperationsMutex.Lock()
+ defer pkgOperationsMutex.Unlock()
+ Current.debugWithCallDepth(staticFuncCallDepth, newLogMessage(v))
+}
+
+// Info formats message using the default formats for its operands and writes to default logger with log level = Info
+func Info(v ...interface{}) {
+ pkgOperationsMutex.Lock()
+ defer pkgOperationsMutex.Unlock()
+ Current.infoWithCallDepth(staticFuncCallDepth, newLogMessage(v))
+}
+
+// Warn formats message using the default formats for its operands and writes to default logger with log level = Warn
+func Warn(v ...interface{}) error {
+ pkgOperationsMutex.Lock()
+ defer pkgOperationsMutex.Unlock()
+ message := newLogMessage(v)
+ Current.warnWithCallDepth(staticFuncCallDepth, message)
+ return errors.New(message.String())
+}
+
+// Error formats message using the default formats for its operands and writes to default logger with log level = Error
+func Error(v ...interface{}) error {
+ pkgOperationsMutex.Lock()
+ defer pkgOperationsMutex.Unlock()
+ message := newLogMessage(v)
+ Current.errorWithCallDepth(staticFuncCallDepth, message)
+ return errors.New(message.String())
+}
+
+// Critical formats message using the default formats for its operands and writes to default logger with log level = Critical
+func Critical(v ...interface{}) error {
+ pkgOperationsMutex.Lock()
+ defer pkgOperationsMutex.Unlock()
+ message := newLogMessage(v)
+ Current.criticalWithCallDepth(staticFuncCallDepth, message)
+ return errors.New(message.String())
+}
+
+// Flush immediately processes all currently queued messages and all currently buffered messages.
+// It is a blocking call which returns only after the queue is empty and all the buffers are empty.
+//
+// If Flush is called for a synchronous logger (type='sync'), it only flushes buffers (e.g. '<buffered>' receivers)
+// , because there is no queue.
+//
+// Call this method when your app is going to shut down not to lose any log messages.
+func Flush() {
+ pkgOperationsMutex.Lock()
+ defer pkgOperationsMutex.Unlock()
+ Current.Flush()
+}
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/logger.go
----------------------------------------------------------------------
diff --git a/traffic_stats/vendor/github.com/cihub/seelog b/traffic_stats/vendor/github.com/cihub/seelog
deleted file mode 160000
index 175e6e3..0000000
--- a/traffic_stats/vendor/github.com/cihub/seelog
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 175e6e3d439fe2e1cee7ab652b12eb546c145a13
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/logger.go b/traffic_stats/vendor/github.com/cihub/seelog/logger.go
new file mode 100644
index 0000000..fc96aed
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/logger.go
@@ -0,0 +1,370 @@
+// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package seelog
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "sync"
+)
+
+func reportInternalError(err error) {
+ fmt.Fprintf(os.Stderr, "seelog internal error: %s\n", err)
+}
+
+// LoggerInterface represents structs capable of logging Seelog messages
+type LoggerInterface interface {
+
+ // Tracef formats message according to format specifier
+ // and writes to log with level = Trace.
+ Tracef(format string, params ...interface{})
+
+ // Debugf formats message according to format specifier
+ // and writes to log with level = Debug.
+ Debugf(format string, params ...interface{})
+
+ // Infof formats message according to format specifier
+ // and writes to log with level = Info.
+ Infof(format string, params ...interface{})
+
+ // Warnf formats message according to format specifier
+ // and writes to log with level = Warn.
+ Warnf(format string, params ...interface{}) error
+
+ // Errorf formats message according to format specifier
+ // and writes to log with level = Error.
+ Errorf(format string, params ...interface{}) error
+
+ // Criticalf formats message according to format specifier
+ // and writes to log with level = Critical.
+ Criticalf(format string, params ...interface{}) error
+
+ // Trace formats message using the default formats for its operands
+ // and writes to log with level = Trace
+ Trace(v ...interface{})
+
+ // Debug formats message using the default formats for its operands
+ // and writes to log with level = Debug
+ Debug(v ...interface{})
+
+ // Info formats message using the default formats for its operands
+ // and writes to log with level = Info
+ Info(v ...interface{})
+
+ // Warn formats message using the default formats for its operands
+ // and writes to log with level = Warn
+ Warn(v ...interface{}) error
+
+ // Error formats message using the default formats for its operands
+ // and writes to log with level = Error
+ Error(v ...interface{}) error
+
+ // Critical formats message using the default formats for its operands
+ // and writes to log with level = Critical
+ Critical(v ...interface{}) error
+
+ traceWithCallDepth(callDepth int, message fmt.Stringer)
+ debugWithCallDepth(callDepth int, message fmt.Stringer)
+ infoWithCallDepth(callDepth int, message fmt.Stringer)
+ warnWithCallDepth(callDepth int, message fmt.Stringer)
+ errorWithCallDepth(callDepth int, message fmt.Stringer)
+ criticalWithCallDepth(callDepth int, message fmt.Stringer)
+
+ // Close flushes all the messages in the logger and closes it. It cannot be used after this operation.
+ Close()
+
+ // Flush flushes all the messages in the logger.
+ Flush()
+
+ // Closed returns true if the logger was previously closed.
+ Closed() bool
+
+ // SetAdditionalStackDepth sets the additional number of frames to skip by runtime.Caller
+ // when getting function information needed to print seelog format identifiers such as %Func or %File.
+ //
+ // This func may be used when you wrap seelog funcs and want to print caller info of you own
+ // wrappers instead of seelog func callers. In this case you should set depth = 1. If you then
+ // wrap your wrapper, you should set depth = 2, etc.
+ //
+ // NOTE: Incorrect depth value may lead to errors in runtime.Caller evaluation or incorrect
+ // function/file names in log files. Do not use it if you are not going to wrap seelog funcs.
+ // You may reset the value to default using a SetAdditionalStackDepth(0) call.
+ SetAdditionalStackDepth(depth int) error
+
+ // Sets logger context that can be used in formatter funcs and custom receivers
+ SetContext(context interface{})
+}
+
+// innerLoggerInterface is an internal logging interface
+type innerLoggerInterface interface {
+ innerLog(level LogLevel, context LogContextInterface, message fmt.Stringer)
+ Flush()
+}
+
+// [file path][func name][level] -> [allowed]
+type allowedContextCache map[string]map[string]map[LogLevel]bool
+
+// commonLogger contains all common data needed for logging and contains methods used to log messages.
+type commonLogger struct {
+ config *logConfig // Config used for logging
+ contextCache allowedContextCache // Caches whether log is enabled for specific "full path-func name-level" sets
+ closed bool // 'true' when all writers are closed, all data is flushed, logger is unusable. Must be accessed while holding closedM
+ closedM sync.RWMutex
+ m sync.Mutex // Mutex for main operations
+ unusedLevels []bool
+ innerLogger innerLoggerInterface
+ addStackDepth int // Additional stack depth needed for correct seelog caller context detection
+ customContext interface{}
+}
+
+func newCommonLogger(config *logConfig, internalLogger innerLoggerInterface) *commonLogger {
+ cLogger := new(commonLogger)
+
+ cLogger.config = config
+ cLogger.contextCache = make(allowedContextCache)
+ cLogger.unusedLevels = make([]bool, Off)
+ cLogger.fillUnusedLevels()
+ cLogger.innerLogger = internalLogger
+
+ return cLogger
+}
+
+func (cLogger *commonLogger) SetAdditionalStackDepth(depth int) error {
+ if depth < 0 {
+ return fmt.Errorf("negative depth: %d", depth)
+ }
+ cLogger.m.Lock()
+ cLogger.addStackDepth = depth
+ cLogger.m.Unlock()
+ return nil
+}
+
+func (cLogger *commonLogger) Tracef(format string, params ...interface{}) {
+ cLogger.traceWithCallDepth(loggerFuncCallDepth, newLogFormattedMessage(format, params))
+}
+
+func (cLogger *commonLogger) Debugf(format string, params ...interface{}) {
+ cLogger.debugWithCallDepth(loggerFuncCallDepth, newLogFormattedMessage(format, params))
+}
+
+func (cLogger *commonLogger) Infof(format string, params ...interface{}) {
+ cLogger.infoWithCallDepth(loggerFuncCallDepth, newLogFormattedMessage(format, params))
+}
+
+func (cLogger *commonLogger) Warnf(format string, params ...interface{}) error {
+ message := newLogFormattedMessage(format, params)
+ cLogger.warnWithCallDepth(loggerFuncCallDepth, message)
+ return errors.New(message.String())
+}
+
+func (cLogger *commonLogger) Errorf(format string, params ...interface{}) error {
+ message := newLogFormattedMessage(format, params)
+ cLogger.errorWithCallDepth(loggerFuncCallDepth, message)
+ return errors.New(message.String())
+}
+
+func (cLogger *commonLogger) Criticalf(format string, params ...interface{}) error {
+ message := newLogFormattedMessage(format, params)
+ cLogger.criticalWithCallDepth(loggerFuncCallDepth, message)
+ return errors.New(message.String())
+}
+
+func (cLogger *commonLogger) Trace(v ...interface{}) {
+ cLogger.traceWithCallDepth(loggerFuncCallDepth, newLogMessage(v))
+}
+
+func (cLogger *commonLogger) Debug(v ...interface{}) {
+ cLogger.debugWithCallDepth(loggerFuncCallDepth, newLogMessage(v))
+}
+
+func (cLogger *commonLogger) Info(v ...interface{}) {
+ cLogger.infoWithCallDepth(loggerFuncCallDepth, newLogMessage(v))
+}
+
+func (cLogger *commonLogger) Warn(v ...interface{}) error {
+ message := newLogMessage(v)
+ cLogger.warnWithCallDepth(loggerFuncCallDepth, message)
+ return errors.New(message.String())
+}
+
+func (cLogger *commonLogger) Error(v ...interface{}) error {
+ message := newLogMessage(v)
+ cLogger.errorWithCallDepth(loggerFuncCallDepth, message)
+ return errors.New(message.String())
+}
+
+func (cLogger *commonLogger) Critical(v ...interface{}) error {
+ message := newLogMessage(v)
+ cLogger.criticalWithCallDepth(loggerFuncCallDepth, message)
+ return errors.New(message.String())
+}
+
+func (cLogger *commonLogger) SetContext(c interface{}) {
+ cLogger.customContext = c
+}
+
+func (cLogger *commonLogger) traceWithCallDepth(callDepth int, message fmt.Stringer) {
+ cLogger.log(TraceLvl, message, callDepth)
+}
+
+func (cLogger *commonLogger) debugWithCallDepth(callDepth int, message fmt.Stringer) {
+ cLogger.log(DebugLvl, message, callDepth)
+}
+
+func (cLogger *commonLogger) infoWithCallDepth(callDepth int, message fmt.Stringer) {
+ cLogger.log(InfoLvl, message, callDepth)
+}
+
+func (cLogger *commonLogger) warnWithCallDepth(callDepth int, message fmt.Stringer) {
+ cLogger.log(WarnLvl, message, callDepth)
+}
+
+func (cLogger *commonLogger) errorWithCallDepth(callDepth int, message fmt.Stringer) {
+ cLogger.log(ErrorLvl, message, callDepth)
+}
+
+func (cLogger *commonLogger) criticalWithCallDepth(callDepth int, message fmt.Stringer) {
+ cLogger.log(CriticalLvl, message, callDepth)
+ cLogger.innerLogger.Flush()
+}
+
+func (cLogger *commonLogger) Closed() bool {
+ cLogger.closedM.RLock()
+ defer cLogger.closedM.RUnlock()
+ return cLogger.closed
+}
+
+func (cLogger *commonLogger) fillUnusedLevels() {
+ for i := 0; i < len(cLogger.unusedLevels); i++ {
+ cLogger.unusedLevels[i] = true
+ }
+
+ cLogger.fillUnusedLevelsByContraint(cLogger.config.Constraints)
+
+ for _, exception := range cLogger.config.Exceptions {
+ cLogger.fillUnusedLevelsByContraint(exception)
+ }
+}
+
+func (cLogger *commonLogger) fillUnusedLevelsByContraint(constraint logLevelConstraints) {
+ for i := 0; i < len(cLogger.unusedLevels); i++ {
+ if constraint.IsAllowed(LogLevel(i)) {
+ cLogger.unusedLevels[i] = false
+ }
+ }
+}
+
+// stackCallDepth is used to indicate the call depth of 'log' func.
+// This depth level is used in the runtime.Caller(...) call. See
+// common_context.go -> specifyContext, extractCallerInfo for details.
+func (cLogger *commonLogger) log(level LogLevel, message fmt.Stringer, stackCallDepth int) {
+ if cLogger.unusedLevels[level] {
+ return
+ }
+ cLogger.m.Lock()
+ defer cLogger.m.Unlock()
+
+ if cLogger.Closed() {
+ return
+ }
+ context, _ := specifyContext(stackCallDepth+cLogger.addStackDepth, cLogger.customContext)
+ // Context errors are not reported because there are situations
+ // in which context errors are normal Seelog usage cases. For
+ // example in executables with stripped symbols.
+ // Error contexts are returned instead. See common_context.go.
+ /*if err != nil {
+ reportInternalError(err)
+ return
+ }*/
+ cLogger.innerLogger.innerLog(level, context, message)
+}
+
+func (cLogger *commonLogger) processLogMsg(level LogLevel, message fmt.Stringer, context LogContextInterface) {
+ defer func() {
+ if err := recover(); err != nil {
+ reportInternalError(fmt.Errorf("recovered from panic during message processing: %s", err))
+ }
+ }()
+ if cLogger.config.IsAllowed(level, context) {
+ cLogger.config.RootDispatcher.Dispatch(message.String(), level, context, reportInternalError)
+ }
+}
+
+func (cLogger *commonLogger) isAllowed(level LogLevel, context LogContextInterface) bool {
+ funcMap, ok := cLogger.contextCache[context.FullPath()]
+ if !ok {
+ funcMap = make(map[string]map[LogLevel]bool, 0)
+ cLogger.contextCache[context.FullPath()] = funcMap
+ }
+
+ levelMap, ok := funcMap[context.Func()]
+ if !ok {
+ levelMap = make(map[LogLevel]bool, 0)
+ funcMap[context.Func()] = levelMap
+ }
+
+ isAllowValue, ok := levelMap[level]
+ if !ok {
+ isAllowValue = cLogger.config.IsAllowed(level, context)
+ levelMap[level] = isAllowValue
+ }
+
+ return isAllowValue
+}
+
+type logMessage struct {
+ params []interface{}
+}
+
+type logFormattedMessage struct {
+ format string
+ params []interface{}
+}
+
+func newLogMessage(params []interface{}) fmt.Stringer {
+ message := new(logMessage)
+
+ message.params = params
+
+ return message
+}
+
+func newLogFormattedMessage(format string, params []interface{}) *logFormattedMessage {
+ message := new(logFormattedMessage)
+
+ message.params = params
+ message.format = format
+
+ return message
+}
+
+func (message *logMessage) String() string {
+ return fmt.Sprint(message.params...)
+}
+
+func (message *logFormattedMessage) String() string {
+ return fmt.Sprintf(message.format, message.params...)
+}
http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/writers_bufferedwriter.go
----------------------------------------------------------------------
diff --git a/traffic_stats/vendor/github.com/cihub/seelog b/traffic_stats/vendor/github.com/cihub/seelog
deleted file mode 160000
index 175e6e3..0000000
--- a/traffic_stats/vendor/github.com/cihub/seelog
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 175e6e3d439fe2e1cee7ab652b12eb546c145a13
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/writers_bufferedwriter.go b/traffic_stats/vendor/github.com/cihub/seelog/writers_bufferedwriter.go
new file mode 100644
index 0000000..37d75c8
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/writers_bufferedwriter.go
@@ -0,0 +1,161 @@
+// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+package seelog
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "io"
+ "sync"
+ "time"
+)
+
+// bufferedWriter stores data in memory and flushes it every flushPeriod or when buffer is full
+type bufferedWriter struct {
+ flushPeriod time.Duration // data flushes interval (in microseconds)
+ bufferMutex *sync.Mutex // mutex for buffer operations syncronization
+ innerWriter io.Writer // inner writer
+ buffer *bufio.Writer // buffered wrapper for inner writer
+ bufferSize int // max size of data chunk in bytes
+}
+
+// NewBufferedWriter creates a new buffered writer struct.
+// bufferSize -- size of memory buffer in bytes
+// flushPeriod -- period in which data flushes from memory buffer in milliseconds. 0 - turn off this functionality
+func NewBufferedWriter(innerWriter io.Writer, bufferSize int, flushPeriod time.Duration) (*bufferedWriter, error) {
+
+ if innerWriter == nil {
+ return nil, errors.New("argument is nil: innerWriter")
+ }
+ if flushPeriod < 0 {
+ return nil, fmt.Errorf("flushPeriod can not be less than 0. Got: %d", flushPeriod)
+ }
+
+ if bufferSize <= 0 {
+ return nil, fmt.Errorf("bufferSize can not be less or equal to 0. Got: %d", bufferSize)
+ }
+
+ buffer := bufio.NewWriterSize(innerWriter, bufferSize)
+
+ /*if err != nil {
+ return nil, err
+ }*/
+
+ newWriter := new(bufferedWriter)
+
+ newWriter.innerWriter = innerWriter
+ newWriter.buffer = buffer
+ newWriter.bufferSize = bufferSize
+ newWriter.flushPeriod = flushPeriod * 1e6
+ newWriter.bufferMutex = new(sync.Mutex)
+
+ if flushPeriod != 0 {
+ go newWriter.flushPeriodically()
+ }
+
+ return newWriter, nil
+}
+
+func (bufWriter *bufferedWriter) writeBigChunk(bytes []byte) (n int, err error) {
+ bufferedLen := bufWriter.buffer.Buffered()
+
+ n, err = bufWriter.flushInner()
+ if err != nil {
+ return
+ }
+
+ written, writeErr := bufWriter.innerWriter.Write(bytes)
+ return bufferedLen + written, writeErr
+}
+
+// Sends data to buffer manager. Waits until all buffers are full.
+func (bufWriter *bufferedWriter) Write(bytes []byte) (n int, err error) {
+
+ bufWriter.bufferMutex.Lock()
+ defer bufWriter.bufferMutex.Unlock()
+
+ bytesLen := len(bytes)
+
+ if bytesLen > bufWriter.bufferSize {
+ return bufWriter.writeBigChunk(bytes)
+ }
+
+ if bytesLen > bufWriter.buffer.Available() {
+ n, err = bufWriter.flushInner()
+ if err != nil {
+ return
+ }
+ }
+
+ bufWriter.buffer.Write(bytes)
+
+ return len(bytes), nil
+}
+
+func (bufWriter *bufferedWriter) Close() error {
+ closer, ok := bufWriter.innerWriter.(io.Closer)
+ if ok {
+ return closer.Close()
+ }
+
+ return nil
+}
+
+func (bufWriter *bufferedWriter) Flush() {
+
+ bufWriter.bufferMutex.Lock()
+ defer bufWriter.bufferMutex.Unlock()
+
+ bufWriter.flushInner()
+}
+
+func (bufWriter *bufferedWriter) flushInner() (n int, err error) {
+ bufferedLen := bufWriter.buffer.Buffered()
+ flushErr := bufWriter.buffer.Flush()
+
+ return bufWriter.buffer.Buffered() - bufferedLen, flushErr
+}
+
+func (bufWriter *bufferedWriter) flushBuffer() {
+ bufWriter.bufferMutex.Lock()
+ defer bufWriter.bufferMutex.Unlock()
+
+ bufWriter.buffer.Flush()
+}
+
+func (bufWriter *bufferedWriter) flushPeriodically() {
+ if bufWriter.flushPeriod > 0 {
+ ticker := time.NewTicker(bufWriter.flushPeriod)
+ for {
+ <-ticker.C
+ bufWriter.flushBuffer()
+ }
+ }
+}
+
+func (bufWriter *bufferedWriter) String() string {
+ return fmt.Sprintf("bufferedWriter size: %d, flushPeriod: %d", bufWriter.bufferSize, bufWriter.flushPeriod)
+}