github.com/zhouyu0/docker-note@v0.0.0-20190722021225-b8d3825084db/daemon/logger/local/local_test.go (about) 1 package local 2 3 import ( 4 "context" 5 "encoding/binary" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "testing" 10 "time" 11 12 "bytes" 13 "fmt" 14 15 "strings" 16 17 "io" 18 19 "github.com/docker/docker/api/types/backend" 20 "github.com/docker/docker/api/types/plugins/logdriver" 21 "github.com/docker/docker/daemon/logger" 22 protoio "github.com/gogo/protobuf/io" 23 "gotest.tools/assert" 24 is "gotest.tools/assert/cmp" 25 ) 26 27 func TestWriteLog(t *testing.T) { 28 t.Parallel() 29 30 dir, err := ioutil.TempDir("", t.Name()) 31 assert.NilError(t, err) 32 defer os.RemoveAll(dir) 33 34 logPath := filepath.Join(dir, "test.log") 35 36 l, err := New(logger.Info{LogPath: logPath}) 37 assert.NilError(t, err) 38 defer l.Close() 39 40 m1 := logger.Message{Source: "stdout", Timestamp: time.Now().Add(-1 * 30 * time.Minute), Line: []byte("message 1")} 41 m2 := logger.Message{Source: "stdout", Timestamp: time.Now().Add(-1 * 20 * time.Minute), Line: []byte("message 2"), PLogMetaData: &backend.PartialLogMetaData{Last: true, ID: "0001", Ordinal: 1}} 42 m3 := logger.Message{Source: "stderr", Timestamp: time.Now().Add(-1 * 10 * time.Minute), Line: []byte("message 3")} 43 44 // copy the log message because the underying log writer resets the log message and returns it to a buffer pool 45 err = l.Log(copyLogMessage(&m1)) 46 assert.NilError(t, err) 47 err = l.Log(copyLogMessage(&m2)) 48 assert.NilError(t, err) 49 err = l.Log(copyLogMessage(&m3)) 50 assert.NilError(t, err) 51 52 f, err := os.Open(logPath) 53 assert.NilError(t, err) 54 defer f.Close() 55 dec := protoio.NewUint32DelimitedReader(f, binary.BigEndian, 1e6) 56 57 var ( 58 proto logdriver.LogEntry 59 testProto logdriver.LogEntry 60 partial logdriver.PartialLogEntryMetadata 61 ) 62 63 lenBuf := make([]byte, encodeBinaryLen) 64 seekMsgLen := func() { 65 io.ReadFull(f, lenBuf) 66 } 67 68 err = dec.ReadMsg(&proto) 69 assert.NilError(t, err) 70 messageToProto(&m1, &testProto, &partial) 71 assert.Check(t, is.DeepEqual(testProto, proto), "expected:\n%+v\ngot:\n%+v", testProto, proto) 72 seekMsgLen() 73 74 err = dec.ReadMsg(&proto) 75 assert.NilError(t, err) 76 messageToProto(&m2, &testProto, &partial) 77 assert.Check(t, is.DeepEqual(testProto, proto)) 78 seekMsgLen() 79 80 err = dec.ReadMsg(&proto) 81 assert.NilError(t, err) 82 messageToProto(&m3, &testProto, &partial) 83 assert.Check(t, is.DeepEqual(testProto, proto), "expected:\n%+v\ngot:\n%+v", testProto, proto) 84 } 85 86 func TestReadLog(t *testing.T) { 87 t.Parallel() 88 89 dir, err := ioutil.TempDir("", t.Name()) 90 assert.NilError(t, err) 91 defer os.RemoveAll(dir) 92 93 logPath := filepath.Join(dir, "test.log") 94 l, err := New(logger.Info{LogPath: logPath}) 95 assert.NilError(t, err) 96 defer l.Close() 97 98 m1 := logger.Message{Source: "stdout", Timestamp: time.Now().Add(-1 * 30 * time.Minute), Line: []byte("a message")} 99 m2 := logger.Message{Source: "stdout", Timestamp: time.Now().Add(-1 * 20 * time.Minute), Line: []byte("another message"), PLogMetaData: &backend.PartialLogMetaData{Ordinal: 1, Last: true}} 100 longMessage := []byte("a really long message " + strings.Repeat("a", initialBufSize*2)) 101 m3 := logger.Message{Source: "stderr", Timestamp: time.Now().Add(-1 * 10 * time.Minute), Line: longMessage} 102 m4 := logger.Message{Source: "stderr", Timestamp: time.Now().Add(-1 * 10 * time.Minute), Line: []byte("just one more message")} 103 104 // copy the log message because the underlying log writer resets the log message and returns it to a buffer pool 105 err = l.Log(copyLogMessage(&m1)) 106 assert.NilError(t, err) 107 err = l.Log(copyLogMessage(&m2)) 108 assert.NilError(t, err) 109 err = l.Log(copyLogMessage(&m3)) 110 assert.NilError(t, err) 111 err = l.Log(copyLogMessage(&m4)) 112 assert.NilError(t, err) 113 114 lr := l.(logger.LogReader) 115 116 testMessage := func(t *testing.T, lw *logger.LogWatcher, m *logger.Message) { 117 t.Helper() 118 ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) 119 defer cancel() 120 select { 121 case <-ctx.Done(): 122 assert.Assert(t, ctx.Err()) 123 case err := <-lw.Err: 124 assert.NilError(t, err) 125 case msg, open := <-lw.Msg: 126 if !open { 127 select { 128 case err := <-lw.Err: 129 assert.NilError(t, err) 130 default: 131 assert.Assert(t, m == nil) 132 return 133 } 134 } 135 assert.Assert(t, m != nil) 136 if m.PLogMetaData == nil { 137 // a `\n` is appended on read to make this work with the existing API's when the message is not a partial. 138 // make sure it's the last entry in the line, and then truncate it for the deep equal below. 139 assert.Check(t, msg.Line[len(msg.Line)-1] == '\n') 140 msg.Line = msg.Line[:len(msg.Line)-1] 141 } 142 assert.Check(t, is.DeepEqual(m, msg), fmt.Sprintf("\n%+v\n%+v", m, msg)) 143 } 144 } 145 146 t.Run("tail exact", func(t *testing.T) { 147 lw := lr.ReadLogs(logger.ReadConfig{Tail: 4}) 148 149 testMessage(t, lw, &m1) 150 testMessage(t, lw, &m2) 151 testMessage(t, lw, &m3) 152 testMessage(t, lw, &m4) 153 testMessage(t, lw, nil) // no more messages 154 }) 155 156 t.Run("tail less than available", func(t *testing.T) { 157 lw := lr.ReadLogs(logger.ReadConfig{Tail: 2}) 158 159 testMessage(t, lw, &m3) 160 testMessage(t, lw, &m4) 161 testMessage(t, lw, nil) // no more messages 162 }) 163 164 t.Run("tail more than available", func(t *testing.T) { 165 lw := lr.ReadLogs(logger.ReadConfig{Tail: 100}) 166 167 testMessage(t, lw, &m1) 168 testMessage(t, lw, &m2) 169 testMessage(t, lw, &m3) 170 testMessage(t, lw, &m4) 171 testMessage(t, lw, nil) // no more messages 172 }) 173 } 174 175 func BenchmarkLogWrite(b *testing.B) { 176 f, err := ioutil.TempFile("", b.Name()) 177 assert.Assert(b, err) 178 defer os.Remove(f.Name()) 179 f.Close() 180 181 local, err := New(logger.Info{LogPath: f.Name()}) 182 assert.Assert(b, err) 183 defer local.Close() 184 185 t := time.Now().UTC() 186 for _, data := range [][]byte{ 187 []byte(""), 188 []byte("a short string"), 189 bytes.Repeat([]byte("a long string"), 100), 190 bytes.Repeat([]byte("a really long string"), 10000), 191 } { 192 b.Run(fmt.Sprintf("%d", len(data)), func(b *testing.B) { 193 entry := &logdriver.LogEntry{Line: data, Source: "stdout", TimeNano: t.UnixNano()} 194 b.SetBytes(int64(entry.Size() + encodeBinaryLen + encodeBinaryLen)) 195 b.ResetTimer() 196 for i := 0; i < b.N; i++ { 197 msg := logger.NewMessage() 198 msg.Line = data 199 msg.Timestamp = t 200 msg.Source = "stdout" 201 if err := local.Log(msg); err != nil { 202 b.Fatal(err) 203 } 204 } 205 }) 206 } 207 } 208 209 func copyLogMessage(src *logger.Message) *logger.Message { 210 dst := logger.NewMessage() 211 dst.Source = src.Source 212 dst.Timestamp = src.Timestamp 213 dst.Attrs = src.Attrs 214 dst.Err = src.Err 215 dst.Line = append(dst.Line, src.Line...) 216 if src.PLogMetaData != nil { 217 dst.PLogMetaData = &(*src.PLogMetaData) 218 } 219 return dst 220 }