github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/pkg/devicemapper/devmapper_log.go (about)

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