forked from TrueCloudLab/rclone
vendor: update all dependencies to latest versions
This commit is contained in:
parent
8e83fb6fb9
commit
7d3a17725d
4878 changed files with 1974229 additions and 201215 deletions
2
vendor/github.com/pengsrc/go-shared/.travis.yml
generated
vendored
2
vendor/github.com/pengsrc/go-shared/.travis.yml
generated
vendored
|
@ -1,5 +1,7 @@
|
|||
language: go
|
||||
go:
|
||||
- master
|
||||
- 1.9
|
||||
- 1.8
|
||||
- 1.7
|
||||
- 1.6
|
||||
|
|
18
vendor/github.com/pengsrc/go-shared/Makefile
generated
vendored
18
vendor/github.com/pengsrc/go-shared/Makefile
generated
vendored
|
@ -23,32 +23,32 @@ check: format vet lint
|
|||
.PHONY: format
|
||||
format:
|
||||
@gofmt -w .
|
||||
@echo "ok"
|
||||
@echo "Done"
|
||||
|
||||
.PHONY: vet
|
||||
vet:
|
||||
@echo "go tool vet, skipping vendor packages"
|
||||
@echo "Go tool vet, skipping vendor packages"
|
||||
@go tool vet -all ${DIRS_TO_CHECK}
|
||||
@echo "ok"
|
||||
@echo "Done"
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
@echo "golint, skipping vendor packages"
|
||||
@echo "Golint, skipping vendor packages"
|
||||
@lint=$$(for pkg in ${PKGS_TO_CHECK}; do golint $${pkg}; done); \
|
||||
lint=$$(echo "$${lint}"); \
|
||||
if [[ -n $${lint} ]]; then echo "$${lint}"; exit 1; fi
|
||||
@echo "ok"
|
||||
@echo "Done"
|
||||
|
||||
.PHONY: update
|
||||
.PHONY: test
|
||||
test:
|
||||
@echo "run test"
|
||||
@echo "Run test"
|
||||
@go test -v ${PKGS_TO_CHECK}
|
||||
@echo "ok"
|
||||
@echo "Done"
|
||||
|
||||
.PHONY: test-coverage
|
||||
test-coverage:
|
||||
@echo "run test with coverage"
|
||||
@echo "Run test with coverage"
|
||||
@for pkg in ${PKGS_TO_CHECK}; do \
|
||||
output="coverage$${pkg#${PACKAGE_NAME}}"; \
|
||||
mkdir -p $${output}; \
|
||||
|
@ -58,4 +58,4 @@ test-coverage:
|
|||
-o "$${output}/profile.html"; \
|
||||
fi; \
|
||||
done
|
||||
@echo "ok"
|
||||
@echo "Done"
|
||||
|
|
2
vendor/github.com/pengsrc/go-shared/README.md
generated
vendored
2
vendor/github.com/pengsrc/go-shared/README.md
generated
vendored
|
@ -2,7 +2,7 @@
|
|||
|
||||
[](https://travis-ci.org/pengsrc/go-shared)
|
||||
[](https://goreportcard.com/report/github.com/pengsrc/go-shared)
|
||||
[](https://github.com/yunify/qingstor-sdk-go/blob/master/LICENSE)
|
||||
[](https://github.com/pengsrc/go-shared/blob/master/LICENSE)
|
||||
|
||||
Useful packages for the Go programming language.
|
||||
|
||||
|
|
102
vendor/github.com/pengsrc/go-shared/buffer/bytes.go
generated
vendored
Normal file
102
vendor/github.com/pengsrc/go-shared/buffer/bytes.go
generated
vendored
Normal file
|
@ -0,0 +1,102 @@
|
|||
// Package buffer provides a thin wrapper around a byte slice. Unlike the
|
||||
// standard library's bytes.BytesBuffer, it supports a portion of the strconv
|
||||
// package's zero-allocation formatters.
|
||||
package buffer
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BytesBuffer is a thin wrapper around a byte slice. It's intended to be
|
||||
// pooled, so the only way to construct one is via a BytesBufferPool.
|
||||
type BytesBuffer struct {
|
||||
bs []byte
|
||||
pool BytesBufferPool
|
||||
}
|
||||
|
||||
// Free returns the BytesBuffer to its BytesBufferPool.
|
||||
// Callers must not retain references to the BytesBuffer after calling Free.
|
||||
func (b *BytesBuffer) Free() {
|
||||
b.pool.put(b)
|
||||
}
|
||||
|
||||
// Len returns the length of the underlying byte slice.
|
||||
func (b *BytesBuffer) Len() int {
|
||||
return len(b.bs)
|
||||
}
|
||||
|
||||
// Cap returns the capacity of the underlying byte slice.
|
||||
func (b *BytesBuffer) Cap() int {
|
||||
return cap(b.bs)
|
||||
}
|
||||
|
||||
// Bytes returns a mutable reference to the underlying byte slice.
|
||||
func (b *BytesBuffer) Bytes() []byte {
|
||||
return b.bs
|
||||
}
|
||||
|
||||
// String returns a string copy of the underlying byte slice.
|
||||
func (b *BytesBuffer) String() string {
|
||||
return string(b.bs)
|
||||
}
|
||||
|
||||
// Reset resets the underlying byte slice. Subsequent writes re-use the slice's
|
||||
// backing array.
|
||||
func (b *BytesBuffer) Reset() {
|
||||
b.bs = b.bs[:0]
|
||||
}
|
||||
|
||||
// Write implements io.Writer.
|
||||
func (b *BytesBuffer) Write(bs []byte) (int, error) {
|
||||
b.bs = append(b.bs, bs...)
|
||||
return len(bs), nil
|
||||
}
|
||||
|
||||
// AppendByte writes a single byte to the BytesBuffer.
|
||||
func (b *BytesBuffer) AppendByte(v byte) {
|
||||
b.bs = append(b.bs, v)
|
||||
}
|
||||
|
||||
// AppendBytes writes bytes to the BytesBuffer.
|
||||
func (b *BytesBuffer) AppendBytes(bs []byte) {
|
||||
b.bs = append(b.bs, bs...)
|
||||
}
|
||||
|
||||
// AppendString writes a string to the BytesBuffer.
|
||||
func (b *BytesBuffer) AppendString(s string) {
|
||||
b.bs = append(b.bs, s...)
|
||||
}
|
||||
|
||||
// AppendInt appends an integer to the underlying buffer (assuming base 10).
|
||||
func (b *BytesBuffer) AppendInt(i int64) {
|
||||
b.bs = strconv.AppendInt(b.bs, i, 10)
|
||||
}
|
||||
|
||||
// AppendUint appends an unsigned integer to the underlying buffer (assuming
|
||||
// base 10).
|
||||
func (b *BytesBuffer) AppendUint(i uint64) {
|
||||
b.bs = strconv.AppendUint(b.bs, i, 10)
|
||||
}
|
||||
|
||||
// AppendFloat appends a float to the underlying buffer. It doesn't quote NaN
|
||||
// or +/- Inf.
|
||||
func (b *BytesBuffer) AppendFloat(f float64, bitSize int) {
|
||||
b.bs = strconv.AppendFloat(b.bs, f, 'f', -1, bitSize)
|
||||
}
|
||||
|
||||
// AppendBool appends a bool to the underlying buffer.
|
||||
func (b *BytesBuffer) AppendBool(v bool) {
|
||||
b.bs = strconv.AppendBool(b.bs, v)
|
||||
}
|
||||
|
||||
// AppendTime appends a time to the underlying buffer.
|
||||
func (b *BytesBuffer) AppendTime(t time.Time, format string) {
|
||||
if format == "" {
|
||||
b.bs = strconv.AppendInt(b.bs, t.Unix(), 10)
|
||||
} else {
|
||||
b.bs = t.AppendFormat(b.bs, format)
|
||||
}
|
||||
}
|
||||
|
||||
const defaultSize = 1024 // Create 1 KiB buffers by default
|
39
vendor/github.com/pengsrc/go-shared/buffer/bytes_pool.go
generated
vendored
Normal file
39
vendor/github.com/pengsrc/go-shared/buffer/bytes_pool.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
package buffer
|
||||
|
||||
import "sync"
|
||||
|
||||
// A BytesBufferPool is a type-safe wrapper around a sync.BytesBufferPool.
|
||||
type BytesBufferPool struct {
|
||||
p *sync.Pool
|
||||
}
|
||||
|
||||
// NewBytesPool constructs a new BytesBufferPool.
|
||||
func NewBytesPool() BytesBufferPool {
|
||||
return BytesBufferPool{
|
||||
p: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &BytesBuffer{bs: make([]byte, 0, defaultSize)}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Get retrieves a BytesBuffer from the pool, creating one if necessary.
|
||||
func (p BytesBufferPool) Get() *BytesBuffer {
|
||||
buf := p.p.Get().(*BytesBuffer)
|
||||
buf.Reset()
|
||||
buf.pool = p
|
||||
return buf
|
||||
}
|
||||
|
||||
func (p BytesBufferPool) put(buf *BytesBuffer) {
|
||||
p.p.Put(buf)
|
||||
}
|
||||
|
||||
// GlobalBytesPool returns the global buffer pool.
|
||||
func GlobalBytesPool() *BytesBufferPool {
|
||||
return &bytesPool
|
||||
}
|
||||
|
||||
// bytesPool is a pool of buffer bytes.
|
||||
var bytesPool = NewBytesPool()
|
36
vendor/github.com/pengsrc/go-shared/buffer/bytes_pool_test.go
generated
vendored
Normal file
36
vendor/github.com/pengsrc/go-shared/buffer/bytes_pool_test.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
package buffer
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBuffers(t *testing.T) {
|
||||
const dummyData = "dummy data"
|
||||
p := NewBytesPool()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for g := 0; g < 10; g++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
buf := p.Get()
|
||||
assert.Zero(t, buf.Len(), "Expected truncated buffer")
|
||||
assert.NotZero(t, buf.Cap(), "Expected non-zero capacity")
|
||||
|
||||
buf.AppendString(dummyData)
|
||||
assert.Equal(t, buf.Len(), len(dummyData), "Expected buffer to contain dummy data")
|
||||
|
||||
buf.Free()
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestGlobalBytesPool(t *testing.T) {
|
||||
assert.NotNil(t, GlobalBytesPool())
|
||||
}
|
76
vendor/github.com/pengsrc/go-shared/buffer/bytes_test.go
generated
vendored
Normal file
76
vendor/github.com/pengsrc/go-shared/buffer/bytes_test.go
generated
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
package buffer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/pengsrc/go-shared/convert"
|
||||
)
|
||||
|
||||
func TestBufferWrites(t *testing.T) {
|
||||
buf := NewBytesPool().Get()
|
||||
|
||||
tests := []struct {
|
||||
desc string
|
||||
f func()
|
||||
want string
|
||||
}{
|
||||
{"AppendByte", func() { buf.AppendByte('v') }, "v"},
|
||||
{"AppendBytes", func() { buf.AppendBytes([]byte{'a', 'b', 'c'}) }, "abc"},
|
||||
{"AppendString", func() { buf.AppendString("foo") }, "foo"},
|
||||
{"AppendIntPositive", func() { buf.AppendInt(42) }, "42"},
|
||||
{"AppendIntNegative", func() { buf.AppendInt(-42) }, "-42"},
|
||||
{"AppendUint", func() { buf.AppendUint(42) }, "42"},
|
||||
{"AppendBool", func() { buf.AppendBool(true) }, "true"},
|
||||
{"AppendFloat64", func() { buf.AppendFloat(3.14, 64) }, "3.14"},
|
||||
// Intentionally introduce some floating-point error.
|
||||
{"AppendFloat32", func() { buf.AppendFloat(float64(float32(3.14)), 32) }, "3.14"},
|
||||
{"AppendTime", func() { buf.AppendTime(time.Time{}, convert.ISO8601Milli) }, "0001-01-01T00:00:00.000Z"},
|
||||
{"AppendWrite", func() { buf.Write([]byte("foo")) }, "foo"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.desc, func(t *testing.T) {
|
||||
buf.Reset()
|
||||
tt.f()
|
||||
assert.Equal(t, tt.want, buf.String(), "Unexpected buffer.String().")
|
||||
assert.Equal(t, tt.want, string(buf.Bytes()), "Unexpected string(buffer.Bytes()).")
|
||||
assert.Equal(t, len(tt.want), buf.Len(), "Unexpected buffer length.")
|
||||
// We're not writing more than a kilobyte in tests.
|
||||
assert.Equal(t, defaultSize, buf.Cap(), "Expected buffer capacity to remain constant.")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBuffers(b *testing.B) {
|
||||
// Because we use the strconv.AppendFoo functions so liberally, we can't
|
||||
// use the standard library's bytes.Buffer anyways (without incurring a
|
||||
// bunch of extra allocations). Nevertheless, let's make sure that we're
|
||||
// not losing any precious nanoseconds.
|
||||
str := strings.Repeat("a", 1024)
|
||||
slice := make([]byte, 1024)
|
||||
buf := bytes.NewBuffer(slice)
|
||||
custom := NewBytesPool().Get()
|
||||
b.Run("ByteSlice", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
slice = append(slice, str...)
|
||||
slice = slice[:0]
|
||||
}
|
||||
})
|
||||
b.Run("BytesBuffer", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf.WriteString(str)
|
||||
buf.Reset()
|
||||
}
|
||||
})
|
||||
b.Run("CustomBuffer", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
custom.AppendString(str)
|
||||
custom.Reset()
|
||||
}
|
||||
})
|
||||
}
|
17
vendor/github.com/pengsrc/go-shared/check/dir.go
generated
vendored
Normal file
17
vendor/github.com/pengsrc/go-shared/check/dir.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
package check
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Dir checks the given path, will return error if path not exists or path
|
||||
// is not directory.
|
||||
func Dir(path string) error {
|
||||
if info, err := os.Stat(path); err != nil {
|
||||
return fmt.Errorf(`directory not exists: %s`, path)
|
||||
} else if !info.IsDir() {
|
||||
return fmt.Errorf(`path is not directory: %s`, path)
|
||||
}
|
||||
return nil
|
||||
}
|
23
vendor/github.com/pengsrc/go-shared/check/dir_test.go
generated
vendored
Normal file
23
vendor/github.com/pengsrc/go-shared/check/dir_test.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
package check
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCheckDir(t *testing.T) {
|
||||
// Path not exist.
|
||||
assert.Error(t, Dir("/not-exist-dir"))
|
||||
|
||||
// Path is not directory.
|
||||
f, err := ioutil.TempFile(os.TempDir(), "test-check-dir-")
|
||||
assert.NoError(t, err)
|
||||
f.Close()
|
||||
os.Remove(f.Name())
|
||||
|
||||
// OK.
|
||||
assert.NoError(t, Dir(os.TempDir()))
|
||||
}
|
16
vendor/github.com/pengsrc/go-shared/check/error.go
generated
vendored
16
vendor/github.com/pengsrc/go-shared/check/error.go
generated
vendored
|
@ -5,6 +5,22 @@ import (
|
|||
"os"
|
||||
)
|
||||
|
||||
// ReadableError is just a structure contains readable message that can be
|
||||
// returned directly to end user.
|
||||
type ReadableError struct {
|
||||
Message string
|
||||
}
|
||||
|
||||
// Error returns the description of ReadableError.
|
||||
func (e ReadableError) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
// NewReadableError creates a ReadableError{} from given message.
|
||||
func NewReadableError(message string) ReadableError {
|
||||
return ReadableError{Message: message}
|
||||
}
|
||||
|
||||
// ErrorForExit check the error.
|
||||
// If error is not nil, print the error message and exit the application.
|
||||
// If error is nil, do nothing.
|
||||
|
|
10
vendor/github.com/pengsrc/go-shared/convert/string.go
generated
vendored
Normal file
10
vendor/github.com/pengsrc/go-shared/convert/string.go
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
package convert
|
||||
|
||||
// StringSliceWithConverter converts a list of string using the passed converter function
|
||||
func StringSliceWithConverter(s []string, c func(string) string) []string {
|
||||
out := []string{}
|
||||
for _, i := range s {
|
||||
out = append(out, c(i))
|
||||
}
|
||||
return out
|
||||
}
|
14
vendor/github.com/pengsrc/go-shared/convert/string_test.go
generated
vendored
Normal file
14
vendor/github.com/pengsrc/go-shared/convert/string_test.go
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
package convert
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStringSliceWithConverter(t *testing.T) {
|
||||
s := StringSliceWithConverter([]string{"A", "b", "C"}, strings.ToLower)
|
||||
e := []string{"a", "b", "c"}
|
||||
if s[0] != e[0] || s[1] != e[1] || s[2] != e[2] {
|
||||
t.Errorf("%v != %v", s, e)
|
||||
}
|
||||
}
|
4
vendor/github.com/pengsrc/go-shared/convert/time.go
generated
vendored
4
vendor/github.com/pengsrc/go-shared/convert/time.go
generated
vendored
|
@ -37,6 +37,10 @@ func StringToTime(timeString string, format string) (time.Time, error) {
|
|||
|
||||
// TimeToTimestamp transforms given time to unix time int.
|
||||
func TimeToTimestamp(t time.Time) int64 {
|
||||
zero := time.Time{}
|
||||
if t == zero {
|
||||
t = time.Unix(0, 0).UTC()
|
||||
}
|
||||
return t.Unix()
|
||||
}
|
||||
|
||||
|
|
73
vendor/github.com/pengsrc/go-shared/convert/types.go
generated
vendored
73
vendor/github.com/pengsrc/go-shared/convert/types.go
generated
vendored
|
@ -299,6 +299,79 @@ func Int64ValueMap(src map[string]*int64) map[string]int64 {
|
|||
return dst
|
||||
}
|
||||
|
||||
// Int64Uint returns a uint pointer to the given int64 value.
|
||||
func Int64Uint(src int64) *uint {
|
||||
dst := uint(src)
|
||||
return &dst
|
||||
}
|
||||
|
||||
// Uint8 return a uint8 pointer to the given uint8 value.
|
||||
func Uint8(src uint8) *uint8 {
|
||||
dst := uint8(src)
|
||||
return &dst
|
||||
}
|
||||
|
||||
// Uint8Value returns the value of the given uint8 pointer or
|
||||
// 0 if the pointer is nil.
|
||||
func Uint8Value(v *uint8) uint8 {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Uint32 return a uint32 pointer to the given uint32 value.
|
||||
func Uint32(src uint32) *uint32 {
|
||||
dst := uint32(src)
|
||||
return &dst
|
||||
}
|
||||
|
||||
// Uint32Value returns the value of the given uint32 pointer or
|
||||
// 0 if the pointer is nil.
|
||||
func Uint32Value(v *uint32) uint32 {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Uint64 return a uint64 pointer to the given uint64 value.
|
||||
func Uint64(src uint64) *uint64 {
|
||||
dst := uint64(src)
|
||||
return &dst
|
||||
}
|
||||
|
||||
// Uint64Value returns the value of the given uint64 pointer or
|
||||
// 0 if the pointer is nil.
|
||||
func Uint64Value(v *uint64) uint64 {
|
||||
if v != nil {
|
||||
return *v
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Uint64Slice converts a slice of uint64 values into a slice of
|
||||
// uint64 pointers
|
||||
func Uint64Slice(src []uint64) []*uint64 {
|
||||
dst := make([]*uint64, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
dst[i] = &(src[i])
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Uint64ValueSlice converts a slice of uint64 pointers into a slice of
|
||||
// uint64 values
|
||||
func Uint64ValueSlice(src []*uint64) []uint64 {
|
||||
dst := make([]uint64, len(src))
|
||||
for i := 0; i < len(src); i++ {
|
||||
if src[i] != nil {
|
||||
dst[i] = *(src[i])
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// Float32 returns a pointer to the given float32 value.
|
||||
func Float32(v float32) *float32 {
|
||||
return &v
|
||||
|
|
236
vendor/github.com/pengsrc/go-shared/convert/types_test.go
generated
vendored
236
vendor/github.com/pengsrc/go-shared/convert/types_test.go
generated
vendored
|
@ -244,84 +244,6 @@ func TestIntMap(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestInt64Value(t *testing.T) {
|
||||
i := int64(1024)
|
||||
assert.Equal(t, i, Int64Value(Int64(i)))
|
||||
|
||||
assert.Equal(t, int64(0), Int64Value(nil))
|
||||
}
|
||||
|
||||
var testCasesInt64Slice = [][]int64{
|
||||
{1, 2, 3, 4},
|
||||
}
|
||||
|
||||
func TestInt64Slice(t *testing.T) {
|
||||
for idx, in := range testCasesInt64Slice {
|
||||
if in == nil {
|
||||
continue
|
||||
}
|
||||
out := Int64Slice(in)
|
||||
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||
for i := range out {
|
||||
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||
}
|
||||
|
||||
out2 := Int64ValueSlice(out)
|
||||
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||
}
|
||||
}
|
||||
|
||||
var testCasesInt64ValueSlice = [][]*int64{}
|
||||
|
||||
func TestInt64ValueSlice(t *testing.T) {
|
||||
for idx, in := range testCasesInt64ValueSlice {
|
||||
if in == nil {
|
||||
continue
|
||||
}
|
||||
out := Int64ValueSlice(in)
|
||||
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||
for i := range out {
|
||||
if in[i] == nil {
|
||||
assert.Empty(t, out[i], "Unexpected value at idx %d", idx)
|
||||
} else {
|
||||
assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx)
|
||||
}
|
||||
}
|
||||
|
||||
out2 := Int64Slice(out)
|
||||
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||
for i := range out2 {
|
||||
if in[i] == nil {
|
||||
assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx)
|
||||
} else {
|
||||
assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var testCasesInt64Map = []map[string]int64{
|
||||
{"a": 3, "b": 2, "c": 1},
|
||||
}
|
||||
|
||||
func TestInt64Map(t *testing.T) {
|
||||
for idx, in := range testCasesInt64Map {
|
||||
if in == nil {
|
||||
continue
|
||||
}
|
||||
out := Int64Map(in)
|
||||
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||
for i := range out {
|
||||
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||
}
|
||||
|
||||
out2 := Int64ValueMap(out)
|
||||
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt32Value(t *testing.T) {
|
||||
i := int32(1024)
|
||||
assert.Equal(t, i, Int32Value(Int32(i)))
|
||||
|
@ -400,42 +322,42 @@ func TestInt32Map(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestFloat64Value(t *testing.T) {
|
||||
i := float64(1024)
|
||||
assert.Equal(t, i, Float64Value(Float64(i)))
|
||||
func TestInt64Value(t *testing.T) {
|
||||
i := int64(1024)
|
||||
assert.Equal(t, i, Int64Value(Int64(i)))
|
||||
|
||||
assert.Equal(t, float64(0), Float64Value(nil))
|
||||
assert.Equal(t, int64(0), Int64Value(nil))
|
||||
}
|
||||
|
||||
var testCasesFloat64Slice = [][]float64{
|
||||
var testCasesInt64Slice = [][]int64{
|
||||
{1, 2, 3, 4},
|
||||
}
|
||||
|
||||
func TestFloat64Slice(t *testing.T) {
|
||||
for idx, in := range testCasesFloat64Slice {
|
||||
func TestInt64Slice(t *testing.T) {
|
||||
for idx, in := range testCasesInt64Slice {
|
||||
if in == nil {
|
||||
continue
|
||||
}
|
||||
out := Float64Slice(in)
|
||||
out := Int64Slice(in)
|
||||
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||
for i := range out {
|
||||
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||
}
|
||||
|
||||
out2 := Float64ValueSlice(out)
|
||||
out2 := Int64ValueSlice(out)
|
||||
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||
}
|
||||
}
|
||||
|
||||
var testCasesFloat64ValueSlice = [][]*float64{}
|
||||
var testCasesInt64ValueSlice = [][]*int64{}
|
||||
|
||||
func TestFloat64ValueSlice(t *testing.T) {
|
||||
for idx, in := range testCasesFloat64ValueSlice {
|
||||
func TestInt64ValueSlice(t *testing.T) {
|
||||
for idx, in := range testCasesInt64ValueSlice {
|
||||
if in == nil {
|
||||
continue
|
||||
}
|
||||
out := Float64ValueSlice(in)
|
||||
out := Int64ValueSlice(in)
|
||||
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||
for i := range out {
|
||||
if in[i] == nil {
|
||||
|
@ -445,7 +367,7 @@ func TestFloat64ValueSlice(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
out2 := Float64Slice(out)
|
||||
out2 := Int64Slice(out)
|
||||
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||
for i := range out2 {
|
||||
if in[i] == nil {
|
||||
|
@ -457,22 +379,64 @@ func TestFloat64ValueSlice(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
var testCasesFloat64Map = []map[string]float64{
|
||||
var testCasesInt64Map = []map[string]int64{
|
||||
{"a": 3, "b": 2, "c": 1},
|
||||
}
|
||||
|
||||
func TestFloat64Map(t *testing.T) {
|
||||
for idx, in := range testCasesFloat64Map {
|
||||
func TestInt64Map(t *testing.T) {
|
||||
for idx, in := range testCasesInt64Map {
|
||||
if in == nil {
|
||||
continue
|
||||
}
|
||||
out := Float64Map(in)
|
||||
out := Int64Map(in)
|
||||
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||
for i := range out {
|
||||
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||
}
|
||||
|
||||
out2 := Float64ValueMap(out)
|
||||
out2 := Int64ValueMap(out)
|
||||
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUint8Value(t *testing.T) {
|
||||
i := uint8(128)
|
||||
assert.Equal(t, i, Uint8Value(Uint8(i)))
|
||||
|
||||
assert.Equal(t, uint8(0), Uint8Value(nil))
|
||||
}
|
||||
|
||||
func TestUint32Value(t *testing.T) {
|
||||
i := uint32(1024)
|
||||
assert.Equal(t, i, Uint32Value(Uint32(i)))
|
||||
|
||||
assert.Equal(t, uint32(0), Uint32Value(nil))
|
||||
}
|
||||
|
||||
func TestUint64Value(t *testing.T) {
|
||||
i := uint64(1024)
|
||||
assert.Equal(t, i, Uint64Value(Uint64(i)))
|
||||
|
||||
assert.Equal(t, uint64(0), Uint64Value(nil))
|
||||
}
|
||||
|
||||
var testCasesUint64Slice = [][]uint64{
|
||||
{1, 2, 3, 4},
|
||||
}
|
||||
|
||||
func TestUint64Slice(t *testing.T) {
|
||||
for idx, in := range testCasesUint64Slice {
|
||||
if in == nil {
|
||||
continue
|
||||
}
|
||||
out := Uint64Slice(in)
|
||||
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||
for i := range out {
|
||||
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||
}
|
||||
|
||||
out2 := Uint64ValueSlice(out)
|
||||
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||
}
|
||||
|
@ -556,6 +520,84 @@ func TestFloat32Map(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestFloat64Value(t *testing.T) {
|
||||
i := float64(1024)
|
||||
assert.Equal(t, i, Float64Value(Float64(i)))
|
||||
|
||||
assert.Equal(t, float64(0), Float64Value(nil))
|
||||
}
|
||||
|
||||
var testCasesFloat64Slice = [][]float64{
|
||||
{1, 2, 3, 4},
|
||||
}
|
||||
|
||||
func TestFloat64Slice(t *testing.T) {
|
||||
for idx, in := range testCasesFloat64Slice {
|
||||
if in == nil {
|
||||
continue
|
||||
}
|
||||
out := Float64Slice(in)
|
||||
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||
for i := range out {
|
||||
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||
}
|
||||
|
||||
out2 := Float64ValueSlice(out)
|
||||
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||
}
|
||||
}
|
||||
|
||||
var testCasesFloat64ValueSlice = [][]*float64{}
|
||||
|
||||
func TestFloat64ValueSlice(t *testing.T) {
|
||||
for idx, in := range testCasesFloat64ValueSlice {
|
||||
if in == nil {
|
||||
continue
|
||||
}
|
||||
out := Float64ValueSlice(in)
|
||||
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||
for i := range out {
|
||||
if in[i] == nil {
|
||||
assert.Empty(t, out[i], "Unexpected value at idx %d", idx)
|
||||
} else {
|
||||
assert.Equal(t, *(in[i]), out[i], "Unexpected value at idx %d", idx)
|
||||
}
|
||||
}
|
||||
|
||||
out2 := Float64Slice(out)
|
||||
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||
for i := range out2 {
|
||||
if in[i] == nil {
|
||||
assert.Empty(t, *(out2[i]), "Unexpected value at idx %d", idx)
|
||||
} else {
|
||||
assert.Equal(t, in[i], out2[i], "Unexpected value at idx %d", idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var testCasesFloat64Map = []map[string]float64{
|
||||
{"a": 3, "b": 2, "c": 1},
|
||||
}
|
||||
|
||||
func TestFloat64Map(t *testing.T) {
|
||||
for idx, in := range testCasesFloat64Map {
|
||||
if in == nil {
|
||||
continue
|
||||
}
|
||||
out := Float64Map(in)
|
||||
assert.Len(t, out, len(in), "Unexpected len at idx %d", idx)
|
||||
for i := range out {
|
||||
assert.Equal(t, in[i], *(out[i]), "Unexpected value at idx %d", idx)
|
||||
}
|
||||
|
||||
out2 := Float64ValueMap(out)
|
||||
assert.Len(t, out2, len(in), "Unexpected len at idx %d", idx)
|
||||
assert.Equal(t, in, out2, "Unexpected value at idx %d", idx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeValue(t *testing.T) {
|
||||
tm := time.Time{}
|
||||
assert.Equal(t, tm, TimeValue(Time(tm)))
|
||||
|
|
20
vendor/github.com/pengsrc/go-shared/glide.lock
generated
vendored
20
vendor/github.com/pengsrc/go-shared/glide.lock
generated
vendored
|
@ -1,28 +1,18 @@
|
|||
hash: d9fb8784d4c53a209ba12b5be1dc51d43b925ff53a06b5df850a9f7afa5e3e08
|
||||
updated: 2017-04-11T15:44:30.513321945+08:00
|
||||
hash: 2943a8fbc1720796d8d0a1ba8b1372e04a2bdaa6a4bd9829182a6d29aa330f42
|
||||
updated: 2017-11-25T10:26:15.990096+08:00
|
||||
imports:
|
||||
- name: github.com/Jeffail/gabs
|
||||
version: 2a3aa15961d5fee6047b8151b67ac2f08ba2c48c
|
||||
testImports:
|
||||
- name: github.com/davecgh/go-spew
|
||||
version: 346938d642f2ec3594ed81d874461961cd0faa76
|
||||
subpackages:
|
||||
- spew
|
||||
- name: github.com/Jeffail/gabs
|
||||
version: 2a3aa15961d5fee6047b8151b67ac2f08ba2c48c
|
||||
- name: github.com/pmezard/go-difflib
|
||||
version: 792786c7400a136282c1664665ae0a8db921c6c2
|
||||
subpackages:
|
||||
- difflib
|
||||
- name: github.com/Sirupsen/logrus
|
||||
version: d26492970760ca5d33129d2d799e34be5c4782eb
|
||||
- name: github.com/stretchr/testify
|
||||
version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0
|
||||
subpackages:
|
||||
- assert
|
||||
- name: golang.org/x/sys
|
||||
version: f3918c30c5c2cb527c0b071a27c35120a6c0719a
|
||||
repo: https://github.com/golang/sys.git
|
||||
subpackages:
|
||||
- unix
|
||||
- name: gopkg.in/yaml.v2
|
||||
version: a5b47d31c556af34a302ce5d659e6fea44d90de0
|
||||
repo: https://github.com/go-yaml/yaml.git
|
||||
testImports: []
|
||||
|
|
18
vendor/github.com/pengsrc/go-shared/glide.yaml
generated
vendored
18
vendor/github.com/pengsrc/go-shared/glide.yaml
generated
vendored
|
@ -1,5 +1,7 @@
|
|||
package: github.com/pengsrc/go-shared
|
||||
import:
|
||||
|
||||
testImport:
|
||||
|
||||
# Test
|
||||
- package: github.com/stretchr/testify
|
||||
version: v1.1.4
|
||||
|
@ -7,17 +9,3 @@ import:
|
|||
version: v1.1.0
|
||||
- package: github.com/pmezard/go-difflib
|
||||
version: v1.0.0
|
||||
# JSON
|
||||
- package: github.com/Jeffail/gabs
|
||||
version: 1.0
|
||||
# YAML
|
||||
- package: gopkg.in/yaml.v2
|
||||
version: a5b47d31c556af34a302ce5d659e6fea44d90de0
|
||||
repo: https://github.com/go-yaml/yaml.git
|
||||
# Logging
|
||||
- package: github.com/Sirupsen/logrus
|
||||
version: v0.11.0
|
||||
# GoLang
|
||||
- package: golang.org/x/sys
|
||||
version: f3918c30c5c2cb527c0b071a27c35120a6c0719a
|
||||
repo: https://github.com/golang/sys.git
|
||||
|
|
50
vendor/github.com/pengsrc/go-shared/json/json.go
generated
vendored
50
vendor/github.com/pengsrc/go-shared/json/json.go
generated
vendored
|
@ -1,50 +0,0 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Encode encode given interface to json byte slice.
|
||||
func Encode(source interface{}, unescape bool) ([]byte, error) {
|
||||
bytesResult, err := json.Marshal(source)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
if unescape {
|
||||
bytesResult = bytes.Replace(bytesResult, []byte("\\u003c"), []byte("<"), -1)
|
||||
bytesResult = bytes.Replace(bytesResult, []byte("\\u003e"), []byte(">"), -1)
|
||||
bytesResult = bytes.Replace(bytesResult, []byte("\\u0026"), []byte("&"), -1)
|
||||
}
|
||||
|
||||
return bytesResult, nil
|
||||
}
|
||||
|
||||
// Decode decode given json byte slice to corresponding struct.
|
||||
func Decode(content []byte, destinations ...interface{}) (interface{}, error) {
|
||||
var destination interface{}
|
||||
var err error
|
||||
if len(destinations) == 1 {
|
||||
destination = destinations[0]
|
||||
err = json.Unmarshal(content, destination)
|
||||
} else {
|
||||
err = json.Unmarshal(content, &destination)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return destination, err
|
||||
}
|
||||
|
||||
// FormatToReadable formats given json byte slice prettily.
|
||||
func FormatToReadable(source []byte) ([]byte, error) {
|
||||
var out bytes.Buffer
|
||||
err := json.Indent(&out, source, "", " ") // Using 2 space indent
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
return out.Bytes(), nil
|
||||
}
|
79
vendor/github.com/pengsrc/go-shared/json/json_test.go
generated
vendored
79
vendor/github.com/pengsrc/go-shared/json/json_test.go
generated
vendored
|
@ -1,79 +0,0 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestJSONDecodeUnknown(t *testing.T) {
|
||||
jsonString := `{
|
||||
"key1" : "This is a string.",
|
||||
"key2" : 10.50,
|
||||
"key3": [null, {"nestedKey1": "Another string"}]
|
||||
}`
|
||||
|
||||
anyData, err := Decode([]byte(jsonString))
|
||||
assert.NoError(t, err)
|
||||
data := anyData.(map[string]interface{})
|
||||
assert.Equal(t, 10.50, data["key2"])
|
||||
|
||||
var anotherData interface{}
|
||||
_, err = Decode([]byte(jsonString), &anotherData)
|
||||
assert.NoError(t, err)
|
||||
data = anyData.(map[string]interface{})
|
||||
assert.Equal(t, 10.50, data["key2"])
|
||||
|
||||
_, err = Decode([]byte(`- - -`), &JSONMustError{})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestJSONDecodeKnown(t *testing.T) {
|
||||
type SampleJSON struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
sampleJSONString := `{"name": "NAME"}`
|
||||
|
||||
sample := SampleJSON{Name: "NaMe", Description: "DeScRiPtIoN"}
|
||||
anyDataPointer, err := Decode([]byte(sampleJSONString), &sample)
|
||||
assert.NoError(t, err)
|
||||
data := anyDataPointer.(*SampleJSON)
|
||||
assert.Equal(t, "NAME", sample.Name)
|
||||
assert.Equal(t, "DeScRiPtIoN", sample.Description)
|
||||
assert.Equal(t, "NAME", (*data).Name)
|
||||
assert.Equal(t, "DeScRiPtIoN", (*data).Description)
|
||||
}
|
||||
|
||||
func TestJSONEncode(t *testing.T) {
|
||||
type SampleJSON struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
sample := SampleJSON{Name: "NaMe", Description: "DeScRiPtIoN"}
|
||||
|
||||
jsonBytes, err := Encode(sample, true)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, `{"name":"NaMe","description":"DeScRiPtIoN"}`, string(jsonBytes))
|
||||
|
||||
_, err = Encode(&JSONMustError{}, true)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestJSONFormatToReadable(t *testing.T) {
|
||||
sampleJSONString := `{"name": "NAME"}`
|
||||
|
||||
jsonBytes, err := FormatToReadable([]byte(sampleJSONString))
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "{\n \"name\": \"NAME\"\n}", string(jsonBytes))
|
||||
|
||||
_, err = FormatToReadable([]byte(`XXXXX`))
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
type JSONMustError struct{}
|
||||
|
||||
func (*JSONMustError) MarshalJSON() ([]byte, error) {
|
||||
return []byte{}, errors.New("marshal error")
|
||||
}
|
460
vendor/github.com/pengsrc/go-shared/log/event.go
generated
vendored
Normal file
460
vendor/github.com/pengsrc/go-shared/log/event.go
generated
vendored
Normal file
|
@ -0,0 +1,460 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pengsrc/go-shared/buffer"
|
||||
"github.com/pengsrc/go-shared/convert"
|
||||
)
|
||||
|
||||
// A EventCallerPool is a type-safe wrapper around a sync.BytesBufferPool.
|
||||
type EventCallerPool struct {
|
||||
p *sync.Pool
|
||||
}
|
||||
|
||||
// NewEventCallerPool constructs a new BytesBufferPool.
|
||||
func NewEventCallerPool() EventCallerPool {
|
||||
return EventCallerPool{
|
||||
p: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &EventCaller{}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Get retrieves a EventCaller from the pool, creating one if necessary.
|
||||
func (p EventCallerPool) Get() *EventCaller {
|
||||
e := p.p.Get().(*EventCaller)
|
||||
|
||||
e.pool = p
|
||||
|
||||
e.Defined = false
|
||||
e.PC = 0
|
||||
e.File = ""
|
||||
e.Line = 0
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (p EventCallerPool) put(caller *EventCaller) {
|
||||
p.p.Put(caller)
|
||||
}
|
||||
|
||||
// EventCaller represents the caller of a logging function.
|
||||
type EventCaller struct {
|
||||
pool EventCallerPool
|
||||
|
||||
Defined bool
|
||||
PC uintptr
|
||||
File string
|
||||
Line int
|
||||
}
|
||||
|
||||
// Free returns the EventCaller to its EventCallerPool.
|
||||
// Callers must not retain references to the EventCaller after calling Free.
|
||||
func (ec *EventCaller) Free() {
|
||||
ec.pool.put(ec)
|
||||
}
|
||||
|
||||
// String returns the full path and line number of the caller.
|
||||
func (ec EventCaller) String() string {
|
||||
return ec.FullPath()
|
||||
}
|
||||
|
||||
// FullPath returns a /full/path/to/package/file:line description of the
|
||||
// caller.
|
||||
func (ec EventCaller) FullPath() string {
|
||||
if !ec.Defined {
|
||||
return "undefined"
|
||||
}
|
||||
buf := buffer.GlobalBytesPool().Get()
|
||||
defer buf.Free()
|
||||
buf.AppendString(ec.File)
|
||||
buf.AppendByte(':')
|
||||
buf.AppendInt(int64(ec.Line))
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// TrimmedPath returns a package/file:line description of the caller,
|
||||
// preserving only the leaf directory name and file name.
|
||||
func (ec EventCaller) TrimmedPath() string {
|
||||
if !ec.Defined {
|
||||
return "undefined"
|
||||
}
|
||||
// nb. To make sure we trim the path correctly on Windows too, we
|
||||
// counter-intuitively need to use '/' and *not* os.PathSeparator here,
|
||||
// because the path given originates from Go stdlib, specifically
|
||||
// runtime.Caller() which (as of Mar/17) returns forward slashes even on
|
||||
// Windows.
|
||||
//
|
||||
// See https://github.com/golang/go/issues/3335
|
||||
// and https://github.com/golang/go/issues/18151
|
||||
//
|
||||
// for discussion on the issue on Go side.
|
||||
//
|
||||
// Find the last separator.
|
||||
//
|
||||
idx := strings.LastIndexByte(ec.File, '/')
|
||||
if idx == -1 {
|
||||
return ec.FullPath()
|
||||
}
|
||||
// Find the penultimate separator.
|
||||
idx = strings.LastIndexByte(ec.File[:idx], '/')
|
||||
if idx == -1 {
|
||||
return ec.FullPath()
|
||||
}
|
||||
buf := buffer.GlobalBytesPool().Get()
|
||||
defer buf.Free()
|
||||
// Keep everything after the penultimate separator.
|
||||
buf.AppendString(ec.File[idx+1:])
|
||||
buf.AppendByte(':')
|
||||
buf.AppendInt(int64(ec.Line))
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// A EventPool is a type-safe wrapper around a sync.BytesBufferPool.
|
||||
type EventPool struct {
|
||||
p *sync.Pool
|
||||
}
|
||||
|
||||
// NewEventPool constructs a new BytesBufferPool.
|
||||
func NewEventPool() EventPool {
|
||||
return EventPool{
|
||||
p: &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &Event{}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Get retrieves a Event from the pool, creating one if necessary.
|
||||
func (p EventPool) Get() *Event {
|
||||
e := p.p.Get().(*Event)
|
||||
|
||||
e.buffer = buffer.GlobalBytesPool().Get()
|
||||
e.pool = p
|
||||
|
||||
e.level = MuteLevel
|
||||
e.lw = nil
|
||||
|
||||
e.ctx = nil
|
||||
e.ctxKeys = nil
|
||||
|
||||
e.isEnabled = false
|
||||
e.isCallerEnabled = false
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func (p EventPool) put(event *Event) {
|
||||
event.buffer.Free()
|
||||
|
||||
event.ctx = nil
|
||||
event.ctxKeys = nil
|
||||
|
||||
p.p.Put(event)
|
||||
}
|
||||
|
||||
// Event represents a log event. It is instanced by one of the with method of
|
||||
// logger and finalized by the log method such as Debug().
|
||||
type Event struct {
|
||||
buffer *buffer.BytesBuffer
|
||||
pool EventPool
|
||||
|
||||
level Level
|
||||
lw LevelWriter
|
||||
|
||||
ctx context.Context
|
||||
ctxKeys *[]interface{}
|
||||
ctxKeysMap *map[interface{}]string
|
||||
|
||||
isEnabled bool
|
||||
isCallerEnabled bool
|
||||
}
|
||||
|
||||
// Free returns the Event to its EventPool.
|
||||
// Callers must not retain references to the Event after calling Free.
|
||||
func (e *Event) Free() {
|
||||
e.pool.put(e)
|
||||
}
|
||||
|
||||
// Message writes the *Event to level writer.
|
||||
//
|
||||
// NOTICE: Once this method is called, the *Event should be disposed.
|
||||
// Calling twice can have unexpected result.
|
||||
func (e *Event) Message(message string) {
|
||||
if !e.isEnabled {
|
||||
return
|
||||
}
|
||||
e.write(message)
|
||||
}
|
||||
|
||||
// Messagef writes the *Event to level writer.
|
||||
//
|
||||
// NOTICE: Once this method is called, the *Event should be disposed.
|
||||
// Calling twice can have unexpected result.
|
||||
func (e *Event) Messagef(format string, v ...interface{}) {
|
||||
if !e.isEnabled {
|
||||
return
|
||||
}
|
||||
e.write(format, v...)
|
||||
}
|
||||
|
||||
// Byte appends string key and byte value to event.
|
||||
func (e *Event) Byte(key string, value byte) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendByte(value) })
|
||||
}
|
||||
|
||||
// Bytes appends string key and bytes value to event.
|
||||
func (e *Event) Bytes(key string, value []byte) *Event {
|
||||
return e.appendField(key, func() {
|
||||
if needsQuote(string(value)) {
|
||||
e.buffer.AppendString(strconv.Quote(string(value)))
|
||||
} else {
|
||||
e.buffer.AppendBytes(value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// String appends string key and string value to event.
|
||||
func (e *Event) String(key string, value string) *Event {
|
||||
return e.appendField(key, func() {
|
||||
if needsQuote(string(value)) {
|
||||
e.buffer.AppendString(strconv.Quote(value))
|
||||
} else {
|
||||
e.buffer.AppendString(value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Int appends string key and int value to event.
|
||||
func (e *Event) Int(key string, value int) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendInt(int64(value)) })
|
||||
}
|
||||
|
||||
// Int32 appends string key and int32 value to event.
|
||||
func (e *Event) Int32(key string, value int32) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendInt(int64(value)) })
|
||||
}
|
||||
|
||||
// Int64 appends string key and int64 value to event.
|
||||
func (e *Event) Int64(key string, value int64) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendInt(value) })
|
||||
}
|
||||
|
||||
// Uint appends string key and uint value to event.
|
||||
func (e *Event) Uint(key string, value uint) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendUint(uint64(value)) })
|
||||
}
|
||||
|
||||
// Uint32 appends string key and uint32 value to event.
|
||||
func (e *Event) Uint32(key string, value uint32) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendUint(uint64(value)) })
|
||||
}
|
||||
|
||||
// Uint64 appends string key and uint64 value to event.
|
||||
func (e *Event) Uint64(key string, value uint64) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendUint(value) })
|
||||
}
|
||||
|
||||
// Float32 appends string key and float32 value to event.
|
||||
func (e *Event) Float32(key string, value float32) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendFloat(float64(value), 32) })
|
||||
}
|
||||
|
||||
// Float64 appends string key and float value to event.
|
||||
func (e *Event) Float64(key string, value float64) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendFloat(value, 64) })
|
||||
}
|
||||
|
||||
// Bool appends string key and bool value to event.
|
||||
func (e *Event) Bool(key string, value bool) *Event {
|
||||
return e.appendField(key, func() { e.buffer.AppendBool(value) })
|
||||
}
|
||||
|
||||
// Time appends string key and time value to event.
|
||||
func (e *Event) Time(key string, value time.Time, format string) *Event {
|
||||
buf := buffer.GlobalBytesPool().Get()
|
||||
defer buf.Free()
|
||||
|
||||
buf.AppendTime(value, format)
|
||||
return e.Bytes(key, buf.Bytes())
|
||||
}
|
||||
|
||||
// Error appends string key and error value to event.
|
||||
func (e *Event) Error(key string, err error) *Event {
|
||||
return e.String(key, err.Error())
|
||||
}
|
||||
|
||||
// Interface appends string key and interface value to event.
|
||||
func (e *Event) Interface(key string, value interface{}) *Event {
|
||||
switch v := value.(type) {
|
||||
case byte:
|
||||
e.Byte(key, v)
|
||||
case []byte:
|
||||
e.Bytes(key, v)
|
||||
case string:
|
||||
e.String(key, v)
|
||||
case int:
|
||||
e.Int(key, v)
|
||||
case int32:
|
||||
e.Int32(key, v)
|
||||
case int64:
|
||||
e.Int64(key, v)
|
||||
case uint:
|
||||
e.Uint(key, v)
|
||||
case uint32:
|
||||
e.Uint32(key, v)
|
||||
case uint64:
|
||||
e.Uint64(key, v)
|
||||
case float32:
|
||||
e.Float32(key, v)
|
||||
case float64:
|
||||
e.Float64(key, v)
|
||||
case bool:
|
||||
e.Bool(key, v)
|
||||
case time.Time:
|
||||
e.Time(key, v, convert.ISO8601Milli)
|
||||
case error:
|
||||
e.Error(key, v)
|
||||
case nil:
|
||||
e.String(key, "nil")
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown field type: %v", value))
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Event) appendField(key string, appendFunc func()) *Event {
|
||||
if !e.isEnabled {
|
||||
return e
|
||||
}
|
||||
|
||||
// Ignore field with empty key.
|
||||
if len(key) <= 0 {
|
||||
return e
|
||||
}
|
||||
|
||||
// Append space if event field not empty.
|
||||
if e.buffer.Len() != 0 {
|
||||
e.buffer.AppendByte(' ')
|
||||
}
|
||||
|
||||
e.buffer.AppendString(key)
|
||||
e.buffer.AppendString("=")
|
||||
|
||||
appendFunc()
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *Event) write(format string, v ...interface{}) {
|
||||
defer e.Free()
|
||||
|
||||
if !e.isEnabled {
|
||||
return
|
||||
}
|
||||
|
||||
// Append interested contexts.
|
||||
if e.ctx != nil && e.ctxKeys != nil && e.ctxKeysMap != nil {
|
||||
for _, key := range *e.ctxKeys {
|
||||
if value := e.ctx.Value(key); value != nil {
|
||||
e.Interface((*e.ctxKeysMap)[key], e.ctx.Value(key))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Append caller.
|
||||
if e.isCallerEnabled {
|
||||
ec := newEventCaller(runtime.Caller(callerSkipOffset))
|
||||
e.String("source", ec.TrimmedPath())
|
||||
}
|
||||
|
||||
// Compose and store current log.
|
||||
buf := buffer.GlobalBytesPool().Get()
|
||||
defer buf.Free()
|
||||
|
||||
// Format print message.
|
||||
if format != "" {
|
||||
if len(v) == 0 {
|
||||
fmt.Fprint(buf, format)
|
||||
} else {
|
||||
fmt.Fprintf(buf, format, v...)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprint(buf, v...)
|
||||
}
|
||||
|
||||
// Append filed.
|
||||
buf.AppendByte(' ')
|
||||
buf.AppendBytes(e.buffer.Bytes())
|
||||
|
||||
// Finally write.
|
||||
if _, err := e.lw.WriteLevel(e.level, buf.Bytes()); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "log: could not write event: %v", err)
|
||||
}
|
||||
|
||||
switch e.level {
|
||||
case PanicLevel:
|
||||
panic(buf.String())
|
||||
case FatalLevel:
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func newEventCaller(pc uintptr, file string, line int, ok bool) (ec *EventCaller) {
|
||||
ec = eventCallerPool.Get()
|
||||
|
||||
if ok {
|
||||
ec.PC = pc
|
||||
ec.File = file
|
||||
ec.Line = line
|
||||
ec.Defined = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func newEvent(
|
||||
ctx context.Context,
|
||||
ctxKeys *[]interface{}, ctxKeysMap *map[interface{}]string,
|
||||
level Level, lw LevelWriter,
|
||||
isEnabled bool, isCallerEnabled bool,
|
||||
) (e *Event) {
|
||||
e = eventPool.Get()
|
||||
|
||||
e.level = level
|
||||
e.lw = lw
|
||||
|
||||
e.ctx = ctx
|
||||
e.ctxKeys = ctxKeys
|
||||
e.ctxKeysMap = ctxKeysMap
|
||||
|
||||
e.isEnabled = isEnabled
|
||||
e.isCallerEnabled = isCallerEnabled
|
||||
return
|
||||
}
|
||||
|
||||
func needsQuote(s string) bool {
|
||||
for i := range s {
|
||||
if s[i] < 0x20 || s[i] > 0x7e || s[i] == ' ' || s[i] == '\\' || s[i] == '"' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const callerSkipOffset = 2
|
||||
|
||||
// eventCallerPool is a pool of newEvent callers.
|
||||
var eventCallerPool = NewEventCallerPool()
|
||||
|
||||
// eventPool is a pool of events.
|
||||
var eventPool = NewEventPool()
|
145
vendor/github.com/pengsrc/go-shared/log/event_test.go
generated
vendored
Normal file
145
vendor/github.com/pengsrc/go-shared/log/event_test.go
generated
vendored
Normal file
|
@ -0,0 +1,145 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/pengsrc/go-shared/buffer"
|
||||
"github.com/pengsrc/go-shared/convert"
|
||||
)
|
||||
|
||||
func TestEventCallerPool(t *testing.T) {
|
||||
p := NewEventCallerPool()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for g := 0; g < 10; g++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
eventCaller := p.Get()
|
||||
assert.NotNil(t, eventCaller)
|
||||
eventCaller.Free()
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestEntryCaller(t *testing.T) {
|
||||
tests := []struct {
|
||||
caller *EventCaller
|
||||
full string
|
||||
short string
|
||||
}{
|
||||
{
|
||||
caller: newEventCaller(100, "/path/to/foo.go", 42, false),
|
||||
full: "undefined",
|
||||
short: "undefined",
|
||||
},
|
||||
{
|
||||
caller: newEventCaller(100, "/path/to/foo.go", 42, true),
|
||||
full: "/path/to/foo.go:42",
|
||||
short: "to/foo.go:42",
|
||||
},
|
||||
{
|
||||
caller: newEventCaller(100, "to/foo.go", 42, true),
|
||||
full: "to/foo.go:42",
|
||||
short: "to/foo.go:42",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
assert.Equal(t, tt.full, tt.caller.String(), "Unexpected string from EntryCaller.")
|
||||
assert.Equal(t, tt.full, tt.caller.FullPath(), "Unexpected FullPath from EntryCaller.")
|
||||
assert.Equal(t, tt.short, tt.caller.TrimmedPath(), "Unexpected TrimmedPath from EntryCaller.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventPool(t *testing.T) {
|
||||
p := NewEventPool()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for g := 0; g < 10; g++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
for i := 0; i < 100; i++ {
|
||||
event := p.Get()
|
||||
assert.NotNil(t, event)
|
||||
event.Free()
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestEvent(t *testing.T) {
|
||||
buf := buffer.GlobalBytesPool().Get()
|
||||
defer buf.Free()
|
||||
|
||||
l, err := NewLogger(buf, "DEBUG")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.DebugEvent(context.TODO()).Byte("b", 'b').Message("DEBUG b")
|
||||
assert.Contains(t, buf.String(), "DEBUG b b=b")
|
||||
t.Log(buf.String())
|
||||
buf.Reset()
|
||||
|
||||
l.DebugEvent(context.TODO()).Bytes("bs", []byte("bs")).Message("DEBUG bs")
|
||||
l.DebugEvent(context.TODO()).Bytes("bs", []byte("bs bs")).Messagef("DEBUG %s", "bs")
|
||||
assert.Contains(t, buf.String(), "DEBUG bs bs=bs")
|
||||
assert.Contains(t, buf.String(), `DEBUG bs bs="bs bs"`)
|
||||
buf.Reset()
|
||||
|
||||
l.DebugEvent(context.TODO()).String("s", "s").Message("DEBUG s")
|
||||
l.DebugEvent(context.TODO()).String("s", "s s").Messagef("DEBUG %d", 1024)
|
||||
assert.Contains(t, buf.String(), "DEBUG s s=s")
|
||||
assert.Contains(t, buf.String(), `DEBUG 1024 s="s s"`)
|
||||
buf.Reset()
|
||||
|
||||
l.InfoEvent(context.TODO()).
|
||||
Int("i", 1).Int32("i32", int32(2)).Int64("i64", int64(3)).
|
||||
Messagef("INFO %d", 123)
|
||||
assert.Contains(t, buf.String(), "INFO 123 i=1 i32=2 i64=3")
|
||||
buf.Reset()
|
||||
|
||||
l.InfoEvent(context.TODO()).
|
||||
Uint("i", 1).Uint32("i32", uint32(2)).Uint64("i64", uint64(3)).
|
||||
Messagef("INFO %d", 123)
|
||||
assert.Contains(t, buf.String(), "INFO 123 i=1 i32=2 i64=3")
|
||||
buf.Reset()
|
||||
|
||||
l.WarnEvent(context.TODO()).
|
||||
Float32("f32", float32(32.2333)).Float64("f64", float64(64.6444)).
|
||||
Messagef("WARN %s %d.", "hello", 1024)
|
||||
assert.Contains(t, buf.String(), "WARN hello 1024. f32=32.2333 f64=64.6444")
|
||||
buf.Reset()
|
||||
|
||||
l.WarnEvent(context.TODO()).
|
||||
Bool("true", true).Bool("false", false).
|
||||
Message("WARN bool.")
|
||||
assert.Contains(t, buf.String(), "WARN bool. true=true false=false")
|
||||
buf.Reset()
|
||||
|
||||
l.ErrorEvent(context.TODO()).
|
||||
Time("time", time.Time{}, convert.RFC822).Error("error", errors.New("error message")).
|
||||
Message("Error.")
|
||||
assert.Contains(t, buf.String(), `Error. time="Mon, 01 Jan 0001 00:00:00 GMT" error="error message"`)
|
||||
buf.Reset()
|
||||
|
||||
l.DebugEvent(context.TODO()).
|
||||
String("a", "a a").
|
||||
String("b", "b'b").
|
||||
String("c", `c"c`).
|
||||
Message("yes")
|
||||
assert.Contains(t, buf.String(), `a="a a"`)
|
||||
assert.Contains(t, buf.String(), `b=b'b`)
|
||||
assert.Contains(t, buf.String(), `c="c\"c"`)
|
||||
buf.Reset()
|
||||
}
|
151
vendor/github.com/pengsrc/go-shared/log/exported.go
generated
vendored
Normal file
151
vendor/github.com/pengsrc/go-shared/log/exported.go
generated
vendored
Normal file
|
@ -0,0 +1,151 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// Fatal logs a message with severity FATAL followed by a call to os.Exit(1).
|
||||
func Fatal(ctx context.Context, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, FatalLevel).write("", v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Panic logs a message with severity PANIC followed by a call to panic().
|
||||
func Panic(ctx context.Context, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, PanicLevel).write("", v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Error logs a message with severity ERROR.
|
||||
func Error(ctx context.Context, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, ErrorLevel).write("", v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Warn logs a message with severity WARN.
|
||||
func Warn(ctx context.Context, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, WarnLevel).write("", v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Info logs a message with severity INFO.
|
||||
func Info(ctx context.Context, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, InfoLevel).write("", v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Debug logs a message with severity DEBUG.
|
||||
func Debug(ctx context.Context, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, DebugLevel).write("", v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Fatalf logs a message with severity FATAL in format followed by a call to
|
||||
// os.Exit(1).
|
||||
func Fatalf(ctx context.Context, format string, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, FatalLevel).write(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Panicf logs a message with severity PANIC in format followed by a call to
|
||||
// panic().
|
||||
func Panicf(ctx context.Context, format string, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, PanicLevel).write(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf logs a message with severity ERROR in format.
|
||||
func Errorf(ctx context.Context, format string, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, ErrorLevel).write(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Warnf logs a message with severity WARN in format.
|
||||
func Warnf(ctx context.Context, format string, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, WarnLevel).write(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Infof logs a message with severity INFO in format.
|
||||
func Infof(ctx context.Context, format string, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, InfoLevel).write(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// Debugf logs a message with severity DEBUG in format.
|
||||
func Debugf(ctx context.Context, format string, v ...interface{}) {
|
||||
if globalLogger != nil {
|
||||
globalLogger.event(ctx, DebugLevel).write(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
// FatalEvent returns a log event with severity FATAL.
|
||||
func FatalEvent(ctx context.Context) *Event {
|
||||
if globalLogger != nil {
|
||||
return globalLogger.event(ctx, FatalLevel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PanicEvent returns a log event with severity PANIC.
|
||||
func PanicEvent(ctx context.Context) *Event {
|
||||
if globalLogger != nil {
|
||||
return globalLogger.event(ctx, PanicLevel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ErrorEvent returns a log event with severity ERROR.
|
||||
func ErrorEvent(ctx context.Context) *Event {
|
||||
if globalLogger != nil {
|
||||
return globalLogger.event(ctx, ErrorLevel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WarnEvent returns a log event with severity WARN.
|
||||
func WarnEvent(ctx context.Context) *Event {
|
||||
if globalLogger != nil {
|
||||
return globalLogger.event(ctx, WarnLevel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InfoEvent returns a log event with severity INFO.
|
||||
func InfoEvent(ctx context.Context) *Event {
|
||||
if globalLogger != nil {
|
||||
return globalLogger.event(ctx, InfoLevel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DebugEvent returns a log event with severity DEBUG.
|
||||
func DebugEvent(ctx context.Context) *Event {
|
||||
if globalLogger != nil {
|
||||
return globalLogger.event(ctx, DebugLevel)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetGlobalLogger sets a logger as global logger.
|
||||
func SetGlobalLogger(l *Logger) {
|
||||
globalLogger = l
|
||||
}
|
||||
|
||||
// GlobalLogger returns the global logger.
|
||||
func GlobalLogger() *Logger {
|
||||
return globalLogger
|
||||
}
|
||||
|
||||
var globalLogger *Logger
|
21
vendor/github.com/pengsrc/go-shared/log/exported_test.go
generated
vendored
Normal file
21
vendor/github.com/pengsrc/go-shared/log/exported_test.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestExported(t *testing.T) {
|
||||
l, err := NewTerminalLogger("DEBUG")
|
||||
assert.NoError(t, err)
|
||||
SetGlobalLogger(l)
|
||||
|
||||
c := context.Background()
|
||||
|
||||
l.Debug(c, "Singleton logger")
|
||||
assert.NotNil(t, InfoEvent(context.Background()))
|
||||
|
||||
Debugf(c, "Debug message test, hi %s", "Apple")
|
||||
}
|
67
vendor/github.com/pengsrc/go-shared/log/level.go
generated
vendored
Normal file
67
vendor/github.com/pengsrc/go-shared/log/level.go
generated
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Level defines log levels.
|
||||
type Level uint8
|
||||
|
||||
// String returns name of the level.
|
||||
func (l Level) String() string {
|
||||
switch l {
|
||||
case MuteLevel:
|
||||
return "MUTE"
|
||||
case FatalLevel:
|
||||
return "FATAL"
|
||||
case PanicLevel:
|
||||
return "PANIC"
|
||||
case ErrorLevel:
|
||||
return "ERROR"
|
||||
case WarnLevel:
|
||||
return "WARN"
|
||||
case InfoLevel:
|
||||
return "INFO"
|
||||
case DebugLevel:
|
||||
return "DEBUG"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
const (
|
||||
// MuteLevel disables the logger.
|
||||
MuteLevel Level = iota
|
||||
// FatalLevel defines fatal log level.
|
||||
FatalLevel
|
||||
// PanicLevel defines panic log level.
|
||||
PanicLevel
|
||||
// ErrorLevel defines error log level.
|
||||
ErrorLevel
|
||||
// WarnLevel defines warn log level.
|
||||
WarnLevel
|
||||
// InfoLevel defines info log level.
|
||||
InfoLevel
|
||||
// DebugLevel defines debug log level.
|
||||
DebugLevel
|
||||
)
|
||||
|
||||
// ParseLevel takes a string level and returns the log level constant.
|
||||
func ParseLevel(level string) (Level, error) {
|
||||
switch strings.ToUpper(level) {
|
||||
case "FATAL":
|
||||
return FatalLevel, nil
|
||||
case "PANIC":
|
||||
return PanicLevel, nil
|
||||
case "ERROR":
|
||||
return ErrorLevel, nil
|
||||
case "WARN":
|
||||
return WarnLevel, nil
|
||||
case "INFO":
|
||||
return InfoLevel, nil
|
||||
case "DEBUG":
|
||||
return DebugLevel, nil
|
||||
}
|
||||
|
||||
return MuteLevel, fmt.Errorf(`"%q" is not a valid log Level`, level)
|
||||
}
|
45
vendor/github.com/pengsrc/go-shared/log/level_test.go
generated
vendored
Normal file
45
vendor/github.com/pengsrc/go-shared/log/level_test.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseLevel(t *testing.T) {
|
||||
var l Level
|
||||
var err error
|
||||
assert.Equal(t, l, MuteLevel)
|
||||
assert.NoError(t, err)
|
||||
|
||||
l, err = ParseLevel("FATAL")
|
||||
assert.Equal(t, l, FatalLevel)
|
||||
assert.NoError(t, err)
|
||||
l, err = ParseLevel("PANIC")
|
||||
assert.Equal(t, l, PanicLevel)
|
||||
assert.NoError(t, err)
|
||||
l, err = ParseLevel("ERROR")
|
||||
assert.Equal(t, l, ErrorLevel)
|
||||
assert.NoError(t, err)
|
||||
l, err = ParseLevel("WARN")
|
||||
assert.Equal(t, l, WarnLevel)
|
||||
assert.NoError(t, err)
|
||||
l, err = ParseLevel("INFO")
|
||||
assert.Equal(t, l, InfoLevel)
|
||||
assert.NoError(t, err)
|
||||
l, err = ParseLevel("DEBUG")
|
||||
assert.Equal(t, l, DebugLevel)
|
||||
assert.NoError(t, err)
|
||||
|
||||
l, err = ParseLevel("invalid")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestLevelString(t *testing.T) {
|
||||
assert.Equal(t, "FATAL", FatalLevel.String())
|
||||
assert.Equal(t, "PANIC", PanicLevel.String())
|
||||
assert.Equal(t, "ERROR", ErrorLevel.String())
|
||||
assert.Equal(t, "WARN", WarnLevel.String())
|
||||
assert.Equal(t, "INFO", InfoLevel.String())
|
||||
assert.Equal(t, "DEBUG", DebugLevel.String())
|
||||
}
|
327
vendor/github.com/pengsrc/go-shared/log/logger.go
generated
vendored
Normal file
327
vendor/github.com/pengsrc/go-shared/log/logger.go
generated
vendored
Normal file
|
@ -0,0 +1,327 @@
|
|||
// Package log provides support for logging to stdout, stderr and file.
|
||||
package log
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/pengsrc/go-shared/check"
|
||||
"github.com/pengsrc/go-shared/reopen"
|
||||
)
|
||||
|
||||
// Logger presents a logger.
|
||||
// The only way to initialize a logger is using the convention construct
|
||||
// functions like NewLogger().
|
||||
type Logger struct {
|
||||
level Level
|
||||
lw LevelWriter
|
||||
|
||||
// Interested context keys.
|
||||
ctxKeys []interface{}
|
||||
ctxKeysMap map[interface{}]string
|
||||
|
||||
// isCallerEnabled sets whether to annotating logs with the calling
|
||||
// function's file name and line number. By default, all logs are annotated.
|
||||
isCallerEnabled bool
|
||||
}
|
||||
|
||||
// GetLevel get the log level string.
|
||||
func (l *Logger) GetLevel() string {
|
||||
return l.level.String()
|
||||
}
|
||||
|
||||
// SetLevel sets the log level.
|
||||
// Valid levels are "debug", "info", "warn", "error", and "fatal".
|
||||
func (l *Logger) SetLevel(level string) (err error) {
|
||||
levelFlag, err := ParseLevel(level)
|
||||
if err == nil {
|
||||
l.level = levelFlag
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SetInterestContextKeys sets the contexts keys that the logger should be
|
||||
// interested in. Value of the interested context key will extract and print as
|
||||
// newEvent filed.
|
||||
func (l *Logger) SetInterestContextKeys(keys []interface{}) {
|
||||
l.ctxKeys = keys
|
||||
|
||||
l.ctxKeysMap = make(map[interface{}]string)
|
||||
for _, key := range l.ctxKeys {
|
||||
l.ctxKeysMap[key] = fmt.Sprintf("%v", key)
|
||||
}
|
||||
}
|
||||
|
||||
// SetCallerFlag sets whether to annotating logs with the caller.
|
||||
func (l *Logger) SetCallerFlag(isEnabled bool) {
|
||||
l.isCallerEnabled = isEnabled
|
||||
}
|
||||
|
||||
// Flush writes buffered logs.
|
||||
func (l *Logger) Flush() {
|
||||
if flusher, ok := l.lw.(Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
// Fatal logs a message with severity FATAL followed by a call to os.Exit(1).
|
||||
func (l *Logger) Fatal(ctx context.Context, v ...interface{}) {
|
||||
l.event(ctx, FatalLevel).write("", v...)
|
||||
}
|
||||
|
||||
// Panic logs a message with severity PANIC followed by a call to panic().
|
||||
func (l *Logger) Panic(ctx context.Context, v ...interface{}) {
|
||||
l.event(ctx, PanicLevel).write("", v...)
|
||||
}
|
||||
|
||||
// Error logs a message with severity ERROR.
|
||||
func (l *Logger) Error(ctx context.Context, v ...interface{}) {
|
||||
l.event(ctx, ErrorLevel).write("", v...)
|
||||
}
|
||||
|
||||
// Warn logs a message with severity WARN.
|
||||
func (l *Logger) Warn(ctx context.Context, v ...interface{}) {
|
||||
l.event(ctx, WarnLevel).write("", v...)
|
||||
}
|
||||
|
||||
// Info logs a message with severity INFO.
|
||||
func (l *Logger) Info(ctx context.Context, v ...interface{}) {
|
||||
l.event(ctx, InfoLevel).write("", v...)
|
||||
}
|
||||
|
||||
// Debug logs a message with severity DEBUG.
|
||||
func (l *Logger) Debug(ctx context.Context, v ...interface{}) {
|
||||
l.event(ctx, DebugLevel).write("", v...)
|
||||
}
|
||||
|
||||
// Fatalf logs a message with severity FATAL in format followed by a call to
|
||||
// os.Exit(1).
|
||||
func (l *Logger) Fatalf(ctx context.Context, format string, v ...interface{}) {
|
||||
l.event(ctx, FatalLevel).write(format, v...)
|
||||
}
|
||||
|
||||
// Panicf logs a message with severity PANIC in format followed by a call to
|
||||
// panic().
|
||||
func (l *Logger) Panicf(ctx context.Context, format string, v ...interface{}) {
|
||||
l.event(ctx, PanicLevel).write(format, v...)
|
||||
}
|
||||
|
||||
// Errorf logs a message with severity ERROR in format.
|
||||
func (l *Logger) Errorf(ctx context.Context, format string, v ...interface{}) {
|
||||
l.event(ctx, ErrorLevel).write(format, v...)
|
||||
}
|
||||
|
||||
// Warnf logs a message with severity WARN in format.
|
||||
func (l *Logger) Warnf(ctx context.Context, format string, v ...interface{}) {
|
||||
l.event(ctx, WarnLevel).write(format, v...)
|
||||
}
|
||||
|
||||
// Infof logs a message with severity INFO in format.
|
||||
func (l *Logger) Infof(ctx context.Context, format string, v ...interface{}) {
|
||||
l.event(ctx, InfoLevel).write(format, v...)
|
||||
}
|
||||
|
||||
// Debugf logs a message with severity DEBUG in format.
|
||||
func (l *Logger) Debugf(ctx context.Context, format string, v ...interface{}) {
|
||||
l.event(ctx, DebugLevel).write(format, v...)
|
||||
}
|
||||
|
||||
// FatalEvent returns a log event with severity FATAL.
|
||||
func (l *Logger) FatalEvent(ctx context.Context) *Event {
|
||||
return l.event(ctx, FatalLevel)
|
||||
}
|
||||
|
||||
// PanicEvent returns a log event with severity PANIC.
|
||||
func (l *Logger) PanicEvent(ctx context.Context) *Event {
|
||||
return l.event(ctx, PanicLevel)
|
||||
}
|
||||
|
||||
// ErrorEvent returns a log event with severity ERROR.
|
||||
func (l *Logger) ErrorEvent(ctx context.Context) *Event {
|
||||
return l.event(ctx, ErrorLevel)
|
||||
}
|
||||
|
||||
// WarnEvent returns a log event with severity WARN.
|
||||
func (l *Logger) WarnEvent(ctx context.Context) *Event {
|
||||
return l.event(ctx, WarnLevel)
|
||||
}
|
||||
|
||||
// InfoEvent returns a log event with severity INFO.
|
||||
func (l *Logger) InfoEvent(ctx context.Context) *Event {
|
||||
return l.event(ctx, InfoLevel)
|
||||
}
|
||||
|
||||
// DebugEvent returns a log event with severity DEBUG.
|
||||
func (l *Logger) DebugEvent(ctx context.Context) *Event {
|
||||
return l.event(ctx, DebugLevel)
|
||||
}
|
||||
|
||||
func (l *Logger) event(ctx context.Context, level Level) (e *Event) {
|
||||
var ctxKeys *[]interface{}
|
||||
var ctxKeysMap *map[interface{}]string
|
||||
|
||||
if len(l.ctxKeys) > 0 {
|
||||
ctxKeys = &l.ctxKeys
|
||||
}
|
||||
if len(l.ctxKeysMap) > 0 {
|
||||
ctxKeysMap = &l.ctxKeysMap
|
||||
}
|
||||
|
||||
return newEvent(ctx, ctxKeys, ctxKeysMap, level, l.lw, level <= l.level, l.isCallerEnabled)
|
||||
}
|
||||
|
||||
// NewLogger creates a new logger for given out and level, and the level is
|
||||
// optional.
|
||||
func NewLogger(out io.Writer, level ...string) (*Logger, error) {
|
||||
return NewLoggerWithError(out, nil, level...)
|
||||
}
|
||||
|
||||
// NewLoggerWithError creates a new logger for given out, err out, level, and the
|
||||
// err out can be nil, and the level is optional.
|
||||
func NewLoggerWithError(out, errOut io.Writer, level ...string) (l *Logger, err error) {
|
||||
if out == nil {
|
||||
return nil, errors.New("logger output must specified")
|
||||
}
|
||||
|
||||
sw := &StandardWriter{w: out, ew: errOut, pid: os.Getpid()}
|
||||
l = &Logger{lw: sw}
|
||||
|
||||
if len(level) == 1 {
|
||||
if err = l.SetLevel(level[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// NewTerminalLogger creates a logger that write into terminal.
|
||||
func NewTerminalLogger(level ...string) (*Logger, error) {
|
||||
return NewLogger(os.Stdout, level...)
|
||||
}
|
||||
|
||||
// NewBufferedTerminalLogger creates a buffered logger that write into terminal.
|
||||
func NewBufferedTerminalLogger(level ...string) (*Logger, error) {
|
||||
return NewLogger(bufio.NewWriter(os.Stdout), level...)
|
||||
}
|
||||
|
||||
// NewFileLogger creates a logger that write into file.
|
||||
func NewFileLogger(filePath string, level ...string) (*Logger, error) {
|
||||
return NewFileLoggerWithError(filePath, "", level...)
|
||||
}
|
||||
|
||||
// NewFileLoggerWithError creates a logger that write into files.
|
||||
func NewFileLoggerWithError(filePath, errFilePath string, level ...string) (*Logger, error) {
|
||||
if err := check.Dir(path.Dir(filePath)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if errFilePath != "" {
|
||||
if err := check.Dir(path.Dir(errFilePath)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
out, err := reopen.NewFileWriter(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var errOut *reopen.FileWriter
|
||||
if errFilePath != "" {
|
||||
errOut, err = reopen.NewFileWriter(errFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
c := make(chan os.Signal)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-c:
|
||||
out.Reopen()
|
||||
if errOut != nil {
|
||||
errOut.Reopen()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
signal.Notify(c, syscall.SIGHUP)
|
||||
|
||||
if errOut == nil {
|
||||
return NewLoggerWithError(out, nil, level...)
|
||||
}
|
||||
return NewLoggerWithError(out, errOut, level...)
|
||||
}
|
||||
|
||||
// NewBufferedFileLogger creates a logger that write into file with buffer.
|
||||
// The flushSeconds's unit is second.
|
||||
func NewBufferedFileLogger(filePath string, flushInterval int, level ...string) (*Logger, error) {
|
||||
return NewBufferedFileLoggerWithError(filePath, "", flushInterval, level...)
|
||||
}
|
||||
|
||||
// NewBufferedFileLoggerWithError creates a logger that write into files with buffer.
|
||||
// The flushSeconds's unit is second.
|
||||
func NewBufferedFileLoggerWithError(filePath, errFilePath string, flushInterval int, level ...string) (*Logger, error) {
|
||||
if err := check.Dir(path.Dir(filePath)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if errFilePath != "" {
|
||||
if err := check.Dir(path.Dir(errFilePath)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if flushInterval == 0 {
|
||||
flushInterval = 10
|
||||
}
|
||||
|
||||
out, err := reopen.NewFileWriter(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var errOut *reopen.FileWriter
|
||||
if errFilePath != "" {
|
||||
errOut, err = reopen.NewFileWriter(errFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
bufferedOut := reopen.NewBufferedFileWriter(out)
|
||||
var bufferedErrOut *reopen.BufferedFileWriter
|
||||
if errOut != nil {
|
||||
bufferedErrOut = reopen.NewBufferedFileWriter(errOut)
|
||||
}
|
||||
|
||||
c := make(chan os.Signal)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-c:
|
||||
bufferedOut.Reopen()
|
||||
if bufferedErrOut != nil {
|
||||
bufferedErrOut.Reopen()
|
||||
}
|
||||
case <-time.After(time.Duration(flushInterval) * time.Second):
|
||||
bufferedOut.Flush()
|
||||
if bufferedErrOut != nil {
|
||||
bufferedErrOut.Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
signal.Notify(c, syscall.SIGHUP)
|
||||
|
||||
if bufferedErrOut == nil {
|
||||
return NewLoggerWithError(bufferedOut, nil, level...)
|
||||
}
|
||||
return NewLoggerWithError(bufferedOut, bufferedErrOut, level...)
|
||||
}
|
331
vendor/github.com/pengsrc/go-shared/log/logger_test.go
generated
vendored
Normal file
331
vendor/github.com/pengsrc/go-shared/log/logger_test.go
generated
vendored
Normal file
|
@ -0,0 +1,331 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/pengsrc/go-shared/buffer"
|
||||
)
|
||||
|
||||
func TestSetAndGetLevel(t *testing.T) {
|
||||
l, err := NewTerminalLogger()
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.SetLevel("ERROR")
|
||||
assert.Equal(t, "ERROR", l.GetLevel())
|
||||
}
|
||||
|
||||
func TestNewLogger(t *testing.T) {
|
||||
buf := buffer.GlobalBytesPool().Get()
|
||||
defer buf.Free()
|
||||
|
||||
l, err := NewLogger(buf, "INFO")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.Debug(context.Background(), "DEBUG message")
|
||||
l.Info(context.Background(), "INFO message")
|
||||
|
||||
assert.NotContains(t, buf.String(), "DEBUG message")
|
||||
assert.Contains(t, buf.String(), "INFO message")
|
||||
buf.Reset()
|
||||
|
||||
// Test logging for context.
|
||||
type contextKey string
|
||||
const traceID contextKey = "trace_id"
|
||||
ctx := context.WithValue(nil, traceID, "60b725f10c9c85c70d97880dfe8191b3")
|
||||
|
||||
l.SetInterestContextKeys([]interface{}{traceID})
|
||||
|
||||
l.Info(ctx, "Hello World!")
|
||||
assert.Contains(t, buf.String(), "trace_id=60b725f10c9c85c70d97880dfe8191b3")
|
||||
t.Log(buf.String())
|
||||
buf.Reset()
|
||||
|
||||
l.SetCallerFlag(true)
|
||||
|
||||
l.Info(ctx, "Hello World!")
|
||||
assert.Contains(t, buf.String(), "source=log/logger_test.go")
|
||||
t.Log(buf.String())
|
||||
buf.Reset()
|
||||
|
||||
l.Infof(ctx, "Hello %s!", "World")
|
||||
assert.Contains(t, buf.String(), "source=log/logger_test.go")
|
||||
t.Log(buf.String())
|
||||
buf.Reset()
|
||||
|
||||
l.InfoEvent(ctx).Messagef("Hello %s!", "World")
|
||||
assert.Contains(t, buf.String(), "source=log/logger_test.go")
|
||||
t.Log(buf.String())
|
||||
buf.Reset()
|
||||
|
||||
l.InfoEvent(ctx).Int("count", 1024).Messagef("Hello %s!", "World")
|
||||
assert.Contains(t, buf.String(), "source=log/logger_test.go")
|
||||
t.Log(buf.String())
|
||||
buf.Reset()
|
||||
}
|
||||
|
||||
func TestNewLoggerWithError(t *testing.T) {
|
||||
buf := buffer.GlobalBytesPool().Get()
|
||||
errBuf := buffer.GlobalBytesPool().Get()
|
||||
defer buf.Free()
|
||||
defer errBuf.Free()
|
||||
|
||||
l, err := NewLoggerWithError(buf, errBuf, "INFO")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.Debug(context.Background(), "DEBUG message")
|
||||
l.Info(context.Background(), "INFO message")
|
||||
l.Error(context.Background(), "ERROR message")
|
||||
|
||||
assert.NotContains(t, buf.String(), "DEBUG message")
|
||||
assert.Contains(t, buf.String(), "INFO message")
|
||||
assert.Contains(t, buf.String(), "ERROR message")
|
||||
|
||||
assert.NotContains(t, errBuf.String(), "DEBUG message")
|
||||
assert.NotContains(t, errBuf.String(), "INFO message")
|
||||
assert.Contains(t, errBuf.String(), "ERROR message")
|
||||
}
|
||||
|
||||
func TestNewFileLogger(t *testing.T) {
|
||||
t.Skip()
|
||||
|
||||
lFile := path.Join(os.TempDir(), "test.log")
|
||||
defer os.Remove(lFile)
|
||||
|
||||
// Create logger failed.
|
||||
_, err := NewLoggerWithError(nil, nil, "DEBUG")
|
||||
assert.Error(t, err)
|
||||
_, err = NewLoggerWithError(ioutil.Discard, ioutil.Discard, "INVALID")
|
||||
assert.Error(t, err)
|
||||
|
||||
// Create logger success.
|
||||
l, err := NewFileLogger(lFile, "INFO")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.Debug(context.Background(), "file - DEBUG message")
|
||||
l.Info(context.Background(), "file - INFO message")
|
||||
l.Warn(context.Background(), "file - WARN message")
|
||||
l.Error(context.Background(), "file - ERROR message")
|
||||
|
||||
data, err := ioutil.ReadFile(lFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 4, len(strings.Split(string(data), "\n")))
|
||||
|
||||
// Move log file.
|
||||
movedLFile := fmt.Sprintf(`%s.move`, lFile)
|
||||
err = os.Rename(lFile, movedLFile)
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(movedLFile)
|
||||
|
||||
l.Error(context.Background(), "file - ERROR message")
|
||||
|
||||
data, err = ioutil.ReadFile(movedLFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 5, len(strings.Split(string(data), "\n")))
|
||||
|
||||
// Reopen.
|
||||
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
l.Warn(context.Background(), "file - WARN message")
|
||||
l.Error(context.Background(), "file - ERROR message")
|
||||
|
||||
data, err = ioutil.ReadFile(lFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(strings.Split(string(data), "\n")))
|
||||
}
|
||||
|
||||
func TestNewFileLoggerWithError(t *testing.T) {
|
||||
t.Skip()
|
||||
|
||||
lFile := path.Join(os.TempDir(), "test.log")
|
||||
errLFile := path.Join(os.TempDir(), "test.log.wf")
|
||||
defer os.Remove(lFile)
|
||||
defer os.Remove(errLFile)
|
||||
|
||||
// Create logger failed.
|
||||
_, err := NewFileLoggerWithError("/not/exists/dir", "/not/exists/dir", "DEBUG")
|
||||
assert.Error(t, err)
|
||||
_, err = NewFileLoggerWithError(lFile, "/not/exists/dir", "DEBUG")
|
||||
assert.Error(t, err)
|
||||
_, err = NewFileLoggerWithError(os.TempDir(), os.TempDir(), "DEBUG")
|
||||
assert.Error(t, err)
|
||||
_, err = NewFileLoggerWithError(lFile, os.TempDir(), "DEBUG")
|
||||
assert.Error(t, err)
|
||||
|
||||
// Create logger success.
|
||||
l, err := NewFileLoggerWithError(lFile, errLFile, "DEBUG")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.Debug(context.Background(), "file - DEBUG message")
|
||||
l.Info(context.Background(), "file - INFO message")
|
||||
l.Warn(context.Background(), "file - WARN message")
|
||||
l.Error(context.Background(), "file - ERROR message")
|
||||
|
||||
data, err := ioutil.ReadFile(lFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 5, len(strings.Split(string(data), "\n")))
|
||||
|
||||
errLog, err := ioutil.ReadFile(errLFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(strings.Split(string(errLog), "\n")))
|
||||
|
||||
// Move data file.
|
||||
movedLogFile := fmt.Sprintf(`%s.move`, lFile)
|
||||
err = os.Rename(lFile, movedLogFile)
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(movedLogFile)
|
||||
|
||||
movedErrLogFile := fmt.Sprintf(`%s.move`, errLFile)
|
||||
err = os.Rename(errLFile, movedErrLogFile)
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove(movedErrLogFile)
|
||||
|
||||
l.Error(context.Background(), "file - ERROR message")
|
||||
|
||||
data, err = ioutil.ReadFile(movedLogFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 6, len(strings.Split(string(data), "\n")))
|
||||
|
||||
errLog, err = ioutil.ReadFile(movedErrLogFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 4, len(strings.Split(string(errLog), "\n")))
|
||||
|
||||
// Reopen.
|
||||
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
l.Warn(context.Background(), "file - WARN message")
|
||||
l.Error(context.Background(), "file - ERROR message")
|
||||
|
||||
data, err = ioutil.ReadFile(lFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(strings.Split(string(data), "\n")))
|
||||
|
||||
errLog, err = ioutil.ReadFile(errLFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(strings.Split(string(errLog), "\n")))
|
||||
}
|
||||
|
||||
func TestBufferedFileLogger(t *testing.T) {
|
||||
lFile := path.Join(os.TempDir(), "test.log")
|
||||
defer os.Remove(lFile)
|
||||
|
||||
l, err := NewBufferedFileLogger(lFile, 1, "DEBUG")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.Debug(context.Background(), "file - DEBUG message")
|
||||
l.Info(context.Background(), "file - INFO message")
|
||||
l.Warn(context.Background(), "file - WARN message")
|
||||
l.Error(context.Background(), "file - ERROR message")
|
||||
|
||||
data, err := ioutil.ReadFile(lFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(strings.Split(string(data), "\n")))
|
||||
|
||||
// Wait timeout.
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
data, err = ioutil.ReadFile(lFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 5, len(strings.Split(string(data), "\n")))
|
||||
}
|
||||
|
||||
func TestBufferedFileLoggerWithError(t *testing.T) {
|
||||
lFile := path.Join(os.TempDir(), "test.log")
|
||||
errLFile := path.Join(os.TempDir(), "test.log.wf")
|
||||
defer os.Remove(lFile)
|
||||
defer os.Remove(errLFile)
|
||||
|
||||
// Create logger failed.
|
||||
_, err := NewBufferedFileLoggerWithError("/not/exists/dir", "/not/exists/dir", 0, "DEBUG")
|
||||
assert.Error(t, err)
|
||||
_, err = NewBufferedFileLoggerWithError(lFile, "/not/exists/dir", 0, "DEBUG")
|
||||
assert.Error(t, err)
|
||||
_, err = NewBufferedFileLoggerWithError(os.TempDir(), os.TempDir(), 0, "DEBUG")
|
||||
assert.Error(t, err)
|
||||
_, err = NewBufferedFileLoggerWithError(lFile, os.TempDir(), 0, "DEBUG")
|
||||
assert.Error(t, err)
|
||||
|
||||
// Create logger success.
|
||||
errL, err := NewBufferedFileLoggerWithError(lFile, errLFile, 1, "DEBUG")
|
||||
assert.NoError(t, err)
|
||||
|
||||
errL.Debug(context.Background(), "file - DEBUG message")
|
||||
errL.Info(context.Background(), "file - INFO message")
|
||||
errL.Warn(context.Background(), "file - WARN message")
|
||||
errL.Error(context.Background(), "file - ERROR message")
|
||||
|
||||
data, err := ioutil.ReadFile(lFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(strings.Split(string(data), "\n")))
|
||||
|
||||
errData, err := ioutil.ReadFile(errLFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(strings.Split(string(errData), "\n")))
|
||||
|
||||
// Flush log.
|
||||
errL.Flush()
|
||||
|
||||
data, err = ioutil.ReadFile(lFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 5, len(strings.Split(string(data), "\n")))
|
||||
|
||||
errData, err = ioutil.ReadFile(errLFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(strings.Split(string(errData), "\n")))
|
||||
|
||||
// Wait timeout to improve test coverage.
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Reopen using signal to improve test coverage.
|
||||
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
|
||||
}
|
||||
|
||||
func TestTerminalLogger(t *testing.T) {
|
||||
l, err := NewTerminalLogger("DEBUG")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.Debug(context.Background(), "terminal - DEBUG message")
|
||||
l.Info(context.Background(), "terminal - INFO message")
|
||||
l.Warn(context.Background(), "terminal - WARN message")
|
||||
l.Error(context.Background(), "terminal - ERROR message")
|
||||
|
||||
l.Debugf(context.Background(), "terminal - DEBUG message - %d", time.Now().Unix())
|
||||
l.Infof(context.Background(), "terminal - INFO message - %d", time.Now().Unix())
|
||||
l.Warnf(context.Background(), "terminal - WARN message - %d", time.Now().Unix())
|
||||
l.Errorf(context.Background(), "terminal - ERROR message - %d", time.Now().Unix())
|
||||
}
|
||||
|
||||
func TestBufferedTerminalLogger(t *testing.T) {
|
||||
l, err := NewBufferedTerminalLogger("DEBUG")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.Debug(context.Background(), "terminal - DEBUG message")
|
||||
l.Info(context.Background(), "terminal - INFO message")
|
||||
l.Warn(context.Background(), "terminal - WARN message")
|
||||
l.Error(context.Background(), "terminal - ERROR message")
|
||||
|
||||
l.Flush()
|
||||
}
|
||||
|
||||
func BenchmarkLogger(b *testing.B) {
|
||||
l, err := NewLogger(ioutil.Discard, "DEBUG")
|
||||
assert.NoError(b, err)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
l.DebugEvent(ctx).String("key", "value").Messagef("Hello %s!", "World")
|
||||
}
|
||||
}
|
83
vendor/github.com/pengsrc/go-shared/log/writer.go
generated
vendored
Normal file
83
vendor/github.com/pengsrc/go-shared/log/writer.go
generated
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/pengsrc/go-shared/buffer"
|
||||
"github.com/pengsrc/go-shared/convert"
|
||||
)
|
||||
|
||||
// LevelWriter defines as interface a writer may implement in order
|
||||
// to receive level information with payload.
|
||||
type LevelWriter interface {
|
||||
io.Writer
|
||||
WriteLevel(level Level, message []byte) (n int, err error)
|
||||
}
|
||||
|
||||
// Flusher defines a interface with Flush() method.
|
||||
type Flusher interface {
|
||||
Flush() error
|
||||
}
|
||||
|
||||
// StandardWriter implements io.Writer{} and LevelWriter{} interface.
|
||||
type StandardWriter struct {
|
||||
w io.Writer
|
||||
ew io.Writer // Writer for WARN, ERROR, FATAL, PANIC
|
||||
|
||||
dl Level // Default level
|
||||
pid int
|
||||
}
|
||||
|
||||
// Write implements the io.Writer{} interface.
|
||||
func (sw *StandardWriter) Write(p []byte) (n int, err error) {
|
||||
return sw.WriteLevel(sw.dl, p)
|
||||
}
|
||||
|
||||
// WriteLevel implements the LevelWriter{} interface.
|
||||
func (sw *StandardWriter) WriteLevel(level Level, message []byte) (n int, err error) {
|
||||
levelString := level.String()
|
||||
if len(levelString) == 4 {
|
||||
levelString = " " + levelString
|
||||
}
|
||||
|
||||
buf := buffer.GlobalBytesPool().Get()
|
||||
defer buf.Free()
|
||||
|
||||
buf.AppendString("[")
|
||||
buf.AppendTime(time.Now().UTC(), convert.ISO8601Milli)
|
||||
buf.AppendString(" #")
|
||||
buf.AppendInt(int64(sw.pid))
|
||||
buf.AppendString("] ")
|
||||
buf.AppendString(levelString)
|
||||
buf.AppendString(" -- : ")
|
||||
buf.AppendBytes(message)
|
||||
buf.AppendString("\n")
|
||||
|
||||
if sw.ew != nil {
|
||||
if level > MuteLevel && level <= WarnLevel {
|
||||
n, err = sw.ew.Write(buf.Bytes())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return sw.w.Write(buf.Bytes())
|
||||
}
|
||||
|
||||
// Flush implements the Flusher{} interface.
|
||||
func (sw *StandardWriter) Flush() (err error) {
|
||||
if flusher, ok := sw.w.(Flusher); ok {
|
||||
err = flusher.Flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if sw.ew != nil {
|
||||
if flusher, ok := sw.ew.(Flusher); ok {
|
||||
err = flusher.Flush()
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
36
vendor/github.com/pengsrc/go-shared/log/writer_test.go
generated
vendored
Normal file
36
vendor/github.com/pengsrc/go-shared/log/writer_test.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStandardWriter(t *testing.T) {
|
||||
var w io.Writer
|
||||
w = &StandardWriter{
|
||||
w: os.Stdout, ew: os.Stderr,
|
||||
dl: MuteLevel, pid: os.Getpid(),
|
||||
}
|
||||
|
||||
lw, ok := w.(LevelWriter)
|
||||
assert.True(t, ok)
|
||||
|
||||
_, ok = w.(Flusher)
|
||||
assert.True(t, ok)
|
||||
|
||||
_, err := lw.Write([]byte("Hello World!"))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func BenchmarkStandardWriter(b *testing.B) {
|
||||
lw := &StandardWriter{w: ioutil.Discard, ew: ioutil.Discard}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
lw.Write([]byte("Hello World!"))
|
||||
}
|
||||
}
|
358
vendor/github.com/pengsrc/go-shared/logger/logger.go
generated
vendored
358
vendor/github.com/pengsrc/go-shared/logger/logger.go
generated
vendored
|
@ -1,358 +0,0 @@
|
|||
// Package logger provides support for logging to stdout and stderr.
|
||||
package logger
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
|
||||
"github.com/pengsrc/go-shared/convert"
|
||||
"github.com/pengsrc/go-shared/reopen"
|
||||
)
|
||||
|
||||
// LogFormatter is used to format log entry.
|
||||
type LogFormatter struct{}
|
||||
|
||||
// Format formats a given log entry, returns byte slice and error.
|
||||
func (c *LogFormatter) Format(entry *log.Entry) ([]byte, error) {
|
||||
level := strings.ToUpper(entry.Level.String())
|
||||
if level == "WARNING" {
|
||||
level = "WARN"
|
||||
}
|
||||
if len(level) < 5 {
|
||||
level = strings.Repeat(" ", 5-len(level)) + level
|
||||
}
|
||||
|
||||
return []byte(
|
||||
fmt.Sprintf(
|
||||
"[%s #%d] %s -- : %s\n",
|
||||
convert.TimeToString(time.Now(), convert.ISO8601Milli),
|
||||
os.Getpid(),
|
||||
level,
|
||||
entry.Message,
|
||||
),
|
||||
), nil
|
||||
}
|
||||
|
||||
// NewLogFormatter creates a new log formatter.
|
||||
func NewLogFormatter() *LogFormatter {
|
||||
return &LogFormatter{}
|
||||
}
|
||||
|
||||
// ErrorHook presents error hook.
|
||||
type ErrorHook struct {
|
||||
levels []log.Level
|
||||
|
||||
out io.Writer
|
||||
formatter log.Formatter
|
||||
}
|
||||
|
||||
// Levels returns error log levels.
|
||||
func (eh *ErrorHook) Levels() []log.Level {
|
||||
return eh.levels
|
||||
}
|
||||
|
||||
// Fire triggers before logging.
|
||||
func (eh *ErrorHook) Fire(entry *log.Entry) error {
|
||||
formatted, err := eh.formatter.Format(entry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = eh.out.Write(formatted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewErrorHook creates new error hook.
|
||||
func NewErrorHook(out io.Writer) *ErrorHook {
|
||||
return &ErrorHook{
|
||||
levels: []log.Level{
|
||||
log.WarnLevel,
|
||||
log.ErrorLevel,
|
||||
log.FatalLevel,
|
||||
log.PanicLevel,
|
||||
},
|
||||
out: out,
|
||||
formatter: NewLogFormatter(),
|
||||
}
|
||||
}
|
||||
|
||||
// Logger presents a logger.
|
||||
type Logger struct {
|
||||
origLogger *log.Logger
|
||||
|
||||
out io.Writer
|
||||
errOut io.Writer
|
||||
|
||||
bufferedOut Flusher
|
||||
bufferedErrOut Flusher
|
||||
}
|
||||
|
||||
// Flusher defines a interface with Flush() method.
|
||||
type Flusher interface {
|
||||
Flush()
|
||||
}
|
||||
|
||||
// GetLevel get the log level string.
|
||||
func (l *Logger) GetLevel() string {
|
||||
return l.origLogger.Level.String()
|
||||
}
|
||||
|
||||
// SetLevel sets the log level. Valid levels are "debug", "info", "warn", "error", and "fatal".
|
||||
func (l *Logger) SetLevel(level string) {
|
||||
lvl, err := log.ParseLevel(level)
|
||||
if err != nil {
|
||||
l.Fatal(fmt.Sprintf(`log level not valid: "%s"`, level))
|
||||
}
|
||||
l.origLogger.Level = lvl
|
||||
}
|
||||
|
||||
// Flush writes buffered logs.
|
||||
func (l *Logger) Flush() {
|
||||
if l.bufferedOut != nil {
|
||||
l.bufferedOut.Flush()
|
||||
}
|
||||
if l.bufferedErrOut != nil {
|
||||
l.bufferedErrOut.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
// Debug logs a message with severity DEBUG.
|
||||
func (l *Logger) Debug(message string) {
|
||||
l.output(l.origLogger.Debug, message)
|
||||
}
|
||||
|
||||
// Info logs a message with severity INFO.
|
||||
func (l *Logger) Info(message string) {
|
||||
l.output(l.origLogger.Info, message)
|
||||
}
|
||||
|
||||
// Warn logs a message with severity WARN.
|
||||
func (l *Logger) Warn(message string) {
|
||||
l.output(l.origLogger.Warn, message)
|
||||
}
|
||||
|
||||
// Error logs a message with severity ERROR.
|
||||
func (l *Logger) Error(message string) {
|
||||
l.output(l.origLogger.Error, message)
|
||||
}
|
||||
|
||||
// Fatal logs a message with severity ERROR followed by a call to os.Exit().
|
||||
func (l *Logger) Fatal(message string) {
|
||||
l.output(l.origLogger.Fatal, message)
|
||||
}
|
||||
|
||||
// Debugf logs a message with severity DEBUG in format.
|
||||
func (l *Logger) Debugf(format string, v ...interface{}) {
|
||||
l.output(l.origLogger.Debug, format, v...)
|
||||
}
|
||||
|
||||
// Infof logs a message with severity INFO in format.
|
||||
func (l *Logger) Infof(format string, v ...interface{}) {
|
||||
l.output(l.origLogger.Info, format, v...)
|
||||
}
|
||||
|
||||
// Warnf logs a message with severity WARN in format.
|
||||
func (l *Logger) Warnf(format string, v ...interface{}) {
|
||||
l.output(l.origLogger.Warn, format, v...)
|
||||
}
|
||||
|
||||
// Errorf logs a message with severity ERROR in format.
|
||||
func (l *Logger) Errorf(format string, v ...interface{}) {
|
||||
l.output(l.origLogger.Error, format, v...)
|
||||
}
|
||||
|
||||
// Fatalf logs a message with severity ERROR in format followed by a call to
|
||||
// os.Exit().
|
||||
func (l *Logger) Fatalf(format string, v ...interface{}) {
|
||||
l.output(l.origLogger.Fatal, format, v...)
|
||||
}
|
||||
|
||||
func (l *Logger) output(origin func(...interface{}), formatOrMessage string, v ...interface{}) {
|
||||
if len(v) > 0 {
|
||||
origin(fmt.Sprintf(formatOrMessage, v...))
|
||||
} else {
|
||||
origin(formatOrMessage)
|
||||
}
|
||||
}
|
||||
|
||||
// CheckLevel checks whether the log level is valid.
|
||||
func CheckLevel(level string) error {
|
||||
if _, err := log.ParseLevel(level); err != nil {
|
||||
return fmt.Errorf(`log level not valid: "%s"`, level)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewFileLogger creates a logger that write into file.
|
||||
func NewFileLogger(filePath string, level ...string) (*Logger, error) {
|
||||
return NewFileLoggerWithErr(filePath, "", level...)
|
||||
}
|
||||
|
||||
// NewFileLoggerWithErr creates a logger that write into files.
|
||||
func NewFileLoggerWithErr(filePath, errFilePath string, level ...string) (*Logger, error) {
|
||||
if err := checkDir(path.Dir(filePath)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if errFilePath != "" {
|
||||
if err := checkDir(path.Dir(errFilePath)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
out, err := reopen.NewFileWriter(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var errOut *reopen.FileWriter
|
||||
if errFilePath != "" {
|
||||
errOut, err = reopen.NewFileWriter(errFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
c := make(chan os.Signal)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-c:
|
||||
out.Reopen()
|
||||
if errOut != nil {
|
||||
errOut.Reopen()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
signal.Notify(c, syscall.SIGHUP)
|
||||
|
||||
if errOut == nil {
|
||||
return NewLoggerWithErr(out, nil, level...)
|
||||
}
|
||||
return NewLoggerWithErr(out, errOut, level...)
|
||||
}
|
||||
|
||||
// NewBufferedFileLogger creates a logger that write into file with buffer.
|
||||
func NewBufferedFileLogger(filePath string, level ...string) (*Logger, error) {
|
||||
return NewBufferedFileLoggerWithErr(filePath, "", level...)
|
||||
}
|
||||
|
||||
// NewBufferedFileLoggerWithErr creates a logger that write into files with buffer.
|
||||
func NewBufferedFileLoggerWithErr(filePath, errFilePath string, level ...string) (*Logger, error) {
|
||||
if err := checkDir(path.Dir(filePath)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if errFilePath != "" {
|
||||
if err := checkDir(path.Dir(errFilePath)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
out, err := reopen.NewFileWriter(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var errOut *reopen.FileWriter
|
||||
if errFilePath != "" {
|
||||
errOut, err = reopen.NewFileWriter(errFilePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
bufferedOut := reopen.NewBufferedFileWriter(out)
|
||||
var bufferedErrOut *reopen.BufferedFileWriter
|
||||
if errOut != nil {
|
||||
bufferedErrOut = reopen.NewBufferedFileWriter(errOut)
|
||||
}
|
||||
|
||||
c := make(chan os.Signal)
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-c:
|
||||
bufferedOut.Reopen()
|
||||
if bufferedErrOut != nil {
|
||||
bufferedErrOut.Reopen()
|
||||
}
|
||||
case <-time.After(10 * time.Second):
|
||||
bufferedOut.Flush()
|
||||
if bufferedErrOut != nil {
|
||||
bufferedErrOut.Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
signal.Notify(c, syscall.SIGHUP)
|
||||
|
||||
if bufferedErrOut == nil {
|
||||
return NewLoggerWithErr(bufferedOut, nil, level...)
|
||||
}
|
||||
return NewLoggerWithErr(bufferedOut, bufferedErrOut, level...)
|
||||
}
|
||||
|
||||
// NewTerminalLogger creates a logger that write into terminal.
|
||||
func NewTerminalLogger(level ...string) (*Logger, error) {
|
||||
return NewLogger(os.Stdout, level...)
|
||||
}
|
||||
|
||||
// NewTerminalLoggerWithErr creates a logger that write into terminal.
|
||||
func NewTerminalLoggerWithErr(level ...string) (*Logger, error) {
|
||||
return NewLoggerWithErr(os.Stdout, os.Stderr, level...)
|
||||
}
|
||||
|
||||
// NewLogger creates a new logger for given out and level, and the level is
|
||||
// optional.
|
||||
func NewLogger(out io.Writer, level ...string) (*Logger, error) {
|
||||
return NewLoggerWithErr(out, nil, level...)
|
||||
}
|
||||
|
||||
// NewLoggerWithErr creates a new logger for given out, err out, level, and the
|
||||
// err out can be nil, and the level is optional.
|
||||
func NewLoggerWithErr(out, errOut io.Writer, level ...string) (*Logger, error) {
|
||||
if out == nil {
|
||||
return nil, errors.New(`must specify the output for logger`)
|
||||
}
|
||||
l := &Logger{
|
||||
origLogger: &log.Logger{
|
||||
Out: out,
|
||||
Formatter: NewLogFormatter(),
|
||||
Hooks: log.LevelHooks{},
|
||||
Level: log.WarnLevel,
|
||||
},
|
||||
out: out,
|
||||
errOut: errOut,
|
||||
}
|
||||
|
||||
if errOut != nil {
|
||||
l.origLogger.Hooks.Add(NewErrorHook(l.errOut))
|
||||
}
|
||||
|
||||
if len(level) == 1 {
|
||||
if err := CheckLevel(level[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l.SetLevel(level[0])
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func checkDir(dir string) error {
|
||||
if info, err := os.Stat(dir); err != nil {
|
||||
return fmt.Errorf(`directory not exists: %s`, dir)
|
||||
} else if !info.IsDir() {
|
||||
return fmt.Errorf(`path is not directory: %s`, dir)
|
||||
}
|
||||
return nil
|
||||
}
|
218
vendor/github.com/pengsrc/go-shared/logger/logger_test.go
generated
vendored
218
vendor/github.com/pengsrc/go-shared/logger/logger_test.go
generated
vendored
|
@ -1,218 +0,0 @@
|
|||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCheckLevel(t *testing.T) {
|
||||
assert.NoError(t, CheckLevel("warn"))
|
||||
assert.Error(t, CheckLevel("invalid"))
|
||||
}
|
||||
|
||||
func TestSetAndGetLevel(t *testing.T) {
|
||||
l, err := NewTerminalLogger()
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.SetLevel("error")
|
||||
assert.Equal(t, "error", l.GetLevel())
|
||||
}
|
||||
|
||||
func TestNewFileLogger(t *testing.T) {
|
||||
logFile := "/tmp/logger-test/test.log"
|
||||
dir := path.Dir(logFile)
|
||||
err := os.MkdirAll(dir, 0775)
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
l, err := NewFileLogger(logFile, "debug")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.Debug("file - debug")
|
||||
l.Info("file - info")
|
||||
l.Warn("file - warn")
|
||||
l.Error("file - error")
|
||||
|
||||
log, err := ioutil.ReadFile(logFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 5, len(strings.Split(string(log), "\n")))
|
||||
|
||||
// Move log file.
|
||||
movedLogFile := fmt.Sprintf(`%s.move`, logFile)
|
||||
os.Rename(logFile, movedLogFile)
|
||||
|
||||
l.Error("file - error")
|
||||
|
||||
log, err = ioutil.ReadFile(movedLogFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 6, len(strings.Split(string(log), "\n")))
|
||||
|
||||
// Reopen.
|
||||
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
l.Warn("file - warn")
|
||||
l.Error("file - error")
|
||||
|
||||
log, err = ioutil.ReadFile(logFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(strings.Split(string(log), "\n")))
|
||||
}
|
||||
|
||||
func TestNewFileLoggerWithWf(t *testing.T) {
|
||||
logFile := "/tmp/logger-test/test.log"
|
||||
errLogFile := "/tmp/logger-test/test.log.wf"
|
||||
dir := path.Dir(logFile)
|
||||
err := os.MkdirAll(dir, 0775)
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
l, err := NewFileLoggerWithErr(logFile, errLogFile, "debug")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.Debug("file - debug")
|
||||
l.Info("file - info")
|
||||
l.Warn("file - warn")
|
||||
l.Error("file - error")
|
||||
|
||||
log, err := ioutil.ReadFile(logFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 5, len(strings.Split(string(log), "\n")))
|
||||
|
||||
errLog, err := ioutil.ReadFile(errLogFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(strings.Split(string(errLog), "\n")))
|
||||
|
||||
// Move log file.
|
||||
movedLogFile := fmt.Sprintf(`%s.move`, logFile)
|
||||
os.Rename(logFile, movedLogFile)
|
||||
|
||||
movedErrLogFile := fmt.Sprintf(`%s.move`, errLogFile)
|
||||
os.Rename(errLogFile, movedErrLogFile)
|
||||
|
||||
l.Error("file - error")
|
||||
|
||||
log, err = ioutil.ReadFile(movedLogFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 6, len(strings.Split(string(log), "\n")))
|
||||
|
||||
errLog, err = ioutil.ReadFile(movedErrLogFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 4, len(strings.Split(string(errLog), "\n")))
|
||||
|
||||
// Reopen.
|
||||
syscall.Kill(syscall.Getpid(), syscall.SIGHUP)
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
l.Warn("file - warn")
|
||||
l.Error("file - error")
|
||||
|
||||
log, err = ioutil.ReadFile(logFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(strings.Split(string(log), "\n")))
|
||||
|
||||
errLog, err = ioutil.ReadFile(errLogFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 3, len(strings.Split(string(errLog), "\n")))
|
||||
}
|
||||
|
||||
func TestBufferedFileLogger(t *testing.T) {
|
||||
logFile := "/tmp/logger-test/test.log"
|
||||
dir := path.Dir(logFile)
|
||||
err := os.MkdirAll(dir, 0775)
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
l, err := NewBufferedFileLogger(logFile, "debug")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.Debug("file - debug")
|
||||
l.Info("file - info")
|
||||
l.Warn("file - warn")
|
||||
l.Error("file - error")
|
||||
|
||||
log, err := ioutil.ReadFile(logFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(strings.Split(string(log), "\n")))
|
||||
|
||||
// Wait timeout.
|
||||
//time.Sleep(10*time.Second + 10*time.Millisecond)
|
||||
//
|
||||
//log, err = ioutil.ReadFile(logFile)
|
||||
//assert.NoError(t, err)
|
||||
//assert.Equal(t, 5, len(strings.Split(string(log), "\n")))
|
||||
}
|
||||
|
||||
func TestBufferedFileLoggerWithErr(t *testing.T) {
|
||||
logFile := "/tmp/logger-test/test.log"
|
||||
errLogFile := "/tmp/logger-test/test.log.wf"
|
||||
dir := path.Dir(logFile)
|
||||
err := os.MkdirAll(dir, 0775)
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
errL, err := NewBufferedFileLoggerWithErr(logFile, errLogFile, "debug")
|
||||
assert.NoError(t, err)
|
||||
|
||||
errL.Debug("file - debug")
|
||||
errL.Info("file - info")
|
||||
errL.Warn("file - warn")
|
||||
errL.Error("file - error")
|
||||
|
||||
log, err := ioutil.ReadFile(logFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(strings.Split(string(log), "\n")))
|
||||
|
||||
errLog, err := ioutil.ReadFile(errLogFile)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, len(strings.Split(string(errLog), "\n")))
|
||||
|
||||
// Wait timeout.
|
||||
//time.Sleep(10*time.Second + 10*time.Millisecond)
|
||||
//
|
||||
//log, err = ioutil.ReadFile(logFile)
|
||||
//assert.NoError(t, err)
|
||||
//assert.Equal(t, 5, len(strings.Split(string(log), "\n")))
|
||||
//
|
||||
//errLog, err = ioutil.ReadFile(errLogFile)
|
||||
//assert.NoError(t, err)
|
||||
//assert.Equal(t, 3, len(strings.Split(string(errLog), "\n")))
|
||||
}
|
||||
|
||||
func TestTerminalLogger(t *testing.T) {
|
||||
l, err := NewTerminalLogger("debug")
|
||||
assert.NoError(t, err)
|
||||
|
||||
l.Debug("terminal - debug")
|
||||
l.Info("terminal - info")
|
||||
l.Warn("terminal - warn")
|
||||
l.Error("terminal - error")
|
||||
|
||||
l.Debugf("terminal - debug - %d", time.Now().Unix())
|
||||
l.Infof("terminal - info - %d", time.Now().Unix())
|
||||
l.Warnf("terminal - warn - %d", time.Now().Unix())
|
||||
l.Errorf("terminal - error - %d", time.Now().Unix())
|
||||
}
|
||||
|
||||
func TestTerminalLoggerWithErr(t *testing.T) {
|
||||
errL, err := NewTerminalLoggerWithErr("debug")
|
||||
assert.NoError(t, err)
|
||||
|
||||
errL.Debug("terminal - debug - err")
|
||||
errL.Info("terminal - info - err")
|
||||
errL.Warn("terminal - warn - err")
|
||||
errL.Error("terminal - error - err")
|
||||
|
||||
errL.Debugf("terminal - debug - err - %d", time.Now().Unix())
|
||||
errL.Infof("terminal - info - err - %d", time.Now().Unix())
|
||||
errL.Warnf("terminal - warn - err - %d", time.Now().Unix())
|
||||
errL.Errorf("terminal - error - err - %d", time.Now().Unix())
|
||||
}
|
5
vendor/github.com/pengsrc/go-shared/pid/pidfile.go
generated
vendored
5
vendor/github.com/pengsrc/go-shared/pid/pidfile.go
generated
vendored
|
@ -42,8 +42,5 @@ func New(path string) (*File, error) {
|
|||
|
||||
// Remove removes the File.
|
||||
func (file File) Remove() error {
|
||||
if err := os.Remove(file.path); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return os.Remove(file.path)
|
||||
}
|
||||
|
|
4
vendor/github.com/pengsrc/go-shared/pid/pidfile_test.go
generated
vendored
4
vendor/github.com/pengsrc/go-shared/pid/pidfile_test.go
generated
vendored
|
@ -27,6 +27,10 @@ func TestNewAndRemove(t *testing.T) {
|
|||
if err := file.Remove(); err != nil {
|
||||
t.Fatal("Could not delete created test file")
|
||||
}
|
||||
|
||||
if err := os.Remove(dir); err != nil {
|
||||
t.Fatal("Could not delete test dir")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveInvalidPath(t *testing.T) {
|
||||
|
|
20
vendor/github.com/pengsrc/go-shared/reopen/reopen.go
generated
vendored
20
vendor/github.com/pengsrc/go-shared/reopen/reopen.go
generated
vendored
|
@ -142,11 +142,17 @@ func (bw *BufferedFileWriter) Write(p []byte) (int, error) {
|
|||
}
|
||||
|
||||
// Flush flushes the buffer.
|
||||
func (bw *BufferedFileWriter) Flush() {
|
||||
func (bw *BufferedFileWriter) Flush() (err error) {
|
||||
bw.mu.Lock()
|
||||
bw.BufWriter.Flush()
|
||||
bw.OrigWriter.f.Sync()
|
||||
bw.mu.Unlock()
|
||||
defer bw.mu.Unlock()
|
||||
|
||||
if err = bw.BufWriter.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = bw.OrigWriter.f.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// flushDaemon periodically flushes the log file buffers.
|
||||
|
@ -156,9 +162,6 @@ func (bw *BufferedFileWriter) flushDaemon(interval time.Duration) {
|
|||
}
|
||||
}
|
||||
|
||||
const bufferSize = 256 * 1024
|
||||
const flushInterval = 30 * time.Second
|
||||
|
||||
// NewBufferedFileWriter opens a buffered file that is periodically flushed.
|
||||
func NewBufferedFileWriter(w *FileWriter) *BufferedFileWriter {
|
||||
return NewBufferedFileWriterSize(w, bufferSize, flushInterval)
|
||||
|
@ -174,3 +177,6 @@ func NewBufferedFileWriterSize(w *FileWriter, size int, flush time.Duration) *Bu
|
|||
go bw.flushDaemon(flush)
|
||||
return &bw
|
||||
}
|
||||
|
||||
const bufferSize = 256 * 1024
|
||||
const flushInterval = 30 * time.Second
|
||||
|
|
32
vendor/github.com/pengsrc/go-shared/yaml/yaml.go
generated
vendored
32
vendor/github.com/pengsrc/go-shared/yaml/yaml.go
generated
vendored
|
@ -1,32 +0,0 @@
|
|||
package yaml
|
||||
|
||||
import (
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// Encode encode given interface to yaml byte slice.
|
||||
func Encode(source interface{}) ([]byte, error) {
|
||||
bytesResult, err := yaml.Marshal(source)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
return bytesResult, nil
|
||||
}
|
||||
|
||||
// Decode decode given yaml byte slice to corresponding struct.
|
||||
func Decode(content []byte, destinations ...interface{}) (interface{}, error) {
|
||||
var destination interface{}
|
||||
var err error
|
||||
if len(destinations) == 1 {
|
||||
destination = destinations[0]
|
||||
err = yaml.Unmarshal(content, destination)
|
||||
} else {
|
||||
err = yaml.Unmarshal(content, &destination)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return destination, err
|
||||
}
|
72
vendor/github.com/pengsrc/go-shared/yaml/yaml_test.go
generated
vendored
72
vendor/github.com/pengsrc/go-shared/yaml/yaml_test.go
generated
vendored
|
@ -1,72 +0,0 @@
|
|||
package yaml
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestYAMLDecodeUnknown(t *testing.T) {
|
||||
yamlString := `
|
||||
key1: "This is a string." # Single Line Comment
|
||||
key2: 10.50
|
||||
key3:
|
||||
- null
|
||||
- nestedKey1: Anothor string
|
||||
`
|
||||
|
||||
anyData, err := Decode([]byte(yamlString))
|
||||
assert.NoError(t, err)
|
||||
data := anyData.(map[interface{}]interface{})
|
||||
assert.Equal(t, 10.50, data["key2"])
|
||||
}
|
||||
|
||||
func TestYAMLDecodeKnown(t *testing.T) {
|
||||
type SampleYAML struct {
|
||||
Name string `yaml:"name"`
|
||||
Description string `yaml:"description"`
|
||||
}
|
||||
sampleYAMLString := `name: "NAME"`
|
||||
|
||||
sample := SampleYAML{Name: "NaMe", Description: "DeScRiPtIoN"}
|
||||
anyDataPointer, err := Decode([]byte(sampleYAMLString), &sample)
|
||||
assert.NoError(t, err)
|
||||
data := anyDataPointer.(*SampleYAML)
|
||||
assert.Equal(t, "NAME", sample.Name)
|
||||
assert.Equal(t, "DeScRiPtIoN", sample.Description)
|
||||
assert.Equal(t, "NAME", (*data).Name)
|
||||
assert.Equal(t, "DeScRiPtIoN", (*data).Description)
|
||||
|
||||
_, err = Decode([]byte(`- - -`), &YAMLMustError{})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestYAMLDecodeEmpty(t *testing.T) {
|
||||
yamlString := ""
|
||||
|
||||
anyData, err := Decode([]byte(yamlString))
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, anyData)
|
||||
}
|
||||
|
||||
func TestYAMLEncode(t *testing.T) {
|
||||
type SampleYAML struct {
|
||||
Name string `yaml:"name"`
|
||||
Description string `yaml:"description"`
|
||||
}
|
||||
sample := SampleYAML{Name: "NaMe", Description: "DeScRiPtIoN"}
|
||||
|
||||
yamlBytes, err := Encode(sample)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "name: NaMe\ndescription: DeScRiPtIoN\n", string(yamlBytes))
|
||||
|
||||
_, err = Encode(&YAMLMustError{})
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
type YAMLMustError struct{}
|
||||
|
||||
func (*YAMLMustError) MarshalYAML() (interface{}, error) {
|
||||
return nil, errors.New("marshal error")
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue