85357a11 |
// +build linux,cgo |
24c03b2d |
|
e2f8fbfb |
package devicemapper |
b440ec01 |
import "C"
|
586a511c |
import ( |
cfd39e8d |
"fmt" |
586a511c |
"strings" |
cfd39e8d |
|
1009e6a4 |
"github.com/sirupsen/logrus" |
586a511c |
)
|
cfd39e8d |
// DevmapperLogger defines methods required to register as a callback for |
94cefa21 |
// logging events received from devicemapper. Note that devicemapper will send |
cfd39e8d |
// *all* logs regardless to callbacks (including debug logs) so it's
// recommended to not spam the console with the outputs.
type DevmapperLogger interface {
// DMLog is the logging callback containing all of the information from
// devicemapper. The interface is identical to the C libdm counterpart.
DMLog(level int, file string, line int, dmError int, message string)
}
// dmLogger is the current logger in use that is being forwarded our messages.
var dmLogger DevmapperLogger
// LogInit changes the logging callback called after processing libdm logs for
// error message information. The default logger simply forwards all logs to
// logrus. Calling LogInit(nil) disables the calling of callbacks.
func LogInit(logger DevmapperLogger) {
dmLogger = logger
}
|
b440ec01 |
// Due to the way cgo works this has to be in a separate file, as devmapper.go has
// definitions in the cgo block, which is incompatible with using "//export"
|
cfd39e8d |
// DevmapperLogCallback exports the devmapper log callback for cgo. Note that
// because we are using callbacks, this function will be called for *every* log
// in libdm (even debug ones because there's no way of setting the verbosity
// level for an external logging callback). |
b440ec01 |
//export DevmapperLogCallback |
cfd39e8d |
func DevmapperLogCallback(level C.int, file *C.char, line, dmErrnoOrClass C.int, message *C.char) { |
586a511c |
msg := C.GoString(message) |
cfd39e8d |
// Track what errno libdm saw, because the library only gives us 0 or 1.
if level < LogLevelDebug { |
586a511c |
if strings.Contains(msg, "busy") {
dmSawBusy = true
} |
f26203cf |
if strings.Contains(msg, "File exists") {
dmSawExist = true
} |
20b38f42 |
if strings.Contains(msg, "No such device or address") {
dmSawEnxio = true
} |
8451d03d |
if strings.Contains(msg, "No data available") {
dmSawEnoData = true
} |
586a511c |
}
|
b440ec01 |
if dmLogger != nil { |
0c70eb83 |
dmLogger.DMLog(int(level), C.GoString(file), int(line), int(dmErrnoOrClass), msg) |
b440ec01 |
}
} |
cfd39e8d |
// DefaultLogger is the default logger used by pkg/devicemapper. It forwards
// all logs that are of higher or equal priority to the given level to the
// corresponding logrus level.
type DefaultLogger struct {
// Level corresponds to the highest libdm level that will be forwarded to
// logrus. In order to change this, register a new DefaultLogger.
Level int
}
// DMLog is the logging callback containing all of the information from
// devicemapper. The interface is identical to the C libdm counterpart.
func (l DefaultLogger) DMLog(level int, file string, line, dmError int, message string) { |
2f5f0af3 |
if level <= l.Level { |
cfd39e8d |
// Forward the log to the correct logrus level, if allowed by dmLogLevel.
logMsg := fmt.Sprintf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
switch level {
case LogLevelFatal, LogLevelErr:
logrus.Error(logMsg)
case LogLevelWarn:
logrus.Warn(logMsg)
case LogLevelNotice, LogLevelInfo:
logrus.Info(logMsg)
case LogLevelDebug:
logrus.Debug(logMsg)
default:
// Don't drop any "unknown" levels.
logrus.Info(logMsg)
}
}
}
// registerLogCallback registers our own logging callback function for libdm
// (which is DevmapperLogCallback).
//
// Because libdm only gives us {0,1} error codes we need to parse the logs
// produced by libdm (to set dmSawBusy and so on). Note that by registering a
// callback using DevmapperLogCallback, libdm will no longer output logs to
// stderr so we have to log everything ourselves. None of this handling is
// optional because we depend on log callbacks to parse the logs, and if we
// don't forward the log information we'll be in a lot of trouble when
// debugging things.
func registerLogCallback() {
LogWithErrnoInit()
}
func init() {
// Use the default logger by default. We only allow LogLevelFatal by
// default, because internally we mask a lot of libdm errors by retrying
// and similar tricks. Also, libdm is very chatty and we don't want to
// worry users for no reason.
dmLogger = DefaultLogger{
Level: LogLevelFatal,
}
// Register as early as possible so we don't miss anything.
registerLogCallback()
} |