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  }