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:17 UTC

[06/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/writers_bufferedwriter_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/writers_bufferedwriter_test.go b/traffic_stats/vendor/github.com/cihub/seelog/writers_bufferedwriter_test.go
new file mode 100644
index 0000000..03f74f7
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/writers_bufferedwriter_test.go
@@ -0,0 +1,78 @@
+// 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 (
+	"testing"
+)
+
+func TestChunkWriteOnFilling(t *testing.T) {
+	writer, _ := newBytesVerifier(t)
+	bufferedWriter, err := NewBufferedWriter(writer, 1024, 0)
+
+	if err != nil {
+		t.Fatalf("Unexpected buffered writer creation error: %s", err.Error())
+	}
+
+	bytes := make([]byte, 1000)
+
+	bufferedWriter.Write(bytes)
+	writer.ExpectBytes(bytes)
+	bufferedWriter.Write(bytes)
+}
+
+func TestFlushByTimePeriod(t *testing.T) {
+	writer, _ := newBytesVerifier(t)
+	bufferedWriter, err := NewBufferedWriter(writer, 1024, 10)
+
+	if err != nil {
+		t.Fatalf("Unexpected buffered writer creation error: %s", err.Error())
+	}
+
+	bytes := []byte("Hello")
+
+	for i := 0; i < 2; i++ {
+		writer.ExpectBytes(bytes)
+		bufferedWriter.Write(bytes)
+	}
+}
+
+func TestBigMessageMustPassMemoryBuffer(t *testing.T) {
+	writer, _ := newBytesVerifier(t)
+	bufferedWriter, err := NewBufferedWriter(writer, 1024, 0)
+
+	if err != nil {
+		t.Fatalf("Unexpected buffered writer creation error: %s", err.Error())
+	}
+
+	bytes := make([]byte, 5000)
+
+	for i := 0; i < len(bytes); i++ {
+		bytes[i] = uint8(i % 255)
+	}
+
+	writer.ExpectBytes(bytes)
+	bufferedWriter.Write(bytes)
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/writers_connwriter.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_connwriter.go b/traffic_stats/vendor/github.com/cihub/seelog/writers_connwriter.go
new file mode 100644
index 0000000..d199894
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/writers_connwriter.go
@@ -0,0 +1,144 @@
+// 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 (
+	"crypto/tls"
+	"fmt"
+	"io"
+	"net"
+)
+
+// connWriter is used to write to a stream-oriented network connection.
+type connWriter struct {
+	innerWriter    io.WriteCloser
+	reconnectOnMsg bool
+	reconnect      bool
+	net            string
+	addr           string
+	useTLS         bool
+	configTLS      *tls.Config
+}
+
+// Creates writer to the address addr on the network netName.
+// Connection will be opened on each write if reconnectOnMsg = true
+func NewConnWriter(netName string, addr string, reconnectOnMsg bool) *connWriter {
+	newWriter := new(connWriter)
+
+	newWriter.net = netName
+	newWriter.addr = addr
+	newWriter.reconnectOnMsg = reconnectOnMsg
+
+	return newWriter
+}
+
+// Creates a writer that uses SSL/TLS
+func newTLSWriter(netName string, addr string, reconnectOnMsg bool, config *tls.Config) *connWriter {
+	newWriter := new(connWriter)
+
+	newWriter.net = netName
+	newWriter.addr = addr
+	newWriter.reconnectOnMsg = reconnectOnMsg
+	newWriter.useTLS = true
+	newWriter.configTLS = config
+
+	return newWriter
+}
+
+func (connWriter *connWriter) Close() error {
+	if connWriter.innerWriter == nil {
+		return nil
+	}
+
+	return connWriter.innerWriter.Close()
+}
+
+func (connWriter *connWriter) Write(bytes []byte) (n int, err error) {
+	if connWriter.neededConnectOnMsg() {
+		err = connWriter.connect()
+		if err != nil {
+			return 0, err
+		}
+	}
+
+	if connWriter.reconnectOnMsg {
+		defer connWriter.innerWriter.Close()
+	}
+
+	n, err = connWriter.innerWriter.Write(bytes)
+	if err != nil {
+		connWriter.reconnect = true
+	}
+
+	return
+}
+
+func (connWriter *connWriter) String() string {
+	return fmt.Sprintf("Conn writer: [%s, %s, %v]", connWriter.net, connWriter.addr, connWriter.reconnectOnMsg)
+}
+
+func (connWriter *connWriter) connect() error {
+	if connWriter.innerWriter != nil {
+		connWriter.innerWriter.Close()
+		connWriter.innerWriter = nil
+	}
+
+	if connWriter.useTLS {
+		conn, err := tls.Dial(connWriter.net, connWriter.addr, connWriter.configTLS)
+		if err != nil {
+			return err
+		}
+		connWriter.innerWriter = conn
+
+		return nil
+	}
+
+	conn, err := net.Dial(connWriter.net, connWriter.addr)
+	if err != nil {
+		return err
+	}
+
+	tcpConn, ok := conn.(*net.TCPConn)
+	if ok {
+		tcpConn.SetKeepAlive(true)
+	}
+
+	connWriter.innerWriter = conn
+
+	return nil
+}
+
+func (connWriter *connWriter) neededConnectOnMsg() bool {
+	if connWriter.reconnect {
+		connWriter.reconnect = false
+		return true
+	}
+
+	if connWriter.innerWriter == nil {
+		return true
+	}
+
+	return connWriter.reconnectOnMsg
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/writers_consolewriter.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_consolewriter.go b/traffic_stats/vendor/github.com/cihub/seelog/writers_consolewriter.go
new file mode 100644
index 0000000..3eb79af
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/writers_consolewriter.go
@@ -0,0 +1,47 @@
+// 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"
+
+// consoleWriter is used to write to console
+type consoleWriter struct {
+}
+
+// Creates a new console writer. Returns error, if the console writer couldn't be created.
+func NewConsoleWriter() (writer *consoleWriter, err error) {
+	newWriter := new(consoleWriter)
+
+	return newWriter, nil
+}
+
+// Create folder and file on WriteLog/Write first call
+func (console *consoleWriter) Write(bytes []byte) (int, error) {
+	return fmt.Print(string(bytes))
+}
+
+func (console *consoleWriter) String() string {
+	return "Console writer"
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/writers_filewriter.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_filewriter.go b/traffic_stats/vendor/github.com/cihub/seelog/writers_filewriter.go
new file mode 100644
index 0000000..8d3ae27
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/writers_filewriter.go
@@ -0,0 +1,92 @@
+// 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"
+	"io"
+	"os"
+	"path/filepath"
+)
+
+// fileWriter is used to write to a file.
+type fileWriter struct {
+	innerWriter io.WriteCloser
+	fileName    string
+}
+
+// Creates a new file and a corresponding writer. Returns error, if the file couldn't be created.
+func NewFileWriter(fileName string) (writer *fileWriter, err error) {
+	newWriter := new(fileWriter)
+	newWriter.fileName = fileName
+
+	return newWriter, nil
+}
+
+func (fw *fileWriter) Close() error {
+	if fw.innerWriter != nil {
+		err := fw.innerWriter.Close()
+		if err != nil {
+			return err
+		}
+		fw.innerWriter = nil
+	}
+	return nil
+}
+
+// Create folder and file on WriteLog/Write first call
+func (fw *fileWriter) Write(bytes []byte) (n int, err error) {
+	if fw.innerWriter == nil {
+		if err := fw.createFile(); err != nil {
+			return 0, err
+		}
+	}
+	return fw.innerWriter.Write(bytes)
+}
+
+func (fw *fileWriter) createFile() error {
+	folder, _ := filepath.Split(fw.fileName)
+	var err error
+
+	if 0 != len(folder) {
+		err = os.MkdirAll(folder, defaultDirectoryPermissions)
+		if err != nil {
+			return err
+		}
+	}
+
+	// If exists
+	fw.innerWriter, err = os.OpenFile(fw.fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, defaultFilePermissions)
+
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (fw *fileWriter) String() string {
+	return fmt.Sprintf("File writer: %s", fw.fileName)
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/writers_filewriter_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/writers_filewriter_test.go b/traffic_stats/vendor/github.com/cihub/seelog/writers_filewriter_test.go
new file mode 100644
index 0000000..f723912
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/writers_filewriter_test.go
@@ -0,0 +1,257 @@
+// 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"
+	"io"
+	"os"
+	"path/filepath"
+	"strings"
+	"testing"
+)
+
+const (
+	messageLen = 10
+)
+
+var bytesFileTest = []byte(strings.Repeat("A", messageLen))
+
+func TestSimpleFileWriter(t *testing.T) {
+	t.Logf("Starting file writer tests")
+	NewFileWriterTester(simplefileWriterTests, simplefileWriterGetter, t).test()
+}
+
+//===============================================================
+
+func simplefileWriterGetter(testCase *fileWriterTestCase) (io.WriteCloser, error) {
+	return NewFileWriter(testCase.fileName)
+}
+
+//===============================================================
+type fileWriterTestCase struct {
+	files           []string
+	fileName        string
+	rollingType     rollingType
+	fileSize        int64
+	maxRolls        int
+	datePattern     string
+	writeCount      int
+	resFiles        []string
+	nameMode        rollingNameMode
+	archiveType     rollingArchiveType
+	archiveExploded bool
+	archivePath     string
+}
+
+func createSimplefileWriterTestCase(fileName string, writeCount int) *fileWriterTestCase {
+	return &fileWriterTestCase{[]string{}, fileName, rollingTypeSize, 0, 0, "", writeCount, []string{fileName}, 0, rollingArchiveNone, false, ""}
+}
+
+var simplefileWriterTests = []*fileWriterTestCase{
+	createSimplefileWriterTestCase("log.testlog", 1),
+	createSimplefileWriterTestCase("log.testlog", 50),
+	createSimplefileWriterTestCase(filepath.Join("dir", "log.testlog"), 50),
+}
+
+//===============================================================
+
+type fileWriterTester struct {
+	testCases    []*fileWriterTestCase
+	writerGetter func(*fileWriterTestCase) (io.WriteCloser, error)
+	t            *testing.T
+}
+
+func NewFileWriterTester(
+	testCases []*fileWriterTestCase,
+	writerGetter func(*fileWriterTestCase) (io.WriteCloser, error),
+	t *testing.T) *fileWriterTester {
+
+	return &fileWriterTester{testCases, writerGetter, t}
+}
+
+func isWriterTestFile(fn string) bool {
+	return strings.Contains(fn, ".testlog") || strings.Contains(fn, ".zip") || strings.Contains(fn, ".gz")
+}
+
+func cleanupWriterTest(t *testing.T) {
+	toDel, err := getDirFilePaths(".", isWriterTestFile, true)
+	if nil != err {
+		t.Fatal("Cannot list files in test directory!")
+	}
+
+	for _, p := range toDel {
+		if err = tryRemoveFile(p); nil != err {
+			t.Errorf("cannot remove file %s in test directory: %s", p, err.Error())
+		}
+	}
+
+	if err = os.RemoveAll("dir"); nil != err {
+		t.Errorf("cannot remove temp test directory: %s", err.Error())
+	}
+}
+
+func getWriterTestResultFiles() ([]string, error) {
+	var p []string
+
+	visit := func(path string, f os.FileInfo, err error) error {
+		if !f.IsDir() && isWriterTestFile(path) {
+			abs, err := filepath.Abs(path)
+			if err != nil {
+				return fmt.Errorf("filepath.Abs failed for %s", path)
+			}
+
+			p = append(p, abs)
+		}
+
+		return nil
+	}
+
+	err := filepath.Walk(".", visit)
+	if nil != err {
+		return nil, err
+	}
+
+	return p, nil
+}
+
+func (tester *fileWriterTester) testCase(testCase *fileWriterTestCase, testNum int) {
+	defer cleanupWriterTest(tester.t)
+
+	tester.t.Logf("Start test  [%v]\n", testNum)
+
+	for _, filePath := range testCase.files {
+		dir, _ := filepath.Split(filePath)
+
+		var err error
+
+		if 0 != len(dir) {
+			err = os.MkdirAll(dir, defaultDirectoryPermissions)
+			if err != nil {
+				tester.t.Error(err)
+				return
+			}
+		}
+
+		fi, err := os.Create(filePath)
+		if err != nil {
+			tester.t.Error(err)
+			return
+		}
+
+		err = fi.Close()
+		if err != nil {
+			tester.t.Error(err)
+			return
+		}
+	}
+
+	fwc, err := tester.writerGetter(testCase)
+	if err != nil {
+		tester.t.Error(err)
+		return
+	}
+	defer fwc.Close()
+
+	tester.performWrite(fwc, testCase.writeCount)
+
+	files, err := getWriterTestResultFiles()
+	if err != nil {
+		tester.t.Error(err)
+		return
+	}
+
+	tester.checkRequiredFilesExist(testCase, files)
+	tester.checkJustRequiredFilesExist(testCase, files)
+
+}
+
+func (tester *fileWriterTester) test() {
+	for i, tc := range tester.testCases {
+		cleanupWriterTest(tester.t)
+		tester.testCase(tc, i)
+	}
+}
+
+func (tester *fileWriterTester) performWrite(fileWriter io.Writer, count int) {
+	for i := 0; i < count; i++ {
+		_, err := fileWriter.Write(bytesFileTest)
+
+		if err != nil {
+			tester.t.Error(err)
+			return
+		}
+	}
+}
+
+func (tester *fileWriterTester) checkRequiredFilesExist(testCase *fileWriterTestCase, files []string) {
+	var found bool
+	for _, expected := range testCase.resFiles {
+		found = false
+		exAbs, err := filepath.Abs(expected)
+		if err != nil {
+			tester.t.Errorf("filepath.Abs failed for %s", expected)
+			continue
+		}
+
+		for _, f := range files {
+			if af, e := filepath.Abs(f); e == nil {
+				tester.t.Log(af)
+				if exAbs == af {
+					found = true
+					break
+				}
+			} else {
+				tester.t.Errorf("filepath.Abs failed for %s", f)
+			}
+		}
+
+		if !found {
+			tester.t.Errorf("expected file: %s doesn't exist. Got %v\n", exAbs, files)
+		}
+	}
+}
+
+func (tester *fileWriterTester) checkJustRequiredFilesExist(testCase *fileWriterTestCase, files []string) {
+	for _, f := range files {
+		found := false
+		for _, expected := range testCase.resFiles {
+
+			exAbs, err := filepath.Abs(expected)
+			if err != nil {
+				tester.t.Errorf("filepath.Abs failed for %s", expected)
+			} else {
+				if exAbs == f {
+					found = true
+					break
+				}
+			}
+		}
+
+		if !found {
+			tester.t.Errorf("unexpected file: %v", f)
+		}
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/writers_formattedwriter.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_formattedwriter.go b/traffic_stats/vendor/github.com/cihub/seelog/writers_formattedwriter.go
new file mode 100644
index 0000000..bf44a41
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/writers_formattedwriter.go
@@ -0,0 +1,62 @@
+// 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"
+	"io"
+)
+
+type formattedWriter struct {
+	writer    io.Writer
+	formatter *formatter
+}
+
+func NewFormattedWriter(writer io.Writer, formatter *formatter) (*formattedWriter, error) {
+	if formatter == nil {
+		return nil, errors.New("formatter can not be nil")
+	}
+
+	return &formattedWriter{writer, formatter}, nil
+}
+
+func (formattedWriter *formattedWriter) Write(message string, level LogLevel, context LogContextInterface) error {
+	str := formattedWriter.formatter.Format(message, level, context)
+	_, err := formattedWriter.writer.Write([]byte(str))
+	return err
+}
+
+func (formattedWriter *formattedWriter) String() string {
+	return fmt.Sprintf("writer: %s, format: %s", formattedWriter.writer, formattedWriter.formatter)
+}
+
+func (formattedWriter *formattedWriter) Writer() io.Writer {
+	return formattedWriter.writer
+}
+
+func (formattedWriter *formattedWriter) Format() *formatter {
+	return formattedWriter.formatter
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/writers_formattedwriter_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/writers_formattedwriter_test.go b/traffic_stats/vendor/github.com/cihub/seelog/writers_formattedwriter_test.go
new file mode 100644
index 0000000..351ac4e
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/writers_formattedwriter_test.go
@@ -0,0 +1,65 @@
+// 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 (
+	"testing"
+)
+
+func TestformattedWriter(t *testing.T) {
+	formatStr := "%Level %LEVEL %Msg"
+	message := "message"
+	var logLevel = LogLevel(TraceLvl)
+
+	bytesVerifier, err := newBytesVerifier(t)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	formatter, err := NewFormatter(formatStr)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	writer, err := NewFormattedWriter(bytesVerifier, formatter)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	context, err := currentContext(nil)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	logMessage := formatter.Format(message, logLevel, context)
+
+	bytesVerifier.ExpectBytes([]byte(logMessage))
+	writer.Write(message, logLevel, context)
+	bytesVerifier.MustNotExpect()
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/writers_rollingfilewriter.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_rollingfilewriter.go b/traffic_stats/vendor/github.com/cihub/seelog/writers_rollingfilewriter.go
new file mode 100644
index 0000000..d3903bb
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/writers_rollingfilewriter.go
@@ -0,0 +1,770 @@
+// Copyright (c) 2013 - 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"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"sort"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+
+	"github.com/cihub/seelog/archive"
+	"github.com/cihub/seelog/archive/gzip"
+	"github.com/cihub/seelog/archive/tar"
+	"github.com/cihub/seelog/archive/zip"
+)
+
+// Common constants
+const (
+	rollingLogHistoryDelimiter = "."
+)
+
+// Types of the rolling writer: roll by date, by time, etc.
+type rollingType uint8
+
+const (
+	rollingTypeSize = iota
+	rollingTypeTime
+)
+
+// Types of the rolled file naming mode: prefix, postfix, etc.
+type rollingNameMode uint8
+
+const (
+	rollingNameModePostfix = iota
+	rollingNameModePrefix
+)
+
+var rollingNameModesStringRepresentation = map[rollingNameMode]string{
+	rollingNameModePostfix: "postfix",
+	rollingNameModePrefix:  "prefix",
+}
+
+func rollingNameModeFromString(rollingNameStr string) (rollingNameMode, bool) {
+	for tp, tpStr := range rollingNameModesStringRepresentation {
+		if tpStr == rollingNameStr {
+			return tp, true
+		}
+	}
+
+	return 0, false
+}
+
+var rollingTypesStringRepresentation = map[rollingType]string{
+	rollingTypeSize: "size",
+	rollingTypeTime: "date",
+}
+
+func rollingTypeFromString(rollingTypeStr string) (rollingType, bool) {
+	for tp, tpStr := range rollingTypesStringRepresentation {
+		if tpStr == rollingTypeStr {
+			return tp, true
+		}
+	}
+
+	return 0, false
+}
+
+// Old logs archivation type.
+type rollingArchiveType uint8
+
+const (
+	rollingArchiveNone = iota
+	rollingArchiveZip
+	rollingArchiveGzip
+)
+
+var rollingArchiveTypesStringRepresentation = map[rollingArchiveType]string{
+	rollingArchiveNone: "none",
+	rollingArchiveZip:  "zip",
+	rollingArchiveGzip: "gzip",
+}
+
+type archiver func(f *os.File, exploded bool) archive.WriteCloser
+
+type unarchiver func(f *os.File) (archive.ReadCloser, error)
+
+type compressionType struct {
+	extension             string
+	handleMultipleEntries bool
+	archiver              archiver
+	unarchiver            unarchiver
+}
+
+var compressionTypes = map[rollingArchiveType]compressionType{
+	rollingArchiveZip: {
+		extension:             ".zip",
+		handleMultipleEntries: true,
+		archiver: func(f *os.File, _ bool) archive.WriteCloser {
+			return zip.NewWriter(f)
+		},
+		unarchiver: func(f *os.File) (archive.ReadCloser, error) {
+			fi, err := f.Stat()
+			if err != nil {
+				return nil, err
+			}
+			r, err := zip.NewReader(f, fi.Size())
+			if err != nil {
+				return nil, err
+			}
+			return archive.NopCloser(r), nil
+		},
+	},
+	rollingArchiveGzip: {
+		extension:             ".gz",
+		handleMultipleEntries: false,
+		archiver: func(f *os.File, exploded bool) archive.WriteCloser {
+			gw := gzip.NewWriter(f)
+			if exploded {
+				return gw
+			}
+			return tar.NewWriteMultiCloser(gw, gw)
+		},
+		unarchiver: func(f *os.File) (archive.ReadCloser, error) {
+			gr, err := gzip.NewReader(f, f.Name())
+			if err != nil {
+				return nil, err
+			}
+
+			// Determine if the gzip is a tar
+			tr := tar.NewReader(gr)
+			_, err = tr.Next()
+			isTar := err == nil
+
+			// Reset to beginning of file
+			if _, err := f.Seek(0, os.SEEK_SET); err != nil {
+				return nil, err
+			}
+			gr.Reset(f)
+
+			if isTar {
+				return archive.NopCloser(tar.NewReader(gr)), nil
+			}
+			return gr, nil
+		},
+	},
+}
+
+func (compressionType *compressionType) rollingArchiveTypeName(name string, exploded bool) string {
+	if !compressionType.handleMultipleEntries && !exploded {
+		return name + ".tar" + compressionType.extension
+	} else {
+		return name + compressionType.extension
+	}
+
+}
+
+func rollingArchiveTypeFromString(rollingArchiveTypeStr string) (rollingArchiveType, bool) {
+	for tp, tpStr := range rollingArchiveTypesStringRepresentation {
+		if tpStr == rollingArchiveTypeStr {
+			return tp, true
+		}
+	}
+
+	return 0, false
+}
+
+// Default names for different archive types
+var rollingArchiveDefaultExplodedName = "old"
+
+func rollingArchiveTypeDefaultName(archiveType rollingArchiveType, exploded bool) (string, error) {
+	compressionType, ok := compressionTypes[archiveType]
+	if !ok {
+		return "", fmt.Errorf("cannot get default filename for archive type = %v", archiveType)
+	}
+	return compressionType.rollingArchiveTypeName("log", exploded), nil
+}
+
+// rollerVirtual is an interface that represents all virtual funcs that are
+// called in different rolling writer subtypes.
+type rollerVirtual interface {
+	needsToRoll() bool                                  // Returns true if needs to switch to another file.
+	isFileRollNameValid(rname string) bool              // Returns true if logger roll file name (postfix/prefix/etc.) is ok.
+	sortFileRollNamesAsc(fs []string) ([]string, error) // Sorts logger roll file names in ascending order of their creation by logger.
+
+	// getNewHistoryRollFileName is called whenever we are about to roll the
+	// current log file. It returns the name the current log file should be
+	// rolled to.
+	getNewHistoryRollFileName(otherHistoryFiles []string) string
+
+	getCurrentFileName() string
+}
+
+// rollingFileWriter writes received messages to a file, until time interval passes
+// or file exceeds a specified limit. After that the current log file is renamed
+// and writer starts to log into a new file. You can set a limit for such renamed
+// files count, if you want, and then the rolling writer would delete older ones when
+// the files count exceed the specified limit.
+type rollingFileWriter struct {
+	fileName        string // log file name
+	currentDirPath  string
+	currentFile     *os.File
+	currentName     string
+	currentFileSize int64
+	rollingType     rollingType // Rolling mode (Files roll by size/date/...)
+	archiveType     rollingArchiveType
+	archivePath     string
+	archiveExploded bool
+	fullName        bool
+	maxRolls        int
+	nameMode        rollingNameMode
+	self            rollerVirtual // Used for virtual calls
+	rollLock        sync.Mutex
+}
+
+func newRollingFileWriter(fpath string, rtype rollingType, atype rollingArchiveType, apath string, maxr int, namemode rollingNameMode,
+	archiveExploded bool, fullName bool) (*rollingFileWriter, error) {
+	rw := new(rollingFileWriter)
+	rw.currentDirPath, rw.fileName = filepath.Split(fpath)
+	if len(rw.currentDirPath) == 0 {
+		rw.currentDirPath = "."
+	}
+
+	rw.rollingType = rtype
+	rw.archiveType = atype
+	rw.archivePath = apath
+	rw.nameMode = namemode
+	rw.maxRolls = maxr
+	rw.archiveExploded = archiveExploded
+	rw.fullName = fullName
+	return rw, nil
+}
+
+func (rw *rollingFileWriter) hasRollName(file string) bool {
+	switch rw.nameMode {
+	case rollingNameModePostfix:
+		rname := rw.fileName + rollingLogHistoryDelimiter
+		return strings.HasPrefix(file, rname)
+	case rollingNameModePrefix:
+		rname := rollingLogHistoryDelimiter + rw.fileName
+		return strings.HasSuffix(file, rname)
+	}
+	return false
+}
+
+func (rw *rollingFileWriter) createFullFileName(originalName, rollname string) string {
+	switch rw.nameMode {
+	case rollingNameModePostfix:
+		return originalName + rollingLogHistoryDelimiter + rollname
+	case rollingNameModePrefix:
+		return rollname + rollingLogHistoryDelimiter + originalName
+	}
+	return ""
+}
+
+func (rw *rollingFileWriter) getSortedLogHistory() ([]string, error) {
+	files, err := getDirFilePaths(rw.currentDirPath, nil, true)
+	if err != nil {
+		return nil, err
+	}
+	var validRollNames []string
+	for _, file := range files {
+		if rw.hasRollName(file) {
+			rname := rw.getFileRollName(file)
+			if rw.self.isFileRollNameValid(rname) {
+				validRollNames = append(validRollNames, rname)
+			}
+		}
+	}
+	sortedTails, err := rw.self.sortFileRollNamesAsc(validRollNames)
+	if err != nil {
+		return nil, err
+	}
+	validSortedFiles := make([]string, len(sortedTails))
+	for i, v := range sortedTails {
+		validSortedFiles[i] = rw.createFullFileName(rw.fileName, v)
+	}
+	return validSortedFiles, nil
+}
+
+func (rw *rollingFileWriter) createFileAndFolderIfNeeded(first bool) error {
+	var err error
+
+	if len(rw.currentDirPath) != 0 {
+		err = os.MkdirAll(rw.currentDirPath, defaultDirectoryPermissions)
+
+		if err != nil {
+			return err
+		}
+	}
+	rw.currentName = rw.self.getCurrentFileName()
+	filePath := filepath.Join(rw.currentDirPath, rw.currentName)
+
+	// If exists
+	stat, err := os.Lstat(filePath)
+	if err == nil {
+		rw.currentFile, err = os.OpenFile(filePath, os.O_WRONLY|os.O_APPEND, defaultFilePermissions)
+		if err != nil {
+			return err
+		}
+
+		stat, err = os.Lstat(filePath)
+		if err != nil {
+			return err
+		}
+
+		rw.currentFileSize = stat.Size()
+	} else {
+		rw.currentFile, err = os.Create(filePath)
+		rw.currentFileSize = 0
+	}
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (rw *rollingFileWriter) archiveExplodedLogs(logFilename string, compressionType compressionType) (err error) {
+	closeWithError := func(c io.Closer) {
+		if cerr := c.Close(); cerr != nil && err == nil {
+			err = cerr
+		}
+	}
+
+	rollPath := filepath.Join(rw.currentDirPath, logFilename)
+	src, err := os.Open(rollPath)
+	if err != nil {
+		return err
+	}
+	defer src.Close() // Read-only
+
+	// Buffer to a temporary file on the same partition
+	// Note: archivePath is a path to a directory when handling exploded logs
+	dst, err := rw.tempArchiveFile(rw.archivePath)
+	if err != nil {
+		return err
+	}
+	defer func() {
+		closeWithError(dst)
+		if err != nil {
+			os.Remove(dst.Name()) // Can't do anything when we fail to remove temp file
+			return
+		}
+
+		// Finalize archive by swapping the buffered archive into place
+		err = os.Rename(dst.Name(), filepath.Join(rw.archivePath,
+			compressionType.rollingArchiveTypeName(logFilename, true)))
+	}()
+
+	// archive entry
+	w := compressionType.archiver(dst, true)
+	defer closeWithError(w)
+	fi, err := src.Stat()
+	if err != nil {
+		return err
+	}
+	if err := w.NextFile(logFilename, fi); err != nil {
+		return err
+	}
+	_, err = io.Copy(w, src)
+	return err
+}
+
+func (rw *rollingFileWriter) archiveUnexplodedLogs(compressionType compressionType, rollsToDelete int, history []string) (err error) {
+	closeWithError := func(c io.Closer) {
+		if cerr := c.Close(); cerr != nil && err == nil {
+			err = cerr
+		}
+	}
+
+	// Buffer to a temporary file on the same partition
+	// Note: archivePath is a path to a file when handling unexploded logs
+	dst, err := rw.tempArchiveFile(filepath.Dir(rw.archivePath))
+	if err != nil {
+		return err
+	}
+	defer func() {
+		closeWithError(dst)
+		if err != nil {
+			os.Remove(dst.Name()) // Can't do anything when we fail to remove temp file
+			return
+		}
+
+		// Finalize archive by moving the buffered archive into place
+		err = os.Rename(dst.Name(), rw.archivePath)
+	}()
+
+	w := compressionType.archiver(dst, false)
+	defer closeWithError(w)
+
+	src, err := os.Open(rw.archivePath)
+	switch {
+	// Archive exists
+	case err == nil:
+		defer src.Close() // Read-only
+
+		r, err := compressionType.unarchiver(src)
+		if err != nil {
+			return err
+		}
+		defer r.Close() // Read-only
+
+		if err := archive.Copy(w, r); err != nil {
+			return err
+		}
+
+	// Failed to stat
+	case !os.IsNotExist(err):
+		return err
+	}
+
+	// Add new files to the archive
+	for i := 0; i < rollsToDelete; i++ {
+		rollPath := filepath.Join(rw.currentDirPath, history[i])
+		src, err := os.Open(rollPath)
+		if err != nil {
+			return err
+		}
+		defer src.Close() // Read-only
+		fi, err := src.Stat()
+		if err != nil {
+			return err
+		}
+		if err := w.NextFile(src.Name(), fi); err != nil {
+			return err
+		}
+		if _, err := io.Copy(w, src); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (rw *rollingFileWriter) deleteOldRolls(history []string) error {
+	if rw.maxRolls <= 0 {
+		return nil
+	}
+
+	rollsToDelete := len(history) - rw.maxRolls
+	if rollsToDelete <= 0 {
+		return nil
+	}
+
+	if rw.archiveType != rollingArchiveNone {
+		if rw.archiveExploded {
+			os.MkdirAll(rw.archivePath, defaultDirectoryPermissions)
+
+			// Archive logs
+			for i := 0; i < rollsToDelete; i++ {
+				rw.archiveExplodedLogs(history[i], compressionTypes[rw.archiveType])
+			}
+		} else {
+			os.MkdirAll(filepath.Dir(rw.archivePath), defaultDirectoryPermissions)
+
+			rw.archiveUnexplodedLogs(compressionTypes[rw.archiveType], rollsToDelete, history)
+		}
+	}
+
+	var err error
+	// In all cases (archive files or not) the files should be deleted.
+	for i := 0; i < rollsToDelete; i++ {
+		// Try best to delete files without breaking the loop.
+		if err = tryRemoveFile(filepath.Join(rw.currentDirPath, history[i])); err != nil {
+			reportInternalError(err)
+		}
+	}
+
+	return nil
+}
+
+func (rw *rollingFileWriter) getFileRollName(fileName string) string {
+	switch rw.nameMode {
+	case rollingNameModePostfix:
+		return fileName[len(rw.fileName+rollingLogHistoryDelimiter):]
+	case rollingNameModePrefix:
+		return fileName[:len(fileName)-len(rw.fileName+rollingLogHistoryDelimiter)]
+	}
+	return ""
+}
+
+func (rw *rollingFileWriter) roll() error {
+	// First, close current file.
+	err := rw.currentFile.Close()
+	if err != nil {
+		return err
+	}
+	rw.currentFile = nil
+
+	// Current history of all previous log files.
+	// For file roller it may be like this:
+	//     * ...
+	//     * file.log.4
+	//     * file.log.5
+	//     * file.log.6
+	//
+	// For date roller it may look like this:
+	//     * ...
+	//     * file.log.11.Aug.13
+	//     * file.log.15.Aug.13
+	//     * file.log.16.Aug.13
+	// Sorted log history does NOT include current file.
+	history, err := rw.getSortedLogHistory()
+	if err != nil {
+		return err
+	}
+	// Renames current file to create a new roll history entry
+	// For file roller it may be like this:
+	//     * ...
+	//     * file.log.4
+	//     * file.log.5
+	//     * file.log.6
+	//     n file.log.7  <---- RENAMED (from file.log)
+	newHistoryName := rw.createFullFileName(rw.fileName,
+		rw.self.getNewHistoryRollFileName(history))
+
+	err = os.Rename(filepath.Join(rw.currentDirPath, rw.currentName), filepath.Join(rw.currentDirPath, newHistoryName))
+	if err != nil {
+		return err
+	}
+
+	// Finally, add the newly added history file to the history archive
+	// and, if after that the archive exceeds the allowed max limit, older rolls
+	// must the removed/archived.
+	history = append(history, newHistoryName)
+	if len(history) > rw.maxRolls {
+		err = rw.deleteOldRolls(history)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+func (rw *rollingFileWriter) Write(bytes []byte) (n int, err error) {
+	rw.rollLock.Lock()
+	defer rw.rollLock.Unlock()
+
+	if rw.self.needsToRoll() {
+		if err := rw.roll(); err != nil {
+			return 0, err
+		}
+	}
+
+	if rw.currentFile == nil {
+		err := rw.createFileAndFolderIfNeeded(true)
+		if err != nil {
+			return 0, err
+		}
+	}
+
+	n, err = rw.currentFile.Write(bytes)
+	rw.currentFileSize += int64(n)
+	return n, err
+}
+
+func (rw *rollingFileWriter) Close() error {
+	if rw.currentFile != nil {
+		e := rw.currentFile.Close()
+		if e != nil {
+			return e
+		}
+		rw.currentFile = nil
+	}
+	return nil
+}
+
+func (rw *rollingFileWriter) tempArchiveFile(archiveDir string) (*os.File, error) {
+	tmp := filepath.Join(archiveDir, ".seelog_tmp")
+	if err := os.MkdirAll(tmp, defaultDirectoryPermissions); err != nil {
+		return nil, err
+	}
+	return ioutil.TempFile(tmp, "archived_logs")
+}
+
+// =============================================================================================
+//      Different types of rolling writers
+// =============================================================================================
+
+// --------------------------------------------------
+//      Rolling writer by SIZE
+// --------------------------------------------------
+
+// rollingFileWriterSize performs roll when file exceeds a specified limit.
+type rollingFileWriterSize struct {
+	*rollingFileWriter
+	maxFileSize int64
+}
+
+func NewRollingFileWriterSize(fpath string, atype rollingArchiveType, apath string, maxSize int64, maxRolls int, namemode rollingNameMode, archiveExploded bool) (*rollingFileWriterSize, error) {
+	rw, err := newRollingFileWriter(fpath, rollingTypeSize, atype, apath, maxRolls, namemode, archiveExploded, false)
+	if err != nil {
+		return nil, err
+	}
+	rws := &rollingFileWriterSize{rw, maxSize}
+	rws.self = rws
+	return rws, nil
+}
+
+func (rws *rollingFileWriterSize) needsToRoll() bool {
+	return rws.currentFileSize >= rws.maxFileSize
+}
+
+func (rws *rollingFileWriterSize) isFileRollNameValid(rname string) bool {
+	if len(rname) == 0 {
+		return false
+	}
+	_, err := strconv.Atoi(rname)
+	return err == nil
+}
+
+type rollSizeFileTailsSlice []string
+
+func (p rollSizeFileTailsSlice) Len() int {
+	return len(p)
+}
+func (p rollSizeFileTailsSlice) Less(i, j int) bool {
+	v1, _ := strconv.Atoi(p[i])
+	v2, _ := strconv.Atoi(p[j])
+	return v1 < v2
+}
+func (p rollSizeFileTailsSlice) Swap(i, j int) {
+	p[i], p[j] = p[j], p[i]
+}
+
+func (rws *rollingFileWriterSize) sortFileRollNamesAsc(fs []string) ([]string, error) {
+	ss := rollSizeFileTailsSlice(fs)
+	sort.Sort(ss)
+	return ss, nil
+}
+
+func (rws *rollingFileWriterSize) getNewHistoryRollFileName(otherLogFiles []string) string {
+	v := 0
+	if len(otherLogFiles) != 0 {
+		latest := otherLogFiles[len(otherLogFiles)-1]
+		v, _ = strconv.Atoi(rws.getFileRollName(latest))
+	}
+	return fmt.Sprintf("%d", v+1)
+}
+
+func (rws *rollingFileWriterSize) getCurrentFileName() string {
+	return rws.fileName
+}
+
+func (rws *rollingFileWriterSize) String() string {
+	return fmt.Sprintf("Rolling file writer (By SIZE): filename: %s, archive: %s, archivefile: %s, maxFileSize: %v, maxRolls: %v",
+		rws.fileName,
+		rollingArchiveTypesStringRepresentation[rws.archiveType],
+		rws.archivePath,
+		rws.maxFileSize,
+		rws.maxRolls)
+}
+
+// --------------------------------------------------
+//      Rolling writer by TIME
+// --------------------------------------------------
+
+// rollingFileWriterTime performs roll when a specified time interval has passed.
+type rollingFileWriterTime struct {
+	*rollingFileWriter
+	timePattern         string
+	currentTimeFileName string
+}
+
+func NewRollingFileWriterTime(fpath string, atype rollingArchiveType, apath string, maxr int,
+	timePattern string, namemode rollingNameMode, archiveExploded bool, fullName bool) (*rollingFileWriterTime, error) {
+
+	rw, err := newRollingFileWriter(fpath, rollingTypeTime, atype, apath, maxr, namemode, archiveExploded, fullName)
+	if err != nil {
+		return nil, err
+	}
+	rws := &rollingFileWriterTime{rw, timePattern, ""}
+	rws.self = rws
+	return rws, nil
+}
+
+func (rwt *rollingFileWriterTime) needsToRoll() bool {
+	newName := time.Now().Format(rwt.timePattern)
+
+	if rwt.currentTimeFileName == "" {
+		// first run; capture the current name
+		rwt.currentTimeFileName = newName
+		return false
+	}
+
+	return newName != rwt.currentTimeFileName
+}
+
+func (rwt *rollingFileWriterTime) isFileRollNameValid(rname string) bool {
+	if len(rname) == 0 {
+		return false
+	}
+	_, err := time.ParseInLocation(rwt.timePattern, rname, time.Local)
+	return err == nil
+}
+
+type rollTimeFileTailsSlice struct {
+	data    []string
+	pattern string
+}
+
+func (p rollTimeFileTailsSlice) Len() int {
+	return len(p.data)
+}
+
+func (p rollTimeFileTailsSlice) Less(i, j int) bool {
+	t1, _ := time.ParseInLocation(p.pattern, p.data[i], time.Local)
+	t2, _ := time.ParseInLocation(p.pattern, p.data[j], time.Local)
+	return t1.Before(t2)
+}
+
+func (p rollTimeFileTailsSlice) Swap(i, j int) {
+	p.data[i], p.data[j] = p.data[j], p.data[i]
+}
+
+func (rwt *rollingFileWriterTime) sortFileRollNamesAsc(fs []string) ([]string, error) {
+	ss := rollTimeFileTailsSlice{data: fs, pattern: rwt.timePattern}
+	sort.Sort(ss)
+	return ss.data, nil
+}
+
+func (rwt *rollingFileWriterTime) getNewHistoryRollFileName(_ []string) string {
+	newFileName := rwt.currentTimeFileName
+	rwt.currentTimeFileName = time.Now().Format(rwt.timePattern)
+	return newFileName
+}
+
+func (rwt *rollingFileWriterTime) getCurrentFileName() string {
+	if rwt.fullName {
+		return rwt.createFullFileName(rwt.fileName, time.Now().Format(rwt.timePattern))
+	}
+	return rwt.fileName
+}
+
+func (rwt *rollingFileWriterTime) String() string {
+	return fmt.Sprintf("Rolling file writer (By TIME): filename: %s, archive: %s, archivefile: %s, pattern: %s, maxRolls: %v",
+		rwt.fileName,
+		rollingArchiveTypesStringRepresentation[rwt.archiveType],
+		rwt.archivePath,
+		rwt.timePattern,
+		rwt.maxRolls)
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/writers_rollingfilewriter_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/writers_rollingfilewriter_test.go b/traffic_stats/vendor/github.com/cihub/seelog/writers_rollingfilewriter_test.go
new file mode 100644
index 0000000..b23c959
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/writers_rollingfilewriter_test.go
@@ -0,0 +1,116 @@
+// 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"
+	"io"
+	"testing"
+)
+
+// fileWriterTestCase is declared in writers_filewriter_test.go
+
+func createRollingSizeFileWriterTestCase(
+	files []string,
+	fileName string,
+	fileSize int64,
+	maxRolls int,
+	writeCount int,
+	resFiles []string,
+	nameMode rollingNameMode,
+	archiveType rollingArchiveType,
+	archiveExploded bool,
+	archivePath string) *fileWriterTestCase {
+
+	return &fileWriterTestCase{files, fileName, rollingTypeSize, fileSize, maxRolls, "", writeCount, resFiles, nameMode, archiveType, archiveExploded, archivePath}
+}
+
+func createRollingDatefileWriterTestCase(
+	files []string,
+	fileName string,
+	datePattern string,
+	writeCount int,
+	resFiles []string,
+	nameMode rollingNameMode,
+	archiveType rollingArchiveType,
+	archiveExploded bool,
+	archivePath string) *fileWriterTestCase {
+
+	return &fileWriterTestCase{files, fileName, rollingTypeTime, 0, 0, datePattern, writeCount, resFiles, nameMode, archiveType, archiveExploded, archivePath}
+}
+
+func TestShouldArchiveWithTar(t *testing.T) {
+	compressionType := compressionTypes[rollingArchiveGzip]
+
+	archiveName := compressionType.rollingArchiveTypeName("log", false)
+
+	if archiveName != "log.tar.gz" {
+		t.Fatalf("archive name should be log.tar.gz but got %v", archiveName)
+	}
+}
+
+func TestRollingFileWriter(t *testing.T) {
+	t.Logf("Starting rolling file writer tests")
+	NewFileWriterTester(rollingfileWriterTests, rollingFileWriterGetter, t).test()
+}
+
+//===============================================================
+
+func rollingFileWriterGetter(testCase *fileWriterTestCase) (io.WriteCloser, error) {
+	if testCase.rollingType == rollingTypeSize {
+		return NewRollingFileWriterSize(testCase.fileName, testCase.archiveType, testCase.archivePath, testCase.fileSize, testCase.maxRolls, testCase.nameMode, testCase.archiveExploded)
+	} else if testCase.rollingType == rollingTypeTime {
+		return NewRollingFileWriterTime(testCase.fileName, testCase.archiveType, testCase.archivePath, -1, testCase.datePattern, testCase.nameMode, testCase.archiveExploded, false)
+	}
+
+	return nil, fmt.Errorf("incorrect rollingType")
+}
+
+//===============================================================
+var rollingfileWriterTests = []*fileWriterTestCase{
+	createRollingSizeFileWriterTestCase([]string{}, "log.testlog", 10, 10, 1, []string{"log.testlog"}, rollingNameModePostfix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{}, "log.testlog", 10, 10, 2, []string{"log.testlog", "log.testlog.1"}, rollingNameModePostfix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{"1.log.testlog"}, "log.testlog", 10, 10, 2, []string{"log.testlog", "1.log.testlog", "2.log.testlog"}, rollingNameModePrefix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{"log.testlog.1"}, "log.testlog", 10, 1, 2, []string{"log.testlog", "log.testlog.2"}, rollingNameModePostfix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{}, "log.testlog", 10, 1, 2, []string{"log.testlog", "log.testlog.1"}, rollingNameModePostfix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{"log.testlog.9"}, "log.testlog", 10, 1, 2, []string{"log.testlog", "log.testlog.10"}, rollingNameModePostfix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{"log.testlog.a", "log.testlog.1b"}, "log.testlog", 10, 1, 2, []string{"log.testlog", "log.testlog.1", "log.testlog.a", "log.testlog.1b"}, rollingNameModePostfix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{}, `dir/log.testlog`, 10, 10, 1, []string{`dir/log.testlog`}, rollingNameModePostfix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{}, `dir/log.testlog`, 10, 10, 2, []string{`dir/log.testlog`, `dir/1.log.testlog`}, rollingNameModePrefix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{`dir/dir/log.testlog.1`}, `dir/dir/log.testlog`, 10, 10, 2, []string{`dir/dir/log.testlog`, `dir/dir/log.testlog.1`, `dir/dir/log.testlog.2`}, rollingNameModePostfix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{`dir/dir/dir/log.testlog.1`}, `dir/dir/dir/log.testlog`, 10, 1, 2, []string{`dir/dir/dir/log.testlog`, `dir/dir/dir/log.testlog.2`}, rollingNameModePostfix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{}, `./log.testlog`, 10, 1, 2, []string{`log.testlog`, `log.testlog.1`}, rollingNameModePostfix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{`././././log.testlog.9`}, `log.testlog`, 10, 1, 2, []string{`log.testlog`, `log.testlog.10`}, rollingNameModePostfix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{"dir/dir/log.testlog.a", "dir/dir/log.testlog.1b"}, "dir/dir/log.testlog", 10, 1, 2, []string{"dir/dir/log.testlog", "dir/dir/log.testlog.1", "dir/dir/log.testlog.a", "dir/dir/log.testlog.1b"}, rollingNameModePostfix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{}, `././dir/log.testlog`, 10, 10, 1, []string{`dir/log.testlog`}, rollingNameModePostfix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{}, `././dir/log.testlog`, 10, 10, 2, []string{`dir/log.testlog`, `dir/log.testlog.1`}, rollingNameModePostfix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{`././dir/dir/log.testlog.1`}, `dir/dir/log.testlog`, 10, 10, 2, []string{`dir/dir/log.testlog`, `dir/dir/log.testlog.1`, `dir/dir/log.testlog.2`}, rollingNameModePostfix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{`././dir/dir/dir/log.testlog.1`}, `dir/dir/dir/log.testlog`, 10, 1, 2, []string{`dir/dir/dir/log.testlog`, `dir/dir/dir/log.testlog.2`}, rollingNameModePostfix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{}, `././log.testlog`, 10, 1, 2, []string{`log.testlog`, `log.testlog.1`}, rollingNameModePostfix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{`././././log.testlog.9`}, `log.testlog`, 10, 1, 2, []string{`log.testlog`, `log.testlog.10`}, rollingNameModePostfix, rollingArchiveNone, false, ""),
+	createRollingSizeFileWriterTestCase([]string{"././dir/dir/log.testlog.a", "././dir/dir/log.testlog.1b"}, "dir/dir/log.testlog", 10, 1, 2, []string{"dir/dir/log.testlog", "dir/dir/log.testlog.1", "dir/dir/log.testlog.a", "dir/dir/log.testlog.1b"}, rollingNameModePostfix, rollingArchiveNone, true, ""),
+	createRollingSizeFileWriterTestCase([]string{"log.testlog", "log.testlog.1"}, "log.testlog", 10, 1, 2, []string{"log.testlog", "log.testlog.2", "dir/log.testlog.1.zip"}, rollingNameModePostfix, rollingArchiveZip, true, "dir"),
+	// ====================
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/d969e13b/traffic_stats/vendor/github.com/cihub/seelog/writers_smtpwriter.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_smtpwriter.go b/traffic_stats/vendor/github.com/cihub/seelog/writers_smtpwriter.go
new file mode 100644
index 0000000..31b7943
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/writers_smtpwriter.go
@@ -0,0 +1,214 @@
+// 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 (
+	"crypto/tls"
+	"crypto/x509"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"net/smtp"
+	"path/filepath"
+	"strings"
+)
+
+const (
+	// Default subject phrase for sending emails.
+	DefaultSubjectPhrase = "Diagnostic message from server: "
+
+	// Message subject pattern composed according to RFC 5321.
+	rfc5321SubjectPattern = "From: %s <%s>\nSubject: %s\n\n"
+)
+
+// smtpWriter is used to send emails via given SMTP-server.
+type smtpWriter struct {
+	auth               smtp.Auth
+	hostName           string
+	hostPort           string
+	hostNameWithPort   string
+	senderAddress      string
+	senderName         string
+	recipientAddresses []string
+	caCertDirPaths     []string
+	mailHeaders        []string
+	subject            string
+}
+
+// NewSMTPWriter returns a new SMTP-writer.
+func NewSMTPWriter(sa, sn string, ras []string, hn, hp, un, pwd string, cacdps []string, subj string, headers []string) *smtpWriter {
+	return &smtpWriter{
+		auth:               smtp.PlainAuth("", un, pwd, hn),
+		hostName:           hn,
+		hostPort:           hp,
+		hostNameWithPort:   fmt.Sprintf("%s:%s", hn, hp),
+		senderAddress:      sa,
+		senderName:         sn,
+		recipientAddresses: ras,
+		caCertDirPaths:     cacdps,
+		subject:            subj,
+		mailHeaders:        headers,
+	}
+}
+
+func prepareMessage(senderAddr, senderName, subject string, body []byte, headers []string) []byte {
+	headerLines := fmt.Sprintf(rfc5321SubjectPattern, senderName, senderAddr, subject)
+	// Build header lines if configured.
+	if headers != nil && len(headers) > 0 {
+		headerLines += strings.Join(headers, "\n")
+		headerLines += "\n"
+	}
+	return append([]byte(headerLines), body...)
+}
+
+// getTLSConfig gets paths of PEM files with certificates,
+// host server name and tries to create an appropriate TLS.Config.
+func getTLSConfig(pemFileDirPaths []string, hostName string) (config *tls.Config, err error) {
+	if pemFileDirPaths == nil || len(pemFileDirPaths) == 0 {
+		err = errors.New("invalid PEM file paths")
+		return
+	}
+	pemEncodedContent := []byte{}
+	var (
+		e     error
+		bytes []byte
+	)
+	// Create a file-filter-by-extension, set aside non-pem files.
+	pemFilePathFilter := func(fp string) bool {
+		if filepath.Ext(fp) == ".pem" {
+			return true
+		}
+		return false
+	}
+	for _, pemFileDirPath := range pemFileDirPaths {
+		pemFilePaths, err := getDirFilePaths(pemFileDirPath, pemFilePathFilter, false)
+		if err != nil {
+			return nil, err
+		}
+
+		// Put together all the PEM files to decode them as a whole byte slice.
+		for _, pfp := range pemFilePaths {
+			if bytes, e = ioutil.ReadFile(pfp); e == nil {
+				pemEncodedContent = append(pemEncodedContent, bytes...)
+			} else {
+				return nil, fmt.Errorf("cannot read file: %s: %s", pfp, e.Error())
+			}
+		}
+	}
+	config = &tls.Config{RootCAs: x509.NewCertPool(), ServerName: hostName}
+	isAppended := config.RootCAs.AppendCertsFromPEM(pemEncodedContent)
+	if !isAppended {
+		// Extract this into a separate error.
+		err = errors.New("invalid PEM content")
+		return
+	}
+	return
+}
+
+// SendMail accepts TLS configuration, connects to the server at addr,
+// switches to TLS if possible, authenticates with mechanism a if possible,
+// and then sends an email from address from, to addresses to, with message msg.
+func sendMailWithTLSConfig(config *tls.Config, addr string, a smtp.Auth, from string, to []string, msg []byte) error {
+	c, err := smtp.Dial(addr)
+	if err != nil {
+		return err
+	}
+	// Check if the server supports STARTTLS extension.
+	if ok, _ := c.Extension("STARTTLS"); ok {
+		if err = c.StartTLS(config); err != nil {
+			return err
+		}
+	}
+	// Check if the server supports AUTH extension and use given smtp.Auth.
+	if a != nil {
+		if isSupported, _ := c.Extension("AUTH"); isSupported {
+			if err = c.Auth(a); err != nil {
+				return err
+			}
+		}
+	}
+	// Portion of code from the official smtp.SendMail function,
+	// see http://golang.org/src/pkg/net/smtp/smtp.go.
+	if err = c.Mail(from); err != nil {
+		return err
+	}
+	for _, addr := range to {
+		if err = c.Rcpt(addr); err != nil {
+			return err
+		}
+	}
+	w, err := c.Data()
+	if err != nil {
+		return err
+	}
+	_, err = w.Write(msg)
+	if err != nil {
+		return err
+	}
+	err = w.Close()
+	if err != nil {
+		return err
+	}
+	return c.Quit()
+}
+
+// Write pushes a text message properly composed according to RFC 5321
+// to a post server, which sends it to the recipients.
+func (smtpw *smtpWriter) Write(data []byte) (int, error) {
+	var err error
+
+	if smtpw.caCertDirPaths == nil {
+		err = smtp.SendMail(
+			smtpw.hostNameWithPort,
+			smtpw.auth,
+			smtpw.senderAddress,
+			smtpw.recipientAddresses,
+			prepareMessage(smtpw.senderAddress, smtpw.senderName, smtpw.subject, data, smtpw.mailHeaders),
+		)
+	} else {
+		config, e := getTLSConfig(smtpw.caCertDirPaths, smtpw.hostName)
+		if e != nil {
+			return 0, e
+		}
+		err = sendMailWithTLSConfig(
+			config,
+			smtpw.hostNameWithPort,
+			smtpw.auth,
+			smtpw.senderAddress,
+			smtpw.recipientAddresses,
+			prepareMessage(smtpw.senderAddress, smtpw.senderName, smtpw.subject, data, smtpw.mailHeaders),
+		)
+	}
+	if err != nil {
+		return 0, err
+	}
+	return len(data), nil
+}
+
+// Close closes down SMTP-connection.
+func (smtpw *smtpWriter) Close() error {
+	// Do nothing as Write method opens and closes connection automatically.
+	return nil
+}