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 &notDirectoryError{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)
+}