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