├── go.mod
├── trace
├── util.go
├── trace.go
└── trace_test.go
├── tracer.go
├── .gitignore
├── tracer_test.go
├── buffer_pool_bench_test.go
├── uuid
├── v2
│ └── v2.go
├── uuid_test.go
├── v1
│ ├── timestamp.go
│ ├── internal.go
│ ├── v1.go
│ └── v1x.go
├── variant.go
├── rand
│ ├── int.go
│ ├── mac.go
│ ├── rand.go
│ └── read.go
├── helper_test.go
├── helper.go
└── uuid.go
├── concurrent_writer_test.go
├── time_format_bench_test.go
├── concurrent_writer.go
├── helper_test.go
├── location.go
├── json_formatter.go
├── LICENSE
├── helper.go
├── location_test.go
├── buffer_pool.go
├── time_format.go
├── logger_test.go
├── README.md
├── fields.go
├── noop_logger.go
├── level.go
├── json_formatter_test.go
├── text_formatter_test.go
├── time_format_test.go
├── context.go
├── std.go
├── options.go
├── text_formatter.go
├── shortcut.go
├── buffer_pool_test.go
├── fields_test.go
├── level_test.go
├── logger.go
├── options_test.go
├── context_test.go
└── shortcut_without_logger_test.go
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/KeKe-Li/log
2 |
3 | go 1.13
4 |
--------------------------------------------------------------------------------
/trace/util.go:
--------------------------------------------------------------------------------
1 | package trace
2 |
3 | import "github.com/KeKe-Li/log/uuid"
4 |
5 | func NewTraceId() string {
6 | return string(uuid.NewV1().HexEncode())
7 | }
8 |
--------------------------------------------------------------------------------
/tracer.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import "github.com/KeKe-Li/log/trace"
4 |
5 | var _ trace.Tracer = (*logger)(nil)
6 |
7 | func (l *logger) TraceId() string {
8 | return l.getOptions().traceId
9 | }
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, build with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 | .idea
14 |
--------------------------------------------------------------------------------
/tracer_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/KeKe-Li/log/trace"
7 | )
8 |
9 | func TestIsTracer(t *testing.T) {
10 | lg := New()
11 | _, ok := lg.(trace.Tracer)
12 | if !ok {
13 | t.Error("want true")
14 | return
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/buffer_pool_bench_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import "testing"
4 |
5 | func BenchmarkGetBytesBufferPool(b *testing.B) {
6 | b.ReportAllocs()
7 | b.SetParallelism(64)
8 | b.RunParallel(func(pb *testing.PB) {
9 | for pb.Next() {
10 | getBytesBufferPool()
11 | }
12 | })
13 | }
14 |
--------------------------------------------------------------------------------
/uuid/v2/v2.go:
--------------------------------------------------------------------------------
1 | package v2
2 |
3 | import (
4 | "crypto/sha1"
5 | )
6 |
7 | func New(ns [16]byte, name []byte) (uuid [16]byte) {
8 | h := sha1.New()
9 | h.Write(ns[:])
10 | h.Write(name)
11 | copy(uuid[:], h.Sum(nil))
12 | uuid[8] = (uuid[8] & 0x3f) | 0x80 // set variant rfc4122
13 | uuid[6] = (uuid[6] & 0x0f) | 5<<4 // set version 5
14 | return
15 | }
16 |
--------------------------------------------------------------------------------
/uuid/uuid_test.go:
--------------------------------------------------------------------------------
1 | package uuid
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestVariantVersion(t *testing.T) {
8 | u1 := NewV1()
9 | u5 := NewV5(u1, []byte("test"))
10 | if u1.Variant() != VariantRFC4122 || u5.Variant() != VariantRFC4122 {
11 | t.Error("Variant is incorrect")
12 | return
13 | }
14 | if u1.Version() != 1 || u5.Version() != 5 {
15 | t.Error("Version is incorrect")
16 | return
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/concurrent_writer_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "bytes"
5 | "testing"
6 | )
7 |
8 | func TestConcurrentWriter(t *testing.T) {
9 | // nil
10 | {
11 | w := ConcurrentWriter(nil)
12 | if w != nil {
13 | t.Error("want nil")
14 | return
15 | }
16 | }
17 | // non-nil
18 | {
19 | w := bytes.NewBuffer(make([]byte, 0, 64))
20 |
21 | cw := ConcurrentWriter(w)
22 | cw.Write([]byte("123456789"))
23 |
24 | have := w.String()
25 | want := "123456789"
26 | if have != want {
27 | t.Errorf("have:%s, want:%s", have, want)
28 | return
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/time_format_bench_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "testing"
5 | "time"
6 | )
7 |
8 | func BenchmarkFormatTime(b *testing.B) {
9 | now := time.Now()
10 |
11 | b.ReportAllocs()
12 | b.ResetTimer()
13 | for i := 0; i < b.N; i++ {
14 | FormatTime(now)
15 | }
16 | }
17 |
18 | func BenchmarkFormatTimeString(b *testing.B) {
19 | now := time.Now()
20 |
21 | b.ReportAllocs()
22 | b.ResetTimer()
23 | for i := 0; i < b.N; i++ {
24 | FormatTimeString(now)
25 | }
26 | }
27 |
28 | func BenchmarkStdTimeFormat(b *testing.B) {
29 | now := time.Now()
30 |
31 | b.ReportAllocs()
32 | b.ResetTimer()
33 | for i := 0; i < b.N; i++ {
34 | now.Format(TimeFormatLayout)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/concurrent_writer.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "io"
5 | "os"
6 | "sync"
7 | )
8 |
9 | var (
10 | ConcurrentStdout io.Writer = ConcurrentWriter(os.Stdout)
11 | ConcurrentStderr io.Writer = ConcurrentWriter(os.Stderr)
12 | )
13 |
14 | // ConcurrentWriter wraps an io.Writer and returns a concurrent io.Writer.
15 | func ConcurrentWriter(w io.Writer) io.Writer {
16 | if w == nil {
17 | return nil
18 | }
19 | return &concurrentWriter{
20 | w: w,
21 | }
22 | }
23 |
24 | type concurrentWriter struct {
25 | mu sync.Mutex
26 | w io.Writer
27 | }
28 |
29 | func (w *concurrentWriter) Write(p []byte) (n int, err error) {
30 | w.mu.Lock()
31 | defer w.mu.Unlock()
32 | return w.w.Write(p)
33 | }
34 |
--------------------------------------------------------------------------------
/uuid/v1/timestamp.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | // The number of 100-nanoseconds from "1582-10-15 00:00:00 +0000 UTC" to "1970-01-01 00:00:00 +0000 UTC".
8 | const unixToUUID = 122192928000000000
9 |
10 | // uuidTimestamp returns the number of 100-nanoseconds elapsed since "1582-10-15 00:00:00 +0000 UTC"(UUID-epoch).
11 | func uuidTimestamp() int64 {
12 | timeNow := time.Now()
13 | return timeNow.Unix()*1e7 + int64(timeNow.Nanosecond())/100 + unixToUUID
14 | }
15 |
16 | // tillNext100nano spin wait till next 100-nanosecond.
17 | func tillNext100nano(lastTimestamp int64) int64 {
18 | timestamp := uuidTimestamp()
19 | for timestamp <= lastTimestamp {
20 | timestamp = uuidTimestamp()
21 | }
22 | return timestamp
23 | }
24 |
--------------------------------------------------------------------------------
/uuid/variant.go:
--------------------------------------------------------------------------------
1 | package uuid
2 |
3 | type Variant byte
4 |
5 | const (
6 | VariantNCS Variant = iota
7 | VariantRFC4122
8 | VariantMicrosoft
9 | VariantFuture
10 | )
11 |
12 | func (v Variant) String() string {
13 | switch v {
14 | case VariantNCS:
15 | return "NCS-Variant"
16 | case VariantRFC4122:
17 | return "RFC4122-Variant"
18 | case VariantMicrosoft:
19 | return "Microsoft-Variant"
20 | case VariantFuture:
21 | return "Future-Variant"
22 | default:
23 | return "Unknown-Variant"
24 | }
25 | }
26 |
27 | func (uuid UUID) Variant() Variant {
28 | switch {
29 | case (uuid[8] & 0x80) == 0x00:
30 | return VariantNCS
31 | case (uuid[8] & 0xc0) == 0x80:
32 | return VariantRFC4122
33 | case (uuid[8] & 0xe0) == 0xc0:
34 | return VariantMicrosoft
35 | default:
36 | return VariantFuture
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/helper_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import "testing"
4 |
5 | func TestJSON(t *testing.T) {
6 | x := &struct {
7 | A string `json:"a"`
8 | B int `json:"b"`
9 | C string `json:"-"`
10 | }{
11 | A: "a_val",
12 | B: 100,
13 | C: "c",
14 | }
15 | have := JSON(x)
16 | want := `{"a":"a_val","b":100}`
17 | if have != want {
18 | t.Errorf("have:%s, want:%s", have, want)
19 | return
20 | }
21 | }
22 |
23 | func TestXML(t *testing.T) {
24 | x := &struct {
25 | XMLName struct{} `xml:"msg"`
26 | A string `xml:"a"`
27 | B int `xml:"b"`
28 | C string `xml:"-"`
29 | }{
30 | A: "a_val",
31 | B: 100,
32 | C: "c",
33 | }
34 | have := XML(x)
35 | want := `a_val100`
36 | if have != want {
37 | t.Errorf("have:%s, want:%s", have, want)
38 | return
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/uuid/rand/int.go:
--------------------------------------------------------------------------------
1 | package rand
2 |
3 | import (
4 | "encoding/binary"
5 | )
6 |
7 | const (
8 | uint31Mask = ^(uint32(1) << 31)
9 | uint63Mask = ^(uint64(1) << 63)
10 | )
11 |
12 | // Int31 returns a non-negative random 31-bit integer as an int32.
13 | func Int31() int32 {
14 | u32 := Uint32()
15 | return int32(u32 & uint31Mask)
16 | }
17 |
18 | // Int63 returns a non-negative random 63-bit integer as an int64.
19 | func Int63() int64 {
20 | u64 := Uint64()
21 | return int64(u64 & uint63Mask)
22 | }
23 |
24 | // Uint32 returns a random 32-bit integer as a uint32.
25 | func Uint32() uint32 {
26 | var x [4]byte
27 | Read(x[:])
28 | return binary.BigEndian.Uint32(x[:])
29 | }
30 |
31 | // Uint64 returns a random 64-bit integer as a uint64.
32 | func Uint64() uint64 {
33 | var x [8]byte
34 | Read(x[:])
35 | return binary.BigEndian.Uint64(x[:])
36 | }
37 |
--------------------------------------------------------------------------------
/location.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "path"
5 | "runtime"
6 | "strconv"
7 | "strings"
8 | )
9 |
10 | func callerLocation(skip int) string {
11 | pc, file, line, ok := runtime.Caller(skip + 1)
12 | if !ok {
13 | return "???"
14 | }
15 | fn := runtime.FuncForPC(pc)
16 | if fn == nil {
17 | return trimFileName(file) + ":" + strconv.Itoa(line)
18 | }
19 | return trimFuncName(fn.Name()) + "(" + trimFileName(file) + ":" + strconv.Itoa(line) + ")"
20 | }
21 |
22 | func trimFuncName(name string) string {
23 | return path.Base(name)
24 | }
25 |
26 | func trimFileName(name string) string {
27 | i := strings.Index(name, "/src/") + len("/src/")
28 | if i >= len("/src/") && i < len(name) /* BCE */ {
29 | name = name[i:]
30 | }
31 | i = strings.LastIndex(name, "/vendor/") + len("/vendor/")
32 | if i >= len("/vendor/") && i < len(name) /* BCE */ {
33 | return name[i:]
34 | }
35 | return name
36 | }
37 |
--------------------------------------------------------------------------------
/json_formatter.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | )
7 |
8 | var JsonFormatter Formatter = jsonFormatter{}
9 |
10 | type jsonFormatter struct{}
11 |
12 | func (jsonFormatter) Format(entry *Entry) ([]byte, error) {
13 | var buffer *bytes.Buffer
14 | if entry.Buffer != nil {
15 | buffer = entry.Buffer
16 | } else {
17 | buffer = bytes.NewBuffer(make([]byte, 0, 16<<10))
18 | }
19 | var fields map[string]interface{}
20 | if fields = entry.Fields; len(fields) > 0 {
21 | prefixFieldClashes(fields)
22 | for k, v := range fields {
23 | if vv, ok := v.(error); ok {
24 | fields[k] = vv.Error()
25 | }
26 | }
27 | } else {
28 | fields = make(map[string]interface{}, 8)
29 | }
30 | fields[fieldKeyTime] = FormatTimeString(entry.Time.In(_beijingLocation))
31 | fields[fieldKeyLevel] = entry.Level.String()
32 | fields[fieldKeyTraceId] = entry.TraceId
33 | fields[fieldKeyLocation] = entry.Location
34 | fields[fieldKeyMessage] = entry.Message
35 | if err := json.NewEncoder(buffer).Encode(fields); err != nil {
36 | return nil, err
37 | }
38 | return buffer.Bytes(), nil
39 | }
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 keke
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/helper.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "encoding/json"
5 | "encoding/xml"
6 | )
7 |
8 | // JSON is a helper function, following is its function code.
9 | //
10 | // data, _ := json.Marshal(v)
11 | // return string(data)
12 | func JSON(v interface{}) string {
13 | pool := getBytesBufferPool()
14 | buffer := pool.Get()
15 | defer pool.Put(buffer)
16 | buffer.Reset()
17 |
18 | if err := json.NewEncoder(buffer).Encode(v); err != nil {
19 | return ""
20 | }
21 | data := buffer.Bytes()
22 |
23 | // remove the trailing newline
24 | i := len(data) - 1
25 | if i < 0 || i >= len(data) /* BCE */ {
26 | return ""
27 | }
28 | if data[i] == '\n' {
29 | data = data[:i]
30 | }
31 | return string(data)
32 | }
33 |
34 | // XML is a helper function, following is its function code.
35 | //
36 | // data, _ := xml.Marshal(v)
37 | // return string(data)
38 | func XML(v interface{}) string {
39 | pool := getBytesBufferPool()
40 | buffer := pool.Get()
41 | defer pool.Put(buffer)
42 | buffer.Reset()
43 |
44 | if err := xml.NewEncoder(buffer).Encode(v); err != nil {
45 | return ""
46 | }
47 | return string(buffer.Bytes())
48 | }
49 |
--------------------------------------------------------------------------------
/location_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "strings"
5 | "testing"
6 | )
7 |
8 | func testCallerLocation() string {
9 | return callerLocation(0)
10 | }
11 |
12 | func TestCallerLocation(t *testing.T) {
13 | location := testCallerLocation()
14 | switch {
15 | case location == "log.testCallerLocation(github.com/chanxuehong/log/location_test.go:9)":
16 | case strings.HasPrefix(location, "log.testCallerLocation(") && strings.HasSuffix(location, "/log/location_test.go:9)"):
17 | default:
18 | t.Errorf("not expected location: %s", location)
19 | return
20 | }
21 | }
22 |
23 | func TestTrimFileName(t *testing.T) {
24 | tests := []struct {
25 | str string
26 | want string
27 | }{
28 | {
29 | "/a/b/c.go",
30 | "/a/b/c.go",
31 | },
32 | {
33 | "/a/src/b/c.go",
34 | "b/c.go",
35 | },
36 | {
37 | "/a/src/d/vendor/b/c.go",
38 | "b/c.go",
39 | },
40 | {
41 | "/a/vendor/b/c.go",
42 | "b/c.go",
43 | },
44 | }
45 |
46 | for _, v := range tests {
47 | have := trimFileName(v.str)
48 | if have != v.want {
49 | t.Errorf("have:%s, want:%s", have, v.want)
50 | return
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/buffer_pool.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "bytes"
5 | "sync"
6 | "sync/atomic"
7 | "unsafe"
8 | )
9 |
10 | type BytesBufferPool interface {
11 | Get() *bytes.Buffer
12 | Put(*bytes.Buffer)
13 | }
14 |
15 | var _BytesBufferPoolPtr unsafe.Pointer = unsafe.Pointer(&_defaultBytesBufferPool) // *BytesBufferPool
16 |
17 | func getBytesBufferPool() BytesBufferPool {
18 | ptr := (*BytesBufferPool)(atomic.LoadPointer(&_BytesBufferPoolPtr))
19 | return *ptr
20 | }
21 |
22 | func SetBytesBufferPool(pool BytesBufferPool) {
23 | if pool == nil {
24 | return
25 | }
26 | atomic.StorePointer(&_BytesBufferPoolPtr, unsafe.Pointer(&pool))
27 | }
28 |
29 | var _defaultBytesBufferPool BytesBufferPool = &bytesBufferPool{
30 | pool: sync.Pool{
31 | New: syncPoolNew,
32 | },
33 | }
34 |
35 | func syncPoolNew() interface{} {
36 | return bytes.NewBuffer(make([]byte, 0, 16<<10))
37 | }
38 |
39 | type bytesBufferPool struct {
40 | pool sync.Pool
41 | }
42 |
43 | func (p *bytesBufferPool) Get() *bytes.Buffer {
44 | return p.pool.Get().(*bytes.Buffer)
45 | }
46 |
47 | func (p *bytesBufferPool) Put(x *bytes.Buffer) {
48 | if x == nil {
49 | return
50 | }
51 | p.pool.Put(x)
52 | }
53 |
--------------------------------------------------------------------------------
/time_format.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import "time"
4 |
5 | const TimeFormatLayout = "2006-01-02 15:04:05.000"
6 |
7 | // 2006-01-02 15:04:05.000
8 | func FormatTimeString(t time.Time) string {
9 | result := FormatTime(t)
10 | return string(result[:])
11 | }
12 |
13 | // 2006-01-02 15:04:05.000
14 | func FormatTime(t time.Time) (result [23]byte) {
15 | year, month, day := t.Date()
16 | hour, min, sec := t.Clock()
17 | nsec := t.Nanosecond()
18 |
19 | itoa(result[:4], year)
20 | result[4] = '-'
21 | itoa(result[5:7], int(month))
22 | result[7] = '-'
23 | itoa(result[8:10], day)
24 | result[10] = ' '
25 | itoa(result[11:13], hour)
26 | result[13] = ':'
27 | itoa(result[14:16], min)
28 | result[16] = ':'
29 | itoa(result[17:19], sec)
30 | result[19] = '.'
31 | itoa(result[20:], nsec/1e6)
32 | return
33 | }
34 |
35 | // itoa format integer to fixed-width decimal ASCII.
36 | //
37 | // requirement:
38 | // n must be greater than or equal to 0
39 | // buf must be large enough to accommodate n
40 | func itoa(buf []byte, n int) {
41 | i := len(buf) - 1
42 | for n >= 10 {
43 | q := n / 10
44 | buf[i] = byte('0' + n - q*10)
45 | i--
46 | n = q
47 | }
48 | // n < 10
49 | buf[i] = byte('0' + n)
50 | i--
51 | for i >= 0 {
52 | buf[i] = '0'
53 | i--
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/logger_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import "testing"
4 |
5 | func TestLogger_New(t *testing.T) {
6 | lg1 := _New([]Option{
7 | WithFormatter(JsonFormatter),
8 | WithOutput(ConcurrentStderr),
9 | WithLevel(InfoLevel),
10 | })
11 |
12 | lg2 := _New(nil)
13 | lg2.SetFormatter(JsonFormatter)
14 | lg2.SetOutput(ConcurrentStderr)
15 | lg2.SetLevel(InfoLevel)
16 |
17 | opts1 := lg1.getOptions()
18 | opts2 := lg2.getOptions()
19 |
20 | if *opts1 != *opts2 {
21 | t.Errorf("have:%v, want:%v", opts1, opts2)
22 | return
23 | }
24 | }
25 |
26 | func TestLogger_SetOptions_GetOptions(t *testing.T) {
27 | lg := _New(nil)
28 |
29 | // default
30 | {
31 | opts := lg.getOptions()
32 | want := options{
33 | traceId: "",
34 | formatter: TextFormatter,
35 | output: ConcurrentStdout,
36 | level: DebugLevel,
37 | }
38 | if have := *opts; have != want {
39 | t.Errorf("have:%v, want:%v", have, want)
40 | return
41 | }
42 | }
43 | // set and get
44 | {
45 | opts := &options{
46 | traceId: "123456789",
47 | formatter: JsonFormatter,
48 | output: ConcurrentStderr,
49 | level: InfoLevel,
50 | }
51 | lg.setOptions(opts)
52 | have := lg.getOptions()
53 | if *have != *opts {
54 | t.Errorf("have:%v, want:%v", have, opts)
55 | return
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/uuid/helper_test.go:
--------------------------------------------------------------------------------
1 | package uuid
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestEncodeDecode(t *testing.T) {
8 | u1 := NewV1()
9 | encoded := u1.Encode()
10 | u2, err := Decode(encoded)
11 | if err != nil {
12 | t.Error("decode failed")
13 | return
14 | }
15 | if u1 != u2 {
16 | t.Error("encode and decode mismatch")
17 | return
18 | }
19 | }
20 |
21 | //var (
22 | // NamespaceDNS, _ = Decode([]byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
23 | // NamespaceURL, _ = Decode([]byte("6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
24 | // NamespaceOID, _ = Decode([]byte("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
25 | // NamespaceX500, _ = Decode([]byte("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
26 | //)
27 |
28 | func TestNamespace(t *testing.T) {
29 | if NamespaceDNS.String() != "6ba7b810-9dad-11d1-80b4-00c04fd430c8" {
30 | t.Error("decode and encode mismatch")
31 | return
32 | }
33 | if NamespaceURL.String() != "6ba7b811-9dad-11d1-80b4-00c04fd430c8" {
34 | t.Error("decode and encode mismatch")
35 | return
36 | }
37 | if NamespaceOID.String() != "6ba7b812-9dad-11d1-80b4-00c04fd430c8" {
38 | t.Error("decode and encode mismatch")
39 | return
40 | }
41 | if NamespaceX500.String() != "6ba7b814-9dad-11d1-80b4-00c04fd430c8" {
42 | t.Error("decode and encode mismatch")
43 | return
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #### go log system
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | log is a powerful logging framework that provides log custom log level.
10 |
11 | log provides Fatal, Error, Warn, Info, Debug level log. and with the requestID can quickly go to the current line of code.
12 |
13 | If you like,please give a star。
14 |
15 | #### How to Use
16 |
17 | it's very simple and easy to use.
18 |
19 | ```go
20 | func main(){
21 | l := log.New(log.WithTraceId(trace.NewTraceId()))
22 | ctx := log.NewContext(context.Background(), l)
23 |
24 | v, err := rand.Int(rand.Reader, big.NewInt(int64(16)))
25 | if err != nil {
26 | log.ErrorContext(ctx, " rand.In failed", "error", err.Error())
27 | return
28 | }
29 | log.InfoContext(ctx, "the rand Int result", "v", v)
30 | }
31 | ```
32 |
33 | This use is made of the key and the Value output.
34 |
35 | ### License
36 |
37 | This is free software distributed under the terms of the MIT license
38 |
--------------------------------------------------------------------------------
/trace/trace.go:
--------------------------------------------------------------------------------
1 | package trace
2 |
3 | import (
4 | "context"
5 | "net/http"
6 | )
7 |
8 | type Tracer interface {
9 | TraceId() string
10 | }
11 |
12 | type traceIdContextKey struct{}
13 |
14 | var _traceIdContextKey traceIdContextKey
15 |
16 | func NewContext(ctx context.Context, traceId string) context.Context {
17 | if traceId == "" {
18 | return ctx
19 | }
20 | if ctx == nil {
21 | return context.WithValue(context.Background(), _traceIdContextKey, traceId)
22 | }
23 | if value, ok := ctx.Value(_traceIdContextKey).(string); ok && value == traceId {
24 | return ctx
25 | }
26 | return context.WithValue(ctx, _traceIdContextKey, traceId)
27 | }
28 |
29 | func FromContext(ctx context.Context) (traceId string, ok bool) {
30 | if ctx == nil {
31 | return "", false
32 | }
33 | traceId, ok = ctx.Value(_traceIdContextKey).(string)
34 | return
35 | }
36 |
37 | func FromRequest(req *http.Request) (traceId string, ok bool) {
38 | traceId, ok = FromContext(req.Context())
39 | if ok {
40 | return traceId, true
41 | }
42 | return FromHeader(req.Header)
43 | }
44 |
45 | const TraceIdHeaderKey = "X-Request-Id"
46 |
47 | func FromHeader(header http.Header) (traceId string, ok bool) {
48 | traceId = header.Get(TraceIdHeaderKey)
49 | if traceId != "" {
50 | return traceId, true
51 | }
52 | return "", false
53 | }
54 |
--------------------------------------------------------------------------------
/fields.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import "errors"
4 |
5 | var (
6 | _ErrNumberOfFieldsMustNotBeOdd error = errors.New("the number of fields must not be odd")
7 | _ErrTypeOfFieldKeyMustBeString error = errors.New("the type of field key must be string")
8 | _ErrFieldKeyMustNotBeEmpty error = errors.New("the field key must not be empty")
9 | )
10 |
11 | func combineFields(m map[string]interface{}, fields []interface{}) (map[string]interface{}, error) {
12 | if len(fields) == 0 {
13 | return cloneFields(m), nil
14 | }
15 | if len(fields)&1 != 0 {
16 | return cloneFields(m), _ErrNumberOfFieldsMustNotBeOdd
17 | }
18 |
19 | m2 := make(map[string]interface{}, 8+len(m)+len(fields)>>1) // 8 is reserved for the standard field
20 | for k, v := range m {
21 | m2[k] = v
22 | }
23 | var (
24 | k string
25 | ok bool
26 | )
27 | for i, v := range fields {
28 | if i&1 == 0 { // key
29 | k, ok = v.(string)
30 | if !ok {
31 | return m2, _ErrTypeOfFieldKeyMustBeString
32 | }
33 | if k == "" {
34 | return m2, _ErrFieldKeyMustNotBeEmpty
35 | }
36 | } else { // value
37 | m2[k] = v
38 | }
39 | }
40 | return m2, nil
41 | }
42 |
43 | func cloneFields(fields map[string]interface{}) map[string]interface{} {
44 | if len(fields) == 0 {
45 | return nil
46 | }
47 | m := make(map[string]interface{}, len(fields))
48 | for k, v := range fields {
49 | m[k] = v
50 | }
51 | return m
52 | }
53 |
--------------------------------------------------------------------------------
/noop_logger.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import "io"
4 |
5 | // NoopLogger no operation Logger
6 | type NoopLogger struct{}
7 |
8 | // Fatal impl Logger Fatal
9 | func (NoopLogger) Fatal(msg string, fields ...interface{}) {
10 | }
11 |
12 | // Error impl Logger Error
13 | func (NoopLogger) Error(msg string, fields ...interface{}) {
14 | }
15 |
16 | // Warn impl Logger Warn
17 | func (NoopLogger) Warn(msg string, fields ...interface{}) {
18 | }
19 |
20 | // Info impl Logger Info
21 | func (NoopLogger) Info(msg string, fields ...interface{}) {
22 | }
23 |
24 | // Debug impl Logger Debug
25 | func (NoopLogger) Debug(msg string, fields ...interface{}) {
26 | }
27 |
28 | // Output impl Logger Output
29 | func (NoopLogger) Output(calldepth int, level Level, msg string, fields ...interface{}) {
30 | }
31 |
32 | // WithField impl Logger WithField
33 | func (NoopLogger) WithField(key string, value interface{}) Logger {
34 | return NoopLogger{}
35 | }
36 |
37 | // WithFields impl Logger WithFields
38 | func (NoopLogger) WithFields(fields ...interface{}) Logger {
39 | return NoopLogger{}
40 | }
41 |
42 | // SetFormatter impl Logger SetFormatter
43 | func (NoopLogger) SetFormatter(Formatter) {
44 | }
45 |
46 | // SetOutput impl Logger SetOutput
47 | func (NoopLogger) SetOutput(io.Writer) {
48 | }
49 |
50 | // SetLevel impl Logger SetLevel
51 | func (NoopLogger) SetLevel(Level) error {
52 | return nil
53 | }
54 |
55 | // SetLevelString impl Logger SetLevelString
56 | func (NoopLogger) SetLevelString(string) error {
57 | return nil
58 | }
59 |
--------------------------------------------------------------------------------
/level.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | func init() {
9 | // since invalidLevel must be 0, forced check.
10 | if invalidLevel != 0 {
11 | panic("invalidLevel must equal 0")
12 | }
13 | }
14 |
15 | const (
16 | invalidLevel Level = iota
17 | FatalLevel
18 | ErrorLevel
19 | WarnLevel
20 | InfoLevel
21 | DebugLevel
22 | )
23 |
24 | func isValidLevel(level Level) bool {
25 | return level >= FatalLevel && level <= DebugLevel
26 | }
27 | func isLevelEnabled(level, loggerLevel Level) bool {
28 | return loggerLevel >= level
29 | }
30 |
31 | const (
32 | FatalLevelString = "fatal"
33 | ErrorLevelString = "error"
34 | WarnLevelString = "warning"
35 | InfoLevelString = "info"
36 | DebugLevelString = "debug"
37 | )
38 |
39 | func parseLevelString(str string) (level Level, ok bool) {
40 | switch strings.ToLower(str) {
41 | case DebugLevelString:
42 | return DebugLevel, true
43 | case InfoLevelString:
44 | return InfoLevel, true
45 | case WarnLevelString:
46 | return WarnLevel, true
47 | case ErrorLevelString:
48 | return ErrorLevel, true
49 | case FatalLevelString:
50 | return FatalLevel, true
51 | default:
52 | return invalidLevel, false
53 | }
54 | }
55 |
56 | type Level uint
57 |
58 | func (level Level) String() string {
59 | switch level {
60 | case FatalLevel:
61 | return FatalLevelString
62 | case ErrorLevel:
63 | return ErrorLevelString
64 | case WarnLevel:
65 | return WarnLevelString
66 | case InfoLevel:
67 | return InfoLevelString
68 | case DebugLevel:
69 | return DebugLevelString
70 | default:
71 | return fmt.Sprintf("unknown_%d", level)
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/uuid/rand/mac.go:
--------------------------------------------------------------------------------
1 | package rand
2 |
3 | import (
4 | "bytes"
5 | "net"
6 | )
7 |
8 | var MAC [6]byte = getMAC() // One MAC of this machine; Particular case, it is a random bytes.
9 |
10 | var zeroMAC [8]byte
11 |
12 | func getMAC() (mac [6]byte) {
13 | interfaces, err := net.Interfaces()
14 | if err != nil {
15 | return genMAC()
16 | }
17 |
18 | // Gets a MAC from interfaces of this machine,
19 | // the MAC of up state interface is preferred.
20 | found := false // Says it has found a MAC
21 | for _, itf := range interfaces {
22 | if itf.Flags&net.FlagLoopback == net.FlagLoopback ||
23 | itf.Flags&net.FlagPointToPoint == net.FlagPointToPoint {
24 | continue
25 | }
26 |
27 | switch hardwareAddr := itf.HardwareAddr; len(hardwareAddr) {
28 | case 6: // MAC-48, EUI-48
29 | if bytes.Equal(hardwareAddr, zeroMAC[:6]) {
30 | continue
31 | }
32 | if itf.Flags&net.FlagUp == 0 {
33 | if !found {
34 | copy(mac[:], hardwareAddr)
35 | found = true
36 | }
37 | continue
38 | }
39 | copy(mac[:], hardwareAddr)
40 | return
41 | case 8: // EUI-64
42 | if bytes.Equal(hardwareAddr, zeroMAC[:]) {
43 | continue
44 | }
45 | if itf.Flags&net.FlagUp == 0 {
46 | if !found {
47 | copy(mac[:3], hardwareAddr)
48 | copy(mac[3:], hardwareAddr[5:])
49 | found = true
50 | }
51 | continue
52 | }
53 | copy(mac[:3], hardwareAddr)
54 | copy(mac[3:], hardwareAddr[5:])
55 | return
56 | }
57 | }
58 | if found {
59 | return
60 | }
61 |
62 | return genMAC()
63 | }
64 |
65 | // generates a random MAC.
66 | func genMAC() (mac [6]byte) {
67 | Read(mac[:])
68 | mac[0] |= 0x01 // multicast
69 | return
70 | }
71 |
--------------------------------------------------------------------------------
/uuid/v1/internal.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | "bytes"
5 | "net"
6 |
7 | "github.com/KeKe-Li/log/uuid/rand"
8 | )
9 |
10 | var MAC [6]byte = getMAC() // One MAC of this machine; Particular case, it is a random bytes.
11 |
12 | var zeroMAC [8]byte
13 |
14 | func getMAC() (mac [6]byte) {
15 | interfaces, err := net.Interfaces()
16 | if err != nil {
17 | return genMAC()
18 | }
19 |
20 | // Gets a MAC from interfaces of this machine,
21 | // the MAC of up state interface is preferred.
22 | found := false // Says it has found a MAC
23 | for _, itf := range interfaces {
24 | if itf.Flags&net.FlagLoopback == net.FlagLoopback ||
25 | itf.Flags&net.FlagPointToPoint == net.FlagPointToPoint {
26 | continue
27 | }
28 |
29 | switch hardwareAddr := itf.HardwareAddr; len(hardwareAddr) {
30 | case 6: // MAC-48, EUI-48
31 | if bytes.Equal(hardwareAddr, zeroMAC[:6]) {
32 | continue
33 | }
34 | if itf.Flags&net.FlagUp == 0 {
35 | if !found {
36 | copy(mac[:], hardwareAddr)
37 | found = true
38 | }
39 | continue
40 | }
41 | copy(mac[:], hardwareAddr)
42 | return
43 | case 8: // EUI-64
44 | if bytes.Equal(hardwareAddr, zeroMAC[:]) {
45 | continue
46 | }
47 | if itf.Flags&net.FlagUp == 0 {
48 | if !found {
49 | copy(mac[:3], hardwareAddr)
50 | copy(mac[3:], hardwareAddr[5:])
51 | found = true
52 | }
53 | continue
54 | }
55 | copy(mac[:3], hardwareAddr)
56 | copy(mac[3:], hardwareAddr[5:])
57 | return
58 | }
59 | }
60 | if found {
61 | return
62 | }
63 |
64 | return genMAC()
65 | }
66 |
67 | // generates a random MAC.
68 | func genMAC() (mac [6]byte) {
69 | rand.Read(mac[:])
70 | mac[0] |= 0x01 // multicast
71 | return
72 | }
73 |
--------------------------------------------------------------------------------
/uuid/rand/rand.go:
--------------------------------------------------------------------------------
1 | package rand
2 |
3 | import (
4 | "crypto/md5"
5 | "encoding/hex"
6 | "sync"
7 | "sync/atomic"
8 | "time"
9 | )
10 |
11 | const (
12 | saltLen = 45 // see New(), 6+4+45==55<56, Best performance for md5
13 | saltUpdateInterval = 3600 // seconds
14 | )
15 |
16 | var (
17 | gSalt = make([]byte, saltLen)
18 | gSequence = Uint32()
19 |
20 | gMutex sync.Mutex
21 | gSaltLastUpdateTimestamp int64 = -saltUpdateInterval
22 | )
23 |
24 | // New returns 16-byte raw random bytes.
25 | // It is not printable, you can use encoding/hex or encoding/base64 to print it.
26 | func New() (rd [16]byte) {
27 | timeNow := time.Now()
28 | timeNowUnix := timeNow.Unix()
29 |
30 | if timeNowUnix >= atomic.LoadInt64(&gSaltLastUpdateTimestamp)+saltUpdateInterval {
31 | gMutex.Lock() // Lock
32 | if timeNowUnix >= gSaltLastUpdateTimestamp+saltUpdateInterval {
33 | gSaltLastUpdateTimestamp = timeNowUnix
34 | gMutex.Unlock() // Unlock
35 |
36 | Read(gSalt)
37 | copy(rd[:], gSalt)
38 | return
39 | }
40 | gMutex.Unlock() // Unlock
41 | }
42 | sequence := atomic.AddUint32(&gSequence, 1)
43 |
44 | timeNowUnixNano := timeNow.UnixNano()
45 | var src [6 + 4 + saltLen]byte // 6+4+45==55
46 | src[0] = byte(timeNowUnixNano >> 40)
47 | src[1] = byte(timeNowUnixNano >> 32)
48 | src[2] = byte(timeNowUnixNano >> 24)
49 | src[3] = byte(timeNowUnixNano >> 16)
50 | src[4] = byte(timeNowUnixNano >> 8)
51 | src[5] = byte(timeNowUnixNano)
52 | src[6] = byte(sequence >> 24)
53 | src[7] = byte(sequence >> 16)
54 | src[8] = byte(sequence >> 8)
55 | src[9] = byte(sequence)
56 | copy(src[10:], gSalt)
57 |
58 | return md5.Sum(src[:])
59 | }
60 |
61 | // NewHex returns 32-byte hex-encoded bytes.
62 | func NewHex() (rd []byte) {
63 | rdx := New()
64 | rd = make([]byte, hex.EncodedLen(len(rdx)))
65 | hex.Encode(rd, rdx[:])
66 | return
67 | }
68 |
--------------------------------------------------------------------------------
/uuid/helper.go:
--------------------------------------------------------------------------------
1 | package uuid
2 |
3 | import (
4 | "encoding/hex"
5 | "errors"
6 | )
7 |
8 | func HexEncode(uuid UUID) []byte {
9 | buf := make([]byte, 32)
10 | hex.Encode(buf, uuid[:])
11 | return buf
12 | }
13 |
14 | // Encode encodes UUID to "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" format.
15 | func Encode(uuid UUID) []byte {
16 | const encodedUUIDLen = 36
17 | var buf [encodedUUIDLen]byte
18 | hex.Encode(buf[:8], uuid[:4])
19 | buf[8] = '-'
20 | hex.Encode(buf[9:13], uuid[4:6])
21 | buf[13] = '-'
22 | hex.Encode(buf[14:18], uuid[6:8])
23 | buf[18] = '-'
24 | hex.Encode(buf[19:23], uuid[8:10])
25 | buf[23] = '-'
26 | hex.Encode(buf[24:], uuid[10:])
27 | return buf[:]
28 | }
29 |
30 | var (
31 | NamespaceDNS, _ = Decode([]byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
32 | NamespaceURL, _ = Decode([]byte("6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
33 | NamespaceOID, _ = Decode([]byte("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
34 | NamespaceX500, _ = Decode([]byte("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
35 | )
36 |
37 | // Decode decodes data with "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" format into UUID.
38 | func Decode(data []byte) (uuid UUID, err error) {
39 | const encodedUUIDLen = 36
40 | if len(data) != encodedUUIDLen {
41 | err = errors.New("invalid UUID data")
42 | return
43 | }
44 | _ = data[encodedUUIDLen-1]
45 | if data[8] != '-' || data[13] != '-' || data[18] != '-' || data[23] != '-' {
46 | err = errors.New("invalid UUID data")
47 | return
48 | }
49 | if _, err = hex.Decode(uuid[:4], data[:8]); err != nil {
50 | return
51 | }
52 | if _, err = hex.Decode(uuid[4:6], data[9:13]); err != nil {
53 | return
54 | }
55 | if _, err = hex.Decode(uuid[6:8], data[14:18]); err != nil {
56 | return
57 | }
58 | if _, err = hex.Decode(uuid[8:10], data[19:23]); err != nil {
59 | return
60 | }
61 | _, err = hex.Decode(uuid[10:], data[24:])
62 | return
63 | }
64 |
--------------------------------------------------------------------------------
/json_formatter_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "encoding/json"
5 | "reflect"
6 | "testing"
7 | "time"
8 | )
9 |
10 | func TestJsonFormatter_Format(t *testing.T) {
11 | entry := &Entry{
12 | Location: "function(file:line)",
13 | Time: time.Date(2018, time.May, 20, 8, 20, 30, 666000000, time.UTC),
14 | Level: InfoLevel,
15 | TraceId: "trace_id_123456789",
16 | Message: "message_123456789",
17 | Fields: map[string]interface{}{
18 | "key1": "fields_value1",
19 | "key2": "fields_value2",
20 | "key3": &testError{X: "123456789"}, // error
21 | fieldKeyTime: "time",
22 | fieldKeyLevel: "level",
23 | fieldKeyTraceId: "request_id",
24 | fieldKeyLocation: "location",
25 | fieldKeyMessage: "msg",
26 | },
27 | Buffer: nil,
28 | }
29 | data, err := JsonFormatter.Format(entry)
30 | if err != nil {
31 | t.Error(err.Error())
32 | return
33 | }
34 | if len(data) == 0 || data[len(data)-1] != '\n' {
35 | t.Error("want end with '\n'")
36 | return
37 | }
38 | var have map[string]string
39 | if err = json.Unmarshal(data, &have); err != nil {
40 | t.Error(err.Error())
41 | return
42 | }
43 | want := map[string]string{
44 | "time": "2018-05-20 16:20:30.666",
45 | "level": "info",
46 | "request_id": "trace_id_123456789",
47 | "location": "function(file:line)",
48 | "msg": "message_123456789",
49 | "fields.level": "level",
50 | "fields.location": "location",
51 | "fields.msg": "msg",
52 | "fields.request_id": "request_id",
53 | "fields.time": "time",
54 | "key1": "fields_value1",
55 | "key2": "fields_value2",
56 | "key3": "test_error_123456789", // error
57 | }
58 | if !reflect.DeepEqual(have, want) {
59 | t.Errorf("\nhave:%v\nwant:%v", have, want)
60 | return
61 | }
62 | }
63 |
64 | type testError struct {
65 | X string `json:"x"`
66 | }
67 |
68 | func (e *testError) Error() string {
69 | return "test_error_123456789"
70 | }
71 |
--------------------------------------------------------------------------------
/text_formatter_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "encoding/json"
5 | "reflect"
6 | "testing"
7 | "time"
8 | )
9 |
10 | func TestTextFormatter_Format(t *testing.T) {
11 | entry := &Entry{
12 | Location: "function(file:line)",
13 | Time: time.Date(2018, time.May, 20, 8, 20, 30, 666777888, time.UTC),
14 | Level: InfoLevel,
15 | TraceId: "trace_id_123456789",
16 | Message: "message_123456789",
17 | Fields: map[string]interface{}{
18 | "key1": "fields_value1",
19 | "key2": "fields_value2",
20 | "key3": &testError{X: "123456789"}, // error
21 | "key4": json.RawMessage([]byte(`{"code":0,"msg":""}`)),
22 | fieldKeyTime: "time",
23 | fieldKeyLevel: "level",
24 | fieldKeyTraceId: "request_id",
25 | fieldKeyLocation: "location",
26 | fieldKeyMessage: "msg",
27 | },
28 | Buffer: nil,
29 | }
30 | have, err := TextFormatter.Format(entry)
31 | if err != nil {
32 | t.Error(err.Error())
33 | return
34 | }
35 | want := `time=2018-05-20 16:20:30.666, level=info, request_id=trace_id_123456789, location=function(file:line), msg=message_123456789, ` +
36 | `fields.level=level, fields.location=location, fields.msg=msg, fields.request_id=request_id, fields.time=time, ` +
37 | `key1=fields_value1, key2=fields_value2, key3=test_error_123456789, key4={"code":0,"msg":""}` + "\n"
38 | if string(have) != want {
39 | t.Errorf("\nhave:%s\nwant:%s", have, want)
40 | return
41 | }
42 | }
43 |
44 | func TestPrefixFieldClashes(t *testing.T) {
45 | m := map[string]interface{}{
46 | "time": "time",
47 | "fields.time": "fields.time",
48 | "level": "level",
49 | "fields.level": "fields.level",
50 | "fields.level.2": "fields.level.2",
51 | }
52 | prefixFieldClashes(m)
53 | want := map[string]interface{}{
54 | "fields.time.2": "time",
55 | "fields.time": "fields.time",
56 | "fields.level.3": "level",
57 | "fields.level": "fields.level",
58 | "fields.level.2": "fields.level.2",
59 | }
60 | if !reflect.DeepEqual(m, want) {
61 | t.Errorf("\nhave:%v\nwant:%v", m, want)
62 | return
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/uuid/rand/read.go:
--------------------------------------------------------------------------------
1 | package rand
2 |
3 | import (
4 | cryptorand "crypto/rand"
5 | mathrand "math/rand"
6 | "time"
7 | )
8 |
9 | func init() {
10 | mathrand.Seed(time.Now().UnixNano())
11 | }
12 |
13 | // Read reads len(p)-byte raw random bytes to p.
14 | func Read(p []byte) {
15 | if len(p) <= 0 {
16 | return
17 | }
18 |
19 | // get from crypto/rand
20 | if _, err := cryptorand.Read(p); err == nil {
21 | return
22 | }
23 |
24 | // get from math/rand
25 | timeNowNano := int64(time.Now().Nanosecond())
26 | timeNowNano = timeNowNano<<32 | timeNowNano
27 | for len(p) > 0 {
28 | n := mathrand.Int63() ^ timeNowNano
29 | switch len(p) {
30 | case 8:
31 | p[0] = byte(n >> 56)
32 | p[1] = byte(n >> 48)
33 | p[2] = byte(n >> 40)
34 | p[3] = byte(n >> 32)
35 | p[4] = byte(n >> 24)
36 | p[5] = byte(n >> 16)
37 | p[6] = byte(n >> 8)
38 | p[7] = byte(n)
39 | return
40 | case 4:
41 | p[0] = byte(n >> 24)
42 | p[1] = byte(n >> 16)
43 | p[2] = byte(n >> 8)
44 | p[3] = byte(n)
45 | return
46 | case 1:
47 | p[0] = byte(n)
48 | return
49 | case 2:
50 | p[0] = byte(n >> 8)
51 | p[1] = byte(n)
52 | return
53 | case 3:
54 | p[0] = byte(n >> 16)
55 | p[1] = byte(n >> 8)
56 | p[2] = byte(n)
57 | return
58 | case 5:
59 | p[0] = byte(n >> 32)
60 | p[1] = byte(n >> 24)
61 | p[2] = byte(n >> 16)
62 | p[3] = byte(n >> 8)
63 | p[4] = byte(n)
64 | return
65 | case 6:
66 | p[0] = byte(n >> 40)
67 | p[1] = byte(n >> 32)
68 | p[2] = byte(n >> 24)
69 | p[3] = byte(n >> 16)
70 | p[4] = byte(n >> 8)
71 | p[5] = byte(n)
72 | return
73 | case 7:
74 | p[0] = byte(n >> 48)
75 | p[1] = byte(n >> 40)
76 | p[2] = byte(n >> 32)
77 | p[3] = byte(n >> 24)
78 | p[4] = byte(n >> 16)
79 | p[5] = byte(n >> 8)
80 | p[6] = byte(n)
81 | return
82 | default: // len(p) > 8
83 | _ = p[8]
84 | p[0] = byte(n >> 56)
85 | p[1] = byte(n >> 48)
86 | p[2] = byte(n >> 40)
87 | p[3] = byte(n >> 32)
88 | p[4] = byte(n >> 24)
89 | p[5] = byte(n >> 16)
90 | p[6] = byte(n >> 8)
91 | p[7] = byte(n)
92 | p = p[8:]
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/time_format_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "bytes"
5 | "testing"
6 | "time"
7 | )
8 |
9 | func TestFormatTime(t *testing.T) {
10 | now := time.Now()
11 | have := FormatTime(now)
12 | want := now.Format(TimeFormatLayout)
13 | if have := string(have[:]); have != want {
14 | t.Errorf("have:%s, want:%s", have, want)
15 | return
16 | }
17 | }
18 |
19 | func TestFormatTimeString(t *testing.T) {
20 | now := time.Now()
21 | have := FormatTimeString(now)
22 | want := now.Format(TimeFormatLayout)
23 | if have != want {
24 | t.Errorf("have:%s, want:%s", have, want)
25 | return
26 | }
27 | }
28 |
29 | func TestItoa(t *testing.T) {
30 | tests := []struct {
31 | buf []byte
32 | n int
33 | want []byte
34 | }{
35 | {
36 | make([]byte, 2),
37 | 0,
38 | []byte{'0', '0'},
39 | },
40 | {
41 | make([]byte, 2),
42 | 1,
43 | []byte{'0', '1'},
44 | },
45 | {
46 | make([]byte, 2),
47 | 2,
48 | []byte{'0', '2'},
49 | },
50 | {
51 | make([]byte, 2),
52 | 11,
53 | []byte{'1', '1'},
54 | },
55 | {
56 | make([]byte, 2),
57 | 98,
58 | []byte{'9', '8'},
59 | },
60 |
61 | {
62 | make([]byte, 3),
63 | 0,
64 | []byte{'0', '0', '0'},
65 | },
66 | {
67 | make([]byte, 3),
68 | 6,
69 | []byte{'0', '0', '6'},
70 | },
71 | {
72 | make([]byte, 3),
73 | 67,
74 | []byte{'0', '6', '7'},
75 | },
76 | {
77 | make([]byte, 3),
78 | 567,
79 | []byte{'5', '6', '7'},
80 | },
81 |
82 | {
83 | make([]byte, 4),
84 | 0,
85 | []byte{'0', '0', '0', '0'},
86 | },
87 | {
88 | make([]byte, 4),
89 | 6,
90 | []byte{'0', '0', '0', '6'},
91 | },
92 | {
93 | make([]byte, 4),
94 | 67,
95 | []byte{'0', '0', '6', '7'},
96 | },
97 | {
98 | make([]byte, 4),
99 | 567,
100 | []byte{'0', '5', '6', '7'},
101 | },
102 | {
103 | make([]byte, 4),
104 | 4567,
105 | []byte{'4', '5', '6', '7'},
106 | },
107 | }
108 |
109 | for _, v := range tests {
110 | itoa(v.buf, v.n)
111 | if !bytes.Equal(v.buf, v.want) {
112 | t.Errorf("n=%d, have=%v, want=%v", v.n, v.buf, v.want)
113 | return
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/context.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "context"
5 | "net/http"
6 | )
7 |
8 | type loggerContextKey struct{}
9 |
10 | var _loggerContextKey loggerContextKey
11 |
12 | func NewContext(ctx context.Context, logger Logger) context.Context {
13 | if logger == nil {
14 | return ctx
15 | }
16 | if ctx == nil {
17 | return context.WithValue(context.Background(), _loggerContextKey, logger)
18 | }
19 | if value, ok := ctx.Value(_loggerContextKey).(Logger); ok && value == logger {
20 | return ctx
21 | }
22 | return context.WithValue(ctx, _loggerContextKey, logger)
23 | }
24 |
25 | func FromContext(ctx context.Context) (lg Logger, ok bool) {
26 | if ctx == nil {
27 | return nil, false
28 | }
29 | lg, ok = ctx.Value(_loggerContextKey).(Logger)
30 | return
31 | }
32 |
33 | func MustFromContext(ctx context.Context) Logger {
34 | lg, ok := FromContext(ctx)
35 | if !ok {
36 | panic("log: failed to get from context.Context")
37 | }
38 | return lg
39 | }
40 |
41 | func FromContextOrNew(ctx context.Context, new func() Logger) (lg Logger, ctx2 context.Context, isNew bool) {
42 | lg, ok := FromContext(ctx)
43 | if ok {
44 | return lg, ctx, false
45 | }
46 | if new != nil {
47 | lg = new()
48 | ctx2 = NewContext(ctx, lg)
49 | isNew = true
50 | return
51 | }
52 | lg = New()
53 | ctx2 = NewContext(ctx, lg)
54 | isNew = true
55 | return
56 | }
57 |
58 | func NewRequest(req *http.Request, logger Logger) *http.Request {
59 | if logger == nil {
60 | return req
61 | }
62 | ctx := req.Context()
63 | ctx2 := NewContext(ctx, logger)
64 | if ctx2 == ctx {
65 | return req
66 | }
67 | return req.WithContext(ctx2)
68 | }
69 |
70 | func FromRequest(req *http.Request) (lg Logger, ok bool) {
71 | return FromContext(req.Context())
72 | }
73 |
74 | func MustFromRequest(req *http.Request) Logger {
75 | lg, ok := FromRequest(req)
76 | if !ok {
77 | panic("log: failed to get from http.Request")
78 | }
79 | return lg
80 | }
81 |
82 | func FromRequestOrNew(req *http.Request, new func() Logger) (lg Logger, req2 *http.Request, isNew bool) {
83 | lg, ok := FromRequest(req)
84 | if ok {
85 | return lg, req, false
86 | }
87 | if new != nil {
88 | lg = new()
89 | req2 = NewRequest(req, lg)
90 | isNew = true
91 | return
92 | }
93 | lg = New()
94 | req2 = NewRequest(req, lg)
95 | isNew = true
96 | return
97 | }
98 |
--------------------------------------------------------------------------------
/uuid/uuid.go:
--------------------------------------------------------------------------------
1 | package uuid
2 |
3 | import (
4 | "crypto/sha1"
5 |
6 | v1 "github.com/KeKe-Li/log/uuid/v1"
7 | v2 "github.com/KeKe-Li/log/uuid/v2"
8 | )
9 |
10 | // +------ 0 ------+------ 1 ------+------ 2 ------+------ 3 ------+
11 | // +0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+
12 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
13 | // | time_low |
14 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
15 | // | time_mid | time_hi_and_version |
16 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17 | // |clk_seq_hi_res | clk_seq_low | node (0-1) |
18 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19 | // | node (2-5) |
20 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21 |
22 | type UUID [16]byte
23 |
24 | // NewV1 returns a STANDARD version 1 UUID.
25 | func NewV1() UUID {
26 | return v1.New()
27 | }
28 |
29 | // NewV5 returns a STANDARD version 5 UUID.
30 | func NewV5(ns UUID, name []byte) UUID {
31 | return v2.New(ns, name)
32 | }
33 |
34 | // NewV1x returns a NONSTANDARD UUID(lower probability of conflict).
35 | func NewV1x() UUID {
36 | return v1.Newx()
37 | }
38 |
39 | func (uuid UUID) Version() byte {
40 | return uuid[6] >> 4
41 | }
42 |
43 | func (uuid UUID) HexEncode() []byte {
44 | return HexEncode(uuid)
45 | }
46 |
47 | // Encode encodes UUID to "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" format.
48 | func (uuid UUID) Encode() []byte {
49 | return Encode(uuid)
50 | }
51 |
52 | // String returns "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" format.
53 | func (uuid UUID) String() string {
54 | return string(Encode(uuid))
55 | }
56 |
57 | // Decode decodes data with "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" format into UUID.
58 | func (uuid *UUID) Decode(data []byte) (err error) {
59 | u, err := Decode(data)
60 | if err != nil {
61 | return
62 | }
63 | *uuid = u
64 | return
65 | }
66 |
67 | func New(ns [16]byte, name []byte) (uuid [16]byte) {
68 | h := sha1.New()
69 | h.Write(ns[:])
70 | h.Write(name)
71 | copy(uuid[:], h.Sum(nil))
72 | uuid[8] = (uuid[8] & 0x3f) | 0x80 // set variant rfc4122
73 | uuid[6] = (uuid[6] & 0x0f) | 5<<4 // set version 5
74 | return
75 | }
76 |
--------------------------------------------------------------------------------
/std.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import "io"
4 |
5 | var _std = _New(nil)
6 |
7 | // Fatal logs a message at FatalLevel on the standard logger.
8 | // For more information see the Logger interface.
9 | func Fatal(msg string, fields ...interface{}) {
10 | _std.output(1, FatalLevel, msg, fields)
11 | }
12 |
13 | // Error logs a message at ErrorLevel on the standard logger.
14 | // For more information see the Logger interface.
15 | func Error(msg string, fields ...interface{}) {
16 | _std.output(1, ErrorLevel, msg, fields)
17 | }
18 |
19 | // Warn logs a message at WarnLevel on the standard logger.
20 | // For more information see the Logger interface.
21 | func Warn(msg string, fields ...interface{}) {
22 | _std.output(1, WarnLevel, msg, fields)
23 | }
24 |
25 | // Info logs a message at InfoLevel on the standard logger.
26 | // For more information see the Logger interface.
27 | func Info(msg string, fields ...interface{}) {
28 | _std.output(1, InfoLevel, msg, fields)
29 | }
30 |
31 | // Debug logs a message at DebugLevel on the standard logger.
32 | // For more information see the Logger interface.
33 | func Debug(msg string, fields ...interface{}) {
34 | _std.output(1, DebugLevel, msg, fields)
35 | }
36 |
37 | // Output logs a message at specified level on the standard logger.
38 | // For more information see the Logger interface.
39 | func Output(calldepth int, level Level, msg string, fields ...interface{}) {
40 | _std.Output(calldepth+1, level, msg, fields...)
41 | }
42 |
43 | // WithField creates a new Logger from the standard Logger and adds a field to it.
44 | // For more information see the Logger interface.
45 | func WithField(key string, value interface{}) Logger {
46 | return _std.WithField(key, value)
47 | }
48 |
49 | // WithFields creates a new Logger from the standard Logger and adds multiple fields to it.
50 | // For more information see the Logger interface.
51 | func WithFields(fields ...interface{}) Logger {
52 | return _std.WithFields(fields...)
53 | }
54 |
55 | // SetFormatter sets the standard logger formatter.
56 | func SetFormatter(formatter Formatter) {
57 | _std.SetFormatter(formatter)
58 | }
59 |
60 | // SetOutput sets the standard logger output.
61 | // NOTE: output must be thread-safe, see ConcurrentWriter.
62 | func SetOutput(output io.Writer) {
63 | _std.SetOutput(output)
64 | }
65 |
66 | // SetLevel sets the standard logger level.
67 | func SetLevel(level Level) error {
68 | return _std.SetLevel(level)
69 | }
70 |
71 | // SetLevelString sets the standard logger level.
72 | func SetLevelString(str string) error {
73 | return _std.SetLevelString(str)
74 | }
75 |
--------------------------------------------------------------------------------
/uuid/v1/v1.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | "sync"
5 |
6 | "github.com/KeKe-Li/log/uuid/rand"
7 | )
8 |
9 | // +------ 0 ------+------ 1 ------+------ 2 ------+------ 3 ------+
10 | // +0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+
11 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
12 | // | time_low |
13 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
14 | // | time_mid | time_hi_and_version |
15 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
16 | // |clk_seq_hi_res | clk_seq_low | node (0-1) |
17 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
18 | // | node (2-5) |
19 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
20 |
21 | var node = rand.MAC[:] // read only.
22 |
23 | const sequenceMask uint32 = 0x3FFF // 14bits
24 |
25 | var (
26 | gMutex sync.Mutex
27 | gSequenceStart uint32 = rand.Uint32() & sequenceMask
28 | gLastTimestamp int64 = -1
29 | gLastSequence uint32 = gSequenceStart
30 | )
31 |
32 | // New returns a STANDARD version 1 UUID.
33 | func New() (uuid [16]byte) {
34 | var (
35 | timestamp = uuidTimestamp()
36 | sequence uint32
37 | )
38 |
39 | gMutex.Lock() // Lock
40 | switch {
41 | case timestamp > gLastTimestamp:
42 | sequence = gSequenceStart
43 | gLastTimestamp = timestamp
44 | gLastSequence = sequence
45 | gMutex.Unlock() // Unlock
46 | case timestamp == gLastTimestamp:
47 | sequence = (gLastSequence + 1) & sequenceMask
48 | if sequence == gSequenceStart {
49 | timestamp = tillNext100nano(timestamp)
50 | gLastTimestamp = timestamp
51 | }
52 | gLastSequence = sequence
53 | gMutex.Unlock() // Unlock
54 | default: // timestamp < lastTimestamp
55 | gSequenceStart = rand.Uint32() & sequenceMask // NOTE
56 | sequence = gSequenceStart
57 | gLastTimestamp = timestamp
58 | gLastSequence = sequence
59 | gMutex.Unlock() // Unlock
60 | }
61 |
62 | // time_low
63 | uuid[0] = byte(timestamp >> 24)
64 | uuid[1] = byte(timestamp >> 16)
65 | uuid[2] = byte(timestamp >> 8)
66 | uuid[3] = byte(timestamp)
67 |
68 | // time_mid
69 | uuid[4] = byte(timestamp >> 40)
70 | uuid[5] = byte(timestamp >> 32)
71 |
72 | // time_hi_and_version
73 | uuid[6] = byte(timestamp>>56) & 0x0F
74 | uuid[6] |= 0x10 // version 1, 4bits
75 | uuid[7] = byte(timestamp >> 48)
76 |
77 | // clk_seq_hi_res
78 | uuid[8] = byte(sequence>>8) & 0x3F
79 | uuid[8] |= 0x80 // variant, 2bits
80 |
81 | // clk_seq_low
82 | uuid[9] = byte(sequence)
83 |
84 | // node
85 | copy(uuid[10:], node)
86 | return
87 | }
88 |
--------------------------------------------------------------------------------
/options.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "io"
5 | "sync/atomic"
6 | "unsafe"
7 | )
8 |
9 | type Option func(*options)
10 |
11 | func WithTraceId(traceId string) Option {
12 | return func(o *options) {
13 | o.traceId = traceId
14 | }
15 | }
16 |
17 | func WithTraceIdFunc(fn func() string) Option {
18 | return func(o *options) {
19 | if fn == nil {
20 | return
21 | }
22 | o.traceId = fn()
23 | }
24 | }
25 |
26 | // WithOutput sets the logger output.
27 | // NOTE: output must be thread-safe, see ConcurrentWriter.
28 | func WithOutput(output io.Writer) Option {
29 | return func(o *options) {
30 | if output == nil {
31 | return
32 | }
33 | o.output = output
34 | }
35 | }
36 |
37 | func WithFormatter(formatter Formatter) Option {
38 | return func(o *options) {
39 | if formatter == nil {
40 | return
41 | }
42 | o.formatter = formatter
43 | }
44 | }
45 |
46 | func WithLevel(level Level) Option {
47 | return func(o *options) {
48 | if !isValidLevel(level) {
49 | return
50 | }
51 | o.level = level
52 | }
53 | }
54 |
55 | func WithLevelString(str string) Option {
56 | return func(o *options) {
57 | level, ok := parseLevelString(str)
58 | if !ok {
59 | return
60 | }
61 | o.level = level
62 | }
63 | }
64 |
65 | type options struct {
66 | traceId string
67 | formatter Formatter
68 | output io.Writer
69 | level Level
70 | }
71 |
72 | func (opts *options) SetFormatter(formatter Formatter) {
73 | if formatter == nil {
74 | return
75 | }
76 | opts.formatter = formatter
77 | }
78 | func (opts *options) SetOutput(output io.Writer) {
79 | if output == nil {
80 | return
81 | }
82 | opts.output = output
83 | }
84 | func (opts *options) SetLevel(level Level) {
85 | if !isValidLevel(level) {
86 | return
87 | }
88 | opts.level = level
89 | }
90 |
91 | func newOptions(opts []Option) *options {
92 | var o options
93 | for _, opt := range getDefaultOptions() {
94 | if opt == nil {
95 | continue
96 | }
97 | opt(&o)
98 | }
99 | for _, opt := range opts {
100 | if opt == nil {
101 | continue
102 | }
103 | opt(&o)
104 | }
105 | if o.formatter == nil {
106 | o.formatter = TextFormatter
107 | }
108 | if o.output == nil {
109 | o.output = ConcurrentStdout
110 | }
111 | if o.level == invalidLevel {
112 | o.level = DebugLevel
113 | }
114 | return &o
115 | }
116 |
117 | var _defaultOptionsPtr unsafe.Pointer // *[]Option
118 |
119 | func SetDefaultOptions(opts []Option) {
120 | if opts == nil {
121 | atomic.StorePointer(&_defaultOptionsPtr, nil)
122 | return
123 | }
124 | atomic.StorePointer(&_defaultOptionsPtr, unsafe.Pointer(&opts))
125 | }
126 |
127 | func getDefaultOptions() []Option {
128 | ptr := (*[]Option)(atomic.LoadPointer(&_defaultOptionsPtr))
129 | if ptr == nil {
130 | return nil
131 | }
132 | return *ptr
133 | }
134 |
--------------------------------------------------------------------------------
/uuid/v1/v1x.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | "os"
5 | "sync"
6 |
7 | "github.com/KeKe-Li/log/uuid/rand"
8 | )
9 |
10 | // +------ 0 ------+------ 1 ------+------ 2 ------+------ 3 ------+
11 | // +0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+
12 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
13 | // | time_low |
14 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
15 | // | time_mid | time_hi_and_pid_low |
16 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17 | // |clk_seq_hi_pid | clk_seq_low | node (0-1) |
18 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
19 | // | node (2-5) |
20 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21 |
22 | var pid = byte(hash(uint64(os.Getpid()))) // 6-bit hash of os.Getpid(), read only.
23 |
24 | // hash uint64 to a 6-bit integer value.
25 | func hash(x uint64) uint64 {
26 | return (x ^ x>>6 ^ x>>12 ^ x>>18 ^ x>>24 ^ x>>30 ^ x>>36 ^ x>>42 ^ x>>48 ^ x>>54 ^ x>>60) & 0x3f
27 | }
28 |
29 | var xNode = MAC[:] // read only.
30 |
31 | const xSequenceMask uint32 = 0x3FFF // 14bits
32 |
33 | var (
34 | gxMutex sync.Mutex
35 | gxSequenceStart uint32 = rand.Uint32() & xSequenceMask
36 | gxLastTimestamp int64 = -1
37 | gxLastSequence uint32 = gxSequenceStart
38 | )
39 |
40 | // Newx returns a NONSTANDARD UUID(lower probability of conflict).
41 | func Newx() (uuid [16]byte) {
42 | var (
43 | timestamp = uuidTimestamp()
44 | sequence uint32
45 | )
46 |
47 | gxMutex.Lock() // Lock
48 | switch {
49 | case timestamp > gxLastTimestamp:
50 | sequence = gxSequenceStart
51 | gxLastTimestamp = timestamp
52 | gxLastSequence = sequence
53 | gxMutex.Unlock() // Unlock
54 | case timestamp == gxLastTimestamp:
55 | sequence = (gxLastSequence + 1) & xSequenceMask
56 | if sequence == gxSequenceStart {
57 | timestamp = tillNext100nano(timestamp)
58 | gxLastTimestamp = timestamp
59 | }
60 | gxLastSequence = sequence
61 | gxMutex.Unlock() // Unlock
62 | default: // timestamp < xLastTimestamp
63 | gxSequenceStart = rand.Uint32() & xSequenceMask // NOTE
64 | sequence = gxSequenceStart
65 | gxLastTimestamp = timestamp
66 | gxLastSequence = sequence
67 | gxMutex.Unlock() // Unlock
68 | }
69 |
70 | // time_low
71 | uuid[0] = byte(timestamp >> 24)
72 | uuid[1] = byte(timestamp >> 16)
73 | uuid[2] = byte(timestamp >> 8)
74 | uuid[3] = byte(timestamp)
75 |
76 | // time_mid
77 | uuid[4] = byte(timestamp >> 40)
78 | uuid[5] = byte(timestamp >> 32)
79 |
80 | // time_hi_and_pid_low
81 | uuid[6] = byte(timestamp >> 52)
82 | uuid[7] = byte(timestamp>>48) << 4
83 | uuid[7] |= pid & 0x0F // pid, 4bits
84 |
85 | // clk_seq_hi_pid
86 | uuid[8] = byte(sequence>>8) & 0x3F
87 | uuid[8] |= (pid << 2) & 0xC0 // // pid, 2bits
88 |
89 | // clk_seq_low
90 | uuid[9] = byte(sequence)
91 |
92 | // node
93 | copy(uuid[10:], xNode)
94 | return
95 | }
96 |
--------------------------------------------------------------------------------
/text_formatter.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "fmt"
7 | "sort"
8 | "strconv"
9 | "time"
10 | )
11 |
12 | var TextFormatter Formatter = textFormatter{}
13 |
14 | type textFormatter struct{}
15 |
16 | func (f textFormatter) Format(entry *Entry) ([]byte, error) {
17 | var buffer *bytes.Buffer
18 | if entry.Buffer != nil {
19 | buffer = entry.Buffer
20 | } else {
21 | buffer = bytes.NewBuffer(make([]byte, 0, 16<<10))
22 | }
23 | f.appendKeyValue(buffer, fieldKeyTime, FormatTimeString(entry.Time.In(_beijingLocation)))
24 | f.appendKeyValue(buffer, fieldKeyLevel, entry.Level.String())
25 | f.appendKeyValue(buffer, fieldKeyTraceId, entry.TraceId)
26 | f.appendKeyValue(buffer, fieldKeyLocation, entry.Location)
27 | f.appendKeyValue(buffer, fieldKeyMessage, entry.Message)
28 | if fields := entry.Fields; len(fields) > 0 {
29 | prefixFieldClashes(fields)
30 | keys := make([]string, 0, len(fields))
31 | for k := range fields {
32 | keys = append(keys, k)
33 | }
34 | sort.Strings(keys)
35 | for _, k := range keys {
36 | v := fields[k]
37 | f.appendKeyValue(buffer, k, v)
38 | }
39 | }
40 | buffer.WriteByte('\n')
41 | return buffer.Bytes(), nil
42 | }
43 |
44 | func (f textFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
45 | if b.Len() > 0 {
46 | b.WriteString(", ")
47 | }
48 | b.WriteString(key)
49 | b.WriteByte('=')
50 | f.appendValue(b, value)
51 | }
52 |
53 | func (f textFormatter) appendValue(b *bytes.Buffer, value interface{}) {
54 | var stringVal string
55 | switch v := value.(type) {
56 | case string:
57 | stringVal = v
58 | case json.RawMessage:
59 | stringVal = string(v)
60 | default:
61 | stringVal = fmt.Sprint(value)
62 | }
63 | b.WriteString(stringVal)
64 | }
65 |
66 | var _beijingLocation = time.FixedZone("Asia/Shanghai", 8*60*60)
67 |
68 | const (
69 | fieldKeyTime = "time"
70 | fieldKeyLevel = "level"
71 | fieldKeyTraceId = "request_id"
72 | fieldKeyLocation = "location"
73 | fieldKeyMessage = "msg"
74 | )
75 |
76 | func prefixFieldClashes(data map[string]interface{}) {
77 | if v, ok := data[fieldKeyTime]; ok {
78 | delete(data, fieldKeyTime)
79 | newKey := "fields." + fieldKeyTime
80 | for key, i := newKey, 2; ; i++ {
81 | _, ok = data[key]
82 | if !ok {
83 | data[key] = v
84 | break
85 | }
86 | key = newKey + "." + strconv.Itoa(i)
87 | }
88 | }
89 | if v, ok := data[fieldKeyLevel]; ok {
90 | delete(data, fieldKeyLevel)
91 | newKey := "fields." + fieldKeyLevel
92 | for key, i := newKey, 2; ; i++ {
93 | _, ok = data[key]
94 | if !ok {
95 | data[key] = v
96 | break
97 | }
98 | key = newKey + "." + strconv.Itoa(i)
99 | }
100 | }
101 | if v, ok := data[fieldKeyTraceId]; ok {
102 | delete(data, fieldKeyTraceId)
103 | newKey := "fields." + fieldKeyTraceId
104 | for key, i := newKey, 2; ; i++ {
105 | _, ok = data[key]
106 | if !ok {
107 | data[key] = v
108 | break
109 | }
110 | key = newKey + "." + strconv.Itoa(i)
111 | }
112 | }
113 | if v, ok := data[fieldKeyLocation]; ok {
114 | delete(data, fieldKeyLocation)
115 | newKey := "fields." + fieldKeyLocation
116 | for key, i := newKey, 2; ; i++ {
117 | _, ok = data[key]
118 | if !ok {
119 | data[key] = v
120 | break
121 | }
122 | key = newKey + "." + strconv.Itoa(i)
123 | }
124 | }
125 | if v, ok := data[fieldKeyMessage]; ok {
126 | delete(data, fieldKeyMessage)
127 | newKey := "fields." + fieldKeyMessage
128 | for key, i := newKey, 2; ; i++ {
129 | _, ok = data[key]
130 | if !ok {
131 | data[key] = v
132 | break
133 | }
134 | key = newKey + "." + strconv.Itoa(i)
135 | }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/shortcut.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "context"
5 | )
6 |
7 | // FatalContext is a shortcut to the following code:
8 | // lg, ok := FromContext(ctx)
9 | // if ok {
10 | // lg.Output(1, FatalLevel, msg, fields...)
11 | // return
12 | // }
13 | // Output(1, FatalLevel, msg, fields...)
14 | func FatalContext(ctx context.Context, msg string, fields ...interface{}) {
15 | lg, ok := FromContext(ctx)
16 | if ok {
17 | lg.Output(1, FatalLevel, msg, fields...)
18 | return
19 | }
20 | Output(1, FatalLevel, msg, fields...)
21 | }
22 |
23 | // ErrorContext is a shortcut to the following code:
24 | // lg, ok := FromContext(ctx)
25 | // if ok {
26 | // lg.Output(1, ErrorLevel, msg, fields...)
27 | // return
28 | // }
29 | // Output(1, ErrorLevel, msg, fields...)
30 | func ErrorContext(ctx context.Context, msg string, fields ...interface{}) {
31 | lg, ok := FromContext(ctx)
32 | if ok {
33 | lg.Output(1, ErrorLevel, msg, fields...)
34 | return
35 | }
36 | Output(1, ErrorLevel, msg, fields...)
37 | }
38 |
39 | // WarnContext is a shortcut to the following code:
40 | // lg, ok := FromContext(ctx)
41 | // if ok {
42 | // lg.Output(1, WarnLevel, msg, fields...)
43 | // return
44 | // }
45 | // Output(1, WarnLevel, msg, fields...)
46 | func WarnContext(ctx context.Context, msg string, fields ...interface{}) {
47 | lg, ok := FromContext(ctx)
48 | if ok {
49 | lg.Output(1, WarnLevel, msg, fields...)
50 | return
51 | }
52 | Output(1, WarnLevel, msg, fields...)
53 | }
54 |
55 | // InfoContext is a shortcut to the following code:
56 | // lg, ok := FromContext(ctx)
57 | // if ok {
58 | // lg.Output(1, InfoLevel, msg, fields...)
59 | // return
60 | // }
61 | // Output(1, InfoLevel, msg, fields...)
62 | func InfoContext(ctx context.Context, msg string, fields ...interface{}) {
63 | lg, ok := FromContext(ctx)
64 | if ok {
65 | lg.Output(1, InfoLevel, msg, fields...)
66 | return
67 | }
68 | Output(1, InfoLevel, msg, fields...)
69 | }
70 |
71 | // DebugContext is a shortcut to the following code:
72 | // lg, ok := FromContext(ctx)
73 | // if ok {
74 | // lg.Output(1, DebugLevel, msg, fields...)
75 | // return
76 | // }
77 | // Output(1, DebugLevel, msg, fields...)
78 | func DebugContext(ctx context.Context, msg string, fields ...interface{}) {
79 | lg, ok := FromContext(ctx)
80 | if ok {
81 | lg.Output(1, DebugLevel, msg, fields...)
82 | return
83 | }
84 | Output(1, DebugLevel, msg, fields...)
85 | }
86 |
87 | // OutputContext is a shortcut to the following code:
88 | // lg, ok := FromContext(ctx)
89 | // if ok {
90 | // lg.Output(calldepth+1, level, msg, fields...)
91 | // return
92 | // }
93 | // Output(calldepth+1, level, msg, fields...)
94 | func OutputContext(ctx context.Context, calldepth int, level Level, msg string, fields ...interface{}) {
95 | lg, ok := FromContext(ctx)
96 | if ok {
97 | lg.Output(calldepth+1, level, msg, fields...)
98 | return
99 | }
100 | Output(calldepth+1, level, msg, fields...)
101 | }
102 |
103 | // WithFieldContext is a shortcut to the following code:
104 | // lg, ok := FromContext(ctx)
105 | // if ok {
106 | // return lg.WithField(key, value)
107 | // }
108 | // return WithField(key, value)
109 | func WithFieldContext(ctx context.Context, key string, value interface{}) Logger {
110 | lg, ok := FromContext(ctx)
111 | if ok {
112 | return lg.WithField(key, value)
113 | }
114 | return WithField(key, value)
115 | }
116 |
117 | // WithFieldsContext is a shortcut to the following code:
118 | // lg, ok := FromContext(ctx)
119 | // if ok {
120 | // return lg.WithFields(fields...)
121 | // }
122 | // return WithFields(fields...)
123 | func WithFieldsContext(ctx context.Context, fields ...interface{}) Logger {
124 | lg, ok := FromContext(ctx)
125 | if ok {
126 | return lg.WithFields(fields...)
127 | }
128 | return WithFields(fields...)
129 | }
130 |
--------------------------------------------------------------------------------
/buffer_pool_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "bytes"
5 | "runtime/debug"
6 | "testing"
7 | )
8 |
9 | func TestDefaultBytesBufferPool(t *testing.T) {
10 | debug.SetGCPercent(-1)
11 | defer debug.SetGCPercent(100)
12 |
13 | // new
14 | func() {
15 | pool := getBytesBufferPool()
16 |
17 | buffer := pool.Get()
18 | defer pool.Put(buffer)
19 |
20 | have := buffer.String()
21 | want := ""
22 | if have != want {
23 | t.Errorf("have:%s, want:%s", have, want)
24 | return
25 | }
26 | }()
27 |
28 | // clean up
29 | {
30 | pool := getBytesBufferPool()
31 | for i := 0; i < 10; i++ {
32 | pool.Get()
33 | }
34 | }
35 |
36 | // new twice
37 | func() {
38 | pool := getBytesBufferPool()
39 |
40 | buffer := pool.Get()
41 | defer pool.Put(buffer)
42 |
43 | have := buffer.String()
44 | want := ""
45 | if have != want {
46 | t.Errorf("have:%s, want:%s", have, want)
47 | return
48 | }
49 | buffer.WriteString("buffer")
50 |
51 | buffer2 := pool.Get()
52 | defer pool.Put(buffer2)
53 |
54 | have = buffer2.String()
55 | want = ""
56 | if have != want {
57 | t.Errorf("have:%s, want:%s", have, want)
58 | return
59 | }
60 | }()
61 |
62 | // clean up
63 | {
64 | pool := getBytesBufferPool()
65 | for i := 0; i < 10; i++ {
66 | pool.Get()
67 | }
68 | }
69 |
70 | // reuse
71 | func() {
72 | func() {
73 | pool := getBytesBufferPool()
74 |
75 | buffer := pool.Get()
76 | defer pool.Put(buffer)
77 |
78 | have := buffer.String()
79 | want := ""
80 | if have != want {
81 | t.Errorf("have:%s, want:%s", have, want)
82 | return
83 | }
84 | buffer.WriteString("buffer")
85 | }()
86 |
87 | func() {
88 | pool := getBytesBufferPool()
89 |
90 | buffer := pool.Get()
91 | defer pool.Put(buffer)
92 |
93 | have := buffer.String()
94 | want := "buffer"
95 | if have != want {
96 | t.Errorf("have:%s, want:%s", have, want)
97 | return
98 | }
99 | }()
100 | }()
101 |
102 | // clean up
103 | {
104 | pool := getBytesBufferPool()
105 | for i := 0; i < 10; i++ {
106 | pool.Get()
107 | }
108 | }
109 |
110 | // reuse and put nil
111 | func() {
112 | func() {
113 | pool := getBytesBufferPool()
114 |
115 | buffer := pool.Get()
116 | defer pool.Put(buffer)
117 |
118 | have := buffer.String()
119 | want := ""
120 | if have != want {
121 | t.Errorf("have:%s, want:%s", have, want)
122 | return
123 | }
124 | buffer.WriteString("buffer")
125 | }()
126 |
127 | pool := getBytesBufferPool()
128 | for i := 0; i < 10; i++ {
129 | pool.Put(nil)
130 | }
131 |
132 | func() {
133 | pool := getBytesBufferPool()
134 |
135 | buffer := pool.Get()
136 | defer pool.Put(buffer)
137 |
138 | have := buffer.String()
139 | want := "buffer"
140 | if have != want {
141 | t.Errorf("have:%s, want:%s", have, want)
142 | return
143 | }
144 | }()
145 | }()
146 | }
147 |
148 | func Test_SetBytesBufferPool_GetBytesBufferPool(t *testing.T) {
149 | defer SetBytesBufferPool(_defaultBytesBufferPool)
150 |
151 | // get default BytesBufferPool
152 | pool := getBytesBufferPool()
153 | if _, ok := pool.(*bytesBufferPool); !ok {
154 | t.Error("want type *bytesBufferPool")
155 | return
156 | }
157 |
158 | // SetBytesBufferPool with nil
159 | pool = nil
160 | SetBytesBufferPool(pool)
161 | pool = getBytesBufferPool()
162 | if _, ok := pool.(*bytesBufferPool); !ok {
163 | t.Error("want type *bytesBufferPool")
164 | return
165 | }
166 |
167 | // SetBytesBufferPool with non-nil
168 | SetBytesBufferPool(&testBytesBufferPool{})
169 | pool = getBytesBufferPool()
170 | if _, ok := pool.(*testBytesBufferPool); !ok {
171 | t.Error("want type *testBytesBufferPool")
172 | return
173 | }
174 |
175 | // SetBytesBufferPool with nil
176 | pool = nil
177 | SetBytesBufferPool(pool)
178 | pool = getBytesBufferPool()
179 | if _, ok := pool.(*testBytesBufferPool); !ok {
180 | t.Error("want type *testBytesBufferPool")
181 | return
182 | }
183 | }
184 |
185 | type testBytesBufferPool struct{}
186 |
187 | func (*testBytesBufferPool) Get() *bytes.Buffer { return nil }
188 | func (*testBytesBufferPool) Put(x *bytes.Buffer) {}
189 |
--------------------------------------------------------------------------------
/fields_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "reflect"
5 | "testing"
6 | )
7 |
8 | func TestCombineFields(t *testing.T) {
9 | // empty fields
10 | {
11 | old := map[string]interface{}{
12 | "ka": "va",
13 | "kb": "vb",
14 | }
15 |
16 | have, err := combineFields(old, nil)
17 | if err != nil {
18 | t.Error("want nil")
19 | return
20 | }
21 | if !reflect.DeepEqual(have, old) {
22 | t.Errorf("have:%v, old:%v", have, old)
23 | return
24 | }
25 |
26 | // check if it is a new one
27 | for k := range old {
28 | delete(old, k)
29 | }
30 | if len(have) == len(old) {
31 | t.Errorf("want not equal, have:%v, old:%v", have, old)
32 | return
33 | }
34 | }
35 | // ddd number of field
36 | {
37 | old := map[string]interface{}{
38 | "ka": "va",
39 | "kb": "vb",
40 | }
41 |
42 | have, err := combineFields(old, []interface{}{"kc", "vc", "kd"})
43 | if err != _ErrNumberOfFieldsMustNotBeOdd {
44 | t.Error("want equal")
45 | return
46 | }
47 | if !reflect.DeepEqual(have, old) {
48 | t.Errorf("have:%v, old:%v", have, old)
49 | return
50 | }
51 |
52 | // check if it is a new one
53 | for k := range old {
54 | delete(old, k)
55 | }
56 | if len(have) == len(old) {
57 | t.Errorf("want not equal, have:%v, old:%v", have, old)
58 | return
59 | }
60 | }
61 | // nil old
62 | {
63 | have, err := combineFields(nil, []interface{}{"kc", "vc", "kb", "vb2"})
64 | if err != nil {
65 | t.Error("want nil")
66 | return
67 | }
68 | want := map[string]interface{}{
69 | "kb": "vb2",
70 | "kc": "vc",
71 | }
72 | if !reflect.DeepEqual(have, want) {
73 | t.Errorf("have:%v, want:%v", have, want)
74 | return
75 | }
76 | }
77 | // non-nil old
78 | {
79 | old := map[string]interface{}{
80 | "ka": "va",
81 | "kb": "vb",
82 | }
83 |
84 | have, err := combineFields(old, []interface{}{"kc", "vc", "kb", "vb2"})
85 | if err != nil {
86 | t.Error("want nil")
87 | return
88 | }
89 | want := map[string]interface{}{
90 | "ka": "va",
91 | "kb": "vb2",
92 | "kc": "vc",
93 | }
94 | if !reflect.DeepEqual(have, want) {
95 | t.Errorf("have:%v, want:%v", have, want)
96 | return
97 | }
98 |
99 | // check if it is a new one
100 | if reflect.DeepEqual(have, old) {
101 | t.Errorf("want not equal, have:%v, old:%v", have, old)
102 | return
103 | }
104 | }
105 | // non-nil old with non-string type of key
106 | {
107 | old := map[string]interface{}{
108 | "ka": "va",
109 | "kb": "vb",
110 | }
111 |
112 | have, err := combineFields(old, []interface{}{"kc", "vc", "kb", "vb2", 1, 2, "kd", "vd"})
113 | if err != _ErrTypeOfFieldKeyMustBeString {
114 | t.Error("want equal")
115 | return
116 | }
117 | want := map[string]interface{}{
118 | "ka": "va",
119 | "kb": "vb2",
120 | "kc": "vc",
121 | }
122 | if !reflect.DeepEqual(have, want) {
123 | t.Errorf("have:%v, want:%v", have, want)
124 | return
125 | }
126 |
127 | // check if it is a new one
128 | if reflect.DeepEqual(have, old) {
129 | t.Errorf("want not equal, have:%v, old:%v", have, old)
130 | return
131 | }
132 | }
133 | // non-nil old with empty key
134 | {
135 | old := map[string]interface{}{
136 | "ka": "va",
137 | "kb": "vb",
138 | }
139 |
140 | have, err := combineFields(old, []interface{}{"kc", "vc", "kb", "vb2", "", "vd", "ke", "ve"})
141 | if err != _ErrFieldKeyMustNotBeEmpty {
142 | t.Error("want equal")
143 | return
144 | }
145 | want := map[string]interface{}{
146 | "ka": "va",
147 | "kb": "vb2",
148 | "kc": "vc",
149 | }
150 | if !reflect.DeepEqual(have, want) {
151 | t.Errorf("have:%v, want:%v", have, want)
152 | return
153 | }
154 |
155 | // check if it is a new one
156 | if reflect.DeepEqual(have, old) {
157 | t.Errorf("want not equal, have:%v, old:%v", have, old)
158 | return
159 | }
160 | }
161 | }
162 |
163 | func TestCloneFields(t *testing.T) {
164 | // nil source
165 | {
166 | var m map[string]interface{}
167 | m2 := cloneFields(m)
168 | if m2 != nil {
169 | t.Error("want nil")
170 | return
171 | }
172 | }
173 | // non-nil source
174 | {
175 | m := map[string]interface{}{
176 | "a": "va",
177 | "b": "vb",
178 | }
179 | m2 := cloneFields(m)
180 | if !reflect.DeepEqual(m, m2) {
181 | t.Errorf("m2:%v, m:%v", m2, m)
182 | return
183 | }
184 | // check if it is a new one
185 | for k := range m {
186 | delete(m, k)
187 | }
188 | if len(m2) == len(m) {
189 | t.Errorf("want not equal, m2:%v, m:%v", m2, m)
190 | return
191 | }
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/level_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestIsValidLevel(t *testing.T) {
8 | tests := []struct {
9 | level Level
10 | want bool
11 | }{
12 | {
13 | FatalLevel,
14 | true,
15 | },
16 | {
17 | ErrorLevel,
18 | true,
19 | },
20 | {
21 | WarnLevel,
22 | true,
23 | },
24 | {
25 | InfoLevel,
26 | true,
27 | },
28 | {
29 | DebugLevel,
30 | true,
31 | },
32 | {
33 | invalidLevel,
34 | false,
35 | },
36 | {
37 | 0,
38 | false,
39 | },
40 | {
41 | 100,
42 | false,
43 | },
44 | {
45 | 6,
46 | false,
47 | },
48 | }
49 | for _, v := range tests {
50 | have := isValidLevel(v.level)
51 | if have != v.want {
52 | t.Errorf("level:%v, have:%t, want:%t", v.level, have, v.want)
53 | return
54 | }
55 | }
56 | }
57 |
58 | func TestIsLevelEnabled(t *testing.T) {
59 | tests := []struct {
60 | level Level
61 | loggerLevel Level
62 | want bool
63 | }{
64 | // level is fatal
65 | {
66 | FatalLevel,
67 | FatalLevel,
68 | true,
69 | },
70 | {
71 | FatalLevel,
72 | ErrorLevel,
73 | true,
74 | },
75 | {
76 | FatalLevel,
77 | WarnLevel,
78 | true,
79 | },
80 | {
81 | FatalLevel,
82 | InfoLevel,
83 | true,
84 | },
85 | {
86 | FatalLevel,
87 | DebugLevel,
88 | true,
89 | },
90 |
91 | // level is error
92 | {
93 | ErrorLevel,
94 | FatalLevel,
95 | false,
96 | },
97 | {
98 | ErrorLevel,
99 | ErrorLevel,
100 | true,
101 | },
102 | {
103 | ErrorLevel,
104 | WarnLevel,
105 | true,
106 | },
107 | {
108 | ErrorLevel,
109 | InfoLevel,
110 | true,
111 | },
112 | {
113 | ErrorLevel,
114 | DebugLevel,
115 | true,
116 | },
117 |
118 | // level is warning
119 | {
120 | WarnLevel,
121 | FatalLevel,
122 | false,
123 | },
124 | {
125 | WarnLevel,
126 | ErrorLevel,
127 | false,
128 | },
129 | {
130 | WarnLevel,
131 | WarnLevel,
132 | true,
133 | },
134 | {
135 | WarnLevel,
136 | InfoLevel,
137 | true,
138 | },
139 | {
140 | WarnLevel,
141 | DebugLevel,
142 | true,
143 | },
144 |
145 | // level is info
146 | {
147 | InfoLevel,
148 | FatalLevel,
149 | false,
150 | },
151 | {
152 | InfoLevel,
153 | ErrorLevel,
154 | false,
155 | },
156 | {
157 | InfoLevel,
158 | WarnLevel,
159 | false,
160 | },
161 | {
162 | InfoLevel,
163 | InfoLevel,
164 | true,
165 | },
166 | {
167 | InfoLevel,
168 | DebugLevel,
169 | true,
170 | },
171 |
172 | // level is debug
173 | {
174 | DebugLevel,
175 | FatalLevel,
176 | false,
177 | },
178 | {
179 | DebugLevel,
180 | ErrorLevel,
181 | false,
182 | },
183 | {
184 | DebugLevel,
185 | WarnLevel,
186 | false,
187 | },
188 | {
189 | DebugLevel,
190 | InfoLevel,
191 | false,
192 | },
193 | {
194 | DebugLevel,
195 | DebugLevel,
196 | true,
197 | },
198 | }
199 | for _, v := range tests {
200 | have := isLevelEnabled(v.level, v.loggerLevel)
201 | if have != v.want {
202 | t.Errorf("level:%v, loggerLevel:%v, have:%t, want:%t", v.level, v.loggerLevel, have, v.want)
203 | return
204 | }
205 | }
206 | }
207 |
208 | func TestParseLevelString(t *testing.T) {
209 | tests := []struct {
210 | str string
211 | level Level
212 | ok bool
213 | }{
214 | {
215 | "fatal",
216 | FatalLevel,
217 | true,
218 | },
219 | {
220 | "error",
221 | ErrorLevel,
222 | true,
223 | },
224 | {
225 | "warning",
226 | WarnLevel,
227 | true,
228 | },
229 | {
230 | "info",
231 | InfoLevel,
232 | true,
233 | },
234 | {
235 | "debug",
236 | DebugLevel,
237 | true,
238 | },
239 | {
240 | "trace",
241 | invalidLevel,
242 | false,
243 | },
244 | {
245 | "panic",
246 | invalidLevel,
247 | false,
248 | },
249 | {
250 | "",
251 | invalidLevel,
252 | false,
253 | },
254 | }
255 | for _, v := range tests {
256 | level, ok := parseLevelString(v.str)
257 | if level != v.level || ok != v.ok {
258 | t.Errorf("str:%s, have:(%d, %t), want:(%d, %t)", v.str, level, ok, v.level, v.ok)
259 | return
260 | }
261 | }
262 | }
263 |
264 | func TestLevel_String(t *testing.T) {
265 | tests := []struct {
266 | level Level
267 | str string
268 | }{
269 | {
270 | FatalLevel,
271 | "fatal",
272 | },
273 | {
274 | ErrorLevel,
275 | "error",
276 | },
277 | {
278 | WarnLevel,
279 | "warning",
280 | },
281 | {
282 | InfoLevel,
283 | "info",
284 | },
285 | {
286 | DebugLevel,
287 | "debug",
288 | },
289 | }
290 | for _, v := range tests {
291 | str := v.level.String()
292 | if str != v.str {
293 | t.Errorf("level:%v, have:%s, want:%s", v.level, str, v.str)
294 | return
295 | }
296 | }
297 | }
298 |
--------------------------------------------------------------------------------
/logger.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "io"
7 | "sync"
8 | "sync/atomic"
9 | "time"
10 | "unsafe"
11 | )
12 |
13 | type Logger interface {
14 | // Fatal logs a message at FatalLevel.
15 | //
16 | // Unlike other golang log libraries (for example, the golang standard log library),
17 | // Fatal just logs a message and does not call os.Exit, so you need to explicitly call os.Exit if necessary.
18 | //
19 | // For fields, the following conditions must be satisfied
20 | // 1. the len(fields) must be an even number, that is to say len(fields)%2==0
21 | // 2. the even index element of fields must be non-empty string
22 | Fatal(msg string, fields ...interface{})
23 |
24 | // Error logs a message at ErrorLevel.
25 | // The requirements for fields can see the comments of Fatal.
26 | Error(msg string, fields ...interface{})
27 |
28 | // Warn logs a message at WarnLevel.
29 | // The requirements for fields can see the comments of Fatal.
30 | Warn(msg string, fields ...interface{})
31 |
32 | // Info logs a message at InfoLevel.
33 | // The requirements for fields can see the comments of Fatal.
34 | Info(msg string, fields ...interface{})
35 |
36 | // Debug logs a message at DebugLevel.
37 | // The requirements for fields can see the comments of Fatal.
38 | Debug(msg string, fields ...interface{})
39 |
40 | // Output logs a message at specified level.
41 | //
42 | // For level==FatalLevel, unlike other golang log libraries (for example, the golang standard log library),
43 | // Output just logs a message and does not call os.Exit, so you need to explicitly call os.Exit if necessary.
44 | //
45 | // The requirements for fields can see the comments of Fatal.
46 | Output(calldepth int, level Level, msg string, fields ...interface{})
47 |
48 | // WithField creates a new Logger from the current Logger and adds a field to it.
49 | WithField(key string, value interface{}) Logger
50 |
51 | // WithFields creates a new Logger from the current Logger and adds multiple fields to it.
52 | // The requirements for fields can see the comments of Fatal.
53 | WithFields(fields ...interface{}) Logger
54 |
55 | // SetFormatter sets the logger formatter.
56 | SetFormatter(Formatter)
57 |
58 | // SetOutput sets the logger output.
59 | SetOutput(io.Writer)
60 |
61 | // SetLevel sets the logger level.
62 | SetLevel(Level) error
63 |
64 | // SetLevelString sets the logger level.
65 | SetLevelString(string) error
66 | }
67 |
68 | type Formatter interface {
69 | Format(*Entry) ([]byte, error)
70 | }
71 |
72 | type Entry struct {
73 | Location string // function(file:line)
74 | Time time.Time
75 | Level Level
76 | TraceId string
77 | Message string
78 | Fields map[string]interface{}
79 | Buffer *bytes.Buffer
80 | }
81 |
82 | func New(opts ...Option) Logger { return _New(opts) }
83 |
84 | func _New(opts []Option) *logger {
85 | l := &logger{}
86 | l.setOptions(newOptions(opts))
87 | return l
88 | }
89 |
90 | type logger struct {
91 | mu sync.Mutex // protects the following options field
92 | options unsafe.Pointer // *options
93 |
94 | fields map[string]interface{}
95 | }
96 |
97 | func (l *logger) getOptions() (opts *options) {
98 | return (*options)(atomic.LoadPointer(&l.options))
99 | }
100 | func (l *logger) setOptions(opts *options) {
101 | atomic.StorePointer(&l.options, unsafe.Pointer(opts))
102 | }
103 |
104 | func (l *logger) SetFormatter(formatter Formatter) {
105 | if formatter == nil {
106 | return
107 | }
108 | l.mu.Lock()
109 | defer l.mu.Unlock()
110 |
111 | opts := *l.getOptions()
112 | opts.SetFormatter(formatter)
113 | l.setOptions(&opts)
114 | }
115 | func (l *logger) SetOutput(output io.Writer) {
116 | if output == nil {
117 | return
118 | }
119 | l.mu.Lock()
120 | defer l.mu.Unlock()
121 |
122 | opts := *l.getOptions()
123 | opts.SetOutput(output)
124 | l.setOptions(&opts)
125 | }
126 | func (l *logger) SetLevel(level Level) error {
127 | if !isValidLevel(level) {
128 | return fmt.Errorf("invalid level: %d", level)
129 | }
130 | l.setLevel(level)
131 | return nil
132 | }
133 | func (l *logger) SetLevelString(str string) error {
134 | level, ok := parseLevelString(str)
135 | if !ok {
136 | return fmt.Errorf("invalid level string: %q", str)
137 | }
138 | l.setLevel(level)
139 | return nil
140 | }
141 | func (l *logger) setLevel(level Level) {
142 | l.mu.Lock()
143 | defer l.mu.Unlock()
144 |
145 | opts := *l.getOptions()
146 | opts.SetLevel(level)
147 | l.setOptions(&opts)
148 | }
149 |
150 | func (l *logger) Fatal(msg string, fields ...interface{}) {
151 | l.output(1, FatalLevel, msg, fields)
152 | }
153 | func (l *logger) Error(msg string, fields ...interface{}) {
154 | l.output(1, ErrorLevel, msg, fields)
155 | }
156 | func (l *logger) Warn(msg string, fields ...interface{}) {
157 | l.output(1, WarnLevel, msg, fields)
158 | }
159 | func (l *logger) Info(msg string, fields ...interface{}) {
160 | l.output(1, InfoLevel, msg, fields)
161 | }
162 | func (l *logger) Debug(msg string, fields ...interface{}) {
163 | l.output(1, DebugLevel, msg, fields)
164 | }
165 |
166 | func (l *logger) Output(calldepth int, level Level, msg string, fields ...interface{}) {
167 | if !isValidLevel(level) {
168 | return
169 | }
170 | if calldepth < 0 {
171 | calldepth = 0
172 | }
173 | l.output(calldepth+1, level, msg, fields)
174 | }
175 |
176 | func (l *logger) output(calldepth int, level Level, msg string, fields []interface{}) {
177 | opts := l.getOptions()
178 | if !isLevelEnabled(level, opts.level) {
179 | return
180 | }
181 | location := callerLocation(calldepth + 1)
182 |
183 | combinedFields, err := combineFields(l.fields, fields)
184 | if err != nil {
185 | fmt.Fprintf(ConcurrentStderr, "log: failed to combine fields, error=%v, location=%s\n", err, location)
186 | }
187 |
188 | pool := getBytesBufferPool()
189 | buffer := pool.Get()
190 | defer pool.Put(buffer)
191 | buffer.Reset()
192 |
193 | data, err := opts.formatter.Format(&Entry{
194 | Location: location,
195 | Time: time.Now(),
196 | Level: level,
197 | TraceId: opts.traceId,
198 | Message: msg,
199 | Fields: combinedFields,
200 | Buffer: buffer,
201 | })
202 | if err != nil {
203 | fmt.Fprintf(ConcurrentStderr, "log: failed to format Entry, error=%v, location=%s\n", err, location)
204 | return
205 | }
206 | if _, err = opts.output.Write(data); err != nil {
207 | fmt.Fprintf(ConcurrentStderr, "log: failed to write to log, error=%v, location=%s\n", err, location)
208 | return
209 | }
210 | }
211 |
212 | func (l *logger) WithField(key string, value interface{}) Logger {
213 | if key == "" {
214 | return l
215 | }
216 | if len(l.fields) == 0 {
217 | nl := &logger{
218 | fields: map[string]interface{}{key: value},
219 | }
220 | nl.setOptions(l.getOptions())
221 | return nl
222 | }
223 | m := make(map[string]interface{}, len(l.fields)+1)
224 | for k, v := range l.fields {
225 | m[k] = v
226 | }
227 | m[key] = value
228 | nl := &logger{
229 | fields: m,
230 | }
231 | nl.setOptions(l.getOptions())
232 | return nl
233 | }
234 |
235 | func (l *logger) WithFields(fields ...interface{}) Logger {
236 | if len(fields) == 0 {
237 | return l
238 | }
239 | m, err := combineFields(l.fields, fields)
240 | if err != nil {
241 | fmt.Fprintf(ConcurrentStderr, "log: failed to combine fields, error=%v, location=%s\n", err, callerLocation(1))
242 | }
243 | nl := &logger{
244 | fields: m,
245 | }
246 | nl.setOptions(l.getOptions())
247 | return nl
248 | }
249 |
--------------------------------------------------------------------------------
/trace/trace_test.go:
--------------------------------------------------------------------------------
1 | package trace
2 |
3 | import (
4 | "context"
5 | "net/http"
6 | "testing"
7 | )
8 |
9 | func Test_NewContext_FromContext(t *testing.T) {
10 | // no called NewContext yet
11 | {
12 | ctx := context.Background()
13 |
14 | id, ok := FromContext(ctx)
15 | wantId, wantOk := "", false
16 | if id != wantId || ok != wantOk {
17 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
18 | return
19 | }
20 | }
21 |
22 | // called NewContext with empty traceId
23 | {
24 | ctx := context.Background()
25 |
26 | traceId := ""
27 | ctx = NewContext(ctx, traceId)
28 | id, ok := FromContext(ctx)
29 | wantId, wantOk := "", false
30 | if id != wantId || ok != wantOk {
31 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
32 | return
33 | }
34 | }
35 |
36 | // called NewContext with non-empty traceId
37 | {
38 | ctx := context.Background()
39 |
40 | traceId := "123456789"
41 | ctx = NewContext(ctx, traceId)
42 | id, ok := FromContext(ctx)
43 | wantId, wantOk := traceId, true
44 | if id != wantId || ok != wantOk {
45 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
46 | return
47 | }
48 | }
49 | }
50 |
51 | func TestNewContext(t *testing.T) {
52 | // empty traceId
53 | {
54 | ctx := context.Background()
55 |
56 | ctx2 := NewContext(ctx, "")
57 | if ctx != ctx2 {
58 | t.Error("want equal")
59 | return
60 | }
61 | }
62 |
63 | // nil context.Context
64 | {
65 | var ctx context.Context
66 |
67 | ctx2 := NewContext(ctx, "123456789")
68 | if ctx == ctx2 {
69 | t.Error("want not equal")
70 | return
71 | }
72 |
73 | id, ok := FromContext(ctx2)
74 | wantId, wantOk := "123456789", true
75 | if id != wantId || ok != wantOk {
76 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
77 | return
78 | }
79 | }
80 |
81 | // serially NewContext with same traceId
82 | {
83 | ctx := context.Background()
84 |
85 | traceId := "123456789"
86 | ctx2 := NewContext(ctx, traceId)
87 | ctx3 := NewContext(ctx2, traceId)
88 | if ctx2 != ctx3 {
89 | t.Error("want equal")
90 | return
91 | }
92 | }
93 |
94 | // parallel NewContext with same traceId
95 | {
96 | ctx := context.Background()
97 |
98 | traceId := "123456789"
99 | ctx2 := NewContext(ctx, traceId)
100 | ctx3 := NewContext(ctx, traceId)
101 | if ctx2 == ctx3 {
102 | t.Error("want not equal")
103 | return
104 | }
105 | }
106 | }
107 |
108 | func TestFromContext(t *testing.T) {
109 | // nil context
110 | {
111 | var ctx context.Context
112 | id, ok := FromContext(ctx)
113 | wantId, wantOk := "", false
114 | if id != wantId || ok != wantOk {
115 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
116 | return
117 | }
118 | }
119 |
120 | // non-nil context that does not contain traceId
121 | {
122 | ctx := context.Background()
123 | id, ok := FromContext(ctx)
124 | wantId, wantOk := "", false
125 | if id != wantId || ok != wantOk {
126 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
127 | return
128 | }
129 | }
130 |
131 | // non-nil context that contains empty traceId
132 | // this will never happen, see NewContext
133 |
134 | // non-nil context that contains non-empty traceId
135 | {
136 | ctx := context.WithValue(context.Background(), _traceIdContextKey, "123456789")
137 | id, ok := FromContext(ctx)
138 | wantId, wantOk := "123456789", true
139 | if id != wantId || ok != wantOk {
140 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
141 | return
142 | }
143 | }
144 | }
145 |
146 | func TestFromRequest(t *testing.T) {
147 | //// nil *http.Request
148 | //{
149 | // var req *http.Request
150 | // id, ok := FromRequest(req)
151 | // wantId, wantOk := "", false
152 | // if id != wantId || ok != wantOk {
153 | // t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
154 | // return
155 | // }
156 | //}
157 |
158 | // non-nil *http.Request
159 |
160 | // nil Request.Context()
161 | {
162 | // nil Request.Header
163 | {
164 | req := &http.Request{}
165 |
166 | id, ok := FromRequest(req)
167 | wantId, wantOk := "", false
168 | if id != wantId || ok != wantOk {
169 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
170 | return
171 | }
172 | }
173 |
174 | // non-nil Request.Header without valid traceId
175 | {
176 | req := &http.Request{}
177 |
178 | header := make(http.Header)
179 | req.Header = header
180 |
181 | id, ok := FromRequest(req)
182 | wantId, wantOk := "", false
183 | if id != wantId || ok != wantOk {
184 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
185 | return
186 | }
187 | }
188 |
189 | // non-nil Request.Header with valid traceId
190 | {
191 | req := &http.Request{}
192 |
193 | header := make(http.Header)
194 | header.Set(TraceIdHeaderKey, "123456789")
195 | req.Header = header
196 |
197 | id, ok := FromRequest(req)
198 | wantId, wantOk := "123456789", true
199 | if id != wantId || ok != wantOk {
200 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
201 | return
202 | }
203 | }
204 | }
205 |
206 | // non-nil Request.Context() without valid traceId
207 | {
208 | // nil Request.Header
209 | {
210 | req := &http.Request{}
211 | req = req.WithContext(context.Background())
212 |
213 | id, ok := FromRequest(req)
214 | wantId, wantOk := "", false
215 | if id != wantId || ok != wantOk {
216 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
217 | return
218 | }
219 | }
220 |
221 | // non-nil Request.Header without valid traceId
222 | {
223 | req := &http.Request{}
224 | req = req.WithContext(context.Background())
225 |
226 | header := make(http.Header)
227 | req.Header = header
228 |
229 | id, ok := FromRequest(req)
230 | wantId, wantOk := "", false
231 | if id != wantId || ok != wantOk {
232 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
233 | return
234 | }
235 | }
236 |
237 | // non-nil Request.Header with valid traceId
238 | {
239 | req := &http.Request{}
240 | req = req.WithContext(context.Background())
241 |
242 | header := make(http.Header)
243 | header.Set(TraceIdHeaderKey, "123456789")
244 | req.Header = header
245 |
246 | id, ok := FromRequest(req)
247 | wantId, wantOk := "123456789", true
248 | if id != wantId || ok != wantOk {
249 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
250 | return
251 | }
252 | }
253 | }
254 |
255 | // non-nil Request.Context() with valid traceId
256 | {
257 | // nil Request.Header
258 | {
259 | req := &http.Request{}
260 |
261 | traceId := "987654321"
262 | ctx := NewContext(context.Background(), traceId)
263 | req = req.WithContext(ctx)
264 |
265 | id, ok := FromRequest(req)
266 | wantId, wantOk := traceId, true
267 | if id != wantId || ok != wantOk {
268 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
269 | return
270 | }
271 | }
272 |
273 | // non-nil Request.Header without valid traceId
274 | {
275 | req := &http.Request{}
276 |
277 | traceId := "987654321"
278 | ctx := NewContext(context.Background(), traceId)
279 | req = req.WithContext(ctx)
280 |
281 | header := make(http.Header)
282 | req.Header = header
283 |
284 | id, ok := FromRequest(req)
285 | wantId, wantOk := traceId, true
286 | if id != wantId || ok != wantOk {
287 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
288 | return
289 | }
290 | }
291 |
292 | // non-nil Request.Header with valid traceId
293 | {
294 | req := &http.Request{}
295 |
296 | traceId := "987654321"
297 | ctx := NewContext(context.Background(), traceId)
298 | req = req.WithContext(ctx)
299 |
300 | header := make(http.Header)
301 | header.Set(TraceIdHeaderKey, "123456789")
302 | req.Header = header
303 |
304 | id, ok := FromRequest(req)
305 | wantId, wantOk := traceId, true
306 | if id != wantId || ok != wantOk {
307 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
308 | return
309 | }
310 | }
311 | }
312 | }
313 |
314 | func TestFromHeader(t *testing.T) {
315 | // nil header
316 | {
317 | var header http.Header
318 | id, ok := FromHeader(header)
319 | wantId, wantOk := "", false
320 | if id != wantId || ok != wantOk {
321 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
322 | return
323 | }
324 | }
325 |
326 | // non-nil header without TraceIdHeaderKey
327 | {
328 | header := make(http.Header)
329 | id, ok := FromHeader(header)
330 | wantId, wantOk := "", false
331 | if id != wantId || ok != wantOk {
332 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
333 | return
334 | }
335 | }
336 |
337 | // non-nil header with empty value for TraceIdHeaderKey
338 | {
339 | header := make(http.Header)
340 | header.Set(TraceIdHeaderKey, "")
341 | id, ok := FromHeader(header)
342 | wantId, wantOk := "", false
343 | if id != wantId || ok != wantOk {
344 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
345 | return
346 | }
347 | }
348 |
349 | // non-nil header with valid value for TraceIdHeaderKey
350 | {
351 | header := make(http.Header)
352 | header.Set(TraceIdHeaderKey, "123456789")
353 | id, ok := FromHeader(header)
354 | wantId, wantOk := "123456789", true
355 | if id != wantId || ok != wantOk {
356 | t.Errorf("have:(%s, %t), want:(%s, %t)", id, ok, wantId, wantOk)
357 | return
358 | }
359 | }
360 | }
361 |
--------------------------------------------------------------------------------
/options_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "io"
5 | "os"
6 | "reflect"
7 | "testing"
8 | )
9 |
10 | func TestWithTraceId(t *testing.T) {
11 | // empty traceId
12 | {
13 | opt := WithTraceId("")
14 |
15 | var o = options{
16 | traceId: "987654321",
17 | }
18 | opt(&o)
19 | want := options{
20 | traceId: "",
21 | }
22 | if o != want {
23 | t.Errorf("have:%+v, want:%+v", o, want)
24 | return
25 | }
26 | }
27 | // non-empty traceId
28 | {
29 | opt := WithTraceId("123456789")
30 |
31 | var o = options{
32 | traceId: "987654321",
33 | }
34 | opt(&o)
35 | want := options{
36 | traceId: "123456789",
37 | }
38 | if o != want {
39 | t.Errorf("have:%+v, want:%+v", o, want)
40 | return
41 | }
42 | }
43 | }
44 |
45 | func TestWithTraceIdFunc(t *testing.T) {
46 | // nil function
47 | {
48 | var fn func() string
49 | opt := WithTraceIdFunc(fn)
50 |
51 | var o = options{
52 | traceId: "987654321",
53 | }
54 | opt(&o)
55 | want := options{
56 | traceId: "987654321",
57 | }
58 | if o != want {
59 | t.Errorf("have:%+v, want:%+v", o, want)
60 | return
61 | }
62 | }
63 | // non-nil function with empty returns
64 | {
65 | fn := func() string { return "" }
66 | opt := WithTraceIdFunc(fn)
67 |
68 | var o = options{
69 | traceId: "987654321",
70 | }
71 | opt(&o)
72 | want := options{
73 | traceId: "",
74 | }
75 | if o != want {
76 | t.Errorf("have:%+v, want:%+v", o, want)
77 | return
78 | }
79 | }
80 | // non-nil function with non-empty returns
81 | {
82 | fn := func() string { return "123456789" }
83 | opt := WithTraceIdFunc(fn)
84 |
85 | var o = options{
86 | traceId: "987654321",
87 | }
88 | opt(&o)
89 | want := options{
90 | traceId: "123456789",
91 | }
92 | if o != want {
93 | t.Errorf("have:%+v, want:%+v", o, want)
94 | return
95 | }
96 | }
97 | }
98 |
99 | func TestWithFormatter(t *testing.T) {
100 | // nil Formatter
101 | {
102 | var formatter Formatter
103 | opt := WithFormatter(formatter)
104 |
105 | var o = options{
106 | formatter: JsonFormatter,
107 | }
108 | opt(&o)
109 | want := options{
110 | formatter: JsonFormatter,
111 | }
112 | if o != want {
113 | t.Errorf("have:%+v, want:%+v", o, want)
114 | return
115 | }
116 | }
117 | // non-nil Formatter
118 | {
119 | opt := WithFormatter(TextFormatter)
120 |
121 | var o = options{
122 | formatter: JsonFormatter,
123 | }
124 | opt(&o)
125 | want := options{
126 | formatter: TextFormatter,
127 | }
128 | if o != want {
129 | t.Errorf("have:%+v, want:%+v", o, want)
130 | return
131 | }
132 | }
133 | }
134 |
135 | func TestWithOutput(t *testing.T) {
136 | // nil output
137 | {
138 | var output io.Writer
139 | opt := WithOutput(output)
140 |
141 | var o = options{
142 | output: ConcurrentStderr,
143 | }
144 | opt(&o)
145 | want := options{
146 | output: ConcurrentStderr,
147 | }
148 | if o != want {
149 | t.Errorf("have:%+v, want:%+v", o, want)
150 | return
151 | }
152 | }
153 | // non-nil output
154 | {
155 | opt := WithOutput(ConcurrentStdout)
156 |
157 | var o = options{
158 | output: ConcurrentStderr,
159 | }
160 | opt(&o)
161 | want := options{
162 | output: ConcurrentStdout,
163 | }
164 | if o != want {
165 | t.Errorf("have:%+v, want:%+v", o, want)
166 | return
167 | }
168 | }
169 | }
170 |
171 | func TestWithLevel(t *testing.T) {
172 | // fatal-1
173 | {
174 | opt := WithLevel(FatalLevel - 1)
175 |
176 | var o = options{
177 | level: DebugLevel,
178 | }
179 | opt(&o)
180 | want := options{
181 | level: DebugLevel,
182 | }
183 | if o != want {
184 | t.Errorf("have:%+v, want:%+v", o, want)
185 | return
186 | }
187 | }
188 | // fatal
189 | {
190 | opt := WithLevel(FatalLevel)
191 |
192 | var o = options{
193 | level: DebugLevel,
194 | }
195 | opt(&o)
196 | want := options{
197 | level: FatalLevel,
198 | }
199 | if o != want {
200 | t.Errorf("have:%+v, want:%+v", o, want)
201 | return
202 | }
203 | }
204 | // error
205 | {
206 | opt := WithLevel(ErrorLevel)
207 |
208 | var o = options{
209 | level: DebugLevel,
210 | }
211 | opt(&o)
212 | want := options{
213 | level: ErrorLevel,
214 | }
215 | if o != want {
216 | t.Errorf("have:%+v, want:%+v", o, want)
217 | return
218 | }
219 | }
220 | // warning
221 | {
222 | opt := WithLevel(WarnLevel)
223 |
224 | var o = options{
225 | level: DebugLevel,
226 | }
227 | opt(&o)
228 | want := options{
229 | level: WarnLevel,
230 | }
231 | if o != want {
232 | t.Errorf("have:%+v, want:%+v", o, want)
233 | return
234 | }
235 | }
236 | // info
237 | {
238 | opt := WithLevel(InfoLevel)
239 |
240 | var o = options{
241 | level: DebugLevel,
242 | }
243 | opt(&o)
244 | want := options{
245 | level: InfoLevel,
246 | }
247 | if o != want {
248 | t.Errorf("have:%+v, want:%+v", o, want)
249 | return
250 | }
251 | }
252 | // debug
253 | {
254 | opt := WithLevel(DebugLevel)
255 |
256 | var o = options{
257 | level: FatalLevel,
258 | }
259 | opt(&o)
260 | want := options{
261 | level: DebugLevel,
262 | }
263 | if o != want {
264 | t.Errorf("have:%+v, want:%+v", o, want)
265 | return
266 | }
267 | }
268 | // debug+1
269 | {
270 | opt := WithLevel(DebugLevel + 1)
271 |
272 | var o = options{
273 | level: FatalLevel,
274 | }
275 | opt(&o)
276 | want := options{
277 | level: FatalLevel,
278 | }
279 | if o != want {
280 | t.Errorf("have:%+v, want:%+v", o, want)
281 | return
282 | }
283 | }
284 | }
285 |
286 | func TestWithLevelString(t *testing.T) {
287 | // panic
288 | {
289 | opt := WithLevelString("panic")
290 |
291 | var o = options{
292 | level: DebugLevel,
293 | }
294 | opt(&o)
295 | want := options{
296 | level: DebugLevel,
297 | }
298 | if o != want {
299 | t.Errorf("have:%+v, want:%+v", o, want)
300 | return
301 | }
302 | }
303 | // fatal
304 | {
305 | opt := WithLevelString(FatalLevelString)
306 |
307 | var o = options{
308 | level: DebugLevel,
309 | }
310 | opt(&o)
311 | want := options{
312 | level: FatalLevel,
313 | }
314 | if o != want {
315 | t.Errorf("have:%+v, want:%+v", o, want)
316 | return
317 | }
318 | }
319 | // error
320 | {
321 | opt := WithLevelString(ErrorLevelString)
322 |
323 | var o = options{
324 | level: DebugLevel,
325 | }
326 | opt(&o)
327 | want := options{
328 | level: ErrorLevel,
329 | }
330 | if o != want {
331 | t.Errorf("have:%+v, want:%+v", o, want)
332 | return
333 | }
334 | }
335 | // warning
336 | {
337 | opt := WithLevelString(WarnLevelString)
338 |
339 | var o = options{
340 | level: DebugLevel,
341 | }
342 | opt(&o)
343 | want := options{
344 | level: WarnLevel,
345 | }
346 | if o != want {
347 | t.Errorf("have:%+v, want:%+v", o, want)
348 | return
349 | }
350 | }
351 | // info
352 | {
353 | opt := WithLevelString(InfoLevelString)
354 |
355 | var o = options{
356 | level: DebugLevel,
357 | }
358 | opt(&o)
359 | want := options{
360 | level: InfoLevel,
361 | }
362 | if o != want {
363 | t.Errorf("have:%+v, want:%+v", o, want)
364 | return
365 | }
366 | }
367 | // debug
368 | {
369 | opt := WithLevelString(DebugLevelString)
370 |
371 | var o = options{
372 | level: FatalLevel,
373 | }
374 | opt(&o)
375 | want := options{
376 | level: DebugLevel,
377 | }
378 | if o != want {
379 | t.Errorf("have:%+v, want:%+v", o, want)
380 | return
381 | }
382 | }
383 | // trace
384 | {
385 | opt := WithLevelString("trace")
386 |
387 | var o = options{
388 | level: FatalLevel,
389 | }
390 | opt(&o)
391 | want := options{
392 | level: FatalLevel,
393 | }
394 | if o != want {
395 | t.Errorf("have:%+v, want:%+v", o, want)
396 | return
397 | }
398 | }
399 | }
400 |
401 | func TestOptions_SetFormatter(t *testing.T) {
402 | // nil Formatter
403 | {
404 | var o = options{
405 | formatter: JsonFormatter,
406 | }
407 | var formatter Formatter
408 | o.SetFormatter(formatter)
409 |
410 | want := options{
411 | formatter: JsonFormatter,
412 | }
413 | if o != want {
414 | t.Errorf("have:%+v, want:%+v", o, want)
415 | return
416 | }
417 | }
418 | // non-nil Formatter
419 | {
420 | var o = options{
421 | formatter: JsonFormatter,
422 | }
423 | o.SetFormatter(TextFormatter)
424 |
425 | want := options{
426 | formatter: TextFormatter,
427 | }
428 | if o != want {
429 | t.Errorf("have:%+v, want:%+v", o, want)
430 | return
431 | }
432 | }
433 | }
434 |
435 | func TestOptions_SetOutput(t *testing.T) {
436 | // nil output
437 | {
438 | var o = options{
439 | output: ConcurrentStderr,
440 | }
441 | var output io.Writer
442 | o.SetOutput(output)
443 |
444 | want := options{
445 | output: ConcurrentStderr,
446 | }
447 | if o != want {
448 | t.Errorf("have:%+v, want:%+v", o, want)
449 | return
450 | }
451 | }
452 | // non-nil output
453 | {
454 | var o = options{
455 | output: ConcurrentStderr,
456 | }
457 | o.SetOutput(ConcurrentStdout)
458 |
459 | want := options{
460 | output: ConcurrentStdout,
461 | }
462 | if o != want {
463 | t.Errorf("have:%+v, want:%+v", o, want)
464 | return
465 | }
466 | }
467 | }
468 |
469 | func TestOptions_SetLevel(t *testing.T) {
470 | // fatal-1
471 | {
472 | var o = options{
473 | level: DebugLevel,
474 | }
475 | o.SetLevel(FatalLevel - 1)
476 |
477 | want := options{
478 | level: DebugLevel,
479 | }
480 | if o != want {
481 | t.Errorf("have:%+v, want:%+v", o, want)
482 | return
483 | }
484 | }
485 | // fatal
486 | {
487 | var o = options{
488 | level: DebugLevel,
489 | }
490 | o.SetLevel(FatalLevel)
491 |
492 | want := options{
493 | level: FatalLevel,
494 | }
495 | if o != want {
496 | t.Errorf("have:%+v, want:%+v", o, want)
497 | return
498 | }
499 | }
500 | // error
501 | {
502 | var o = options{
503 | level: DebugLevel,
504 | }
505 | o.SetLevel(ErrorLevel)
506 |
507 | want := options{
508 | level: ErrorLevel,
509 | }
510 | if o != want {
511 | t.Errorf("have:%+v, want:%+v", o, want)
512 | return
513 | }
514 | }
515 | // warning
516 | {
517 | var o = options{
518 | level: DebugLevel,
519 | }
520 | o.SetLevel(WarnLevel)
521 |
522 | want := options{
523 | level: WarnLevel,
524 | }
525 | if o != want {
526 | t.Errorf("have:%+v, want:%+v", o, want)
527 | return
528 | }
529 | }
530 | // info
531 | {
532 | var o = options{
533 | level: DebugLevel,
534 | }
535 | o.SetLevel(InfoLevel)
536 |
537 | want := options{
538 | level: InfoLevel,
539 | }
540 | if o != want {
541 | t.Errorf("have:%+v, want:%+v", o, want)
542 | return
543 | }
544 | }
545 | // debug
546 | {
547 | var o = options{
548 | level: FatalLevel,
549 | }
550 | o.SetLevel(DebugLevel)
551 |
552 | want := options{
553 | level: DebugLevel,
554 | }
555 | if o != want {
556 | t.Errorf("have:%+v, want:%+v", o, want)
557 | return
558 | }
559 | }
560 | // debug+1
561 | {
562 | var o = options{
563 | level: FatalLevel,
564 | }
565 | o.SetLevel(DebugLevel + 1)
566 |
567 | want := options{
568 | level: FatalLevel,
569 | }
570 | if o != want {
571 | t.Errorf("have:%+v, want:%+v", o, want)
572 | return
573 | }
574 | }
575 | }
576 |
577 | func TestNewOptions(t *testing.T) {
578 | // nil == getDefaultOptions()
579 | {
580 | SetDefaultOptions(nil)
581 |
582 | defaultWant := options{
583 | traceId: "",
584 | formatter: TextFormatter,
585 | output: ConcurrentStdout,
586 | level: DebugLevel,
587 | }
588 |
589 | // with none
590 | {
591 | opts := newOptions(nil)
592 | want := defaultWant
593 | if have := *opts; have != want {
594 | t.Errorf("have:%+v, want:%+v", have, want)
595 | return
596 | }
597 | }
598 | // WithTraceId
599 | {
600 | opts := newOptions([]Option{WithTraceId("123456789")})
601 | want := defaultWant
602 | want.traceId = "123456789"
603 | if have := *opts; have != want {
604 | t.Errorf("have:%+v, want:%+v", have, want)
605 | return
606 | }
607 | }
608 | // WithTraceIdFunc
609 | {
610 | fn := func() string { return "123456789" }
611 | opts := newOptions([]Option{WithTraceIdFunc(fn)})
612 | want := defaultWant
613 | want.traceId = "123456789"
614 | if have := *opts; have != want {
615 | t.Errorf("have:%+v, want:%+v", have, want)
616 | return
617 | }
618 | }
619 | // WithFormatter
620 | {
621 | opts := newOptions([]Option{WithFormatter(JsonFormatter)})
622 | want := defaultWant
623 | want.formatter = JsonFormatter
624 | if have := *opts; have != want {
625 | t.Errorf("have:%+v, want:%+v", have, want)
626 | return
627 | }
628 | }
629 | // WithOutput
630 | {
631 | opts := newOptions([]Option{WithOutput(ConcurrentStderr)})
632 | want := defaultWant
633 | want.output = ConcurrentStderr
634 | if have := *opts; have != want {
635 | t.Errorf("have:%+v, want:%+v", have, want)
636 | return
637 | }
638 | }
639 | // WithLevel
640 | {
641 | opts := newOptions([]Option{WithLevel(InfoLevel)})
642 | want := defaultWant
643 | want.level = InfoLevel
644 | if have := *opts; have != want {
645 | t.Errorf("have:%+v, want:%+v", have, want)
646 | return
647 | }
648 | }
649 | // WithLevelString
650 | {
651 | opts := newOptions([]Option{WithLevelString(WarnLevelString)})
652 | want := defaultWant
653 | want.level = WarnLevel
654 | if have := *opts; have != want {
655 | t.Errorf("have:%+v, want:%+v", have, want)
656 | return
657 | }
658 | }
659 | }
660 |
661 | // nil != getDefaultOptions()
662 | {
663 | formatter := testJsonFormatter{}
664 | output := ConcurrentWriter(os.Stdout)
665 | SetDefaultOptions([]Option{WithTraceId("987654321"), WithFormatter(formatter), WithOutput(output), WithLevel(FatalLevel)})
666 | defer SetDefaultOptions(nil)
667 |
668 | defaultWant := options{
669 | traceId: "987654321",
670 | formatter: formatter,
671 | output: output,
672 | level: FatalLevel,
673 | }
674 |
675 | // with none
676 | {
677 | opts := newOptions(nil)
678 | want := defaultWant
679 | if have := *opts; have != want {
680 | t.Errorf("have:%+v, want:%+v", have, want)
681 | return
682 | }
683 | }
684 | // WithTraceId
685 | {
686 | opts := newOptions([]Option{WithTraceId("123456789")})
687 | want := defaultWant
688 | want.traceId = "123456789"
689 | if have := *opts; have != want {
690 | t.Errorf("have:%+v, want:%+v", have, want)
691 | return
692 | }
693 | }
694 | // WithTraceIdFunc
695 | {
696 | fn := func() string { return "123456789" }
697 | opts := newOptions([]Option{WithTraceIdFunc(fn)})
698 | want := defaultWant
699 | want.traceId = "123456789"
700 | if have := *opts; have != want {
701 | t.Errorf("have:%+v, want:%+v", have, want)
702 | return
703 | }
704 | }
705 | // WithFormatter
706 | {
707 | opts := newOptions([]Option{WithFormatter(TextFormatter)})
708 | want := defaultWant
709 | want.formatter = TextFormatter
710 | if have := *opts; have != want {
711 | t.Errorf("have:%+v, want:%+v", have, want)
712 | return
713 | }
714 | }
715 | // WithOutput
716 | {
717 | opts := newOptions([]Option{WithOutput(ConcurrentStdout)})
718 | want := defaultWant
719 | want.output = ConcurrentStdout
720 | if have := *opts; have != want {
721 | t.Errorf("have:%+v, want:%+v", have, want)
722 | return
723 | }
724 | }
725 | // WithLevel
726 | {
727 | opts := newOptions([]Option{WithLevel(InfoLevel)})
728 | want := defaultWant
729 | want.level = InfoLevel
730 | if have := *opts; have != want {
731 | t.Errorf("have:%+v, want:%+v", have, want)
732 | return
733 | }
734 | }
735 | // WithLevelString
736 | {
737 | opts := newOptions([]Option{WithLevelString(WarnLevelString)})
738 | want := defaultWant
739 | want.level = WarnLevel
740 | if have := *opts; have != want {
741 | t.Errorf("have:%+v, want:%+v", have, want)
742 | return
743 | }
744 | }
745 | }
746 | }
747 |
748 | func Test_SetDefaultOptions_GetDefaultOptions(t *testing.T) {
749 | defer SetDefaultOptions(nil)
750 |
751 | // no called SetDefaultOptions yet
752 | opts := getDefaultOptions()
753 | if opts != nil {
754 | t.Errorf("have:%v, want:nil", opts)
755 | return
756 | }
757 |
758 | // call SetDefaultOptions with non-nil []Option
759 | opt := WithFormatter(JsonFormatter)
760 | opts = []Option{
761 | opt,
762 | }
763 | SetDefaultOptions(opts)
764 | have := getDefaultOptions()
765 | if !reflect.DeepEqual(have, opts) {
766 | t.Errorf("have:%v, want:%v", have, opts)
767 | return
768 | }
769 |
770 | // call SetDefaultOptions with nil []Option
771 | SetDefaultOptions(nil)
772 | opts = getDefaultOptions()
773 | if opts != nil {
774 | t.Errorf("have:%v, want:nil", opts)
775 | return
776 | }
777 |
778 | // call SetDefaultOptions with non-nil []Option
779 | opt = WithFormatter(TextFormatter)
780 | opts = []Option{
781 | opt,
782 | }
783 | SetDefaultOptions(opts)
784 | have = getDefaultOptions()
785 | if !reflect.DeepEqual(have, opts) {
786 | t.Errorf("have:%v, want:%v", have, opts)
787 | return
788 | }
789 | }
790 |
--------------------------------------------------------------------------------
/context_test.go:
--------------------------------------------------------------------------------
1 | package log
2 |
3 | import (
4 | "context"
5 | "net/http"
6 | "testing"
7 | )
8 |
9 | func Test_NewContext_FromContext(t *testing.T) {
10 | // no called NewContext yet
11 | {
12 | ctx := context.Background()
13 |
14 | lg, ok := FromContext(ctx)
15 | want, wantOk := Logger(nil), false
16 | if lg != want || ok != wantOk {
17 | t.Errorf("have:(%#v, %t), want:(%#v, %t)", lg, ok, want, wantOk)
18 | return
19 | }
20 | }
21 |
22 | // called NewContext with nil Logger
23 | {
24 | ctx := context.Background()
25 |
26 | _lg := Logger(nil)
27 | ctx = NewContext(ctx, _lg)
28 |
29 | lg, ok := FromContext(ctx)
30 | want, wantOk := Logger(nil), false
31 | if lg != want || ok != wantOk {
32 | t.Errorf("have:(%#v, %t), want:(%#v, %t)", lg, ok, want, wantOk)
33 | return
34 | }
35 | }
36 |
37 | // called NewContext with non-nil Logger, nil context.Context
38 | {
39 | var ctx context.Context
40 |
41 | _lg := New()
42 | ctx = NewContext(ctx, _lg)
43 |
44 | lg, ok := FromContext(ctx)
45 | want, wantOk := _lg, true
46 | if lg != want || ok != wantOk {
47 | t.Errorf("have:(%#v, %t), want:(%#v, %t)", lg, ok, want, wantOk)
48 | return
49 | }
50 | }
51 |
52 | // called NewContext with non-nil Logger, non-nil context.Context
53 | {
54 | ctx := context.Background()
55 |
56 | _lg := New()
57 | ctx = NewContext(ctx, _lg)
58 |
59 | lg, ok := FromContext(ctx)
60 | want, wantOk := _lg, true
61 | if lg != want || ok != wantOk {
62 | t.Errorf("have:(%#v, %t), want:(%#v, %t)", lg, ok, want, wantOk)
63 | return
64 | }
65 | }
66 | }
67 |
68 | func TestNewContext(t *testing.T) {
69 | // nil Logger
70 | {
71 | ctx := context.Background()
72 |
73 | ctx2 := NewContext(ctx, Logger(nil))
74 | if ctx != ctx2 {
75 | t.Error("want equal")
76 | return
77 | }
78 | }
79 |
80 | // nil context.Context
81 | {
82 | var ctx context.Context
83 |
84 | _lg := New()
85 | ctx2 := NewContext(ctx, _lg)
86 | if ctx == ctx2 {
87 | t.Error("want not equal")
88 | return
89 | }
90 |
91 | lg, ok := FromContext(ctx2)
92 | want, wantOk := _lg, true
93 | if lg != want || ok != wantOk {
94 | t.Errorf("have:(%#v, %t), want:(%#v, %t)", lg, ok, want, wantOk)
95 | return
96 | }
97 | }
98 |
99 | // serially NewContext with same Logger
100 | {
101 | ctx := context.Background()
102 |
103 | _lg := New()
104 | ctx2 := NewContext(ctx, _lg)
105 | ctx3 := NewContext(ctx2, _lg)
106 | if ctx2 != ctx3 {
107 | t.Error("want equal")
108 | return
109 | }
110 | }
111 |
112 | // parallel NewContext with same Logger
113 | {
114 | ctx := context.Background()
115 |
116 | _lg := New()
117 | ctx2 := NewContext(ctx, _lg)
118 | ctx3 := NewContext(ctx, _lg)
119 | if ctx2 == ctx3 {
120 | t.Error("want not equal")
121 | return
122 | }
123 | }
124 | }
125 |
126 | func TestFromContext(t *testing.T) {
127 | // nil context
128 | {
129 | var ctx context.Context
130 |
131 | lg, ok := FromContext(ctx)
132 | want, wantOk := Logger(nil), false
133 | if lg != want || ok != wantOk {
134 | t.Errorf("have:(%#v, %t), want:(%#v, %t)", lg, ok, want, wantOk)
135 | return
136 | }
137 | }
138 |
139 | // non-nil context that does not contain Logger
140 | {
141 | ctx := context.Background()
142 |
143 | lg, ok := FromContext(ctx)
144 | want, wantOk := Logger(nil), false
145 | if lg != want || ok != wantOk {
146 | t.Errorf("have:(%#v, %t), want:(%#v, %t)", lg, ok, want, wantOk)
147 | return
148 | }
149 | }
150 |
151 | // non-nil context that contains nil Logger
152 | // this will never happen, see NewContext
153 |
154 | // non-nil context that contains non-nil Logger
155 | {
156 | _lg := New()
157 | ctx := context.WithValue(context.Background(), _loggerContextKey, _lg)
158 |
159 | lg, ok := FromContext(ctx)
160 | want, wantOk := _lg, true
161 | if lg != want || ok != wantOk {
162 | t.Errorf("have:(%#v, %t), want:(%#v, %t)", lg, ok, want, wantOk)
163 | return
164 | }
165 | }
166 | }
167 |
168 | func TestMustFromContext(t *testing.T) {
169 | // nil context
170 | func() {
171 | var lg Logger
172 |
173 | defer func() {
174 | if err := recover(); err != nil {
175 | if lg != nil {
176 | t.Error("lg must nil")
177 | }
178 | } else {
179 | t.Error("must panic")
180 | }
181 | }()
182 |
183 | var ctx context.Context
184 | lg = MustFromContext(ctx)
185 | }()
186 |
187 | // non-nil context that does not contain Logger
188 | func() {
189 | var lg Logger
190 |
191 | defer func() {
192 | if err := recover(); err != nil {
193 | if lg != nil {
194 | t.Error("lg must nil")
195 | }
196 | } else {
197 | t.Error("must panic")
198 | }
199 | }()
200 |
201 | ctx := context.Background()
202 | lg = MustFromContext(ctx)
203 | }()
204 |
205 | // non-nil context that contains nil Logger
206 | // this will never happen, see NewContext
207 |
208 | // non-nil context that contains non-nil Logger
209 | func() {
210 | var lg Logger
211 |
212 | defer func() {
213 | if err := recover(); err != nil {
214 | t.Error("must not panic")
215 | } else {
216 | if lg == nil {
217 | t.Error("lg must not nil")
218 | }
219 | }
220 | }()
221 |
222 | _lg := New()
223 | ctx := context.WithValue(context.Background(), _loggerContextKey, _lg)
224 |
225 | lg = MustFromContext(ctx)
226 | if lg != _lg {
227 | t.Error("want equal")
228 | return
229 | }
230 | }()
231 | }
232 |
233 | func TestFromContextOrNew(t *testing.T) {
234 | // nil new function
235 | {
236 | // nil context
237 | {
238 | var ctx context.Context
239 |
240 | lg, ctx2, ok := FromContextOrNew(ctx, nil)
241 | if lg == nil {
242 | t.Error("want non-nil")
243 | return
244 | }
245 | if !ok {
246 | t.Error("want true")
247 | return
248 | }
249 | if ctx2 == ctx {
250 | t.Error("want not equal")
251 | return
252 | }
253 | lg2, ok := FromContext(ctx2)
254 | if !ok {
255 | t.Error("want true")
256 | return
257 | }
258 | if lg2 != lg {
259 | t.Error("want equal")
260 | return
261 | }
262 | }
263 |
264 | // non-nil context that does not contain Logger
265 | {
266 | ctx := context.Background()
267 |
268 | lg, ctx2, ok := FromContextOrNew(ctx, nil)
269 | if lg == nil {
270 | t.Error("want non-nil")
271 | return
272 | }
273 | if !ok {
274 | t.Error("want true")
275 | return
276 | }
277 | if ctx2 == ctx {
278 | t.Error("want not equal")
279 | return
280 | }
281 | lg2, ok := FromContext(ctx2)
282 | if !ok {
283 | t.Error("want true")
284 | return
285 | }
286 | if lg2 != lg {
287 | t.Error("want equal")
288 | return
289 | }
290 | }
291 |
292 | // non-nil context that contains nil Logger
293 | // this will never happen, see NewContext
294 |
295 | // non-nil context that contains non-nil Logger
296 | {
297 | _lg := New()
298 | ctx := context.WithValue(context.Background(), _loggerContextKey, _lg)
299 |
300 | lg, ctx2, ok := FromContextOrNew(ctx, nil)
301 | if lg != _lg {
302 | t.Error("want equal")
303 | return
304 | }
305 | if ok {
306 | t.Error("want false")
307 | return
308 | }
309 | if ctx2 != ctx {
310 | t.Error("want equal")
311 | return
312 | }
313 | lg2, ok := FromContext(ctx2)
314 | if !ok {
315 | t.Error("want true")
316 | return
317 | }
318 | if lg2 != lg {
319 | t.Error("want equal")
320 | return
321 | }
322 | }
323 | }
324 |
325 | // non-nil new func
326 | {
327 | var _newLogger = New()
328 | var _new = func() Logger { return _newLogger }
329 |
330 | // nil context
331 | {
332 | var ctx context.Context
333 |
334 | lg, ctx2, ok := FromContextOrNew(ctx, _new)
335 | if lg != _newLogger {
336 | t.Error("want equal")
337 | return
338 | }
339 | if !ok {
340 | t.Error("want true")
341 | return
342 | }
343 | if ctx2 == ctx {
344 | t.Error("want not equal")
345 | return
346 | }
347 | lg2, ok := FromContext(ctx2)
348 | if !ok {
349 | t.Error("want true")
350 | return
351 | }
352 | if lg2 != lg {
353 | t.Error("want equal")
354 | return
355 | }
356 | }
357 |
358 | // non-nil context that does not contain Logger
359 | {
360 | ctx := context.Background()
361 |
362 | lg, ctx2, ok := FromContextOrNew(ctx, _new)
363 | if lg != _newLogger {
364 | t.Error("want equal")
365 | return
366 | }
367 | if !ok {
368 | t.Error("want true")
369 | return
370 | }
371 | if ctx2 == ctx {
372 | t.Error("want not equal")
373 | return
374 | }
375 | lg2, ok := FromContext(ctx2)
376 | if !ok {
377 | t.Error("want true")
378 | return
379 | }
380 | if lg2 != lg {
381 | t.Error("want equal")
382 | return
383 | }
384 | }
385 |
386 | // non-nil context that contains nil Logger
387 | // this will never happen, see NewContext
388 |
389 | // non-nil context that contains non-nil Logger
390 | {
391 | _lg := New()
392 | ctx := context.WithValue(context.Background(), _loggerContextKey, _lg)
393 |
394 | lg, ctx2, ok := FromContextOrNew(ctx, _new)
395 | if lg != _lg {
396 | t.Error("want equal")
397 | return
398 | }
399 | if ok {
400 | t.Error("want false")
401 | return
402 | }
403 | if ctx2 != ctx {
404 | t.Error("want equal")
405 | return
406 | }
407 | lg2, ok := FromContext(ctx2)
408 | if !ok {
409 | t.Error("want true")
410 | return
411 | }
412 | if lg2 != lg {
413 | t.Error("want equal")
414 | return
415 | }
416 | }
417 | }
418 | }
419 |
420 | func Test_NewRequest_FromRequest(t *testing.T) {
421 | // no called NewContext yet
422 | {
423 | req := &http.Request{}
424 | req = req.WithContext(context.Background())
425 |
426 | lg, ok := FromRequest(req)
427 | want, wantOk := Logger(nil), false
428 | if lg != want || ok != wantOk {
429 | t.Errorf("have:(%#v, %t), want:(%#v, %t)", lg, ok, want, wantOk)
430 | return
431 | }
432 | }
433 |
434 | // called NewContext with nil Logger
435 | {
436 | req := &http.Request{}
437 | req = req.WithContext(context.Background())
438 |
439 | _lg := Logger(nil)
440 | req = NewRequest(req, _lg)
441 |
442 | lg, ok := FromRequest(req)
443 | want, wantOk := Logger(nil), false
444 | if lg != want || ok != wantOk {
445 | t.Errorf("have:(%#v, %t), want:(%#v, %t)", lg, ok, want, wantOk)
446 | return
447 | }
448 | }
449 |
450 | // called NewContext with non-nil Logger, nil context.Context
451 | {
452 | req := &http.Request{}
453 |
454 | _lg := New()
455 | req = NewRequest(req, _lg)
456 |
457 | lg, ok := FromRequest(req)
458 | want, wantOk := _lg, true
459 | if lg != want || ok != wantOk {
460 | t.Errorf("have:(%#v, %t), want:(%#v, %t)", lg, ok, want, wantOk)
461 | return
462 | }
463 | }
464 |
465 | // called NewContext with non-nil Logger, non-nil context.Context
466 | {
467 | req := &http.Request{}
468 | req = req.WithContext(context.Background())
469 |
470 | _lg := New()
471 | req = NewRequest(req, _lg)
472 |
473 | lg, ok := FromRequest(req)
474 | want, wantOk := _lg, true
475 | if lg != want || ok != wantOk {
476 | t.Errorf("have:(%#v, %t), want:(%#v, %t)", lg, ok, want, wantOk)
477 | return
478 | }
479 | }
480 | }
481 |
482 | func TestNewRequest(t *testing.T) {
483 | // nil Logger
484 | {
485 | req := &http.Request{}
486 |
487 | req2 := NewRequest(req, Logger(nil))
488 | if req != req2 {
489 | t.Error("want equal")
490 | return
491 | }
492 | }
493 |
494 | // nil context.Context
495 | {
496 | req := &http.Request{}
497 |
498 | _lg := New()
499 | req2 := NewRequest(req, _lg)
500 | if req == req2 {
501 | t.Error("want not equal")
502 | return
503 | }
504 |
505 | lg, ok := FromRequest(req2)
506 | want, wantOk := _lg, true
507 | if lg != want || ok != wantOk {
508 | t.Errorf("have:(%#v, %t), want:(%#v, %t)", lg, ok, want, wantOk)
509 | return
510 | }
511 | }
512 |
513 | // serially NewContext with same Logger
514 | {
515 | req := &http.Request{}
516 | req = req.WithContext(context.Background())
517 |
518 | _lg := New()
519 | req2 := NewRequest(req, _lg)
520 | req3 := NewRequest(req2, _lg)
521 | if req2 != req3 {
522 | t.Error("want equal")
523 | return
524 | }
525 | }
526 |
527 | // parallel NewContext with same Logger
528 | {
529 | req := &http.Request{}
530 | req = req.WithContext(context.Background())
531 |
532 | _lg := New()
533 | req2 := NewRequest(req, _lg)
534 | req3 := NewRequest(req, _lg)
535 | if req2 == req3 {
536 | t.Error("want not equal")
537 | return
538 | }
539 | }
540 | }
541 |
542 | func TestFromRequest(t *testing.T) {
543 | //// nil Request
544 | //{
545 | // var req *http.Request
546 | //
547 | // lg, ok := FromRequest(req)
548 | // want, wantOk := Logger(nil), false
549 | // if lg != want || ok != wantOk {
550 | // t.Errorf("have:(%#v, %t), want:(%#v, %t)", lg, ok, want, wantOk)
551 | // return
552 | // }
553 | //}
554 |
555 | // nil context
556 | {
557 | req := &http.Request{}
558 |
559 | lg, ok := FromRequest(req)
560 | want, wantOk := Logger(nil), false
561 | if lg != want || ok != wantOk {
562 | t.Errorf("have:(%#v, %t), want:(%#v, %t)", lg, ok, want, wantOk)
563 | return
564 | }
565 | }
566 |
567 | // non-nil context that does not contain Logger
568 | {
569 | req := &http.Request{}
570 |
571 | ctx := context.Background()
572 | req = req.WithContext(ctx)
573 |
574 | lg, ok := FromRequest(req)
575 | want, wantOk := Logger(nil), false
576 | if lg != want || ok != wantOk {
577 | t.Errorf("have:(%#v, %t), want:(%#v, %t)", lg, ok, want, wantOk)
578 | return
579 | }
580 | }
581 |
582 | // non-nil context that contains nil Logger
583 | // this will never happen, see NewContext
584 |
585 | // non-nil context that contains non-nil Logger
586 | {
587 | req := &http.Request{}
588 |
589 | _lg := New()
590 | ctx := context.WithValue(context.Background(), _loggerContextKey, _lg)
591 | req = req.WithContext(ctx)
592 |
593 | lg, ok := FromRequest(req)
594 | want, wantOk := _lg, true
595 | if lg != want || ok != wantOk {
596 | t.Errorf("have:(%#v, %t), want:(%#v, %t)", lg, ok, want, wantOk)
597 | return
598 | }
599 | }
600 | }
601 |
602 | func TestMustFromRequest(t *testing.T) {
603 | //// nil Request
604 | //func() {
605 | // var lg Logger
606 | //
607 | // defer func() {
608 | // if err := recover(); err != nil {
609 | // if lg != nil {
610 | // t.Error("lg must nil")
611 | // }
612 | // } else {
613 | // t.Error("must panic")
614 | // }
615 | // }()
616 | //
617 | // var req *http.Request
618 | //
619 | // lg = MustFromRequest(req)
620 | //}()
621 |
622 | // nil context
623 | func() {
624 | var lg Logger
625 |
626 | defer func() {
627 | if err := recover(); err != nil {
628 | if lg != nil {
629 | t.Error("lg must nil")
630 | }
631 | } else {
632 | t.Error("must panic")
633 | }
634 | }()
635 |
636 | req := &http.Request{}
637 |
638 | lg = MustFromRequest(req)
639 | }()
640 |
641 | // non-nil context that does not contain Logger
642 | func() {
643 | var lg Logger
644 |
645 | defer func() {
646 | if err := recover(); err != nil {
647 | if lg != nil {
648 | t.Error("lg must nil")
649 | }
650 | } else {
651 | t.Error("must panic")
652 | }
653 | }()
654 |
655 | req := &http.Request{}
656 |
657 | ctx := context.Background()
658 | req = req.WithContext(ctx)
659 |
660 | lg = MustFromRequest(req)
661 | }()
662 |
663 | // non-nil context that contains nil Logger
664 | // this will never happen, see NewContext
665 |
666 | // non-nil context that contains non-nil Logger
667 | func() {
668 | var lg Logger
669 |
670 | defer func() {
671 | if err := recover(); err != nil {
672 | t.Error("must not panic")
673 | } else {
674 | if lg == nil {
675 | t.Error("lg must not nil")
676 | }
677 | }
678 | }()
679 |
680 | req := &http.Request{}
681 |
682 | _lg := New()
683 | ctx := context.WithValue(context.Background(), _loggerContextKey, _lg)
684 | req = req.WithContext(ctx)
685 |
686 | lg = MustFromRequest(req)
687 | if lg != _lg {
688 | t.Error("want equal")
689 | return
690 | }
691 | }()
692 | }
693 |
694 | func TestFromRequestOrNew(t *testing.T) {
695 | // nil new function
696 | {
697 | //// nil Request
698 | //{
699 | // var req *http.Request
700 | //
701 | // lg, req2, ok := FromRequestOrNew(req, nil)
702 | // if lg == nil {
703 | // t.Error("want non-nil")
704 | // return
705 | // }
706 | // if !ok {
707 | // t.Error("want true")
708 | // return
709 | // }
710 | // if req2 == req {
711 | // t.Error("want not equal")
712 | // return
713 | // }
714 | // lg2, ok := FromRequest(req2)
715 | // if !ok {
716 | // t.Error("want true")
717 | // return
718 | // }
719 | // if lg2 != lg {
720 | // t.Error("want equal")
721 | // return
722 | // }
723 | //}
724 |
725 | // nil context
726 | {
727 | req := &http.Request{}
728 |
729 | lg, req2, ok := FromRequestOrNew(req, nil)
730 | if lg == nil {
731 | t.Error("want non-nil")
732 | return
733 | }
734 | if !ok {
735 | t.Error("want true")
736 | return
737 | }
738 | if req2 == req {
739 | t.Error("want not equal")
740 | return
741 | }
742 | lg2, ok := FromRequest(req2)
743 | if !ok {
744 | t.Error("want true")
745 | return
746 | }
747 | if lg2 != lg {
748 | t.Error("want equal")
749 | return
750 | }
751 | }
752 |
753 | // non-nil context that does not contain Logger
754 | {
755 | req := &http.Request{}
756 |
757 | ctx := context.Background()
758 | req = req.WithContext(ctx)
759 |
760 | lg, req2, ok := FromRequestOrNew(req, nil)
761 | if lg == nil {
762 | t.Error("want non-nil")
763 | return
764 | }
765 | if !ok {
766 | t.Error("want true")
767 | return
768 | }
769 | if req2 == req {
770 | t.Error("want not equal")
771 | return
772 | }
773 | lg2, ok := FromRequest(req2)
774 | if !ok {
775 | t.Error("want true")
776 | return
777 | }
778 | if lg2 != lg {
779 | t.Error("want equal")
780 | return
781 | }
782 | }
783 |
784 | // non-nil context that contains nil Logger
785 | // this will never happen, see NewContext
786 |
787 | // non-nil context that contains non-nil Logger
788 | {
789 | req := &http.Request{}
790 |
791 | _lg := New()
792 | ctx := context.WithValue(context.Background(), _loggerContextKey, _lg)
793 | req = req.WithContext(ctx)
794 |
795 | lg, req2, ok := FromRequestOrNew(req, nil)
796 | if lg != _lg {
797 | t.Error("want equal")
798 | return
799 | }
800 | if ok {
801 | t.Error("want false")
802 | return
803 | }
804 | if req2 != req {
805 | t.Error("want equal")
806 | return
807 | }
808 | lg2, ok := FromRequest(req2)
809 | if !ok {
810 | t.Error("want true")
811 | return
812 | }
813 | if lg2 != lg {
814 | t.Error("want equal")
815 | return
816 | }
817 | }
818 | }
819 |
820 | // non-nil new function
821 | {
822 | var _newLogger = New()
823 | var _new = func() Logger { return _newLogger }
824 |
825 | //// nil Request
826 | //{
827 | // var req *http.Request
828 | //
829 | // lg, req2, ok := FromRequestOrNew(req, _new)
830 | // if lg != _newLogger {
831 | // t.Error("want equal")
832 | // return
833 | // }
834 | // if !ok {
835 | // t.Error("want true")
836 | // return
837 | // }
838 | // if req2 == req {
839 | // t.Error("want not equal")
840 | // return
841 | // }
842 | // lg2, ok := FromRequest(req2)
843 | // if !ok {
844 | // t.Error("want true")
845 | // return
846 | // }
847 | // if lg2 != lg {
848 | // t.Error("want equal")
849 | // return
850 | // }
851 | //}
852 |
853 | // nil context
854 | {
855 | req := &http.Request{}
856 |
857 | lg, req2, ok := FromRequestOrNew(req, _new)
858 | if lg != _newLogger {
859 | t.Error("want equal")
860 | return
861 | }
862 | if !ok {
863 | t.Error("want true")
864 | return
865 | }
866 | if req2 == req {
867 | t.Error("want not equal")
868 | return
869 | }
870 | lg2, ok := FromRequest(req2)
871 | if !ok {
872 | t.Error("want true")
873 | return
874 | }
875 | if lg2 != lg {
876 | t.Error("want equal")
877 | return
878 | }
879 | }
880 |
881 | // non-nil context that does not contain Logger
882 | {
883 | req := &http.Request{}
884 |
885 | ctx := context.Background()
886 | req = req.WithContext(ctx)
887 |
888 | lg, req2, ok := FromRequestOrNew(req, _new)
889 | if lg != _newLogger {
890 | t.Error("want equal")
891 | return
892 | }
893 | if !ok {
894 | t.Error("want true")
895 | return
896 | }
897 | if req2 == req {
898 | t.Error("want not equal")
899 | return
900 | }
901 | lg2, ok := FromRequest(req2)
902 | if !ok {
903 | t.Error("want true")
904 | return
905 | }
906 | if lg2 != lg {
907 | t.Error("want equal")
908 | return
909 | }
910 | }
911 |
912 | // non-nil context that contains nil Logger
913 | // this will never happen, see NewContext
914 |
915 | // non-nil context that contains non-nil Logger
916 | {
917 | req := &http.Request{}
918 |
919 | _lg := New()
920 | ctx := context.WithValue(context.Background(), _loggerContextKey, _lg)
921 | req = req.WithContext(ctx)
922 |
923 | lg, req2, ok := FromRequestOrNew(req, _new)
924 | if lg != _lg {
925 | t.Error("want equal")
926 | return
927 | }
928 | if ok {
929 | t.Error("want false")
930 | return
931 | }
932 | if req2 != req {
933 | t.Error("want equal")
934 | return
935 | }
936 | lg2, ok := FromRequest(req2)
937 | if !ok {
938 | t.Error("want true")
939 | return
940 | }
941 | if lg2 != lg {
942 | t.Error("want equal")
943 | return
944 | }
945 | }
946 | }
947 | }
948 |
--------------------------------------------------------------------------------
/shortcut_without_logger_test.go:
--------------------------------------------------------------------------------
1 | // <-- to adjust the line number
2 | package log
3 |
4 | import (
5 | "bytes"
6 | "context"
7 | "encoding/json"
8 | "reflect"
9 | "strings"
10 | "testing"
11 | )
12 |
13 | func testFatalContextLocationWithoutLogger() {
14 | FatalContext(testWithoutLoggerContext, "msg")
15 | }
16 | func testErrorContextLocationWithoutLogger() {
17 | ErrorContext(testWithoutLoggerContext, "msg")
18 | }
19 | func testWarnContextLocationWithoutLogger() {
20 | WarnContext(testWithoutLoggerContext, "msg")
21 | }
22 | func testInfoContextLocationWithoutLogger() {
23 | InfoContext(testWithoutLoggerContext, "msg")
24 | }
25 | func testDebugContextLocationWithoutLogger() {
26 | DebugContext(testWithoutLoggerContext, "msg")
27 | }
28 | func testOutputContextLocationWithoutLogger() {
29 | OutputContext(testWithoutLoggerContext, 0, ErrorLevel, "msg")
30 | }
31 |
32 | var testWithoutLoggerContext = context.Background()
33 |
34 | func setWithoutLoggerContextOptionsToDefault() {
35 | SetFormatter(TextFormatter)
36 | SetOutput(ConcurrentStdout)
37 | SetLevel(DebugLevel)
38 | }
39 |
40 | //type locationFormat struct{}
41 | //
42 | //func (locationFormat) Format(entry *Entry) ([]byte, error) {
43 | // return []byte(entry.Location), nil
44 | //}
45 |
46 | func TestLocationContextWithoutLoggerWithoutLogger(t *testing.T) {
47 | defer setWithoutLoggerContextOptionsToDefault()
48 |
49 | // fatal
50 | {
51 | var buf bytes.Buffer
52 | SetOutput(ConcurrentWriter(&buf))
53 | SetFormatter(locationFormat{})
54 |
55 | testFatalContextLocationWithoutLogger()
56 |
57 | location := buf.String()
58 | switch {
59 | case location == "log.testFatalContextLocationWithoutLogger(github.com/chanxuehong/log/shortcut_without_logger_test.go:14)":
60 | case strings.HasPrefix(location, "log.testFatalContextLocationWithoutLogger(") && strings.HasSuffix(location, "/log/shortcut_without_logger_test.go:14)"):
61 | default:
62 | t.Errorf("not expected location: %s", location)
63 | return
64 | }
65 | }
66 |
67 | // error
68 | {
69 | var buf bytes.Buffer
70 | SetOutput(ConcurrentWriter(&buf))
71 | SetFormatter(locationFormat{})
72 |
73 | testErrorContextLocationWithoutLogger()
74 |
75 | location := buf.String()
76 | switch {
77 | case location == "log.testErrorContextLocationWithoutLogger(github.com/chanxuehong/log/shortcut_without_logger_test.go:17)":
78 | case strings.HasPrefix(location, "log.testErrorContextLocationWithoutLogger(") && strings.HasSuffix(location, "/log/shortcut_without_logger_test.go:17)"):
79 | default:
80 | t.Errorf("not expected location: %s", location)
81 | return
82 | }
83 | }
84 |
85 | // warning
86 | {
87 | var buf bytes.Buffer
88 | SetOutput(ConcurrentWriter(&buf))
89 | SetFormatter(locationFormat{})
90 |
91 | testWarnContextLocationWithoutLogger()
92 |
93 | location := buf.String()
94 | switch {
95 | case location == "log.testWarnContextLocationWithoutLogger(github.com/chanxuehong/log/shortcut_without_logger_test.go:20)":
96 | case strings.HasPrefix(location, "log.testWarnContextLocationWithoutLogger(") && strings.HasSuffix(location, "/log/shortcut_without_logger_test.go:20)"):
97 | default:
98 | t.Errorf("not expected location: %s", location)
99 | return
100 | }
101 | }
102 |
103 | // info
104 | {
105 | var buf bytes.Buffer
106 | SetOutput(ConcurrentWriter(&buf))
107 | SetFormatter(locationFormat{})
108 |
109 | testInfoContextLocationWithoutLogger()
110 |
111 | location := buf.String()
112 | switch {
113 | case location == "log.testInfoContextLocationWithoutLogger(github.com/chanxuehong/log/shortcut_without_logger_test.go:23)":
114 | case strings.HasPrefix(location, "log.testInfoContextLocationWithoutLogger(") && strings.HasSuffix(location, "/log/shortcut_without_logger_test.go:23)"):
115 | default:
116 | t.Errorf("not expected location: %s", location)
117 | return
118 | }
119 | }
120 |
121 | // debug
122 | {
123 | var buf bytes.Buffer
124 | SetOutput(ConcurrentWriter(&buf))
125 | SetFormatter(locationFormat{})
126 |
127 | testDebugContextLocationWithoutLogger()
128 |
129 | location := buf.String()
130 | switch {
131 | case location == "log.testDebugContextLocationWithoutLogger(github.com/chanxuehong/log/shortcut_without_logger_test.go:26)":
132 | case strings.HasPrefix(location, "log.testDebugContextLocationWithoutLogger(") && strings.HasSuffix(location, "/log/shortcut_without_logger_test.go:26)"):
133 | default:
134 | t.Errorf("not expected location: %s", location)
135 | return
136 | }
137 | }
138 |
139 | // output
140 | {
141 | var buf bytes.Buffer
142 | SetOutput(ConcurrentWriter(&buf))
143 | SetFormatter(locationFormat{})
144 |
145 | testOutputContextLocationWithoutLogger()
146 |
147 | location := buf.String()
148 | switch {
149 | case location == "log.testOutputContextLocationWithoutLogger(github.com/chanxuehong/log/shortcut_without_logger_test.go:29)":
150 | case strings.HasPrefix(location, "log.testOutputContextLocationWithoutLogger(") && strings.HasSuffix(location, "/log/shortcut_without_logger_test.go:29)"):
151 | default:
152 | t.Errorf("not expected location: %s", location)
153 | return
154 | }
155 | }
156 | }
157 |
158 | //type testJsonFormatter struct{}
159 | //
160 | //func (testJsonFormatter) Format(entry *Entry) ([]byte, error) {
161 | // // ignored Entry.Location
162 | //
163 | // // check entry.Time
164 | // t := time.Now()
165 | // d := t.Sub(entry.Time)
166 | // if d > time.Millisecond || d < -time.Millisecond {
167 | // return nil, fmt.Errorf("time mismatch, have:%v, want:%v", entry.Time, t)
168 | // }
169 | //
170 | // // ignored entry.Time
171 | // m := make(map[string]interface{})
172 | // prefixFieldClashes(entry.Fields)
173 | // m[fieldKeyTraceId] = entry.TraceId
174 | // m[fieldKeyLevel] = entry.Level.String()
175 | // m[fieldKeyMessage] = entry.Message
176 | // for k, v := range entry.Fields {
177 | // m[k] = v
178 | // }
179 | // return json.Marshal(m)
180 | //}
181 |
182 | func TestFatalContextWithoutLogger(t *testing.T) {
183 | defer setWithoutLoggerContextOptionsToDefault()
184 |
185 | var buf bytes.Buffer
186 | SetOutput(ConcurrentWriter(&buf))
187 | SetFormatter(testJsonFormatter{})
188 |
189 | FatalContext(testWithoutLoggerContext, "fatal-msg", "field1-key", "field1-value", "field2-key", "field2-value")
190 | data := buf.Bytes()
191 |
192 | var have map[string]interface{}
193 | if err := json.Unmarshal(data, &have); err != nil {
194 | t.Error(err.Error())
195 | return
196 | }
197 | want := map[string]interface{}{
198 | fieldKeyTraceId: "",
199 | fieldKeyLevel: FatalLevelString,
200 | fieldKeyMessage: "fatal-msg",
201 | "field1-key": "field1-value",
202 | "field2-key": "field2-value",
203 | }
204 | if !reflect.DeepEqual(have, want) {
205 | t.Errorf("\nhave:%v\nwant:%v", have, want)
206 | return
207 | }
208 | }
209 |
210 | func TestErrorContextWithoutLogger(t *testing.T) {
211 | defer setWithoutLoggerContextOptionsToDefault()
212 |
213 | var buf bytes.Buffer
214 | SetOutput(ConcurrentWriter(&buf))
215 | SetFormatter(testJsonFormatter{})
216 |
217 | ErrorContext(testWithoutLoggerContext, "error-msg", "field1-key", "field1-value", "field2-key", "field2-value")
218 | data := buf.Bytes()
219 |
220 | var have map[string]interface{}
221 | if err := json.Unmarshal(data, &have); err != nil {
222 | t.Error(err.Error())
223 | return
224 | }
225 | want := map[string]interface{}{
226 | fieldKeyTraceId: "",
227 | fieldKeyLevel: ErrorLevelString,
228 | fieldKeyMessage: "error-msg",
229 | "field1-key": "field1-value",
230 | "field2-key": "field2-value",
231 | }
232 | if !reflect.DeepEqual(have, want) {
233 | t.Errorf("\nhave:%v\nwant:%v", have, want)
234 | return
235 | }
236 | }
237 |
238 | func TestWarnContextWithoutLogger(t *testing.T) {
239 | defer setWithoutLoggerContextOptionsToDefault()
240 |
241 | var buf bytes.Buffer
242 | SetOutput(ConcurrentWriter(&buf))
243 | SetFormatter(testJsonFormatter{})
244 |
245 | WarnContext(testWithoutLoggerContext, "warning-msg", "field1-key", "field1-value", "field2-key", "field2-value")
246 | data := buf.Bytes()
247 |
248 | var have map[string]interface{}
249 | if err := json.Unmarshal(data, &have); err != nil {
250 | t.Error(err.Error())
251 | return
252 | }
253 | want := map[string]interface{}{
254 | fieldKeyTraceId: "",
255 | fieldKeyLevel: WarnLevelString,
256 | fieldKeyMessage: "warning-msg",
257 | "field1-key": "field1-value",
258 | "field2-key": "field2-value",
259 | }
260 | if !reflect.DeepEqual(have, want) {
261 | t.Errorf("\nhave:%v\nwant:%v", have, want)
262 | return
263 | }
264 | }
265 |
266 | func TestInfoContextWithoutLogger(t *testing.T) {
267 | defer setWithoutLoggerContextOptionsToDefault()
268 |
269 | var buf bytes.Buffer
270 | SetOutput(ConcurrentWriter(&buf))
271 | SetFormatter(testJsonFormatter{})
272 |
273 | InfoContext(testWithoutLoggerContext, "info-msg", "field1-key", "field1-value", "field2-key", "field2-value")
274 | data := buf.Bytes()
275 |
276 | var have map[string]interface{}
277 | if err := json.Unmarshal(data, &have); err != nil {
278 | t.Error(err.Error())
279 | return
280 | }
281 | want := map[string]interface{}{
282 | fieldKeyTraceId: "",
283 | fieldKeyLevel: InfoLevelString,
284 | fieldKeyMessage: "info-msg",
285 | "field1-key": "field1-value",
286 | "field2-key": "field2-value",
287 | }
288 | if !reflect.DeepEqual(have, want) {
289 | t.Errorf("\nhave:%v\nwant:%v", have, want)
290 | return
291 | }
292 | }
293 |
294 | func TestDebugContextWithoutLogger(t *testing.T) {
295 | defer setWithoutLoggerContextOptionsToDefault()
296 |
297 | var buf bytes.Buffer
298 | SetOutput(ConcurrentWriter(&buf))
299 | SetFormatter(testJsonFormatter{})
300 |
301 | DebugContext(testWithoutLoggerContext, "debug-msg", "field1-key", "field1-value", "field2-key", "field2-value")
302 | data := buf.Bytes()
303 |
304 | var have map[string]interface{}
305 | if err := json.Unmarshal(data, &have); err != nil {
306 | t.Error(err.Error())
307 | return
308 | }
309 | want := map[string]interface{}{
310 | fieldKeyTraceId: "",
311 | fieldKeyLevel: DebugLevelString,
312 | fieldKeyMessage: "debug-msg",
313 | "field1-key": "field1-value",
314 | "field2-key": "field2-value",
315 | }
316 | if !reflect.DeepEqual(have, want) {
317 | t.Errorf("\nhave:%v\nwant:%v", have, want)
318 | return
319 | }
320 | }
321 |
322 | func TestOutputContextWithoutLogger(t *testing.T) {
323 | defer setWithoutLoggerContextOptionsToDefault()
324 |
325 | // fatal
326 | {
327 | var buf bytes.Buffer
328 | SetOutput(ConcurrentWriter(&buf))
329 | SetFormatter(testJsonFormatter{})
330 |
331 | OutputContext(testWithoutLoggerContext, 0, FatalLevel, "fatal-msg", "field1-key", "field1-value", "field2-key", "field2-value")
332 | data := buf.Bytes()
333 |
334 | var have map[string]interface{}
335 | if err := json.Unmarshal(data, &have); err != nil {
336 | t.Error(err.Error())
337 | return
338 | }
339 | want := map[string]interface{}{
340 | fieldKeyTraceId: "",
341 | fieldKeyLevel: FatalLevelString,
342 | fieldKeyMessage: "fatal-msg",
343 | "field1-key": "field1-value",
344 | "field2-key": "field2-value",
345 | }
346 | if !reflect.DeepEqual(have, want) {
347 | t.Errorf("\nhave:%v\nwant:%v", have, want)
348 | return
349 | }
350 | }
351 | // error
352 | {
353 | var buf bytes.Buffer
354 | SetOutput(ConcurrentWriter(&buf))
355 | SetFormatter(testJsonFormatter{})
356 |
357 | OutputContext(testWithoutLoggerContext, 0, ErrorLevel, "error-msg", "field1-key", "field1-value", "field2-key", "field2-value")
358 | data := buf.Bytes()
359 |
360 | var have map[string]interface{}
361 | if err := json.Unmarshal(data, &have); err != nil {
362 | t.Error(err.Error())
363 | return
364 | }
365 | want := map[string]interface{}{
366 | fieldKeyTraceId: "",
367 | fieldKeyLevel: ErrorLevelString,
368 | fieldKeyMessage: "error-msg",
369 | "field1-key": "field1-value",
370 | "field2-key": "field2-value",
371 | }
372 | if !reflect.DeepEqual(have, want) {
373 | t.Errorf("\nhave:%v\nwant:%v", have, want)
374 | return
375 | }
376 | }
377 | // warning
378 | {
379 | var buf bytes.Buffer
380 | SetOutput(ConcurrentWriter(&buf))
381 | SetFormatter(testJsonFormatter{})
382 |
383 | OutputContext(testWithoutLoggerContext, 0, WarnLevel, "warning-msg", "field1-key", "field1-value", "field2-key", "field2-value")
384 | data := buf.Bytes()
385 |
386 | var have map[string]interface{}
387 | if err := json.Unmarshal(data, &have); err != nil {
388 | t.Error(err.Error())
389 | return
390 | }
391 | want := map[string]interface{}{
392 | fieldKeyTraceId: "",
393 | fieldKeyLevel: WarnLevelString,
394 | fieldKeyMessage: "warning-msg",
395 | "field1-key": "field1-value",
396 | "field2-key": "field2-value",
397 | }
398 | if !reflect.DeepEqual(have, want) {
399 | t.Errorf("\nhave:%v\nwant:%v", have, want)
400 | return
401 | }
402 | }
403 | // info
404 | {
405 | var buf bytes.Buffer
406 | SetOutput(ConcurrentWriter(&buf))
407 | SetFormatter(testJsonFormatter{})
408 |
409 | OutputContext(testWithoutLoggerContext, 0, InfoLevel, "info-msg", "field1-key", "field1-value", "field2-key", "field2-value")
410 | data := buf.Bytes()
411 |
412 | var have map[string]interface{}
413 | if err := json.Unmarshal(data, &have); err != nil {
414 | t.Error(err.Error())
415 | return
416 | }
417 | want := map[string]interface{}{
418 | fieldKeyTraceId: "",
419 | fieldKeyLevel: InfoLevelString,
420 | fieldKeyMessage: "info-msg",
421 | "field1-key": "field1-value",
422 | "field2-key": "field2-value",
423 | }
424 | if !reflect.DeepEqual(have, want) {
425 | t.Errorf("\nhave:%v\nwant:%v", have, want)
426 | return
427 | }
428 | }
429 | // debug
430 | {
431 | var buf bytes.Buffer
432 | SetOutput(ConcurrentWriter(&buf))
433 | SetFormatter(testJsonFormatter{})
434 |
435 | OutputContext(testWithoutLoggerContext, 0, DebugLevel, "debug-msg", "field1-key", "field1-value", "field2-key", "field2-value")
436 | data := buf.Bytes()
437 |
438 | var have map[string]interface{}
439 | if err := json.Unmarshal(data, &have); err != nil {
440 | t.Error(err.Error())
441 | return
442 | }
443 | want := map[string]interface{}{
444 | fieldKeyTraceId: "",
445 | fieldKeyLevel: DebugLevelString,
446 | fieldKeyMessage: "debug-msg",
447 | "field1-key": "field1-value",
448 | "field2-key": "field2-value",
449 | }
450 | if !reflect.DeepEqual(have, want) {
451 | t.Errorf("\nhave:%v\nwant:%v", have, want)
452 | return
453 | }
454 | }
455 | // fatal - 1
456 | {
457 | var buf bytes.Buffer
458 | SetOutput(ConcurrentWriter(&buf))
459 | SetFormatter(testJsonFormatter{})
460 |
461 | OutputContext(testWithoutLoggerContext, 0, FatalLevel-1, "debug-msg", "field1-key", "field1-value", "field2-key", "field2-value")
462 | data := buf.Bytes()
463 | if len(data) != 0 {
464 | t.Errorf("want empty, but now is: %s", data)
465 | return
466 | }
467 | }
468 | // debug + 1
469 | {
470 | var buf bytes.Buffer
471 | SetOutput(ConcurrentWriter(&buf))
472 | SetFormatter(testJsonFormatter{})
473 |
474 | OutputContext(testWithoutLoggerContext, 0, DebugLevel+1, "debug-msg", "field1-key", "field1-value", "field2-key", "field2-value")
475 | data := buf.Bytes()
476 | if len(data) != 0 {
477 | t.Errorf("want empty, but now is: %s", data)
478 | return
479 | }
480 | }
481 | }
482 |
483 | func TestWithFieldContextWithoutLogger(t *testing.T) {
484 | defer setWithoutLoggerContextOptionsToDefault()
485 |
486 | var buf bytes.Buffer
487 | SetOutput(ConcurrentWriter(&buf))
488 | SetFormatter(testJsonFormatter{})
489 |
490 | {
491 | l := WithFieldContext(testWithoutLoggerContext, "field100-key", "field100-value")
492 | l.Error("error-msg", "field1-key", "field1-value", "field2-key", "field2-value")
493 | data := buf.Bytes()
494 |
495 | var have map[string]interface{}
496 | if err := json.Unmarshal(data, &have); err != nil {
497 | t.Error(err.Error())
498 | return
499 | }
500 | want := map[string]interface{}{
501 | fieldKeyTraceId: "",
502 | fieldKeyLevel: ErrorLevelString,
503 | fieldKeyMessage: "error-msg",
504 | "field1-key": "field1-value",
505 | "field2-key": "field2-value",
506 | "field100-key": "field100-value",
507 | }
508 | if !reflect.DeepEqual(have, want) {
509 | t.Errorf("\nhave:%v\nwant:%v", have, want)
510 | return
511 | }
512 | }
513 |
514 | buf.Reset()
515 |
516 | // WithField cannot affect original logger
517 | {
518 | ErrorContext(testWithoutLoggerContext, "error-msg", "field1-key", "field1-value", "field2-key", "field2-value")
519 | data := buf.Bytes()
520 |
521 | var have map[string]interface{}
522 | if err := json.Unmarshal(data, &have); err != nil {
523 | t.Error(err.Error())
524 | return
525 | }
526 | want := map[string]interface{}{
527 | fieldKeyTraceId: "",
528 | fieldKeyLevel: ErrorLevelString,
529 | fieldKeyMessage: "error-msg",
530 | "field1-key": "field1-value",
531 | "field2-key": "field2-value",
532 | }
533 | if !reflect.DeepEqual(have, want) {
534 | t.Errorf("\nhave:%v\nwant:%v", have, want)
535 | return
536 | }
537 | }
538 | }
539 |
540 | func TestWithFieldsContextWithoutLogger(t *testing.T) {
541 | defer setWithoutLoggerContextOptionsToDefault()
542 |
543 | var buf bytes.Buffer
544 | SetOutput(ConcurrentWriter(&buf))
545 | SetFormatter(testJsonFormatter{})
546 |
547 | {
548 | l := WithFieldsContext(testWithoutLoggerContext, "field100-key", "field100-value", "field200-key", "field200-value")
549 | l.Error("error-msg", "field1-key", "field1-value", "field2-key", "field2-value")
550 | data := buf.Bytes()
551 |
552 | var have map[string]interface{}
553 | if err := json.Unmarshal(data, &have); err != nil {
554 | t.Error(err.Error())
555 | return
556 | }
557 | want := map[string]interface{}{
558 | fieldKeyTraceId: "",
559 | fieldKeyLevel: ErrorLevelString,
560 | fieldKeyMessage: "error-msg",
561 | "field1-key": "field1-value",
562 | "field2-key": "field2-value",
563 | "field100-key": "field100-value",
564 | "field200-key": "field200-value",
565 | }
566 | if !reflect.DeepEqual(have, want) {
567 | t.Errorf("\nhave:%v\nwant:%v", have, want)
568 | return
569 | }
570 | }
571 |
572 | buf.Reset()
573 |
574 | // WithFields cannot affect original logger
575 | {
576 | ErrorContext(testWithoutLoggerContext, "error-msg", "field1-key", "field1-value", "field2-key", "field2-value")
577 | data := buf.Bytes()
578 |
579 | var have map[string]interface{}
580 | if err := json.Unmarshal(data, &have); err != nil {
581 | t.Error(err.Error())
582 | return
583 | }
584 | want := map[string]interface{}{
585 | fieldKeyTraceId: "",
586 | fieldKeyLevel: ErrorLevelString,
587 | fieldKeyMessage: "error-msg",
588 | "field1-key": "field1-value",
589 | "field2-key": "field2-value",
590 | }
591 | if !reflect.DeepEqual(have, want) {
592 | t.Errorf("\nhave:%v\nwant:%v", have, want)
593 | return
594 | }
595 | }
596 | }
597 |
598 | func TestLeveledOutputContextWithoutLogger(t *testing.T) {
599 | defer setWithoutLoggerContextOptionsToDefault()
600 |
601 | // fatal
602 | {
603 | SetLevel(FatalLevel)
604 |
605 | // fatal
606 | {
607 | var buf bytes.Buffer
608 | SetOutput(ConcurrentWriter(&buf))
609 | SetFormatter(testJsonFormatter{})
610 |
611 | OutputContext(testWithoutLoggerContext, 0, FatalLevel, "fatal-msg", "field1-key", "field1-value", "field2-key", "field2-value")
612 | data := buf.Bytes()
613 |
614 | var have map[string]interface{}
615 | if err := json.Unmarshal(data, &have); err != nil {
616 | t.Error(err.Error())
617 | return
618 | }
619 | want := map[string]interface{}{
620 | fieldKeyTraceId: "",
621 | fieldKeyLevel: FatalLevelString,
622 | fieldKeyMessage: "fatal-msg",
623 | "field1-key": "field1-value",
624 | "field2-key": "field2-value",
625 | }
626 | if !reflect.DeepEqual(have, want) {
627 | t.Errorf("\nhave:%v\nwant:%v", have, want)
628 | return
629 | }
630 | }
631 | // error
632 | {
633 | var buf bytes.Buffer
634 | SetOutput(ConcurrentWriter(&buf))
635 | SetFormatter(testJsonFormatter{})
636 |
637 | OutputContext(testWithoutLoggerContext, 0, ErrorLevel, "error-msg", "field1-key", "field1-value", "field2-key", "field2-value")
638 | data := buf.Bytes()
639 | if len(data) != 0 {
640 | t.Errorf("want empty, but now is: %s", data)
641 | return
642 | }
643 | }
644 | // warning
645 | {
646 | var buf bytes.Buffer
647 | SetOutput(ConcurrentWriter(&buf))
648 | SetFormatter(testJsonFormatter{})
649 |
650 | OutputContext(testWithoutLoggerContext, 0, WarnLevel, "warning-msg", "field1-key", "field1-value", "field2-key", "field2-value")
651 | data := buf.Bytes()
652 | if len(data) != 0 {
653 | t.Errorf("want empty, but now is: %s", data)
654 | return
655 | }
656 | }
657 | // info
658 | {
659 | var buf bytes.Buffer
660 | SetOutput(ConcurrentWriter(&buf))
661 | SetFormatter(testJsonFormatter{})
662 |
663 | OutputContext(testWithoutLoggerContext, 0, InfoLevel, "info-msg", "field1-key", "field1-value", "field2-key", "field2-value")
664 | data := buf.Bytes()
665 | if len(data) != 0 {
666 | t.Errorf("want empty, but now is: %s", data)
667 | return
668 | }
669 | }
670 | // debug
671 | {
672 | var buf bytes.Buffer
673 | SetOutput(ConcurrentWriter(&buf))
674 | SetFormatter(testJsonFormatter{})
675 |
676 | OutputContext(testWithoutLoggerContext, 0, DebugLevel, "debug-msg", "field1-key", "field1-value", "field2-key", "field2-value")
677 | data := buf.Bytes()
678 | if len(data) != 0 {
679 | t.Errorf("want empty, but now is: %s", data)
680 | return
681 | }
682 | }
683 | }
684 |
685 | // error
686 | {
687 | SetLevel(ErrorLevel)
688 |
689 | // fatal
690 | {
691 | var buf bytes.Buffer
692 | SetOutput(ConcurrentWriter(&buf))
693 | SetFormatter(testJsonFormatter{})
694 |
695 | OutputContext(testWithoutLoggerContext, 0, FatalLevel, "fatal-msg", "field1-key", "field1-value", "field2-key", "field2-value")
696 | data := buf.Bytes()
697 |
698 | var have map[string]interface{}
699 | if err := json.Unmarshal(data, &have); err != nil {
700 | t.Error(err.Error())
701 | return
702 | }
703 | want := map[string]interface{}{
704 | fieldKeyTraceId: "",
705 | fieldKeyLevel: FatalLevelString,
706 | fieldKeyMessage: "fatal-msg",
707 | "field1-key": "field1-value",
708 | "field2-key": "field2-value",
709 | }
710 | if !reflect.DeepEqual(have, want) {
711 | t.Errorf("\nhave:%v\nwant:%v", have, want)
712 | return
713 | }
714 | }
715 | // error
716 | {
717 | var buf bytes.Buffer
718 | SetOutput(ConcurrentWriter(&buf))
719 | SetFormatter(testJsonFormatter{})
720 |
721 | OutputContext(testWithoutLoggerContext, 0, ErrorLevel, "error-msg", "field1-key", "field1-value", "field2-key", "field2-value")
722 | data := buf.Bytes()
723 |
724 | var have map[string]interface{}
725 | if err := json.Unmarshal(data, &have); err != nil {
726 | t.Error(err.Error())
727 | return
728 | }
729 | want := map[string]interface{}{
730 | fieldKeyTraceId: "",
731 | fieldKeyLevel: ErrorLevelString,
732 | fieldKeyMessage: "error-msg",
733 | "field1-key": "field1-value",
734 | "field2-key": "field2-value",
735 | }
736 | if !reflect.DeepEqual(have, want) {
737 | t.Errorf("\nhave:%v\nwant:%v", have, want)
738 | return
739 | }
740 | }
741 | // warning
742 | {
743 | var buf bytes.Buffer
744 | SetOutput(ConcurrentWriter(&buf))
745 | SetFormatter(testJsonFormatter{})
746 |
747 | OutputContext(testWithoutLoggerContext, 0, WarnLevel, "warning-msg", "field1-key", "field1-value", "field2-key", "field2-value")
748 | data := buf.Bytes()
749 | if len(data) != 0 {
750 | t.Errorf("want empty, but now is: %s", data)
751 | return
752 | }
753 | }
754 | // info
755 | {
756 | var buf bytes.Buffer
757 | SetOutput(ConcurrentWriter(&buf))
758 | SetFormatter(testJsonFormatter{})
759 |
760 | OutputContext(testWithoutLoggerContext, 0, InfoLevel, "info-msg", "field1-key", "field1-value", "field2-key", "field2-value")
761 | data := buf.Bytes()
762 | if len(data) != 0 {
763 | t.Errorf("want empty, but now is: %s", data)
764 | return
765 | }
766 | }
767 | // debug
768 | {
769 | var buf bytes.Buffer
770 | SetOutput(ConcurrentWriter(&buf))
771 | SetFormatter(testJsonFormatter{})
772 |
773 | OutputContext(testWithoutLoggerContext, 0, DebugLevel, "debug-msg", "field1-key", "field1-value", "field2-key", "field2-value")
774 | data := buf.Bytes()
775 | if len(data) != 0 {
776 | t.Errorf("want empty, but now is: %s", data)
777 | return
778 | }
779 | }
780 | }
781 |
782 | // warning
783 | {
784 | SetLevel(WarnLevel)
785 |
786 | // fatal
787 | {
788 | var buf bytes.Buffer
789 | SetOutput(ConcurrentWriter(&buf))
790 | SetFormatter(testJsonFormatter{})
791 |
792 | OutputContext(testWithoutLoggerContext, 0, FatalLevel, "fatal-msg", "field1-key", "field1-value", "field2-key", "field2-value")
793 | data := buf.Bytes()
794 |
795 | var have map[string]interface{}
796 | if err := json.Unmarshal(data, &have); err != nil {
797 | t.Error(err.Error())
798 | return
799 | }
800 | want := map[string]interface{}{
801 | fieldKeyTraceId: "",
802 | fieldKeyLevel: FatalLevelString,
803 | fieldKeyMessage: "fatal-msg",
804 | "field1-key": "field1-value",
805 | "field2-key": "field2-value",
806 | }
807 | if !reflect.DeepEqual(have, want) {
808 | t.Errorf("\nhave:%v\nwant:%v", have, want)
809 | return
810 | }
811 | }
812 | // error
813 | {
814 | var buf bytes.Buffer
815 | SetOutput(ConcurrentWriter(&buf))
816 | SetFormatter(testJsonFormatter{})
817 |
818 | OutputContext(testWithoutLoggerContext, 0, ErrorLevel, "error-msg", "field1-key", "field1-value", "field2-key", "field2-value")
819 | data := buf.Bytes()
820 |
821 | var have map[string]interface{}
822 | if err := json.Unmarshal(data, &have); err != nil {
823 | t.Error(err.Error())
824 | return
825 | }
826 | want := map[string]interface{}{
827 | fieldKeyTraceId: "",
828 | fieldKeyLevel: ErrorLevelString,
829 | fieldKeyMessage: "error-msg",
830 | "field1-key": "field1-value",
831 | "field2-key": "field2-value",
832 | }
833 | if !reflect.DeepEqual(have, want) {
834 | t.Errorf("\nhave:%v\nwant:%v", have, want)
835 | return
836 | }
837 | }
838 | // warning
839 | {
840 | var buf bytes.Buffer
841 | SetOutput(ConcurrentWriter(&buf))
842 | SetFormatter(testJsonFormatter{})
843 |
844 | OutputContext(testWithoutLoggerContext, 0, WarnLevel, "warning-msg", "field1-key", "field1-value", "field2-key", "field2-value")
845 | data := buf.Bytes()
846 |
847 | var have map[string]interface{}
848 | if err := json.Unmarshal(data, &have); err != nil {
849 | t.Error(err.Error())
850 | return
851 | }
852 | want := map[string]interface{}{
853 | fieldKeyTraceId: "",
854 | fieldKeyLevel: WarnLevelString,
855 | fieldKeyMessage: "warning-msg",
856 | "field1-key": "field1-value",
857 | "field2-key": "field2-value",
858 | }
859 | if !reflect.DeepEqual(have, want) {
860 | t.Errorf("\nhave:%v\nwant:%v", have, want)
861 | return
862 | }
863 | }
864 | // info
865 | {
866 | var buf bytes.Buffer
867 | SetOutput(ConcurrentWriter(&buf))
868 | SetFormatter(testJsonFormatter{})
869 |
870 | OutputContext(testWithoutLoggerContext, 0, InfoLevel, "info-msg", "field1-key", "field1-value", "field2-key", "field2-value")
871 | data := buf.Bytes()
872 | if len(data) != 0 {
873 | t.Errorf("want empty, but now is: %s", data)
874 | return
875 | }
876 | }
877 | // debug
878 | {
879 | var buf bytes.Buffer
880 | SetOutput(ConcurrentWriter(&buf))
881 | SetFormatter(testJsonFormatter{})
882 |
883 | OutputContext(testWithoutLoggerContext, 0, DebugLevel, "debug-msg", "field1-key", "field1-value", "field2-key", "field2-value")
884 | data := buf.Bytes()
885 | if len(data) != 0 {
886 | t.Errorf("want empty, but now is: %s", data)
887 | return
888 | }
889 | }
890 | }
891 |
892 | // info
893 | {
894 | SetLevel(InfoLevel)
895 |
896 | // fatal
897 | {
898 | var buf bytes.Buffer
899 | SetOutput(ConcurrentWriter(&buf))
900 | SetFormatter(testJsonFormatter{})
901 |
902 | OutputContext(testWithoutLoggerContext, 0, FatalLevel, "fatal-msg", "field1-key", "field1-value", "field2-key", "field2-value")
903 | data := buf.Bytes()
904 |
905 | var have map[string]interface{}
906 | if err := json.Unmarshal(data, &have); err != nil {
907 | t.Error(err.Error())
908 | return
909 | }
910 | want := map[string]interface{}{
911 | fieldKeyTraceId: "",
912 | fieldKeyLevel: FatalLevelString,
913 | fieldKeyMessage: "fatal-msg",
914 | "field1-key": "field1-value",
915 | "field2-key": "field2-value",
916 | }
917 | if !reflect.DeepEqual(have, want) {
918 | t.Errorf("\nhave:%v\nwant:%v", have, want)
919 | return
920 | }
921 | }
922 | // error
923 | {
924 | var buf bytes.Buffer
925 | SetOutput(ConcurrentWriter(&buf))
926 | SetFormatter(testJsonFormatter{})
927 |
928 | OutputContext(testWithoutLoggerContext, 0, ErrorLevel, "error-msg", "field1-key", "field1-value", "field2-key", "field2-value")
929 | data := buf.Bytes()
930 |
931 | var have map[string]interface{}
932 | if err := json.Unmarshal(data, &have); err != nil {
933 | t.Error(err.Error())
934 | return
935 | }
936 | want := map[string]interface{}{
937 | fieldKeyTraceId: "",
938 | fieldKeyLevel: ErrorLevelString,
939 | fieldKeyMessage: "error-msg",
940 | "field1-key": "field1-value",
941 | "field2-key": "field2-value",
942 | }
943 | if !reflect.DeepEqual(have, want) {
944 | t.Errorf("\nhave:%v\nwant:%v", have, want)
945 | return
946 | }
947 | }
948 | // warning
949 | {
950 | var buf bytes.Buffer
951 | SetOutput(ConcurrentWriter(&buf))
952 | SetFormatter(testJsonFormatter{})
953 |
954 | OutputContext(testWithoutLoggerContext, 0, WarnLevel, "warning-msg", "field1-key", "field1-value", "field2-key", "field2-value")
955 | data := buf.Bytes()
956 |
957 | var have map[string]interface{}
958 | if err := json.Unmarshal(data, &have); err != nil {
959 | t.Error(err.Error())
960 | return
961 | }
962 | want := map[string]interface{}{
963 | fieldKeyTraceId: "",
964 | fieldKeyLevel: WarnLevelString,
965 | fieldKeyMessage: "warning-msg",
966 | "field1-key": "field1-value",
967 | "field2-key": "field2-value",
968 | }
969 | if !reflect.DeepEqual(have, want) {
970 | t.Errorf("\nhave:%v\nwant:%v", have, want)
971 | return
972 | }
973 | }
974 | // info
975 | {
976 | var buf bytes.Buffer
977 | SetOutput(ConcurrentWriter(&buf))
978 | SetFormatter(testJsonFormatter{})
979 |
980 | OutputContext(testWithoutLoggerContext, 0, InfoLevel, "info-msg", "field1-key", "field1-value", "field2-key", "field2-value")
981 | data := buf.Bytes()
982 |
983 | var have map[string]interface{}
984 | if err := json.Unmarshal(data, &have); err != nil {
985 | t.Error(err.Error())
986 | return
987 | }
988 | want := map[string]interface{}{
989 | fieldKeyTraceId: "",
990 | fieldKeyLevel: InfoLevelString,
991 | fieldKeyMessage: "info-msg",
992 | "field1-key": "field1-value",
993 | "field2-key": "field2-value",
994 | }
995 | if !reflect.DeepEqual(have, want) {
996 | t.Errorf("\nhave:%v\nwant:%v", have, want)
997 | return
998 | }
999 | }
1000 | // debug
1001 | {
1002 | var buf bytes.Buffer
1003 | SetOutput(ConcurrentWriter(&buf))
1004 | SetFormatter(testJsonFormatter{})
1005 |
1006 | OutputContext(testWithoutLoggerContext, 0, DebugLevel, "debug-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1007 | data := buf.Bytes()
1008 | if len(data) != 0 {
1009 | t.Errorf("want empty, but now is: %s", data)
1010 | return
1011 | }
1012 | }
1013 | }
1014 |
1015 | // debug
1016 | {
1017 | SetLevel(DebugLevel)
1018 |
1019 | // fatal
1020 | {
1021 | var buf bytes.Buffer
1022 | SetOutput(ConcurrentWriter(&buf))
1023 | SetFormatter(testJsonFormatter{})
1024 |
1025 | OutputContext(testWithoutLoggerContext, 0, FatalLevel, "fatal-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1026 | data := buf.Bytes()
1027 |
1028 | var have map[string]interface{}
1029 | if err := json.Unmarshal(data, &have); err != nil {
1030 | t.Error(err.Error())
1031 | return
1032 | }
1033 | want := map[string]interface{}{
1034 | fieldKeyTraceId: "",
1035 | fieldKeyLevel: FatalLevelString,
1036 | fieldKeyMessage: "fatal-msg",
1037 | "field1-key": "field1-value",
1038 | "field2-key": "field2-value",
1039 | }
1040 | if !reflect.DeepEqual(have, want) {
1041 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1042 | return
1043 | }
1044 | }
1045 | // error
1046 | {
1047 | var buf bytes.Buffer
1048 | SetOutput(ConcurrentWriter(&buf))
1049 | SetFormatter(testJsonFormatter{})
1050 |
1051 | OutputContext(testWithoutLoggerContext, 0, ErrorLevel, "error-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1052 | data := buf.Bytes()
1053 |
1054 | var have map[string]interface{}
1055 | if err := json.Unmarshal(data, &have); err != nil {
1056 | t.Error(err.Error())
1057 | return
1058 | }
1059 | want := map[string]interface{}{
1060 | fieldKeyTraceId: "",
1061 | fieldKeyLevel: ErrorLevelString,
1062 | fieldKeyMessage: "error-msg",
1063 | "field1-key": "field1-value",
1064 | "field2-key": "field2-value",
1065 | }
1066 | if !reflect.DeepEqual(have, want) {
1067 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1068 | return
1069 | }
1070 | }
1071 | // warning
1072 | {
1073 | var buf bytes.Buffer
1074 | SetOutput(ConcurrentWriter(&buf))
1075 | SetFormatter(testJsonFormatter{})
1076 |
1077 | OutputContext(testWithoutLoggerContext, 0, WarnLevel, "warning-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1078 | data := buf.Bytes()
1079 |
1080 | var have map[string]interface{}
1081 | if err := json.Unmarshal(data, &have); err != nil {
1082 | t.Error(err.Error())
1083 | return
1084 | }
1085 | want := map[string]interface{}{
1086 | fieldKeyTraceId: "",
1087 | fieldKeyLevel: WarnLevelString,
1088 | fieldKeyMessage: "warning-msg",
1089 | "field1-key": "field1-value",
1090 | "field2-key": "field2-value",
1091 | }
1092 | if !reflect.DeepEqual(have, want) {
1093 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1094 | return
1095 | }
1096 | }
1097 | // info
1098 | {
1099 | var buf bytes.Buffer
1100 | SetOutput(ConcurrentWriter(&buf))
1101 | SetFormatter(testJsonFormatter{})
1102 |
1103 | OutputContext(testWithoutLoggerContext, 0, InfoLevel, "info-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1104 | data := buf.Bytes()
1105 |
1106 | var have map[string]interface{}
1107 | if err := json.Unmarshal(data, &have); err != nil {
1108 | t.Error(err.Error())
1109 | return
1110 | }
1111 | want := map[string]interface{}{
1112 | fieldKeyTraceId: "",
1113 | fieldKeyLevel: InfoLevelString,
1114 | fieldKeyMessage: "info-msg",
1115 | "field1-key": "field1-value",
1116 | "field2-key": "field2-value",
1117 | }
1118 | if !reflect.DeepEqual(have, want) {
1119 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1120 | return
1121 | }
1122 | }
1123 | // debug
1124 | {
1125 | var buf bytes.Buffer
1126 | SetOutput(ConcurrentWriter(&buf))
1127 | SetFormatter(testJsonFormatter{})
1128 |
1129 | OutputContext(testWithoutLoggerContext, 0, DebugLevel, "debug-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1130 | data := buf.Bytes()
1131 |
1132 | var have map[string]interface{}
1133 | if err := json.Unmarshal(data, &have); err != nil {
1134 | t.Error(err.Error())
1135 | return
1136 | }
1137 | want := map[string]interface{}{
1138 | fieldKeyTraceId: "",
1139 | fieldKeyLevel: DebugLevelString,
1140 | fieldKeyMessage: "debug-msg",
1141 | "field1-key": "field1-value",
1142 | "field2-key": "field2-value",
1143 | }
1144 | if !reflect.DeepEqual(have, want) {
1145 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1146 | return
1147 | }
1148 | }
1149 | }
1150 | }
1151 |
1152 | func TestLeveledPrintContextWithoutLogger(t *testing.T) {
1153 | defer setWithoutLoggerContextOptionsToDefault()
1154 |
1155 | // fatal
1156 | {
1157 | SetLevel(FatalLevel)
1158 |
1159 | // fatal
1160 | {
1161 | var buf bytes.Buffer
1162 | SetOutput(ConcurrentWriter(&buf))
1163 | SetFormatter(testJsonFormatter{})
1164 |
1165 | FatalContext(testWithoutLoggerContext, "fatal-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1166 | data := buf.Bytes()
1167 |
1168 | var have map[string]interface{}
1169 | if err := json.Unmarshal(data, &have); err != nil {
1170 | t.Error(err.Error())
1171 | return
1172 | }
1173 | want := map[string]interface{}{
1174 | fieldKeyTraceId: "",
1175 | fieldKeyLevel: FatalLevelString,
1176 | fieldKeyMessage: "fatal-msg",
1177 | "field1-key": "field1-value",
1178 | "field2-key": "field2-value",
1179 | }
1180 | if !reflect.DeepEqual(have, want) {
1181 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1182 | return
1183 | }
1184 | }
1185 | // error
1186 | {
1187 | var buf bytes.Buffer
1188 | SetOutput(ConcurrentWriter(&buf))
1189 | SetFormatter(testJsonFormatter{})
1190 |
1191 | ErrorContext(testWithoutLoggerContext, "error-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1192 | data := buf.Bytes()
1193 | if len(data) != 0 {
1194 | t.Errorf("want empty, but now is: %s", data)
1195 | return
1196 | }
1197 | }
1198 | // warning
1199 | {
1200 | var buf bytes.Buffer
1201 | SetOutput(ConcurrentWriter(&buf))
1202 | SetFormatter(testJsonFormatter{})
1203 |
1204 | WarnContext(testWithoutLoggerContext, "warning-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1205 | data := buf.Bytes()
1206 | if len(data) != 0 {
1207 | t.Errorf("want empty, but now is: %s", data)
1208 | return
1209 | }
1210 | }
1211 | // info
1212 | {
1213 | var buf bytes.Buffer
1214 | SetOutput(ConcurrentWriter(&buf))
1215 | SetFormatter(testJsonFormatter{})
1216 |
1217 | InfoContext(testWithoutLoggerContext, "info-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1218 | data := buf.Bytes()
1219 | if len(data) != 0 {
1220 | t.Errorf("want empty, but now is: %s", data)
1221 | return
1222 | }
1223 | }
1224 | // debug
1225 | {
1226 | var buf bytes.Buffer
1227 | SetOutput(ConcurrentWriter(&buf))
1228 | SetFormatter(testJsonFormatter{})
1229 |
1230 | DebugContext(testWithoutLoggerContext, "debug-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1231 | data := buf.Bytes()
1232 | if len(data) != 0 {
1233 | t.Errorf("want empty, but now is: %s", data)
1234 | return
1235 | }
1236 | }
1237 | }
1238 |
1239 | // error
1240 | {
1241 | SetLevel(ErrorLevel)
1242 |
1243 | // fatal
1244 | {
1245 | var buf bytes.Buffer
1246 | SetOutput(ConcurrentWriter(&buf))
1247 | SetFormatter(testJsonFormatter{})
1248 |
1249 | FatalContext(testWithoutLoggerContext, "fatal-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1250 | data := buf.Bytes()
1251 |
1252 | var have map[string]interface{}
1253 | if err := json.Unmarshal(data, &have); err != nil {
1254 | t.Error(err.Error())
1255 | return
1256 | }
1257 | want := map[string]interface{}{
1258 | fieldKeyTraceId: "",
1259 | fieldKeyLevel: FatalLevelString,
1260 | fieldKeyMessage: "fatal-msg",
1261 | "field1-key": "field1-value",
1262 | "field2-key": "field2-value",
1263 | }
1264 | if !reflect.DeepEqual(have, want) {
1265 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1266 | return
1267 | }
1268 | }
1269 | // error
1270 | {
1271 | var buf bytes.Buffer
1272 | SetOutput(ConcurrentWriter(&buf))
1273 | SetFormatter(testJsonFormatter{})
1274 |
1275 | ErrorContext(testWithoutLoggerContext, "error-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1276 | data := buf.Bytes()
1277 |
1278 | var have map[string]interface{}
1279 | if err := json.Unmarshal(data, &have); err != nil {
1280 | t.Error(err.Error())
1281 | return
1282 | }
1283 | want := map[string]interface{}{
1284 | fieldKeyTraceId: "",
1285 | fieldKeyLevel: ErrorLevelString,
1286 | fieldKeyMessage: "error-msg",
1287 | "field1-key": "field1-value",
1288 | "field2-key": "field2-value",
1289 | }
1290 | if !reflect.DeepEqual(have, want) {
1291 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1292 | return
1293 | }
1294 | }
1295 | // warning
1296 | {
1297 | var buf bytes.Buffer
1298 | SetOutput(ConcurrentWriter(&buf))
1299 | SetFormatter(testJsonFormatter{})
1300 |
1301 | WarnContext(testWithoutLoggerContext, "warning-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1302 | data := buf.Bytes()
1303 | if len(data) != 0 {
1304 | t.Errorf("want empty, but now is: %s", data)
1305 | return
1306 | }
1307 | }
1308 | // info
1309 | {
1310 | var buf bytes.Buffer
1311 | SetOutput(ConcurrentWriter(&buf))
1312 | SetFormatter(testJsonFormatter{})
1313 |
1314 | InfoContext(testWithoutLoggerContext, "info-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1315 | data := buf.Bytes()
1316 | if len(data) != 0 {
1317 | t.Errorf("want empty, but now is: %s", data)
1318 | return
1319 | }
1320 | }
1321 | // debug
1322 | {
1323 | var buf bytes.Buffer
1324 | SetOutput(ConcurrentWriter(&buf))
1325 | SetFormatter(testJsonFormatter{})
1326 |
1327 | DebugContext(testWithoutLoggerContext, "debug-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1328 | data := buf.Bytes()
1329 | if len(data) != 0 {
1330 | t.Errorf("want empty, but now is: %s", data)
1331 | return
1332 | }
1333 | }
1334 | }
1335 |
1336 | // warning
1337 | {
1338 | SetLevel(WarnLevel)
1339 |
1340 | // fatal
1341 | {
1342 | var buf bytes.Buffer
1343 | SetOutput(ConcurrentWriter(&buf))
1344 | SetFormatter(testJsonFormatter{})
1345 |
1346 | FatalContext(testWithoutLoggerContext, "fatal-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1347 | data := buf.Bytes()
1348 |
1349 | var have map[string]interface{}
1350 | if err := json.Unmarshal(data, &have); err != nil {
1351 | t.Error(err.Error())
1352 | return
1353 | }
1354 | want := map[string]interface{}{
1355 | fieldKeyTraceId: "",
1356 | fieldKeyLevel: FatalLevelString,
1357 | fieldKeyMessage: "fatal-msg",
1358 | "field1-key": "field1-value",
1359 | "field2-key": "field2-value",
1360 | }
1361 | if !reflect.DeepEqual(have, want) {
1362 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1363 | return
1364 | }
1365 | }
1366 | // error
1367 | {
1368 | var buf bytes.Buffer
1369 | SetOutput(ConcurrentWriter(&buf))
1370 | SetFormatter(testJsonFormatter{})
1371 |
1372 | ErrorContext(testWithoutLoggerContext, "error-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1373 | data := buf.Bytes()
1374 |
1375 | var have map[string]interface{}
1376 | if err := json.Unmarshal(data, &have); err != nil {
1377 | t.Error(err.Error())
1378 | return
1379 | }
1380 | want := map[string]interface{}{
1381 | fieldKeyTraceId: "",
1382 | fieldKeyLevel: ErrorLevelString,
1383 | fieldKeyMessage: "error-msg",
1384 | "field1-key": "field1-value",
1385 | "field2-key": "field2-value",
1386 | }
1387 | if !reflect.DeepEqual(have, want) {
1388 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1389 | return
1390 | }
1391 | }
1392 | // warning
1393 | {
1394 | var buf bytes.Buffer
1395 | SetOutput(ConcurrentWriter(&buf))
1396 | SetFormatter(testJsonFormatter{})
1397 |
1398 | WarnContext(testWithoutLoggerContext, "warning-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1399 | data := buf.Bytes()
1400 |
1401 | var have map[string]interface{}
1402 | if err := json.Unmarshal(data, &have); err != nil {
1403 | t.Error(err.Error())
1404 | return
1405 | }
1406 | want := map[string]interface{}{
1407 | fieldKeyTraceId: "",
1408 | fieldKeyLevel: WarnLevelString,
1409 | fieldKeyMessage: "warning-msg",
1410 | "field1-key": "field1-value",
1411 | "field2-key": "field2-value",
1412 | }
1413 | if !reflect.DeepEqual(have, want) {
1414 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1415 | return
1416 | }
1417 | }
1418 | // info
1419 | {
1420 | var buf bytes.Buffer
1421 | SetOutput(ConcurrentWriter(&buf))
1422 | SetFormatter(testJsonFormatter{})
1423 |
1424 | InfoContext(testWithoutLoggerContext, "info-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1425 | data := buf.Bytes()
1426 | if len(data) != 0 {
1427 | t.Errorf("want empty, but now is: %s", data)
1428 | return
1429 | }
1430 | }
1431 | // debug
1432 | {
1433 | var buf bytes.Buffer
1434 | SetOutput(ConcurrentWriter(&buf))
1435 | SetFormatter(testJsonFormatter{})
1436 |
1437 | DebugContext(testWithoutLoggerContext, "debug-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1438 | data := buf.Bytes()
1439 | if len(data) != 0 {
1440 | t.Errorf("want empty, but now is: %s", data)
1441 | return
1442 | }
1443 | }
1444 | }
1445 |
1446 | // info
1447 | {
1448 | SetLevel(InfoLevel)
1449 |
1450 | // fatal
1451 | {
1452 | var buf bytes.Buffer
1453 | SetOutput(ConcurrentWriter(&buf))
1454 | SetFormatter(testJsonFormatter{})
1455 |
1456 | FatalContext(testWithoutLoggerContext, "fatal-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1457 | data := buf.Bytes()
1458 |
1459 | var have map[string]interface{}
1460 | if err := json.Unmarshal(data, &have); err != nil {
1461 | t.Error(err.Error())
1462 | return
1463 | }
1464 | want := map[string]interface{}{
1465 | fieldKeyTraceId: "",
1466 | fieldKeyLevel: FatalLevelString,
1467 | fieldKeyMessage: "fatal-msg",
1468 | "field1-key": "field1-value",
1469 | "field2-key": "field2-value",
1470 | }
1471 | if !reflect.DeepEqual(have, want) {
1472 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1473 | return
1474 | }
1475 | }
1476 | // error
1477 | {
1478 | var buf bytes.Buffer
1479 | SetOutput(ConcurrentWriter(&buf))
1480 | SetFormatter(testJsonFormatter{})
1481 |
1482 | ErrorContext(testWithoutLoggerContext, "error-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1483 | data := buf.Bytes()
1484 |
1485 | var have map[string]interface{}
1486 | if err := json.Unmarshal(data, &have); err != nil {
1487 | t.Error(err.Error())
1488 | return
1489 | }
1490 | want := map[string]interface{}{
1491 | fieldKeyTraceId: "",
1492 | fieldKeyLevel: ErrorLevelString,
1493 | fieldKeyMessage: "error-msg",
1494 | "field1-key": "field1-value",
1495 | "field2-key": "field2-value",
1496 | }
1497 | if !reflect.DeepEqual(have, want) {
1498 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1499 | return
1500 | }
1501 | }
1502 | // warning
1503 | {
1504 | var buf bytes.Buffer
1505 | SetOutput(ConcurrentWriter(&buf))
1506 | SetFormatter(testJsonFormatter{})
1507 |
1508 | WarnContext(testWithoutLoggerContext, "warning-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1509 | data := buf.Bytes()
1510 |
1511 | var have map[string]interface{}
1512 | if err := json.Unmarshal(data, &have); err != nil {
1513 | t.Error(err.Error())
1514 | return
1515 | }
1516 | want := map[string]interface{}{
1517 | fieldKeyTraceId: "",
1518 | fieldKeyLevel: WarnLevelString,
1519 | fieldKeyMessage: "warning-msg",
1520 | "field1-key": "field1-value",
1521 | "field2-key": "field2-value",
1522 | }
1523 | if !reflect.DeepEqual(have, want) {
1524 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1525 | return
1526 | }
1527 | }
1528 | // info
1529 | {
1530 | var buf bytes.Buffer
1531 | SetOutput(ConcurrentWriter(&buf))
1532 | SetFormatter(testJsonFormatter{})
1533 |
1534 | InfoContext(testWithoutLoggerContext, "info-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1535 | data := buf.Bytes()
1536 |
1537 | var have map[string]interface{}
1538 | if err := json.Unmarshal(data, &have); err != nil {
1539 | t.Error(err.Error())
1540 | return
1541 | }
1542 | want := map[string]interface{}{
1543 | fieldKeyTraceId: "",
1544 | fieldKeyLevel: InfoLevelString,
1545 | fieldKeyMessage: "info-msg",
1546 | "field1-key": "field1-value",
1547 | "field2-key": "field2-value",
1548 | }
1549 | if !reflect.DeepEqual(have, want) {
1550 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1551 | return
1552 | }
1553 | }
1554 | // debug
1555 | {
1556 | var buf bytes.Buffer
1557 | SetOutput(ConcurrentWriter(&buf))
1558 | SetFormatter(testJsonFormatter{})
1559 |
1560 | DebugContext(testWithoutLoggerContext, "debug-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1561 | data := buf.Bytes()
1562 | if len(data) != 0 {
1563 | t.Errorf("want empty, but now is: %s", data)
1564 | return
1565 | }
1566 | }
1567 | }
1568 |
1569 | // debug
1570 | {
1571 | SetLevel(DebugLevel)
1572 |
1573 | // fatal
1574 | {
1575 | var buf bytes.Buffer
1576 | SetOutput(ConcurrentWriter(&buf))
1577 | SetFormatter(testJsonFormatter{})
1578 |
1579 | FatalContext(testWithoutLoggerContext, "fatal-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1580 | data := buf.Bytes()
1581 |
1582 | var have map[string]interface{}
1583 | if err := json.Unmarshal(data, &have); err != nil {
1584 | t.Error(err.Error())
1585 | return
1586 | }
1587 | want := map[string]interface{}{
1588 | fieldKeyTraceId: "",
1589 | fieldKeyLevel: FatalLevelString,
1590 | fieldKeyMessage: "fatal-msg",
1591 | "field1-key": "field1-value",
1592 | "field2-key": "field2-value",
1593 | }
1594 | if !reflect.DeepEqual(have, want) {
1595 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1596 | return
1597 | }
1598 | }
1599 | // error
1600 | {
1601 | var buf bytes.Buffer
1602 | SetOutput(ConcurrentWriter(&buf))
1603 | SetFormatter(testJsonFormatter{})
1604 |
1605 | ErrorContext(testWithoutLoggerContext, "error-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1606 | data := buf.Bytes()
1607 |
1608 | var have map[string]interface{}
1609 | if err := json.Unmarshal(data, &have); err != nil {
1610 | t.Error(err.Error())
1611 | return
1612 | }
1613 | want := map[string]interface{}{
1614 | fieldKeyTraceId: "",
1615 | fieldKeyLevel: ErrorLevelString,
1616 | fieldKeyMessage: "error-msg",
1617 | "field1-key": "field1-value",
1618 | "field2-key": "field2-value",
1619 | }
1620 | if !reflect.DeepEqual(have, want) {
1621 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1622 | return
1623 | }
1624 | }
1625 | // warning
1626 | {
1627 | var buf bytes.Buffer
1628 | SetOutput(ConcurrentWriter(&buf))
1629 | SetFormatter(testJsonFormatter{})
1630 |
1631 | WarnContext(testWithoutLoggerContext, "warning-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1632 | data := buf.Bytes()
1633 |
1634 | var have map[string]interface{}
1635 | if err := json.Unmarshal(data, &have); err != nil {
1636 | t.Error(err.Error())
1637 | return
1638 | }
1639 | want := map[string]interface{}{
1640 | fieldKeyTraceId: "",
1641 | fieldKeyLevel: WarnLevelString,
1642 | fieldKeyMessage: "warning-msg",
1643 | "field1-key": "field1-value",
1644 | "field2-key": "field2-value",
1645 | }
1646 | if !reflect.DeepEqual(have, want) {
1647 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1648 | return
1649 | }
1650 | }
1651 | // info
1652 | {
1653 | var buf bytes.Buffer
1654 | SetOutput(ConcurrentWriter(&buf))
1655 | SetFormatter(testJsonFormatter{})
1656 |
1657 | InfoContext(testWithoutLoggerContext, "info-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1658 | data := buf.Bytes()
1659 |
1660 | var have map[string]interface{}
1661 | if err := json.Unmarshal(data, &have); err != nil {
1662 | t.Error(err.Error())
1663 | return
1664 | }
1665 | want := map[string]interface{}{
1666 | fieldKeyTraceId: "",
1667 | fieldKeyLevel: InfoLevelString,
1668 | fieldKeyMessage: "info-msg",
1669 | "field1-key": "field1-value",
1670 | "field2-key": "field2-value",
1671 | }
1672 | if !reflect.DeepEqual(have, want) {
1673 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1674 | return
1675 | }
1676 | }
1677 | // debug
1678 | {
1679 | var buf bytes.Buffer
1680 | SetOutput(ConcurrentWriter(&buf))
1681 | SetFormatter(testJsonFormatter{})
1682 |
1683 | DebugContext(testWithoutLoggerContext, "debug-msg", "field1-key", "field1-value", "field2-key", "field2-value")
1684 | data := buf.Bytes()
1685 |
1686 | var have map[string]interface{}
1687 | if err := json.Unmarshal(data, &have); err != nil {
1688 | t.Error(err.Error())
1689 | return
1690 | }
1691 | want := map[string]interface{}{
1692 | fieldKeyTraceId: "",
1693 | fieldKeyLevel: DebugLevelString,
1694 | fieldKeyMessage: "debug-msg",
1695 | "field1-key": "field1-value",
1696 | "field2-key": "field2-value",
1697 | }
1698 | if !reflect.DeepEqual(have, want) {
1699 | t.Errorf("\nhave:%v\nwant:%v", have, want)
1700 | return
1701 | }
1702 | }
1703 | }
1704 | }
1705 |
--------------------------------------------------------------------------------