vendor: update all dependencies to latest versions

This commit is contained in:
Nick Craig-Wood 2018-01-16 13:20:59 +00:00
parent 8e83fb6fb9
commit 7d3a17725d
4878 changed files with 1974229 additions and 201215 deletions

View file

@ -1,5 +1,7 @@
language: go
go:
- master
- 1.9
- 1.8
- 1.7
- 1.6

View file

@ -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"

View file

@ -2,7 +2,7 @@
[![Build Status](https://travis-ci.org/pengsrc/go-shared.svg?branch=master)](https://travis-ci.org/pengsrc/go-shared)
[![Go Report Card](https://goreportcard.com/badge/github.com/pengsrc/go-shared)](https://goreportcard.com/report/github.com/pengsrc/go-shared)
[![License](http://img.shields.io/badge/license-apache%20v2-blue.svg)](https://github.com/yunify/qingstor-sdk-go/blob/master/LICENSE)
[![License](http://img.shields.io/badge/license-apache%20v2-blue.svg)](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
View 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

View 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()

View 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())
}

View 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
View 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
View 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()))
}

View file

@ -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
View 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
}

View 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)
}
}

View file

@ -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()
}

View file

@ -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

View file

@ -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)))

View file

@ -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: []

View file

@ -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

View file

@ -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
}

View file

@ -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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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!"))
}
}

View file

@ -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
}

View file

@ -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())
}

View file

@ -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)
}

View file

@ -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) {

View file

@ -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

View file

@ -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
}

View file

@ -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")
}