github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/pkg/devicemapper/devmapper_log.go (about) 1 // +build linux,cgo 2 3 package devicemapper // import "github.com/demonoid81/moby/pkg/devicemapper" 4 5 import "C" 6 7 import ( 8 "fmt" 9 "strings" 10 11 "github.com/sirupsen/logrus" 12 ) 13 14 // DevmapperLogger defines methods required to register as a callback for 15 // logging events received from devicemapper. Note that devicemapper will send 16 // *all* logs regardless to callbacks (including debug logs) so it's 17 // recommended to not spam the console with the outputs. 18 type DevmapperLogger interface { 19 // DMLog is the logging callback containing all of the information from 20 // devicemapper. The interface is identical to the C libdm counterpart. 21 DMLog(level int, file string, line int, dmError int, message string) 22 } 23 24 // dmLogger is the current logger in use that is being forwarded our messages. 25 var dmLogger DevmapperLogger 26 27 // LogInit changes the logging callback called after processing libdm logs for 28 // error message information. The default logger simply forwards all logs to 29 // logrus. Calling LogInit(nil) disables the calling of callbacks. 30 func LogInit(logger DevmapperLogger) { 31 dmLogger = logger 32 } 33 34 // Due to the way cgo works this has to be in a separate file, as devmapper.go has 35 // definitions in the cgo block, which is incompatible with using "//export" 36 37 // DevmapperLogCallback exports the devmapper log callback for cgo. Note that 38 // because we are using callbacks, this function will be called for *every* log 39 // in libdm (even debug ones because there's no way of setting the verbosity 40 // level for an external logging callback). 41 //export DevmapperLogCallback 42 func DevmapperLogCallback(level C.int, file *C.char, line, dmErrnoOrClass C.int, message *C.char) { 43 msg := C.GoString(message) 44 45 // Track what errno libdm saw, because the library only gives us 0 or 1. 46 if level < LogLevelDebug { 47 if strings.Contains(msg, "busy") { 48 dmSawBusy = true 49 } 50 51 if strings.Contains(msg, "File exists") { 52 dmSawExist = true 53 } 54 55 if strings.Contains(msg, "No such device or address") { 56 dmSawEnxio = true 57 } 58 if strings.Contains(msg, "No data available") { 59 dmSawEnoData = true 60 } 61 } 62 63 if dmLogger != nil { 64 dmLogger.DMLog(int(level), C.GoString(file), int(line), int(dmErrnoOrClass), msg) 65 } 66 } 67 68 // DefaultLogger is the default logger used by pkg/devicemapper. It forwards 69 // all logs that are of higher or equal priority to the given level to the 70 // corresponding logrus level. 71 type DefaultLogger struct { 72 // Level corresponds to the highest libdm level that will be forwarded to 73 // logrus. In order to change this, register a new DefaultLogger. 74 Level int 75 } 76 77 // DMLog is the logging callback containing all of the information from 78 // devicemapper. The interface is identical to the C libdm counterpart. 79 func (l DefaultLogger) DMLog(level int, file string, line, dmError int, message string) { 80 if level <= l.Level { 81 // Forward the log to the correct logrus level, if allowed by dmLogLevel. 82 logMsg := fmt.Sprintf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message) 83 switch level { 84 case LogLevelFatal, LogLevelErr: 85 logrus.Error(logMsg) 86 case LogLevelWarn: 87 logrus.Warn(logMsg) 88 case LogLevelNotice, LogLevelInfo: 89 logrus.Info(logMsg) 90 case LogLevelDebug: 91 logrus.Debug(logMsg) 92 default: 93 // Don't drop any "unknown" levels. 94 logrus.Info(logMsg) 95 } 96 } 97 } 98 99 // registerLogCallback registers our own logging callback function for libdm 100 // (which is DevmapperLogCallback). 101 // 102 // Because libdm only gives us {0,1} error codes we need to parse the logs 103 // produced by libdm (to set dmSawBusy and so on). Note that by registering a 104 // callback using DevmapperLogCallback, libdm will no longer output logs to 105 // stderr so we have to log everything ourselves. None of this handling is 106 // optional because we depend on log callbacks to parse the logs, and if we 107 // don't forward the log information we'll be in a lot of trouble when 108 // debugging things. 109 func registerLogCallback() { 110 LogWithErrnoInit() 111 } 112 113 func init() { 114 // Use the default logger by default. We only allow LogLevelFatal by 115 // default, because internally we mask a lot of libdm errors by retrying 116 // and similar tricks. Also, libdm is very chatty and we don't want to 117 // worry users for no reason. 118 dmLogger = DefaultLogger{ 119 Level: LogLevelFatal, 120 } 121 122 // Register as early as possible so we don't miss anything. 123 registerLogCallback() 124 }