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  }