github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/clients/cmd/docker-driver/driver.go (about) 1 package main 2 3 import ( 4 "context" 5 "encoding/binary" 6 "fmt" 7 "io" 8 "os" 9 "path/filepath" 10 "strings" 11 "sync" 12 "syscall" 13 "time" 14 15 "github.com/docker/docker/api/types/backend" 16 "github.com/docker/docker/api/types/plugins/logdriver" 17 "github.com/docker/docker/daemon/logger" 18 "github.com/docker/docker/daemon/logger/jsonfilelog" 19 "github.com/go-kit/log" 20 "github.com/go-kit/log/level" 21 protoio "github.com/gogo/protobuf/io" 22 "github.com/pkg/errors" 23 "github.com/tonistiigi/fifo" 24 ) 25 26 type driver struct { 27 mu sync.Mutex 28 logs map[string]*logPair 29 idx map[string]*logPair 30 logger log.Logger 31 } 32 33 type logPair struct { 34 jsonl logger.Logger 35 lokil logger.Logger 36 stream io.ReadCloser 37 info logger.Info 38 logger log.Logger 39 // folder where json log files will be created. 40 folder string 41 // keep created files after stopping the container. 42 keepFile bool 43 } 44 45 func (l *logPair) Close() { 46 if err := l.stream.Close(); err != nil { 47 level.Error(l.logger).Log("msg", "error while closing fifo stream", "err", err) 48 } 49 if err := l.lokil.Close(); err != nil { 50 level.Error(l.logger).Log("msg", "error while closing loki logger", "err", err) 51 } 52 if l.jsonl == nil { 53 return 54 } 55 if err := l.jsonl.Close(); err != nil { 56 level.Error(l.logger).Log("msg", "error while closing json logger", "err", err) 57 } 58 } 59 60 func newDriver(logger log.Logger) *driver { 61 return &driver{ 62 logs: make(map[string]*logPair), 63 idx: make(map[string]*logPair), 64 logger: logger, 65 } 66 } 67 68 func (d *driver) StartLogging(file string, logCtx logger.Info) error { 69 d.mu.Lock() 70 if _, exists := d.logs[file]; exists { 71 d.mu.Unlock() 72 return fmt.Errorf("logger for %q already exists", file) 73 } 74 d.mu.Unlock() 75 folder := fmt.Sprintf("/var/log/docker/%s/", logCtx.ContainerID) 76 logCtx.LogPath = filepath.Join(folder, "json.log") 77 level.Info(d.logger).Log("msg", "starting logging driver for container", "id", logCtx.ContainerID, "config", fmt.Sprintf("%+v", logCtx.Config), "file", file, "logpath", logCtx.LogPath) 78 79 noFile, err := parseBoolean(cfgNofile, logCtx, false) 80 if err != nil { 81 return err 82 } 83 84 keepFile, err := parseBoolean(cfgKeepFile, logCtx, false) 85 if err != nil { 86 return err 87 } 88 89 var jsonl logger.Logger 90 if !noFile { 91 if err := os.MkdirAll(folder, 0755); err != nil { 92 return errors.Wrap(err, "error setting up logger dir") 93 } 94 95 jsonl, err = jsonfilelog.New(logCtx) 96 if err != nil { 97 return errors.Wrap(err, "error creating jsonfile logger") 98 } 99 } 100 101 lokil, err := New(logCtx, d.logger) 102 if err != nil { 103 return errors.Wrap(err, "error creating loki logger") 104 } 105 f, err := fifo.OpenFifo(context.Background(), file, syscall.O_RDONLY, 0700) 106 if err != nil { 107 return errors.Wrapf(err, "error opening logger fifo: %q", file) 108 } 109 110 d.mu.Lock() 111 lf := &logPair{jsonl, lokil, f, logCtx, d.logger, folder, keepFile} 112 d.logs[file] = lf 113 d.idx[logCtx.ContainerID] = lf 114 d.mu.Unlock() 115 116 go consumeLog(lf) 117 return nil 118 } 119 120 func (d *driver) StopLogging(file string) { 121 level.Debug(d.logger).Log("msg", "Stop logging", "file", file) 122 d.mu.Lock() 123 defer d.mu.Unlock() 124 lf, ok := d.logs[file] 125 if !ok { 126 return 127 } 128 lf.Close() 129 delete(d.logs, file) 130 if !lf.keepFile && lf.jsonl != nil { 131 // delete the folder where all log files were created. 132 if err := os.RemoveAll(lf.folder); err != nil { 133 level.Debug(d.logger).Log("msg", "error deleting folder", "folder", lf.folder) 134 } 135 } 136 } 137 138 func consumeLog(lf *logPair) { 139 dec := protoio.NewUint32DelimitedReader(lf.stream, binary.BigEndian, 1e6) 140 defer dec.Close() 141 defer lf.Close() 142 var buf logdriver.LogEntry 143 for { 144 if err := dec.ReadMsg(&buf); err != nil { 145 if err == io.EOF || err == os.ErrClosed || strings.Contains(err.Error(), "file already closed") { 146 level.Debug(lf.logger).Log("msg", "shutting down log logger", "id", lf.info.ContainerID, "err", err) 147 return 148 } 149 dec = protoio.NewUint32DelimitedReader(lf.stream, binary.BigEndian, 1e6) 150 } 151 var msg logger.Message 152 msg.Line = buf.Line 153 msg.Source = buf.Source 154 if buf.PartialLogMetadata != nil { 155 if msg.PLogMetaData == nil { 156 msg.PLogMetaData = &backend.PartialLogMetaData{} 157 } 158 msg.PLogMetaData.ID = buf.PartialLogMetadata.Id 159 msg.PLogMetaData.Last = buf.PartialLogMetadata.Last 160 msg.PLogMetaData.Ordinal = int(buf.PartialLogMetadata.Ordinal) 161 } 162 msg.Timestamp = time.Unix(0, buf.TimeNano) 163 164 // loki goes first as the json logger reset the message on completion. 165 if err := lf.lokil.Log(&msg); err != nil { 166 level.Error(lf.logger).Log("msg", "error pushing message to loki", "id", lf.info.ContainerID, "err", err, "message", msg) 167 } 168 if lf.jsonl != nil { 169 if err := lf.jsonl.Log(&msg); err != nil { 170 level.Error(lf.logger).Log("msg", "error writing log message", "id", lf.info.ContainerID, "err", err, "message", msg) 171 continue 172 } 173 } 174 175 buf.Reset() 176 } 177 } 178 179 func (d *driver) ReadLogs(info logger.Info, config logger.ReadConfig) (io.ReadCloser, error) { 180 d.mu.Lock() 181 lf, exists := d.idx[info.ContainerID] 182 d.mu.Unlock() 183 if !exists { 184 return nil, fmt.Errorf("logger does not exist for %s", info.ContainerID) 185 } 186 187 if lf.jsonl == nil { 188 return nil, fmt.Errorf("%s option set to true, no reading capability", cfgNofile) 189 } 190 191 r, w := io.Pipe() 192 lr, ok := lf.jsonl.(logger.LogReader) 193 if !ok { 194 return nil, errors.New("logger does not support reading") 195 } 196 197 go func() { 198 watcher := lr.ReadLogs(config) 199 200 enc := protoio.NewUint32DelimitedWriter(w, binary.BigEndian) 201 defer enc.Close() 202 defer watcher.ConsumerGone() 203 204 var buf logdriver.LogEntry 205 for { 206 select { 207 case msg, ok := <-watcher.Msg: 208 if !ok { 209 w.Close() 210 return 211 } 212 213 buf.Line = msg.Line 214 buf.Partial = msg.PLogMetaData != nil 215 buf.TimeNano = msg.Timestamp.UnixNano() 216 buf.Source = msg.Source 217 218 if err := enc.WriteMsg(&buf); err != nil { 219 _ = w.CloseWithError(err) 220 return 221 } 222 case err := <-watcher.Err: 223 _ = w.CloseWithError(err) 224 return 225 } 226 227 buf.Reset() 228 } 229 }() 230 231 return r, nil 232 }