github.com/ttys3/engine@v17.12.1-ce-rc2+incompatible/pkg/devicemapper/devmapper_log.go (about) 1 // +build linux,cgo 2 3 package 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 } 59 60 if dmLogger != nil { 61 dmLogger.DMLog(int(level), C.GoString(file), int(line), int(dmErrnoOrClass), msg) 62 } 63 } 64 65 // DefaultLogger is the default logger used by pkg/devicemapper. It forwards 66 // all logs that are of higher or equal priority to the given level to the 67 // corresponding logrus level. 68 type DefaultLogger struct { 69 // Level corresponds to the highest libdm level that will be forwarded to 70 // logrus. In order to change this, register a new DefaultLogger. 71 Level int 72 } 73 74 // DMLog is the logging callback containing all of the information from 75 // devicemapper. The interface is identical to the C libdm counterpart. 76 func (l DefaultLogger) DMLog(level int, file string, line, dmError int, message string) { 77 if level <= l.Level { 78 // Forward the log to the correct logrus level, if allowed by dmLogLevel. 79 logMsg := fmt.Sprintf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message) 80 switch level { 81 case LogLevelFatal, LogLevelErr: 82 logrus.Error(logMsg) 83 case LogLevelWarn: 84 logrus.Warn(logMsg) 85 case LogLevelNotice, LogLevelInfo: 86 logrus.Info(logMsg) 87 case LogLevelDebug: 88 logrus.Debug(logMsg) 89 default: 90 // Don't drop any "unknown" levels. 91 logrus.Info(logMsg) 92 } 93 } 94 } 95 96 // registerLogCallback registers our own logging callback function for libdm 97 // (which is DevmapperLogCallback). 98 // 99 // Because libdm only gives us {0,1} error codes we need to parse the logs 100 // produced by libdm (to set dmSawBusy and so on). Note that by registering a 101 // callback using DevmapperLogCallback, libdm will no longer output logs to 102 // stderr so we have to log everything ourselves. None of this handling is 103 // optional because we depend on log callbacks to parse the logs, and if we 104 // don't forward the log information we'll be in a lot of trouble when 105 // debugging things. 106 func registerLogCallback() { 107 LogWithErrnoInit() 108 } 109 110 func init() { 111 // Use the default logger by default. We only allow LogLevelFatal by 112 // default, because internally we mask a lot of libdm errors by retrying 113 // and similar tricks. Also, libdm is very chatty and we don't want to 114 // worry users for no reason. 115 dmLogger = DefaultLogger{ 116 Level: LogLevelFatal, 117 } 118 119 // Register as early as possible so we don't miss anything. 120 registerLogCallback() 121 }