github.com/rita33cool1/iot-system-gateway@v0.0.0-20200911033302-e65bde238cc5/docker-engine/daemon/logger/journald/journald.go (about) 1 // +build linux 2 3 // Package journald provides the log driver for forwarding server logs 4 // to endpoints that receive the systemd format. 5 package journald // import "github.com/docker/docker/daemon/logger/journald" 6 7 import ( 8 "fmt" 9 "sync" 10 "unicode" 11 12 "github.com/coreos/go-systemd/journal" 13 "github.com/docker/docker/daemon/logger" 14 "github.com/docker/docker/daemon/logger/loggerutils" 15 "github.com/sirupsen/logrus" 16 ) 17 18 const name = "journald" 19 20 type journald struct { 21 mu sync.Mutex 22 vars map[string]string // additional variables and values to send to the journal along with the log message 23 readers readerList 24 closed bool 25 } 26 27 type readerList struct { 28 readers map[*logger.LogWatcher]*logger.LogWatcher 29 } 30 31 func init() { 32 if err := logger.RegisterLogDriver(name, New); err != nil { 33 logrus.Fatal(err) 34 } 35 if err := logger.RegisterLogOptValidator(name, validateLogOpt); err != nil { 36 logrus.Fatal(err) 37 } 38 } 39 40 // sanitizeKeyMode returns the sanitized string so that it could be used in journald. 41 // In journald log, there are special requirements for fields. 42 // Fields must be composed of uppercase letters, numbers, and underscores, but must 43 // not start with an underscore. 44 func sanitizeKeyMod(s string) string { 45 n := "" 46 for _, v := range s { 47 if 'a' <= v && v <= 'z' { 48 v = unicode.ToUpper(v) 49 } else if ('Z' < v || v < 'A') && ('9' < v || v < '0') { 50 v = '_' 51 } 52 // If (n == "" && v == '_'), then we will skip as this is the beginning with '_' 53 if !(n == "" && v == '_') { 54 n += string(v) 55 } 56 } 57 return n 58 } 59 60 // New creates a journald logger using the configuration passed in on 61 // the context. 62 func New(info logger.Info) (logger.Logger, error) { 63 if !journal.Enabled() { 64 return nil, fmt.Errorf("journald is not enabled on this host") 65 } 66 67 // parse log tag 68 tag, err := loggerutils.ParseLogTag(info, loggerutils.DefaultTemplate) 69 if err != nil { 70 return nil, err 71 } 72 73 vars := map[string]string{ 74 "CONTAINER_ID": info.ContainerID[:12], 75 "CONTAINER_ID_FULL": info.ContainerID, 76 "CONTAINER_NAME": info.Name(), 77 "CONTAINER_TAG": tag, 78 "SYSLOG_IDENTIFIER": tag, 79 } 80 extraAttrs, err := info.ExtraAttributes(sanitizeKeyMod) 81 if err != nil { 82 return nil, err 83 } 84 for k, v := range extraAttrs { 85 vars[k] = v 86 } 87 return &journald{vars: vars, readers: readerList{readers: make(map[*logger.LogWatcher]*logger.LogWatcher)}}, nil 88 } 89 90 // We don't actually accept any options, but we have to supply a callback for 91 // the factory to pass the (probably empty) configuration map to. 92 func validateLogOpt(cfg map[string]string) error { 93 for key := range cfg { 94 switch key { 95 case "labels": 96 case "env": 97 case "env-regex": 98 case "tag": 99 default: 100 return fmt.Errorf("unknown log opt '%s' for journald log driver", key) 101 } 102 } 103 return nil 104 } 105 106 func (s *journald) Log(msg *logger.Message) error { 107 vars := map[string]string{} 108 for k, v := range s.vars { 109 vars[k] = v 110 } 111 if msg.Partial { 112 vars["CONTAINER_PARTIAL_MESSAGE"] = "true" 113 } 114 115 line := string(msg.Line) 116 source := msg.Source 117 logger.PutMessage(msg) 118 119 if source == "stderr" { 120 return journal.Send(line, journal.PriErr, vars) 121 } 122 return journal.Send(line, journal.PriInfo, vars) 123 } 124 125 func (s *journald) Name() string { 126 return name 127 }