github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/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  //
    43  //export DevmapperLogCallback
    44  func DevmapperLogCallback(level C.int, file *C.char, line, dmErrnoOrClass C.int, message *C.char) {
    45  	msg := C.GoString(message)
    46  
    47  	// Track what errno libdm saw, because the library only gives us 0 or 1.
    48  	if level < LogLevelDebug {
    49  		if strings.Contains(msg, "busy") {
    50  			dmSawBusy = true
    51  		}
    52  
    53  		if strings.Contains(msg, "File exists") {
    54  			dmSawExist = true
    55  		}
    56  
    57  		if strings.Contains(msg, "No such device or address") {
    58  			dmSawEnxio = true
    59  		}
    60  		if strings.Contains(msg, "No data available") {
    61  			dmSawEnoData = true
    62  		}
    63  	}
    64  
    65  	if dmLogger != nil {
    66  		dmLogger.DMLog(int(level), C.GoString(file), int(line), int(dmErrnoOrClass), msg)
    67  	}
    68  }
    69  
    70  // DefaultLogger is the default logger used by pkg/devicemapper. It forwards
    71  // all logs that are of higher or equal priority to the given level to the
    72  // corresponding logrus level.
    73  type DefaultLogger struct {
    74  	// Level corresponds to the highest libdm level that will be forwarded to
    75  	// logrus. In order to change this, register a new DefaultLogger.
    76  	Level int
    77  }
    78  
    79  // DMLog is the logging callback containing all of the information from
    80  // devicemapper. The interface is identical to the C libdm counterpart.
    81  func (l DefaultLogger) DMLog(level int, file string, line, dmError int, message string) {
    82  	if level <= l.Level {
    83  		// Forward the log to the correct logrus level, if allowed by dmLogLevel.
    84  		logMsg := fmt.Sprintf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
    85  		switch level {
    86  		case LogLevelFatal, LogLevelErr:
    87  			logrus.Error(logMsg)
    88  		case LogLevelWarn:
    89  			logrus.Warn(logMsg)
    90  		case LogLevelNotice, LogLevelInfo:
    91  			logrus.Info(logMsg)
    92  		case LogLevelDebug:
    93  			logrus.Debug(logMsg)
    94  		default:
    95  			// Don't drop any "unknown" levels.
    96  			logrus.Info(logMsg)
    97  		}
    98  	}
    99  }
   100  
   101  // registerLogCallback registers our own logging callback function for libdm
   102  // (which is DevmapperLogCallback).
   103  //
   104  // Because libdm only gives us {0,1} error codes we need to parse the logs
   105  // produced by libdm (to set dmSawBusy and so on). Note that by registering a
   106  // callback using DevmapperLogCallback, libdm will no longer output logs to
   107  // stderr so we have to log everything ourselves. None of this handling is
   108  // optional because we depend on log callbacks to parse the logs, and if we
   109  // don't forward the log information we'll be in a lot of trouble when
   110  // debugging things.
   111  func registerLogCallback() {
   112  	LogWithErrnoInit()
   113  }
   114  
   115  func init() {
   116  	// Use the default logger by default. We only allow LogLevelFatal by
   117  	// default, because internally we mask a lot of libdm errors by retrying
   118  	// and similar tricks. Also, libdm is very chatty and we don't want to
   119  	// worry users for no reason.
   120  	dmLogger = DefaultLogger{
   121  		Level: LogLevelFatal,
   122  	}
   123  
   124  	// Register as early as possible so we don't miss anything.
   125  	registerLogCallback()
   126  }