summaryrefslogtreecommitdiff
path: root/pkg/devicemapper/devmapper_log.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/devicemapper/devmapper_log.go')
-rw-r--r--pkg/devicemapper/devmapper_log.go92
1 files changed, 89 insertions, 3 deletions
diff --git a/pkg/devicemapper/devmapper_log.go b/pkg/devicemapper/devmapper_log.go
index 7dd0c60891..098d2405ed 100644
--- a/pkg/devicemapper/devmapper_log.go
+++ b/pkg/devicemapper/devmapper_log.go
@@ -5,17 +5,45 @@ package devicemapper
import "C"
import (
+ "fmt"
"strings"
+
+ "github.com/Sirupsen/logrus"
)
+// DevmapperLogger defines methods required to register as a callback for
+// logging events recieved from devicemapper. Note that devicemapper will send
+// *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
+}
+
// 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"
-// DevmapperLogCallback exports the devmapper log callback for cgo.
+// 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).
//export DevmapperLogCallback
-func DevmapperLogCallback(level C.int, file *C.char, line C.int, dmErrnoOrClass C.int, message *C.char) {
+func DevmapperLogCallback(level C.int, file *C.char, line, dmErrnoOrClass C.int, message *C.char) {
msg := C.GoString(message)
- if level < 7 {
+
+ // Track what errno libdm saw, because the library only gives us 0 or 1.
+ if level < LogLevelDebug {
if strings.Contains(msg, "busy") {
dmSawBusy = true
}
@@ -33,3 +61,61 @@ func DevmapperLogCallback(level C.int, file *C.char, line C.int, dmErrnoOrClass
dmLogger.DMLog(int(level), C.GoString(file), int(line), int(dmErrnoOrClass), msg)
}
}
+
+// 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) {
+ if int(level) <= l.Level {
+ // 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()
+}