github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/daemon/logger/jsonfilelog/jsonfilelog.go (about) 1 // Package jsonfilelog provides the default Logger implementation for 2 // Docker logging. This logger logs to files on the host server in the 3 // JSON format. 4 package jsonfilelog 5 6 import ( 7 "bytes" 8 "encoding/json" 9 "fmt" 10 "strconv" 11 "sync" 12 13 "github.com/Sirupsen/logrus" 14 "github.com/docker/docker/daemon/logger" 15 "github.com/docker/docker/daemon/logger/loggerutils" 16 "github.com/docker/docker/pkg/jsonlog" 17 "github.com/docker/go-units" 18 ) 19 20 // Name is the name of the file that the jsonlogger logs to. 21 const Name = "json-file" 22 23 // JSONFileLogger is Logger implementation for default Docker logging. 24 type JSONFileLogger struct { 25 buf *bytes.Buffer 26 writer *loggerutils.RotateFileWriter 27 mu sync.Mutex 28 readers map[*logger.LogWatcher]struct{} // stores the active log followers 29 extra []byte // json-encoded extra attributes 30 } 31 32 func init() { 33 if err := logger.RegisterLogDriver(Name, New); err != nil { 34 logrus.Fatal(err) 35 } 36 if err := logger.RegisterLogOptValidator(Name, ValidateLogOpt); err != nil { 37 logrus.Fatal(err) 38 } 39 } 40 41 // New creates new JSONFileLogger which writes to filename passed in 42 // on given context. 43 func New(info logger.Info) (logger.Logger, error) { 44 var capval int64 = -1 45 if capacity, ok := info.Config["max-size"]; ok { 46 var err error 47 capval, err = units.FromHumanSize(capacity) 48 if err != nil { 49 return nil, err 50 } 51 } 52 var maxFiles = 1 53 if maxFileString, ok := info.Config["max-file"]; ok { 54 var err error 55 maxFiles, err = strconv.Atoi(maxFileString) 56 if err != nil { 57 return nil, err 58 } 59 if maxFiles < 1 { 60 return nil, fmt.Errorf("max-file cannot be less than 1") 61 } 62 } 63 64 writer, err := loggerutils.NewRotateFileWriter(info.LogPath, capval, maxFiles) 65 if err != nil { 66 return nil, err 67 } 68 69 var extra []byte 70 if attrs := info.ExtraAttributes(nil); len(attrs) > 0 { 71 var err error 72 extra, err = json.Marshal(attrs) 73 if err != nil { 74 return nil, err 75 } 76 } 77 78 return &JSONFileLogger{ 79 buf: bytes.NewBuffer(nil), 80 writer: writer, 81 readers: make(map[*logger.LogWatcher]struct{}), 82 extra: extra, 83 }, nil 84 } 85 86 // Log converts logger.Message to jsonlog.JSONLog and serializes it to file. 87 func (l *JSONFileLogger) Log(msg *logger.Message) error { 88 timestamp, err := jsonlog.FastTimeMarshalJSON(msg.Timestamp) 89 if err != nil { 90 return err 91 } 92 l.mu.Lock() 93 logline := msg.Line 94 if !msg.Partial { 95 logline = append(msg.Line, '\n') 96 } 97 err = (&jsonlog.JSONLogs{ 98 Log: logline, 99 Stream: msg.Source, 100 Created: timestamp, 101 RawAttrs: l.extra, 102 }).MarshalJSONBuf(l.buf) 103 if err != nil { 104 l.mu.Unlock() 105 return err 106 } 107 108 l.buf.WriteByte('\n') 109 _, err = l.writer.Write(l.buf.Bytes()) 110 l.buf.Reset() 111 l.mu.Unlock() 112 113 return err 114 } 115 116 // ValidateLogOpt looks for json specific log options max-file & max-size. 117 func ValidateLogOpt(cfg map[string]string) error { 118 for key := range cfg { 119 switch key { 120 case "max-file": 121 case "max-size": 122 case "labels": 123 case "env": 124 default: 125 return fmt.Errorf("unknown log opt '%s' for json-file log driver", key) 126 } 127 } 128 return nil 129 } 130 131 // LogPath returns the location the given json logger logs to. 132 func (l *JSONFileLogger) LogPath() string { 133 return l.writer.LogPath() 134 } 135 136 // Close closes underlying file and signals all readers to stop. 137 func (l *JSONFileLogger) Close() error { 138 l.mu.Lock() 139 err := l.writer.Close() 140 for r := range l.readers { 141 r.Close() 142 delete(l.readers, r) 143 } 144 l.mu.Unlock() 145 return err 146 } 147 148 // Name returns name of this logger. 149 func (l *JSONFileLogger) Name() string { 150 return Name 151 }