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

[11/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
----------------------------------------------------------------------
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/LICENSE.txt b/traffic_stats/vendor/github.com/cihub/seelog/LICENSE.txt
new file mode 100644
index 0000000..8c70681
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/LICENSE.txt
@@ -0,0 +1,24 @@
+Copyright (c) 2012, Cloud Instruments Co., Ltd. <in...@cin.io>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * 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.
+    * Neither the name of the Cloud Instruments Co., Ltd. nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+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 <COPYRIGHT HOLDER> 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.
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/README.markdown b/traffic_stats/vendor/github.com/cihub/seelog/README.markdown
new file mode 100644
index 0000000..7dd1ab3
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/README.markdown
@@ -0,0 +1,116 @@
+Seelog
+=======
+
+Seelog is a powerful and easy-to-learn logging framework that provides functionality for flexible dispatching, filtering, and formatting log messages.
+It is natively written in the [Go](http://golang.org/) programming language. 
+
+[![Build Status](https://drone.io/github.com/cihub/seelog/status.png)](https://drone.io/github.com/cihub/seelog/latest)
+
+Features
+------------------
+
+* Xml configuring to be able to change logger parameters without recompilation
+* Changing configurations on the fly without app restart
+* Possibility to set different log configurations for different project files and functions
+* Adjustable message formatting
+* Simultaneous log output to multiple streams
+* Choosing logger priority strategy to minimize performance hit
+* Different output writers
+  * Console writer
+  * File writer 
+  * Buffered writer (Chunk writer)
+  * Rolling log writer (Logging with rotation)
+  * SMTP writer
+  * Others... (See [Wiki](https://github.com/cihub/seelog/wiki))
+* Log message wrappers (JSON, XML, etc.)
+* Global variables and functions for easy usage in standalone apps
+* Functions for flexible usage in libraries
+
+Quick-start
+-----------
+
+```go
+package main
+
+import log "github.com/cihub/seelog"
+
+func main() {
+    defer log.Flush()
+    log.Info("Hello from Seelog!")
+}
+```
+
+Installation
+------------
+
+If you don't have the Go development environment installed, visit the 
+[Getting Started](http://golang.org/doc/install.html) document and follow the instructions. Once you're ready, execute the following command:
+
+```
+go get -u github.com/cihub/seelog
+```
+
+*IMPORTANT*: If you are not using the latest release version of Go, check out this [wiki page](https://github.com/cihub/seelog/wiki/Notes-on-'go-get')
+
+Documentation
+---------------
+
+Seelog has github wiki pages, which contain detailed how-tos references: https://github.com/cihub/seelog/wiki
+
+Examples
+---------------
+
+Seelog examples can be found here: [seelog-examples](https://github.com/cihub/seelog-examples)
+
+Issues
+---------------
+
+Feel free to push issues that could make Seelog better: https://github.com/cihub/seelog/issues
+
+Changelog
+---------------
+* **v2.6** : Config using code and custom formatters
+    * Configuration using code in addition to xml (All internal receiver/dispatcher/logger types are now exported).
+    * Custom formatters. Check [wiki](https://github.com/cihub/seelog/wiki/Custom-formatters)
+    * Bugfixes and internal improvements.
+* **v2.5** : Interaction with other systems. Part 2: custom receivers
+    * Finished custom receivers feature. Check [wiki](https://github.com/cihub/seelog/wiki/custom-receivers)
+    * Added 'LoggerFromCustomReceiver'
+    * Added 'LoggerFromWriterWithMinLevelAndFormat'
+    * Added 'LoggerFromCustomReceiver'
+    * Added 'LoggerFromParamConfigAs...' 
+* **v2.4** : Interaction with other systems. Part 1: wrapping seelog
+    * Added configurable caller stack skip logic
+    * Added 'SetAdditionalStackDepth' to 'LoggerInterface'
+* **v2.3** : Rethinking 'rolling' receiver
+    * Reimplemented 'rolling' receiver
+    * Added 'Max rolls' feature for 'rolling' receiver with type='date'
+    * Fixed 'rolling' receiver issue: renaming on Windows
+* **v2.2** : go1.0 compatibility point [go1.0 tag]
+    * Fixed internal bugs
+    * Added 'ANSI n [;k]' format identifier:  %EscN
+    * Made current release go1 compatible 
+* **v2.1** : Some new features
+    * Rolling receiver archiving option.
+    * Added format identifier: %Line
+    * Smtp: added paths to PEM files directories
+    * Added format identifier: %FuncShort
+    * Warn, Error and Critical methods now return an error
+* **v2.0** : Second major release. BREAKING CHANGES.
+    * Support of binaries with stripped symbols
+    * Added log strategy: adaptive
+    * Critical message now forces Flush()
+    * Added predefined formats: xml-debug, xml-debug-short, xml, xml-short, json-debug, json-debug-short, json, json-short, debug, debug-short, fast
+    * Added receiver: conn (network connection writer)
+    * BREAKING CHANGE: added Tracef, Debugf, Infof, etc. to satisfy the print/printf principle
+    * Bug fixes
+* **v1.0** : Initial release. Features:
+    * Xml config
+    * Changing configurations on the fly without app restart
+    * Contraints and exceptions
+    * Formatting
+    * Log strategies: sync, async loop, async timer
+    * Receivers: buffered, console, file, rolling, smtp
+
+
+
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/archive/archive.go b/traffic_stats/vendor/github.com/cihub/seelog/archive/archive.go
new file mode 100644
index 0000000..923036f
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/archive/archive.go
@@ -0,0 +1,198 @@
+package archive
+
+import (
+	"archive/tar"
+	"archive/zip"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"time"
+
+	"github.com/cihub/seelog/archive/gzip"
+)
+
+// Reader is the interface for reading files from an archive.
+type Reader interface {
+	NextFile() (name string, err error)
+	io.Reader
+}
+
+// ReadCloser is the interface that groups Reader with the Close method.
+type ReadCloser interface {
+	Reader
+	io.Closer
+}
+
+// Writer is the interface for writing files to an archived format.
+type Writer interface {
+	NextFile(name string, fi os.FileInfo) error
+	io.Writer
+}
+
+// WriteCloser is the interface that groups Writer with the Close method.
+type WriteCloser interface {
+	Writer
+	io.Closer
+}
+
+type nopCloser struct{ Reader }
+
+func (nopCloser) Close() error { return nil }
+
+// NopCloser returns a ReadCloser with a no-op Close method wrapping the
+// provided Reader r.
+func NopCloser(r Reader) ReadCloser {
+	return nopCloser{r}
+}
+
+// Copy copies from src to dest until either EOF is reached on src or an error
+// occurs.
+//
+// When the archive format of src matches that of dst, Copy streams the files
+// directly into dst. Otherwise, copy buffers the contents to disk to compute
+// headers before writing to dst.
+func Copy(dst Writer, src Reader) error {
+	switch src := src.(type) {
+	case tarReader:
+		if dst, ok := dst.(tarWriter); ok {
+			return copyTar(dst, src)
+		}
+	case zipReader:
+		if dst, ok := dst.(zipWriter); ok {
+			return copyZip(dst, src)
+		}
+	// Switch on concrete type because gzip has no special methods
+	case *gzip.Reader:
+		if dst, ok := dst.(*gzip.Writer); ok {
+			_, err := io.Copy(dst, src)
+			return err
+		}
+	}
+
+	return copyBuffer(dst, src)
+}
+
+func copyBuffer(dst Writer, src Reader) (err error) {
+	const defaultFileMode = 0666
+
+	buf, err := ioutil.TempFile("", "archive_copy_buffer")
+	if err != nil {
+		return err
+	}
+	defer os.Remove(buf.Name()) // Do not care about failure removing temp
+	defer buf.Close()           // Do not care about failure closing temp
+	for {
+		// Handle the next file
+		name, err := src.NextFile()
+		switch err {
+		case io.EOF: // Done copying
+			return nil
+		default: // Failed to write: bail out
+			return err
+		case nil: // Proceed below
+		}
+
+		// Buffer the file
+		if _, err := io.Copy(buf, src); err != nil {
+			return fmt.Errorf("buffer to disk: %v", err)
+		}
+
+		// Seek to the start of the file for full file copy
+		if _, err := buf.Seek(0, os.SEEK_SET); err != nil {
+			return err
+		}
+
+		// Set desired file permissions
+		if err := os.Chmod(buf.Name(), defaultFileMode); err != nil {
+			return err
+		}
+		fi, err := buf.Stat()
+		if err != nil {
+			return err
+		}
+
+		// Write the buffered file
+		if err := dst.NextFile(name, fi); err != nil {
+			return err
+		}
+		if _, err := io.Copy(dst, buf); err != nil {
+			return fmt.Errorf("copy to dst: %v", err)
+		}
+		if err := buf.Truncate(0); err != nil {
+			return err
+		}
+		if _, err := buf.Seek(0, os.SEEK_SET); err != nil {
+			return err
+		}
+	}
+}
+
+type tarReader interface {
+	Next() (*tar.Header, error)
+	io.Reader
+}
+
+type tarWriter interface {
+	WriteHeader(hdr *tar.Header) error
+	io.Writer
+}
+
+type zipReader interface {
+	Files() []*zip.File
+}
+
+type zipWriter interface {
+	CreateHeader(fh *zip.FileHeader) (io.Writer, error)
+}
+
+func copyTar(w tarWriter, r tarReader) error {
+	for {
+		hdr, err := r.Next()
+		switch err {
+		case io.EOF:
+			return nil
+		default: // Handle error
+			return err
+		case nil: // Proceed below
+		}
+
+		info := hdr.FileInfo()
+		// Skip directories
+		if info.IsDir() {
+			continue
+		}
+		if err := w.WriteHeader(hdr); err != nil {
+			return err
+		}
+		if _, err := io.Copy(w, r); err != nil {
+			return err
+		}
+	}
+}
+
+func copyZip(zw zipWriter, r zipReader) error {
+	for _, f := range r.Files() {
+		if err := copyZipFile(zw, f); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func copyZipFile(zw zipWriter, f *zip.File) error {
+	rc, err := f.Open()
+	if err != nil {
+		return err
+	}
+	defer rc.Close() // Read-only
+
+	hdr := f.FileHeader
+	hdr.SetModTime(time.Now())
+	w, err := zw.CreateHeader(&hdr)
+	if err != nil {
+		return err
+	}
+	_, err = io.Copy(w, rc)
+	return err
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/archive/archive_test.go b/traffic_stats/vendor/github.com/cihub/seelog/archive/archive_test.go
new file mode 100644
index 0000000..a05cac7
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/archive/archive_test.go
@@ -0,0 +1,178 @@
+package archive_test
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"testing"
+
+	"github.com/cihub/seelog/archive"
+	"github.com/cihub/seelog/archive/gzip"
+	"github.com/cihub/seelog/archive/tar"
+	"github.com/cihub/seelog/archive/zip"
+	"github.com/cihub/seelog/io/iotest"
+)
+
+const (
+	gzipType = "gzip"
+	tarType  = "tar"
+	zipType  = "zip"
+)
+
+var types = []string{gzipType, tarType, zipType}
+
+type file struct {
+	name     string
+	contents []byte
+}
+
+var (
+	oneFile = []file{
+		{
+			name:     "file1",
+			contents: []byte("This is a single log."),
+		},
+	}
+	twoFiles = []file{
+		{
+			name:     "file1",
+			contents: []byte("This is a log."),
+		},
+		{
+			name:     "file2",
+			contents: []byte("This is another log."),
+		},
+	}
+)
+
+type testCase struct {
+	srcType, dstType string
+	in               []file
+}
+
+func copyTests() map[string]testCase {
+	// types X types X files
+	tests := make(map[string]testCase, len(types)*len(types)*2)
+	for _, srct := range types {
+		for _, dstt := range types {
+			tests[fmt.Sprintf("%s to %s: one file", srct, dstt)] = testCase{
+				srcType: srct,
+				dstType: dstt,
+				in:      oneFile,
+			}
+			// gzip does not handle more than one file
+			if srct != gzipType && dstt != gzipType {
+				tests[fmt.Sprintf("%s to %s: two files", srct, dstt)] = testCase{
+					srcType: srct,
+					dstType: dstt,
+					in:      twoFiles,
+				}
+			}
+		}
+	}
+	return tests
+}
+
+func TestCopy(t *testing.T) {
+	srcb, dstb := new(bytes.Buffer), new(bytes.Buffer)
+	for tname, tt := range copyTests() {
+		// Reset buffers between tests
+		srcb.Reset()
+		dstb.Reset()
+
+		// Last file name (needed for gzip.NewReader)
+		var fname string
+
+		// Seed the src
+		srcw := writer(t, tname, srcb, tt.srcType)
+		for _, f := range tt.in {
+			srcw.NextFile(f.name, iotest.FileInfo(t, f.contents))
+			mustCopy(t, tname, srcw, bytes.NewReader(f.contents))
+			fname = f.name
+		}
+		mustClose(t, tname, srcw)
+
+		// Perform the copy
+		srcr := reader(t, tname, srcb, tt.srcType, fname)
+		dstw := writer(t, tname, dstb, tt.dstType)
+		if err := archive.Copy(dstw, srcr); err != nil {
+			t.Fatalf("%s: %v", tname, err)
+		}
+		srcr.Close() // Read-only
+		mustClose(t, tname, dstw)
+
+		// Read back dst to confirm our expectations
+		dstr := reader(t, tname, dstb, tt.dstType, fname)
+		for _, want := range tt.in {
+			buf := new(bytes.Buffer)
+			name, err := dstr.NextFile()
+			if err != nil {
+				t.Fatalf("%s: %v", tname, err)
+			}
+			mustCopy(t, tname, buf, dstr)
+			got := file{
+				name:     name,
+				contents: buf.Bytes(),
+			}
+
+			switch {
+			case got.name != want.name:
+				t.Errorf("%s: got file %q but want file %q",
+					tname, got.name, want.name)
+
+			case !bytes.Equal(got.contents, want.contents):
+				t.Errorf("%s: mismatched contents in %q: got %q but want %q",
+					tname, got.name, got.contents, want.contents)
+			}
+		}
+		dstr.Close()
+	}
+}
+
+func writer(t *testing.T, tname string, w io.Writer, atype string) archive.WriteCloser {
+	switch atype {
+	case gzipType:
+		return gzip.NewWriter(w)
+	case tarType:
+		return tar.NewWriter(w)
+	case zipType:
+		return zip.NewWriter(w)
+	}
+	t.Fatalf("%s: unrecognized archive type: %s", tname, atype)
+	panic("execution continued after (*testing.T).Fatalf")
+}
+
+func reader(t *testing.T, tname string, buf *bytes.Buffer, atype string, fname string) archive.ReadCloser {
+	switch atype {
+	case gzipType:
+		gr, err := gzip.NewReader(buf, fname)
+		if err != nil {
+			t.Fatalf("%s: %v", tname, err)
+		}
+		return gr
+	case tarType:
+		return archive.NopCloser(tar.NewReader(buf))
+	case zipType:
+		zr, err := zip.NewReader(
+			bytes.NewReader(buf.Bytes()),
+			int64(buf.Len()))
+		if err != nil {
+			t.Fatalf("%s: new zip reader: %v", tname, err)
+		}
+		return archive.NopCloser(zr)
+	}
+	t.Fatalf("%s: unrecognized archive type: %s", tname, atype)
+	panic("execution continued after (*testing.T).Fatalf")
+}
+
+func mustCopy(t *testing.T, tname string, dst io.Writer, src io.Reader) {
+	if _, err := io.Copy(dst, src); err != nil {
+		t.Fatalf("%s: copy: %v", tname, err)
+	}
+}
+
+func mustClose(t *testing.T, tname string, c io.Closer) {
+	if err := c.Close(); err != nil {
+		t.Fatalf("%s: close: %v", tname, err)
+	}
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/archive/gzip/gzip.go b/traffic_stats/vendor/github.com/cihub/seelog/archive/gzip/gzip.go
new file mode 100644
index 0000000..ea12101
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/archive/gzip/gzip.go
@@ -0,0 +1,64 @@
+// Package gzip implements reading and writing of gzip format compressed files.
+// See the compress/gzip package for more details.
+package gzip
+
+import (
+	"compress/gzip"
+	"fmt"
+	"io"
+	"os"
+)
+
+// Reader is an io.Reader that can be read to retrieve uncompressed data from a
+// gzip-format compressed file.
+type Reader struct {
+	gzip.Reader
+	name  string
+	isEOF bool
+}
+
+// NewReader creates a new Reader reading the given reader.
+func NewReader(r io.Reader, name string) (*Reader, error) {
+	gr, err := gzip.NewReader(r)
+	if err != nil {
+		return nil, err
+	}
+	return &Reader{
+		Reader: *gr,
+		name:   name,
+	}, nil
+}
+
+// NextFile returns the file name. Calls subsequent to the first call will
+// return EOF.
+func (r *Reader) NextFile() (name string, err error) {
+	if r.isEOF {
+		return "", io.EOF
+	}
+
+	r.isEOF = true
+	return r.name, nil
+}
+
+// Writer is an io.WriteCloser. Writes to a Writer are compressed and written to w.
+type Writer struct {
+	gzip.Writer
+	name        string
+	noMoreFiles bool
+}
+
+// NextFile never returns a next file, and should not be called more than once.
+func (w *Writer) NextFile(name string, _ os.FileInfo) error {
+	if w.noMoreFiles {
+		return fmt.Errorf("gzip: only accepts one file: already received %q and now %q", w.name, name)
+	}
+	w.noMoreFiles = true
+	w.name = name
+	return nil
+}
+
+// NewWriter returns a new Writer. Writes to the returned writer are compressed
+// and written to w.
+func NewWriter(w io.Writer) *Writer {
+	return &Writer{Writer: *gzip.NewWriter(w)}
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/archive/tar/tar.go b/traffic_stats/vendor/github.com/cihub/seelog/archive/tar/tar.go
new file mode 100644
index 0000000..8dd87f5
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/archive/tar/tar.go
@@ -0,0 +1,72 @@
+package tar
+
+import (
+	"archive/tar"
+	"io"
+	"os"
+)
+
+// Reader provides sequential access to the contents of a tar archive.
+type Reader struct {
+	tar.Reader
+}
+
+// NewReader creates a new Reader reading from r.
+func NewReader(r io.Reader) *Reader {
+	return &Reader{Reader: *tar.NewReader(r)}
+}
+
+// NextFile advances to the next file in the tar archive.
+func (r *Reader) NextFile() (name string, err error) {
+	hdr, err := r.Next()
+	if err != nil {
+		return "", err
+	}
+	return hdr.Name, nil
+}
+
+// Writer provides sequential writing of a tar archive in POSIX.1 format.
+type Writer struct {
+	tar.Writer
+	closers []io.Closer
+}
+
+// NewWriter creates a new Writer writing to w.
+func NewWriter(w io.Writer) *Writer {
+	return &Writer{Writer: *tar.NewWriter(w)}
+}
+
+// NewWriteMultiCloser creates a new Writer writing to w that also closes all
+// closers in order on close.
+func NewWriteMultiCloser(w io.WriteCloser, closers ...io.Closer) *Writer {
+	return &Writer{
+		Writer:  *tar.NewWriter(w),
+		closers: closers,
+	}
+}
+
+// NextFile computes and writes a header and prepares to accept the file's
+// contents.
+func (w *Writer) NextFile(name string, fi os.FileInfo) error {
+	if name == "" {
+		name = fi.Name()
+	}
+	hdr, err := tar.FileInfoHeader(fi, name)
+	if err != nil {
+		return err
+	}
+	hdr.Name = name
+	return w.WriteHeader(hdr)
+}
+
+// Close closes the tar archive and all other closers, flushing any unwritten
+// data to the underlying writer.
+func (w *Writer) Close() error {
+	err := w.Writer.Close()
+	for _, c := range w.closers {
+		if cerr := c.Close(); cerr != nil && err == nil {
+			err = cerr
+		}
+	}
+	return err
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/archive/tar/tar_test.go b/traffic_stats/vendor/github.com/cihub/seelog/archive/tar/tar_test.go
new file mode 100644
index 0000000..eeb5b44
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/archive/tar/tar_test.go
@@ -0,0 +1,104 @@
+package tar_test
+
+import (
+	"bytes"
+	"io"
+	"io/ioutil"
+	"os"
+	"testing"
+
+	"github.com/cihub/seelog/archive/tar"
+	"github.com/cihub/seelog/io/iotest"
+)
+
+type file struct {
+	name     string
+	contents []byte
+}
+
+var tarTests = map[string]struct{ want []file }{
+	"one file": {
+		want: []file{
+			{
+				name:     "file",
+				contents: []byte("I am a log file"),
+			},
+		},
+	},
+	"multiple files": {
+		want: []file{
+			{
+				name:     "file1",
+				contents: []byte("I am log file 1"),
+			},
+			{
+				name:     "file2",
+				contents: []byte("I am log file 2"),
+			},
+		},
+	},
+}
+
+func TestWriterAndReader(t *testing.T) {
+	for tname, tt := range tarTests {
+		f, cleanup := iotest.TempFile(t)
+		defer cleanup()
+		writeFiles(t, f, tname, tt.want)
+		readFiles(t, f, tname, tt.want)
+	}
+}
+
+// writeFiles iterates through the files we want and writes them as a tarred
+// file.
+func writeFiles(t *testing.T, f *os.File, tname string, want []file) {
+	w := tar.NewWriter(f)
+	defer w.Close()
+
+	// Write zipped files
+	for _, fwant := range want {
+		fi := iotest.FileInfo(t, fwant.contents)
+
+		// Write the file
+		err := w.NextFile(fwant.name, fi)
+		switch err {
+		case io.EOF:
+			break
+		default:
+			t.Fatalf("%s: write header for next file: %v", tname, err)
+		case nil: // Proceed below
+		}
+		if _, err := io.Copy(w, bytes.NewReader(fwant.contents)); err != nil {
+			t.Fatalf("%s: copy to writer: %v", tname, err)
+		}
+	}
+}
+
+// readFiles iterates through tarred files and ensures they are the same.
+func readFiles(t *testing.T, f *os.File, tname string, want []file) {
+	r := tar.NewReader(f)
+
+	for _, fwant := range want {
+		fname, err := r.NextFile()
+		switch err {
+		case io.EOF:
+			return
+		default:
+			t.Fatalf("%s: read header for next file: %v", tname, err)
+		case nil: // Proceed below
+		}
+
+		if fname != fwant.name {
+			t.Fatalf("%s: incorrect file name: got %q but want %q", tname, fname, fwant.name)
+			continue
+		}
+
+		gotContents, err := ioutil.ReadAll(r)
+		if err != nil {
+			t.Fatalf("%s: read file: %v", tname, err)
+		}
+
+		if !bytes.Equal(gotContents, fwant.contents) {
+			t.Errorf("%s: %q = %q but want %q", tname, fname, gotContents, fwant.contents)
+		}
+	}
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/archive/zip/zip.go b/traffic_stats/vendor/github.com/cihub/seelog/archive/zip/zip.go
new file mode 100644
index 0000000..4210b03
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/archive/zip/zip.go
@@ -0,0 +1,89 @@
+package zip
+
+import (
+	"archive/zip"
+	"io"
+	"os"
+)
+
+// Reader provides sequential access to the contents of a zip archive.
+type Reader struct {
+	zip.Reader
+	unread []*zip.File
+	rc     io.ReadCloser
+}
+
+// NewReader returns a new Reader reading from r, which is assumed to have the
+// given size in bytes.
+func NewReader(r io.ReaderAt, size int64) (*Reader, error) {
+	zr, err := zip.NewReader(r, size)
+	if err != nil {
+		return nil, err
+	}
+	return &Reader{Reader: *zr}, nil
+}
+
+// NextFile advances to the next file in the zip archive.
+func (r *Reader) NextFile() (name string, err error) {
+	// Initialize unread
+	if r.unread == nil {
+		r.unread = r.Files()[:]
+	}
+
+	// Close previous file
+	if r.rc != nil {
+		r.rc.Close() // Read-only
+	}
+
+	if len(r.unread) == 0 {
+		return "", io.EOF
+	}
+
+	// Open and return next unread
+	f := r.unread[0]
+	name, r.unread = f.Name, r.unread[1:]
+	r.rc, err = f.Open()
+	if err != nil {
+		return "", err
+	}
+	return name, nil
+}
+
+func (r *Reader) Read(p []byte) (n int, err error) {
+	return r.rc.Read(p)
+}
+
+// Files returns the full list of files in the zip archive.
+func (r *Reader) Files() []*zip.File {
+	return r.File
+}
+
+// Writer provides sequential writing of a zip archive.1 format.
+type Writer struct {
+	zip.Writer
+	w io.Writer
+}
+
+// NewWriter returns a new Writer writing to w.
+func NewWriter(w io.Writer) *Writer {
+	return &Writer{Writer: *zip.NewWriter(w)}
+}
+
+// NextFile computes and writes a header and prepares to accept the file's
+// contents.
+func (w *Writer) NextFile(name string, fi os.FileInfo) error {
+	if name == "" {
+		name = fi.Name()
+	}
+	hdr, err := zip.FileInfoHeader(fi)
+	if err != nil {
+		return err
+	}
+	hdr.Name = name
+	w.w, err = w.CreateHeader(hdr)
+	return err
+}
+
+func (w *Writer) Write(p []byte) (n int, err error) {
+	return w.w.Write(p)
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/archive/zip/zip_test.go b/traffic_stats/vendor/github.com/cihub/seelog/archive/zip/zip_test.go
new file mode 100644
index 0000000..5bec3df
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/archive/zip/zip_test.go
@@ -0,0 +1,99 @@
+package zip_test
+
+import (
+	"bytes"
+	"io"
+	"io/ioutil"
+	"os"
+	"testing"
+
+	"github.com/cihub/seelog/archive/zip"
+	"github.com/cihub/seelog/io/iotest"
+)
+
+var zipTests = map[string]struct{ want map[string][]byte }{
+	"one file": {
+		want: map[string][]byte{
+			"file": []byte("I am a log file"),
+		},
+	},
+	"multiple files": {
+		want: map[string][]byte{
+			"file1": []byte("I am log file 1"),
+			"file2": []byte("I am log file 2"),
+		},
+	},
+}
+
+func TestWriterAndReader(t *testing.T) {
+	for tname, tt := range zipTests {
+		f, cleanup := iotest.TempFile(t)
+		defer cleanup()
+		writeFiles(t, f, tname, tt.want)
+		readFiles(t, f, tname, tt.want)
+	}
+}
+
+// writeFiles iterates through the files we want and writes them as a zipped
+// file.
+func writeFiles(t *testing.T, f *os.File, tname string, want map[string][]byte) {
+	w := zip.NewWriter(f)
+	defer w.Close()
+
+	// Write zipped files
+	for fname, fbytes := range want {
+		fi := iotest.FileInfo(t, fbytes)
+
+		// Write the file
+		err := w.NextFile(fname, fi)
+		switch err {
+		case io.EOF:
+			break
+		default:
+			t.Fatalf("%s: write header for next file: %v", tname, err)
+		case nil: // Proceed below
+		}
+		if _, err := io.Copy(w, bytes.NewReader(fbytes)); err != nil {
+			t.Fatalf("%s: copy to writer: %v", tname, err)
+		}
+	}
+}
+
+// readFiles iterates through zipped files and ensures they are the same.
+func readFiles(t *testing.T, f *os.File, tname string, want map[string][]byte) {
+	// Get zip Reader
+	fi, err := f.Stat()
+	if err != nil {
+		t.Fatalf("%s: stat zipped file: %v", tname, err)
+	}
+	r, err := zip.NewReader(f, fi.Size())
+	if err != nil {
+		t.Fatalf("%s: %v", tname, err)
+	}
+
+	for {
+		fname, err := r.NextFile()
+		switch err {
+		case io.EOF:
+			return
+		default:
+			t.Fatalf("%s: read header for next file: %v", tname, err)
+		case nil: // Proceed below
+		}
+
+		wantBytes, ok := want[fname]
+		if !ok {
+			t.Errorf("%s: read unwanted file: %v", tname, fname)
+			continue
+		}
+
+		gotBytes, err := ioutil.ReadAll(r)
+		if err != nil {
+			t.Fatalf("%s: read file: %v", tname, err)
+		}
+
+		if !bytes.Equal(gotBytes, wantBytes) {
+			t.Errorf("%s: %q = %q but want %q", tname, fname, gotBytes, wantBytes)
+		}
+	}
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/behavior_adaptive_test.go b/traffic_stats/vendor/github.com/cihub/seelog/behavior_adaptive_test.go
new file mode 100644
index 0000000..e991949
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/behavior_adaptive_test.go
@@ -0,0 +1,124 @@
+// 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"
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"strconv"
+	"testing"
+)
+
+func countSequencedRowsInFile(filePath string) (int64, error) {
+	bts, err := ioutil.ReadFile(filePath)
+	if err != nil {
+		return 0, err
+	}
+
+	bufReader := bufio.NewReader(bytes.NewBuffer(bts))
+
+	var gotCounter int64
+	for {
+		line, _, bufErr := bufReader.ReadLine()
+		if bufErr != nil && bufErr != io.EOF {
+			return 0, bufErr
+		}
+
+		lineString := string(line)
+		if lineString == "" {
+			break
+		}
+
+		intVal, atoiErr := strconv.ParseInt(lineString, 10, 64)
+		if atoiErr != nil {
+			return 0, atoiErr
+		}
+
+		if intVal != gotCounter {
+			return 0, fmt.Errorf("wrong order: %d Expected: %d\n", intVal, gotCounter)
+		}
+
+		gotCounter++
+	}
+
+	return gotCounter, nil
+}
+
+func Test_Adaptive(t *testing.T) {
+	fileName := "beh_test_adaptive.log"
+	count := 100
+
+	Current.Close()
+
+	if e := tryRemoveFile(fileName); e != nil {
+		t.Error(e)
+		return
+	}
+	defer func() {
+		if e := tryRemoveFile(fileName); e != nil {
+			t.Error(e)
+		}
+	}()
+
+	testConfig := `
+<seelog type="adaptive" mininterval="1000" maxinterval="1000000" critmsgcount="100">
+	<outputs formatid="msg">
+		<file path="` + fileName + `"/>
+	</outputs>
+	<formats>
+		<format id="msg" format="%Msg%n"/>
+	</formats>
+</seelog>`
+
+	logger, _ := LoggerFromConfigAsString(testConfig)
+
+	err := ReplaceLogger(logger)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	for i := 0; i < count; i++ {
+		Trace(strconv.Itoa(i))
+	}
+
+	Flush()
+
+	gotCount, err := countSequencedRowsInFile(fileName)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if int64(count) != gotCount {
+		t.Errorf("wrong count of log messages. Expected: %v, got: %v.", count, gotCount)
+		return
+	}
+
+	Current.Close()
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/behavior_adaptivelogger.go b/traffic_stats/vendor/github.com/cihub/seelog/behavior_adaptivelogger.go
new file mode 100644
index 0000000..0c640ca
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/behavior_adaptivelogger.go
@@ -0,0 +1,129 @@
+// 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"
+	"math"
+	"time"
+)
+
+var (
+	adaptiveLoggerMaxInterval         = time.Minute
+	adaptiveLoggerMaxCriticalMsgCount = uint32(1000)
+)
+
+// asyncAdaptiveLogger represents asynchronous adaptive logger which acts like
+// an async timer logger, but its interval depends on the current message count
+// in the queue.
+//
+// Interval = I, minInterval = m, maxInterval = M, criticalMsgCount = C, msgCount = c:
+// I = m + (C - Min(c, C)) / C * (M - m)
+type asyncAdaptiveLogger struct {
+	asyncLogger
+	minInterval      time.Duration
+	criticalMsgCount uint32
+	maxInterval      time.Duration
+}
+
+// NewAsyncLoopLogger creates a new asynchronous adaptive logger
+func NewAsyncAdaptiveLogger(
+	config *logConfig,
+	minInterval time.Duration,
+	maxInterval time.Duration,
+	criticalMsgCount uint32) (*asyncAdaptiveLogger, error) {
+
+	if minInterval <= 0 {
+		return nil, errors.New("async adaptive logger min interval should be > 0")
+	}
+
+	if maxInterval > adaptiveLoggerMaxInterval {
+		return nil, fmt.Errorf("async adaptive logger max interval should be <= %s",
+			adaptiveLoggerMaxInterval)
+	}
+
+	if criticalMsgCount <= 0 {
+		return nil, errors.New("async adaptive logger critical msg count should be > 0")
+	}
+
+	if criticalMsgCount > adaptiveLoggerMaxCriticalMsgCount {
+		return nil, fmt.Errorf("async adaptive logger critical msg count should be <= %s",
+			adaptiveLoggerMaxInterval)
+	}
+
+	asnAdaptiveLogger := new(asyncAdaptiveLogger)
+
+	asnAdaptiveLogger.asyncLogger = *newAsyncLogger(config)
+	asnAdaptiveLogger.minInterval = minInterval
+	asnAdaptiveLogger.maxInterval = maxInterval
+	asnAdaptiveLogger.criticalMsgCount = criticalMsgCount
+
+	go asnAdaptiveLogger.processQueue()
+
+	return asnAdaptiveLogger, nil
+}
+
+func (asnAdaptiveLogger *asyncAdaptiveLogger) processItem() (closed bool, itemCount int) {
+	asnAdaptiveLogger.queueHasElements.L.Lock()
+	defer asnAdaptiveLogger.queueHasElements.L.Unlock()
+
+	for asnAdaptiveLogger.msgQueue.Len() == 0 && !asnAdaptiveLogger.Closed() {
+		asnAdaptiveLogger.queueHasElements.Wait()
+	}
+
+	if asnAdaptiveLogger.Closed() {
+		return true, asnAdaptiveLogger.msgQueue.Len()
+	}
+
+	asnAdaptiveLogger.processQueueElement()
+	return false, asnAdaptiveLogger.msgQueue.Len() - 1
+}
+
+// I = m + (C - Min(c, C)) / C * (M - m) =>
+// I = m + cDiff * mDiff,
+// 		cDiff = (C - Min(c, C)) / C)
+//		mDiff = (M - m)
+func (asnAdaptiveLogger *asyncAdaptiveLogger) calcAdaptiveInterval(msgCount int) time.Duration {
+	critCountF := float64(asnAdaptiveLogger.criticalMsgCount)
+	cDiff := (critCountF - math.Min(float64(msgCount), critCountF)) / critCountF
+	mDiff := float64(asnAdaptiveLogger.maxInterval - asnAdaptiveLogger.minInterval)
+
+	return asnAdaptiveLogger.minInterval + time.Duration(cDiff*mDiff)
+}
+
+func (asnAdaptiveLogger *asyncAdaptiveLogger) processQueue() {
+	for !asnAdaptiveLogger.Closed() {
+		closed, itemCount := asnAdaptiveLogger.processItem()
+
+		if closed {
+			break
+		}
+
+		interval := asnAdaptiveLogger.calcAdaptiveInterval(itemCount)
+
+		<-time.After(interval)
+	}
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/behavior_asynclogger.go b/traffic_stats/vendor/github.com/cihub/seelog/behavior_asynclogger.go
new file mode 100644
index 0000000..7523106
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/behavior_asynclogger.go
@@ -0,0 +1,142 @@
+// 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 (
+	"container/list"
+	"fmt"
+	"sync"
+)
+
+// MaxQueueSize is the critical number of messages in the queue that result in an immediate flush.
+const (
+	MaxQueueSize = 10000
+)
+
+type msgQueueItem struct {
+	level   LogLevel
+	context LogContextInterface
+	message fmt.Stringer
+}
+
+// asyncLogger represents common data for all asynchronous loggers
+type asyncLogger struct {
+	commonLogger
+	msgQueue         *list.List
+	queueHasElements *sync.Cond
+}
+
+// newAsyncLogger creates a new asynchronous logger
+func newAsyncLogger(config *logConfig) *asyncLogger {
+	asnLogger := new(asyncLogger)
+
+	asnLogger.msgQueue = list.New()
+	asnLogger.queueHasElements = sync.NewCond(new(sync.Mutex))
+
+	asnLogger.commonLogger = *newCommonLogger(config, asnLogger)
+
+	return asnLogger
+}
+
+func (asnLogger *asyncLogger) innerLog(
+	level LogLevel,
+	context LogContextInterface,
+	message fmt.Stringer) {
+
+	asnLogger.addMsgToQueue(level, context, message)
+}
+
+func (asnLogger *asyncLogger) Close() {
+	asnLogger.m.Lock()
+	defer asnLogger.m.Unlock()
+
+	if !asnLogger.Closed() {
+		asnLogger.flushQueue(true)
+		asnLogger.config.RootDispatcher.Flush()
+
+		if err := asnLogger.config.RootDispatcher.Close(); err != nil {
+			reportInternalError(err)
+		}
+
+		asnLogger.closedM.Lock()
+		asnLogger.closed = true
+		asnLogger.closedM.Unlock()
+		asnLogger.queueHasElements.Broadcast()
+	}
+}
+
+func (asnLogger *asyncLogger) Flush() {
+	asnLogger.m.Lock()
+	defer asnLogger.m.Unlock()
+
+	if !asnLogger.Closed() {
+		asnLogger.flushQueue(true)
+		asnLogger.config.RootDispatcher.Flush()
+	}
+}
+
+func (asnLogger *asyncLogger) flushQueue(lockNeeded bool) {
+	if lockNeeded {
+		asnLogger.queueHasElements.L.Lock()
+		defer asnLogger.queueHasElements.L.Unlock()
+	}
+
+	for asnLogger.msgQueue.Len() > 0 {
+		asnLogger.processQueueElement()
+	}
+}
+
+func (asnLogger *asyncLogger) processQueueElement() {
+	if asnLogger.msgQueue.Len() > 0 {
+		backElement := asnLogger.msgQueue.Front()
+		msg, _ := backElement.Value.(msgQueueItem)
+		asnLogger.processLogMsg(msg.level, msg.message, msg.context)
+		asnLogger.msgQueue.Remove(backElement)
+	}
+}
+
+func (asnLogger *asyncLogger) addMsgToQueue(
+	level LogLevel,
+	context LogContextInterface,
+	message fmt.Stringer) {
+
+	if !asnLogger.Closed() {
+		asnLogger.queueHasElements.L.Lock()
+		defer asnLogger.queueHasElements.L.Unlock()
+
+		if asnLogger.msgQueue.Len() >= MaxQueueSize {
+			fmt.Printf("Seelog queue overflow: more than %v messages in the queue. Flushing.\n", MaxQueueSize)
+			asnLogger.flushQueue(false)
+		}
+
+		queueItem := msgQueueItem{level, context, message}
+
+		asnLogger.msgQueue.PushBack(queueItem)
+		asnLogger.queueHasElements.Broadcast()
+	} else {
+		err := fmt.Errorf("queue closed! Cannot process element: %d %#v", level, message)
+		reportInternalError(err)
+	}
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/behavior_asyncloop_test.go b/traffic_stats/vendor/github.com/cihub/seelog/behavior_asyncloop_test.go
new file mode 100644
index 0000000..142c4fc
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/behavior_asyncloop_test.go
@@ -0,0 +1,133 @@
+// 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 (
+	"strconv"
+	"testing"
+)
+
+func Test_Asyncloop(t *testing.T) {
+	fileName := "beh_test_asyncloop.log"
+	count := 100
+
+	Current.Close()
+
+	if e := tryRemoveFile(fileName); e != nil {
+		t.Error(e)
+		return
+	}
+	defer func() {
+		if e := tryRemoveFile(fileName); e != nil {
+			t.Error(e)
+		}
+	}()
+
+	testConfig := `
+<seelog type="asyncloop">
+	<outputs formatid="msg">
+		<file path="` + fileName + `"/>
+	</outputs>
+	<formats>
+		<format id="msg" format="%Msg%n"/>
+	</formats>
+</seelog>`
+
+	logger, _ := LoggerFromConfigAsString(testConfig)
+	err := ReplaceLogger(logger)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	for i := 0; i < count; i++ {
+		Trace(strconv.Itoa(i))
+	}
+
+	Flush()
+
+	gotCount, err := countSequencedRowsInFile(fileName)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if int64(count) != gotCount {
+		t.Errorf("wrong count of log messages. Expected: %v, got: %v.", count, gotCount)
+		return
+	}
+
+	Current.Close()
+}
+
+func Test_AsyncloopOff(t *testing.T) {
+	fileName := "beh_test_asyncloopoff.log"
+	count := 100
+
+	Current.Close()
+
+	if e := tryRemoveFile(fileName); e != nil {
+		t.Error(e)
+		return
+	}
+
+	testConfig := `
+<seelog type="asyncloop" levels="off">
+	<outputs formatid="msg">
+		<file path="` + fileName + `"/>
+	</outputs>
+	<formats>
+		<format id="msg" format="%Msg%n"/>
+	</formats>
+</seelog>`
+
+	logger, _ := LoggerFromConfigAsString(testConfig)
+	err := ReplaceLogger(logger)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	for i := 0; i < count; i++ {
+		Trace(strconv.Itoa(i))
+	}
+
+	Flush()
+
+	ex, err := fileExists(fileName)
+	if err != nil {
+		t.Error(err)
+	}
+	if ex {
+		t.Errorf("logger at level OFF is not expected to create log file at all.")
+		defer func() {
+			if e := tryRemoveFile(fileName); e != nil {
+				t.Error(e)
+			}
+		}()
+	}
+
+	Current.Close()
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/behavior_asynclooplogger.go b/traffic_stats/vendor/github.com/cihub/seelog/behavior_asynclooplogger.go
new file mode 100644
index 0000000..972467b
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/behavior_asynclooplogger.go
@@ -0,0 +1,69 @@
+// 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
+
+// asyncLoopLogger represents asynchronous logger which processes the log queue in
+// a 'for' loop
+type asyncLoopLogger struct {
+	asyncLogger
+}
+
+// NewAsyncLoopLogger creates a new asynchronous loop logger
+func NewAsyncLoopLogger(config *logConfig) *asyncLoopLogger {
+
+	asnLoopLogger := new(asyncLoopLogger)
+
+	asnLoopLogger.asyncLogger = *newAsyncLogger(config)
+
+	go asnLoopLogger.processQueue()
+
+	return asnLoopLogger
+}
+
+func (asnLoopLogger *asyncLoopLogger) processItem() (closed bool) {
+	asnLoopLogger.queueHasElements.L.Lock()
+	defer asnLoopLogger.queueHasElements.L.Unlock()
+
+	for asnLoopLogger.msgQueue.Len() == 0 && !asnLoopLogger.Closed() {
+		asnLoopLogger.queueHasElements.Wait()
+	}
+
+	if asnLoopLogger.Closed() {
+		return true
+	}
+
+	asnLoopLogger.processQueueElement()
+	return false
+}
+
+func (asnLoopLogger *asyncLoopLogger) processQueue() {
+	for !asnLoopLogger.Closed() {
+		closed := asnLoopLogger.processItem()
+
+		if closed {
+			break
+		}
+	}
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/behavior_asynctimer_test.go b/traffic_stats/vendor/github.com/cihub/seelog/behavior_asynctimer_test.go
new file mode 100644
index 0000000..37bfa6a
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/behavior_asynctimer_test.go
@@ -0,0 +1,83 @@
+// 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 (
+	"strconv"
+	"testing"
+)
+
+func Test_Asynctimer(t *testing.T) {
+	fileName := "beh_test_asynctimer.log"
+	count := 100
+
+	Current.Close()
+
+	if e := tryRemoveFile(fileName); e != nil {
+		t.Error(e)
+		return
+	}
+	defer func() {
+		if e := tryRemoveFile(fileName); e != nil {
+			t.Error(e)
+		}
+	}()
+
+	testConfig := `
+<seelog type="asynctimer" asyncinterval="100">
+	<outputs formatid="msg">
+		<file path="` + fileName + `"/>
+	</outputs>
+	<formats>
+		<format id="msg" format="%Msg%n"/>
+	</formats>
+</seelog>`
+
+	logger, _ := LoggerFromConfigAsString(testConfig)
+	err := ReplaceLogger(logger)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	for i := 0; i < count; i++ {
+		Trace(strconv.Itoa(i))
+	}
+
+	Flush()
+
+	gotCount, err := countSequencedRowsInFile(fileName)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if int64(count) != gotCount {
+		t.Errorf("wrong count of log messages. Expected: %v, got: %v.", count, gotCount)
+		return
+	}
+
+	Current.Close()
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/behavior_asynctimerlogger.go b/traffic_stats/vendor/github.com/cihub/seelog/behavior_asynctimerlogger.go
new file mode 100644
index 0000000..8118f20
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/behavior_asynctimerlogger.go
@@ -0,0 +1,82 @@
+// 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"
+	"time"
+)
+
+// asyncTimerLogger represents asynchronous logger which processes the log queue each
+// 'duration' nanoseconds
+type asyncTimerLogger struct {
+	asyncLogger
+	interval time.Duration
+}
+
+// NewAsyncLoopLogger creates a new asynchronous loop logger
+func NewAsyncTimerLogger(config *logConfig, interval time.Duration) (*asyncTimerLogger, error) {
+
+	if interval <= 0 {
+		return nil, errors.New("async logger interval should be > 0")
+	}
+
+	asnTimerLogger := new(asyncTimerLogger)
+
+	asnTimerLogger.asyncLogger = *newAsyncLogger(config)
+	asnTimerLogger.interval = interval
+
+	go asnTimerLogger.processQueue()
+
+	return asnTimerLogger, nil
+}
+
+func (asnTimerLogger *asyncTimerLogger) processItem() (closed bool) {
+	asnTimerLogger.queueHasElements.L.Lock()
+	defer asnTimerLogger.queueHasElements.L.Unlock()
+
+	for asnTimerLogger.msgQueue.Len() == 0 && !asnTimerLogger.Closed() {
+		asnTimerLogger.queueHasElements.Wait()
+	}
+
+	if asnTimerLogger.Closed() {
+		return true
+	}
+
+	asnTimerLogger.processQueueElement()
+	return false
+}
+
+func (asnTimerLogger *asyncTimerLogger) processQueue() {
+	for !asnTimerLogger.Closed() {
+		closed := asnTimerLogger.processItem()
+
+		if closed {
+			break
+		}
+
+		<-time.After(asnTimerLogger.interval)
+	}
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/behavior_synclogger.go b/traffic_stats/vendor/github.com/cihub/seelog/behavior_synclogger.go
new file mode 100644
index 0000000..5a022eb
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/behavior_synclogger.go
@@ -0,0 +1,75 @@
+// 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"
+)
+
+// syncLogger performs logging in the same goroutine where 'Trace/Debug/...'
+// func was called
+type syncLogger struct {
+	commonLogger
+}
+
+// NewSyncLogger creates a new synchronous logger
+func NewSyncLogger(config *logConfig) *syncLogger {
+	syncLogger := new(syncLogger)
+
+	syncLogger.commonLogger = *newCommonLogger(config, syncLogger)
+
+	return syncLogger
+}
+
+func (syncLogger *syncLogger) innerLog(
+	level LogLevel,
+	context LogContextInterface,
+	message fmt.Stringer) {
+
+	syncLogger.processLogMsg(level, message, context)
+}
+
+func (syncLogger *syncLogger) Close() {
+	syncLogger.m.Lock()
+	defer syncLogger.m.Unlock()
+
+	if !syncLogger.Closed() {
+		if err := syncLogger.config.RootDispatcher.Close(); err != nil {
+			reportInternalError(err)
+		}
+		syncLogger.closedM.Lock()
+		syncLogger.closed = true
+		syncLogger.closedM.Unlock()
+	}
+}
+
+func (syncLogger *syncLogger) Flush() {
+	syncLogger.m.Lock()
+	defer syncLogger.m.Unlock()
+
+	if !syncLogger.Closed() {
+		syncLogger.config.RootDispatcher.Flush()
+	}
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/behavior_synclogger_test.go b/traffic_stats/vendor/github.com/cihub/seelog/behavior_synclogger_test.go
new file mode 100644
index 0000000..ddcbbb6
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/behavior_synclogger_test.go
@@ -0,0 +1,81 @@
+// 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 (
+	"strconv"
+	"testing"
+)
+
+func Test_Sync(t *testing.T) {
+	fileName := "beh_test_sync.log"
+	count := 100
+
+	Current.Close()
+
+	if e := tryRemoveFile(fileName); e != nil {
+		t.Error(e)
+		return
+	}
+	defer func() {
+		if e := tryRemoveFile(fileName); e != nil {
+			t.Error(e)
+		}
+	}()
+
+	testConfig := `
+<seelog type="sync">
+	<outputs formatid="msg">
+		<file path="` + fileName + `"/>
+	</outputs>
+	<formats>
+		<format id="msg" format="%Msg%n"/>
+	</formats>
+</seelog>`
+
+	logger, _ := LoggerFromConfigAsString(testConfig)
+	err := ReplaceLogger(logger)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	for i := 0; i < count; i++ {
+		Trace(strconv.Itoa(i))
+	}
+
+	gotCount, err := countSequencedRowsInFile(fileName)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if int64(count) != gotCount {
+		t.Errorf("wrong count of log messages. Expected: %v, got: %v.", count, gotCount)
+		return
+	}
+
+	Current.Close()
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/cfg_config.go b/traffic_stats/vendor/github.com/cihub/seelog/cfg_config.go
new file mode 100644
index 0000000..76554fc
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/cfg_config.go
@@ -0,0 +1,212 @@
+// 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"
+	"encoding/xml"
+	"fmt"
+	"io"
+	"os"
+)
+
+// LoggerFromConfigAsFile creates logger with config from file. File should contain valid seelog xml.
+func LoggerFromConfigAsFile(fileName string) (LoggerInterface, error) {
+	file, err := os.Open(fileName)
+	if err != nil {
+		return nil, err
+	}
+	defer file.Close()
+
+	conf, err := configFromReader(file)
+	if err != nil {
+		return nil, err
+	}
+
+	return createLoggerFromFullConfig(conf)
+}
+
+// LoggerFromConfigAsBytes creates a logger with config from bytes stream. Bytes should contain valid seelog xml.
+func LoggerFromConfigAsBytes(data []byte) (LoggerInterface, error) {
+	conf, err := configFromReader(bytes.NewBuffer(data))
+	if err != nil {
+		return nil, err
+	}
+
+	return createLoggerFromFullConfig(conf)
+}
+
+// LoggerFromConfigAsString creates a logger with config from a string. String should contain valid seelog xml.
+func LoggerFromConfigAsString(data string) (LoggerInterface, error) {
+	return LoggerFromConfigAsBytes([]byte(data))
+}
+
+// LoggerFromParamConfigAsFile does the same as LoggerFromConfigAsFile, but includes special parser options.
+// See 'CfgParseParams' comments.
+func LoggerFromParamConfigAsFile(fileName string, parserParams *CfgParseParams) (LoggerInterface, error) {
+	file, err := os.Open(fileName)
+	if err != nil {
+		return nil, err
+	}
+	defer file.Close()
+
+	conf, err := configFromReaderWithConfig(file, parserParams)
+	if err != nil {
+		return nil, err
+	}
+
+	return createLoggerFromFullConfig(conf)
+}
+
+// LoggerFromParamConfigAsBytes does the same as LoggerFromConfigAsBytes, but includes special parser options.
+// See 'CfgParseParams' comments.
+func LoggerFromParamConfigAsBytes(data []byte, parserParams *CfgParseParams) (LoggerInterface, error) {
+	conf, err := configFromReaderWithConfig(bytes.NewBuffer(data), parserParams)
+	if err != nil {
+		return nil, err
+	}
+
+	return createLoggerFromFullConfig(conf)
+}
+
+// LoggerFromParamConfigAsString does the same as LoggerFromConfigAsString, but includes special parser options.
+// See 'CfgParseParams' comments.
+func LoggerFromParamConfigAsString(data string, parserParams *CfgParseParams) (LoggerInterface, error) {
+	return LoggerFromParamConfigAsBytes([]byte(data), parserParams)
+}
+
+// LoggerFromWriterWithMinLevel is shortcut for LoggerFromWriterWithMinLevelAndFormat(output, minLevel, DefaultMsgFormat)
+func LoggerFromWriterWithMinLevel(output io.Writer, minLevel LogLevel) (LoggerInterface, error) {
+	return LoggerFromWriterWithMinLevelAndFormat(output, minLevel, DefaultMsgFormat)
+}
+
+// LoggerFromWriterWithMinLevelAndFormat creates a proxy logger that uses io.Writer as the
+// receiver with minimal level = minLevel and with specified format.
+//
+// All messages with level more or equal to minLevel will be written to output and
+// formatted using the default seelog format.
+//
+// Can be called for usage with non-Seelog systems
+func LoggerFromWriterWithMinLevelAndFormat(output io.Writer, minLevel LogLevel, format string) (LoggerInterface, error) {
+	constraints, err := NewMinMaxConstraints(minLevel, CriticalLvl)
+	if err != nil {
+		return nil, err
+	}
+	formatter, err := NewFormatter(format)
+	if err != nil {
+		return nil, err
+	}
+	dispatcher, err := NewSplitDispatcher(formatter, []interface{}{output})
+	if err != nil {
+		return nil, err
+	}
+
+	conf, err := newFullLoggerConfig(constraints, make([]*LogLevelException, 0), dispatcher, syncloggerTypeFromString, nil, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	return createLoggerFromFullConfig(conf)
+}
+
+// LoggerFromXMLDecoder creates logger with config from a XML decoder starting from a specific node.
+// It should contain valid seelog xml, except for root node name.
+func LoggerFromXMLDecoder(xmlParser *xml.Decoder, rootNode xml.Token) (LoggerInterface, error) {
+	conf, err := configFromXMLDecoder(xmlParser, rootNode)
+	if err != nil {
+		return nil, err
+	}
+
+	return createLoggerFromFullConfig(conf)
+}
+
+// LoggerFromCustomReceiver creates a proxy logger that uses a CustomReceiver as the
+// receiver.
+//
+// All messages will be sent to the specified custom receiver without additional
+// formatting ('%Msg' format is used).
+//
+// Check CustomReceiver, RegisterReceiver for additional info.
+//
+// NOTE 1: CustomReceiver.AfterParse is only called when a receiver is instantiated
+// by the config parser while parsing config. So, if you are not planning to use the
+// same CustomReceiver for both proxying (via LoggerFromCustomReceiver call) and
+// loading from config, just leave AfterParse implementation empty.
+//
+// NOTE 2: Unlike RegisterReceiver, LoggerFromCustomReceiver takes an already initialized
+// instance that implements CustomReceiver. So, fill it with data and perform any initialization
+// logic before calling this func and it won't be lost.
+//
+// So:
+// * RegisterReceiver takes value just to get the reflect.Type from it and then
+// instantiate it as many times as config is reloaded.
+//
+// * LoggerFromCustomReceiver takes value and uses it without modification and
+// reinstantiation, directy passing it to the dispatcher tree.
+func LoggerFromCustomReceiver(receiver CustomReceiver) (LoggerInterface, error) {
+	constraints, err := NewMinMaxConstraints(TraceLvl, CriticalLvl)
+	if err != nil {
+		return nil, err
+	}
+
+	output, err := NewCustomReceiverDispatcherByValue(msgonlyformatter, receiver, "user-proxy", CustomReceiverInitArgs{})
+	if err != nil {
+		return nil, err
+	}
+	dispatcher, err := NewSplitDispatcher(msgonlyformatter, []interface{}{output})
+	if err != nil {
+		return nil, err
+	}
+
+	conf, err := newFullLoggerConfig(constraints, make([]*LogLevelException, 0), dispatcher, syncloggerTypeFromString, nil, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	return createLoggerFromFullConfig(conf)
+}
+
+func CloneLogger(logger LoggerInterface) (LoggerInterface, error) {
+	switch logger := logger.(type) {
+	default:
+		return nil, fmt.Errorf("unexpected type %T", logger)
+	case *asyncAdaptiveLogger:
+		clone, err := NewAsyncAdaptiveLogger(logger.commonLogger.config, logger.minInterval, logger.maxInterval, logger.criticalMsgCount)
+		if err != nil {
+			return nil, err
+		}
+		return clone, nil
+	case *asyncLoopLogger:
+		return NewAsyncLoopLogger(logger.commonLogger.config), nil
+	case *asyncTimerLogger:
+		clone, err := NewAsyncTimerLogger(logger.commonLogger.config, logger.interval)
+		if err != nil {
+			return nil, err
+		}
+		return clone, nil
+	case *syncLogger:
+		return NewSyncLogger(logger.commonLogger.config), nil
+	}
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/cfg_errors.go b/traffic_stats/vendor/github.com/cihub/seelog/cfg_errors.go
new file mode 100644
index 0000000..c1fb4d1
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/cfg_errors.go
@@ -0,0 +1,61 @@
+// 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"
+)
+
+var (
+	errNodeMustHaveChildren   = errors.New("node must have children")
+	errNodeCannotHaveChildren = errors.New("node cannot have children")
+)
+
+type unexpectedChildElementError struct {
+	baseError
+}
+
+func newUnexpectedChildElementError(msg string) *unexpectedChildElementError {
+	custmsg := "Unexpected child element: " + msg
+	return &unexpectedChildElementError{baseError{message: custmsg}}
+}
+
+type missingArgumentError struct {
+	baseError
+}
+
+func newMissingArgumentError(nodeName, attrName string) *missingArgumentError {
+	custmsg := "Output '" + nodeName + "' has no '" + attrName + "' attribute"
+	return &missingArgumentError{baseError{message: custmsg}}
+}
+
+type unexpectedAttributeError struct {
+	baseError
+}
+
+func newUnexpectedAttributeError(nodeName, attr string) *unexpectedAttributeError {
+	custmsg := nodeName + " has unexpected attribute: " + attr
+	return &unexpectedAttributeError{baseError{message: custmsg}}
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/cfg_logconfig.go b/traffic_stats/vendor/github.com/cihub/seelog/cfg_logconfig.go
new file mode 100644
index 0000000..6ba6f9a
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/cfg_logconfig.go
@@ -0,0 +1,141 @@
+// 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"
+)
+
+type loggerTypeFromString uint8
+
+const (
+	syncloggerTypeFromString = iota
+	asyncLooploggerTypeFromString
+	asyncTimerloggerTypeFromString
+	adaptiveLoggerTypeFromString
+	defaultloggerTypeFromString = asyncLooploggerTypeFromString
+)
+
+const (
+	syncloggerTypeFromStringStr       = "sync"
+	asyncloggerTypeFromStringStr      = "asyncloop"
+	asyncTimerloggerTypeFromStringStr = "asynctimer"
+	adaptiveLoggerTypeFromStringStr   = "adaptive"
+)
+
+// asyncTimerLoggerData represents specific data for async timer logger
+type asyncTimerLoggerData struct {
+	AsyncInterval uint32
+}
+
+// adaptiveLoggerData represents specific data for adaptive timer logger
+type adaptiveLoggerData struct {
+	MinInterval      uint32
+	MaxInterval      uint32
+	CriticalMsgCount uint32
+}
+
+var loggerTypeToStringRepresentations = map[loggerTypeFromString]string{
+	syncloggerTypeFromString:       syncloggerTypeFromStringStr,
+	asyncLooploggerTypeFromString:  asyncloggerTypeFromStringStr,
+	asyncTimerloggerTypeFromString: asyncTimerloggerTypeFromStringStr,
+	adaptiveLoggerTypeFromString:   adaptiveLoggerTypeFromStringStr,
+}
+
+// getLoggerTypeFromString parses a string and returns a corresponding logger type, if successful.
+func getLoggerTypeFromString(logTypeString string) (level loggerTypeFromString, found bool) {
+	for logType, logTypeStr := range loggerTypeToStringRepresentations {
+		if logTypeStr == logTypeString {
+			return logType, true
+		}
+	}
+
+	return 0, false
+}
+
+// logConfig stores logging configuration. Contains messages dispatcher, allowed log level rules
+// (general constraints and exceptions)
+type logConfig struct {
+	Constraints    logLevelConstraints  // General log level rules (>min and <max, or set of allowed levels)
+	Exceptions     []*LogLevelException // Exceptions to general rules for specific files or funcs
+	RootDispatcher dispatcherInterface  // Root of output tree
+}
+
+func NewLoggerConfig(c logLevelConstraints, e []*LogLevelException, d dispatcherInterface) *logConfig {
+	return &logConfig{c, e, d}
+}
+
+// configForParsing is used when parsing config from file: logger type is deduced from string, params
+// need to be converted from attributes to values and passed to specific logger constructor. Also,
+// custom registered receivers and other parse params are used in this case.
+type configForParsing struct {
+	logConfig
+	LogType    loggerTypeFromString
+	LoggerData interface{}
+	Params     *CfgParseParams // Check cfg_parser: CfgParseParams
+}
+
+func newFullLoggerConfig(
+	constraints logLevelConstraints,
+	exceptions []*LogLevelException,
+	rootDispatcher dispatcherInterface,
+	logType loggerTypeFromString,
+	logData interface{},
+	cfgParams *CfgParseParams) (*configForParsing, error) {
+	if constraints == nil {
+		return nil, errors.New("constraints can not be nil")
+	}
+	if rootDispatcher == nil {
+		return nil, errors.New("rootDispatcher can not be nil")
+	}
+
+	config := new(configForParsing)
+	config.Constraints = constraints
+	config.Exceptions = exceptions
+	config.RootDispatcher = rootDispatcher
+	config.LogType = logType
+	config.LoggerData = logData
+	config.Params = cfgParams
+
+	return config, nil
+}
+
+// IsAllowed returns true if logging with specified log level is allowed in current context.
+// If any of exception patterns match current context, then exception constraints are applied. Otherwise,
+// the general constraints are used.
+func (config *logConfig) IsAllowed(level LogLevel, context LogContextInterface) bool {
+	allowed := config.Constraints.IsAllowed(level) // General rule
+
+	// Exceptions:
+	if context.IsValid() {
+		for _, exception := range config.Exceptions {
+			if exception.MatchesContext(context) {
+				return exception.IsAllowed(level)
+			}
+		}
+	}
+
+	return allowed
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/cfg_logconfig_test.go b/traffic_stats/vendor/github.com/cihub/seelog/cfg_logconfig_test.go
new file mode 100644
index 0000000..af64a6c
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/cfg_logconfig_test.go
@@ -0,0 +1,99 @@
+// 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"
+)
+
+func TestConfig(t *testing.T) {
+	testConfig :=
+		`
+<seelog levels="trace, debug">
+	<exceptions>
+		<exception funcpattern="*getFirst*" filepattern="*" minlevel="off" />
+		<exception funcpattern="*getSecond*" filepattern="*" levels="info, error" />
+	</exceptions>
+</seelog>
+`
+
+	conf, err := configFromReader(strings.NewReader(testConfig))
+	if err != nil {
+		t.Errorf("parse error: %s\n", err.Error())
+		return
+	}
+
+	context, err := currentContext(nil)
+	if err != nil {
+		t.Errorf("cannot get current context:" + err.Error())
+		return
+	}
+	firstContext, err := getFirstContext()
+	if err != nil {
+		t.Errorf("cannot get current context:" + err.Error())
+		return
+	}
+	secondContext, err := getSecondContext()
+	if err != nil {
+		t.Errorf("cannot get current context:" + err.Error())
+		return
+	}
+
+	if !conf.IsAllowed(TraceLvl, context) {
+		t.Errorf("error: deny trace in current context")
+	}
+	if conf.IsAllowed(TraceLvl, firstContext) {
+		t.Errorf("error: allow trace in first context")
+	}
+	if conf.IsAllowed(ErrorLvl, context) {
+		t.Errorf("error: allow error in current context")
+	}
+	if !conf.IsAllowed(ErrorLvl, secondContext) {
+		t.Errorf("error: deny error in second context")
+	}
+
+	// cache test
+	if !conf.IsAllowed(TraceLvl, context) {
+		t.Errorf("error: deny trace in current context")
+	}
+	if conf.IsAllowed(TraceLvl, firstContext) {
+		t.Errorf("error: allow trace in first context")
+	}
+	if conf.IsAllowed(ErrorLvl, context) {
+		t.Errorf("error: allow error in current context")
+	}
+	if !conf.IsAllowed(ErrorLvl, secondContext) {
+		t.Errorf("error: deny error in second context")
+	}
+}
+
+func getFirstContext() (LogContextInterface, error) {
+	return currentContext(nil)
+}
+
+func getSecondContext() (LogContextInterface, error) {
+	return currentContext(nil)
+}
diff --git a/traffic_stats/vendor/github.com/cihub/seelog/cfg_parser.go b/traffic_stats/vendor/github.com/cihub/seelog/cfg_parser.go
new file mode 100644
index 0000000..921bc16
--- /dev/null
+++ b/traffic_stats/vendor/github.com/cihub/seelog/cfg_parser.go
@@ -0,0 +1,1269 @@
+// 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"
+	"encoding/xml"
+	"errors"
+	"fmt"
+	"io"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// Names of elements of seelog config.
+const (
+	seelogConfigID                   = "seelog"
+	outputsID                        = "outputs"
+	formatsID                        = "formats"
+	minLevelID                       = "minlevel"
+	maxLevelID                       = "maxlevel"
+	levelsID                         = "levels"
+	exceptionsID                     = "exceptions"
+	exceptionID                      = "exception"
+	funcPatternID                    = "funcpattern"
+	filePatternID                    = "filepattern"
+	formatID                         = "format"
+	formatAttrID                     = "format"
+	formatKeyAttrID                  = "id"
+	outputFormatID                   = "formatid"
+	pathID                           = "path"
+	fileWriterID                     = "file"
+	smtpWriterID                     = "smtp"
+	senderaddressID                  = "senderaddress"
+	senderNameID                     = "sendername"
+	recipientID                      = "recipient"
+	mailHeaderID                     = "header"
+	mailHeaderNameID                 = "name"
+	mailHeaderValueID                = "value"
+	addressID                        = "address"
+	hostNameID                       = "hostname"
+	hostPortID                       = "hostport"
+	userNameID                       = "username"
+	userPassID                       = "password"
+	cACertDirpathID                  = "cacertdirpath"
+	subjectID                        = "subject"
+	splitterDispatcherID             = "splitter"
+	consoleWriterID                  = "console"
+	customReceiverID                 = "custom"
+	customNameAttrID                 = "name"
+	customNameDataAttrPrefix         = "data-"
+	filterDispatcherID               = "filter"
+	filterLevelsAttrID               = "levels"
+	rollingfileWriterID              = "rollingfile"
+	rollingFileTypeAttr              = "type"
+	rollingFilePathAttr              = "filename"
+	rollingFileMaxSizeAttr           = "maxsize"
+	rollingFileMaxRollsAttr          = "maxrolls"
+	rollingFileNameModeAttr          = "namemode"
+	rollingFileDataPatternAttr       = "datepattern"
+	rollingFileArchiveAttr           = "archivetype"
+	rollingFileArchivePathAttr       = "archivepath"
+	rollingFileArchiveExplodedAttr   = "archiveexploded"
+	rollingFileFullNameAttr          = "fullname"
+	bufferedWriterID                 = "buffered"
+	bufferedSizeAttr                 = "size"
+	bufferedFlushPeriodAttr          = "flushperiod"
+	loggerTypeFromStringAttr         = "type"
+	asyncLoggerIntervalAttr          = "asyncinterval"
+	adaptLoggerMinIntervalAttr       = "mininterval"
+	adaptLoggerMaxIntervalAttr       = "maxinterval"
+	adaptLoggerCriticalMsgCountAttr  = "critmsgcount"
+	predefinedPrefix                 = "std:"
+	connWriterID                     = "conn"
+	connWriterAddrAttr               = "addr"
+	connWriterNetAttr                = "net"
+	connWriterReconnectOnMsgAttr     = "reconnectonmsg"
+	connWriterUseTLSAttr             = "tls"
+	connWriterInsecureSkipVerifyAttr = "insecureskipverify"
+)
+
+// CustomReceiverProducer is the signature of the function CfgParseParams needs to create
+// custom receivers.
+type CustomReceiverProducer func(CustomReceiverInitArgs) (CustomReceiver, error)
+
+// CfgParseParams represent specific parse options or flags used by parser. It is used if seelog parser needs
+// some special directives or additional info to correctly parse a config.
+type CfgParseParams struct {
+	// CustomReceiverProducers expose the same functionality as RegisterReceiver func
+	// but only in the scope (context) of the config parse func instead of a global package scope.
+	//
+	// It means that if you use custom receivers in your code, you may either register them globally once with
+	// RegisterReceiver or you may call funcs like LoggerFromParamConfigAsFile (with 'ParamConfig')
+	// and use CustomReceiverProducers to provide custom producer funcs.
+	//
+	// A producer func is called when config parser processes a '<custom>' element. It takes the 'name' attribute
+	// of the element and tries to find a match in two places:
+	// 1) CfgParseParams.CustomReceiverProducers map
+	// 2) Global type map, filled by RegisterReceiver
+	//
+	// If a match is found in the CustomReceiverProducers map, parser calls the corresponding producer func
+	// passing the init args to it.	The func takes exactly the same args as CustomReceiver.AfterParse.
+	// The producer func must return a correct receiver or an error. If case of error, seelog will behave
+	// in the same way as with any other config error.
+	//
+	// You may use this param to set custom producers in case you need to pass some context when instantiating
+	// a custom receiver or if you frequently change custom receivers with different parameters or in any other
+	// situation where package-level registering (RegisterReceiver) is not an option for you.
+	CustomReceiverProducers map[string]CustomReceiverProducer
+}
+
+func (cfg *CfgParseParams) String() string {
+	return fmt.Sprintf("CfgParams: {custom_recs=%d}", len(cfg.CustomReceiverProducers))
+}
+
+type elementMapEntry struct {
+	constructor func(node *xmlNode, formatFromParent *formatter, formats map[string]*formatter, cfg *CfgParseParams) (interface{}, error)
+}
+
+var elementMap map[string]elementMapEntry
+var predefinedFormats map[string]*formatter
+
+func init() {
+	elementMap = map[string]elementMapEntry{
+		fileWriterID:         {createfileWriter},
+		splitterDispatcherID: {createSplitter},
+		customReceiverID:     {createCustomReceiver},
+		filterDispatcherID:   {createFilter},
+		consoleWriterID:      {createConsoleWriter},
+		rollingfileWriterID:  {createRollingFileWriter},
+		bufferedWriterID:     {createbufferedWriter},
+		smtpWriterID:         {createSMTPWriter},
+		connWriterID:         {createconnWriter},
+	}
+
+	err := fillPredefinedFormats()
+	if err != nil {
+		panic(fmt.Sprintf("Seelog couldn't start: predefined formats creation failed. Error: %s", err.Error()))
+	}
+}
+
+func fillPredefinedFormats() error {
+	predefinedFormatsWithoutPrefix := map[string]string{
+		"xml-debug":       `<time>%Ns</time><lev>%Lev</lev><msg>%Msg</msg><path>%RelFile</path><func>%Func</func><line>%Line</line>`,
+		"xml-debug-short": `<t>%Ns</t><l>%l</l><m>%Msg</m><p>%RelFile</p><f>%Func</f>`,
+		"xml":             `<time>%Ns</time><lev>%Lev</lev><msg>%Msg</msg>`,
+		"xml-short":       `<t>%Ns</t><l>%l</l><m>%Msg</m>`,
+
+		"json-debug":       `{"time":%Ns,"lev":"%Lev","msg":"%Msg","path":"%RelFile","func":"%Func","line":"%Line"}`,
+		"json-debug-short": `{"t":%Ns,"l":"%Lev","m":"%Msg","p":"%RelFile","f":"%Func"}`,
+		"json":             `{"time":%Ns,"lev":"%Lev","msg":"%Msg"}`,
+		"json-short":       `{"t":%Ns,"l":"%Lev","m":"%Msg"}`,
+
+		"debug":       `[%LEVEL] %RelFile:%Func.%Line %Date %Time %Msg%n`,
+		"debug-short": `[%LEVEL] %Date %Time %Msg%n`,
+		"fast":        `%Ns %l %Msg%n`,
+	}
+
+	predefinedFormats = make(map[string]*formatter)
+
+	for formatKey, format := range predefinedFormatsWithoutPrefix {
+		formatter, err := NewFormatter(format)
+		if err != nil {
+			return err
+		}
+
+		predefinedFormats[predefinedPrefix+formatKey] = formatter
+	}
+
+	return nil
+}
+
+// configFromXMLDecoder parses data from a given XML decoder.
+// Returns parsed config which can be used to create logger in case no errors occured.
+// Returns error if format is incorrect or anything happened.
+func configFromXMLDecoder(xmlParser *xml.Decoder, rootNode xml.Token) (*configForParsing, error) {
+	return configFromXMLDecoderWithConfig(xmlParser, rootNode, nil)
+}
+
+// configFromXMLDecoderWithConfig parses data from a given XML decoder.
+// Returns parsed config which can be used to create logger in case no errors occured.
+// Returns error if format is incorrect or anything happened.
+func configFromXMLDecoderWithConfig(xmlParser *xml.Decoder, rootNode xml.Token, cfg *CfgParseParams) (*configForParsing, error) {
+	_, ok := rootNode.(xml.StartElement)
+	if !ok {
+		return nil, errors.New("rootNode must be XML startElement")
+	}
+
+	config, err := unmarshalNode(xmlParser, rootNode)
+	if err != nil {
+		return nil, err
+	}
+	if config == nil {
+		return nil, errors.New("xml has no content")
+	}
+
+	return configFromXMLNodeWithConfig(config, cfg)
+}
+
+// configFromReader parses data from a given reader.
+// Returns parsed config which can be used to create logger in case no errors occured.
+// Returns error if format is incorrect or anything happened.
+func configFromReader(reader io.Reader) (*configForParsing, error) {
+	return configFromReaderWithConfig(reader, nil)
+}
+
+// configFromReaderWithConfig parses data from a given reader.
+// Returns parsed config which can be used to create logger in case no errors occured.
+// Returns error if format is incorrect or anything happened.
+func configFromReaderWithConfig(reader io.Reader, cfg *CfgParseParams) (*configForParsing, error) {
+	config, err := unmarshalConfig(reader)
+	if err != nil {
+		return nil, err
+	}
+
+	if config.name != seelogConfigID {
+		return nil, errors.New("root xml tag must be '" + seelogConfigID + "'")
+	}
+
+	return configFromXMLNodeWithConfig(config, cfg)
+}
+
+func configFromXMLNodeWithConfig(config *xmlNode, cfg *CfgParseParams) (*configForParsing, error) {
+	err := checkUnexpectedAttribute(
+		config,
+		minLevelID,
+		maxLevelID,
+		levelsID,
+		loggerTypeFromStringAttr,
+		asyncLoggerIntervalAttr,
+		adaptLoggerMinIntervalAttr,
+		adaptLoggerMaxIntervalAttr,
+		adaptLoggerCriticalMsgCountAttr,
+	)
+	if err != nil {
+		return nil, err
+	}
+
+	err = checkExpectedElements(config, optionalElement(outputsID), opt

<TRUNCATED>