Browse code

devicemapper: change LogInit and move all callbacks to pkg

LogInit used to act as a manual way of registering the *necessary*
pkg/devicemapper logging callbacks. In addition, it was used to split up
the logic of pkg/devicemapper into daemon/graphdriver/devmapper (such
that some things were logged from libdm).

The manual aspect of this API was completely non-sensical and was just
begging for incorrect usage of pkg/devicemapper, so remove that semantic
and always register our own libdm callbacks.

In addition, recombine the split out logging callbacks into
pkg/devicemapper so that the default logger is local to the library and
also shown to be the recommended logger. This makes the code
substantially easier to read. Also the new DefaultLogger now has
configurable upper-bound for the log level, which allows for dynamically
changing the logging level.

Signed-off-by: Aleksa Sarai <asarai@suse.de>

Aleksa Sarai authored on 2017/06/28 00:46:47
Showing 3 changed files
... ...
@@ -35,17 +35,13 @@ import (
35 35
 )
36 36
 
37 37
 var (
38
-	defaultDataLoopbackSize     int64  = 100 * 1024 * 1024 * 1024
39
-	defaultMetaDataLoopbackSize int64  = 2 * 1024 * 1024 * 1024
40
-	defaultBaseFsSize           uint64 = 10 * 1024 * 1024 * 1024
41
-	defaultThinpBlockSize       uint32 = 128 // 64K = 128 512b sectors
42
-	defaultUdevSyncOverride            = false
43
-	maxDeviceID                        = 0xffffff // 24 bit, pool limit
44
-	deviceIDMapSz                      = (maxDeviceID + 1) / 8
45
-	// We retry device removal so many a times that even error messages
46
-	// will fill up console during normal operation. So only log Fatal
47
-	// messages by default.
48
-	logLevel                            = devicemapper.LogLevelFatal
38
+	defaultDataLoopbackSize      int64  = 100 * 1024 * 1024 * 1024
39
+	defaultMetaDataLoopbackSize  int64  = 2 * 1024 * 1024 * 1024
40
+	defaultBaseFsSize            uint64 = 10 * 1024 * 1024 * 1024
41
+	defaultThinpBlockSize        uint32 = 128 // 64K = 128 512b sectors
42
+	defaultUdevSyncOverride             = false
43
+	maxDeviceID                         = 0xffffff // 24 bit, pool limit
44
+	deviceIDMapSz                       = (maxDeviceID + 1) / 8
49 45
 	driverDeferredRemovalSupport        = false
50 46
 	enableDeferredRemoval               = false
51 47
 	enableDeferredDeletion              = false
... ...
@@ -1273,26 +1269,6 @@ func setCloseOnExec(name string) {
1273 1273
 	}
1274 1274
 }
1275 1275
 
1276
-// DMLog implements logging using DevMapperLogger interface.
1277
-func (devices *DeviceSet) DMLog(level int, file string, line int, dmError int, message string) {
1278
-	// By default libdm sends us all the messages including debug ones.
1279
-	// We need to filter out messages here and figure out which one
1280
-	// should be printed.
1281
-	if level > logLevel {
1282
-		return
1283
-	}
1284
-
1285
-	// FIXME(vbatts) push this back into ./pkg/devicemapper/
1286
-	if level <= devicemapper.LogLevelErr {
1287
-		logrus.Errorf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
1288
-	} else if level <= devicemapper.LogLevelInfo {
1289
-		logrus.Infof("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
1290
-	} else {
1291
-		// FIXME(vbatts) push this back into ./pkg/devicemapper/
1292
-		logrus.Debugf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
1293
-	}
1294
-}
1295
-
1296 1276
 func major(device uint64) uint64 {
1297 1277
 	return (device >> 8) & 0xfff
1298 1278
 }
... ...
@@ -1690,9 +1666,6 @@ func (devices *DeviceSet) enableDeferredRemovalDeletion() error {
1690 1690
 }
1691 1691
 
1692 1692
 func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) {
1693
-	// give ourselves to libdm as a log handler
1694
-	devicemapper.LogInit(devices)
1695
-
1696 1693
 	if err := devices.enableDeferredRemovalDeletion(); err != nil {
1697 1694
 		return err
1698 1695
 	}
... ...
@@ -13,11 +13,6 @@ import (
13 13
 	"github.com/Sirupsen/logrus"
14 14
 )
15 15
 
16
-// DevmapperLogger defines methods for logging with devicemapper.
17
-type DevmapperLogger interface {
18
-	DMLog(level int, file string, line int, dmError int, message string)
19
-}
20
-
21 16
 const (
22 17
 	deviceCreate TaskType = iota
23 18
 	deviceReload
... ...
@@ -264,14 +259,6 @@ func UdevWait(cookie *uint) error {
264 264
 	return nil
265 265
 }
266 266
 
267
-var dmLogger DevmapperLogger
268
-
269
-// LogInit initializes the logger for the device mapper library.
270
-func LogInit(logger DevmapperLogger) {
271
-	dmLogger = logger
272
-	LogWithErrnoInit()
273
-}
274
-
275 267
 // SetDevDir sets the dev folder for the device mapper library (usually /dev).
276 268
 func SetDevDir(dir string) error {
277 269
 	if res := DmSetDevDir(dir); res != 1 {
... ...
@@ -5,17 +5,45 @@ package devicemapper
5 5
 import "C"
6 6
 
7 7
 import (
8
+	"fmt"
8 9
 	"strings"
10
+
11
+	"github.com/Sirupsen/logrus"
9 12
 )
10 13
 
14
+// DevmapperLogger defines methods required to register as a callback for
15
+// logging events recieved 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
+
11 34
 // Due to the way cgo works this has to be in a separate file, as devmapper.go has
12 35
 // definitions in the cgo block, which is incompatible with using "//export"
13 36
 
14
-// DevmapperLogCallback exports the devmapper log callback for cgo.
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).
15 41
 //export DevmapperLogCallback
16
-func DevmapperLogCallback(level C.int, file *C.char, line C.int, dmErrnoOrClass C.int, message *C.char) {
42
+func DevmapperLogCallback(level C.int, file *C.char, line, dmErrnoOrClass C.int, message *C.char) {
17 43
 	msg := C.GoString(message)
18
-	if level < 7 {
44
+
45
+	// Track what errno libdm saw, because the library only gives us 0 or 1.
46
+	if level < LogLevelDebug {
19 47
 		if strings.Contains(msg, "busy") {
20 48
 			dmSawBusy = true
21 49
 		}
... ...
@@ -33,3 +61,61 @@ func DevmapperLogCallback(level C.int, file *C.char, line C.int, dmErrnoOrClass
33 33
 		dmLogger.DMLog(int(level), C.GoString(file), int(line), int(dmErrnoOrClass), msg)
34 34
 	}
35 35
 }
36
+
37
+// DefaultLogger is the default logger used by pkg/devicemapper. It forwards
38
+// all logs that are of higher or equal priority to the given level to the
39
+// corresponding logrus level.
40
+type DefaultLogger struct {
41
+	// Level corresponds to the highest libdm level that will be forwarded to
42
+	// logrus. In order to change this, register a new DefaultLogger.
43
+	Level int
44
+}
45
+
46
+// DMLog is the logging callback containing all of the information from
47
+// devicemapper. The interface is identical to the C libdm counterpart.
48
+func (l DefaultLogger) DMLog(level int, file string, line, dmError int, message string) {
49
+	if int(level) <= l.Level {
50
+		// Forward the log to the correct logrus level, if allowed by dmLogLevel.
51
+		logMsg := fmt.Sprintf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
52
+		switch level {
53
+		case LogLevelFatal, LogLevelErr:
54
+			logrus.Error(logMsg)
55
+		case LogLevelWarn:
56
+			logrus.Warn(logMsg)
57
+		case LogLevelNotice, LogLevelInfo:
58
+			logrus.Info(logMsg)
59
+		case LogLevelDebug:
60
+			logrus.Debug(logMsg)
61
+		default:
62
+			// Don't drop any "unknown" levels.
63
+			logrus.Info(logMsg)
64
+		}
65
+	}
66
+}
67
+
68
+// registerLogCallback registers our own logging callback function for libdm
69
+// (which is DevmapperLogCallback).
70
+//
71
+// Because libdm only gives us {0,1} error codes we need to parse the logs
72
+// produced by libdm (to set dmSawBusy and so on). Note that by registering a
73
+// callback using DevmapperLogCallback, libdm will no longer output logs to
74
+// stderr so we have to log everything ourselves. None of this handling is
75
+// optional because we depend on log callbacks to parse the logs, and if we
76
+// don't forward the log information we'll be in a lot of trouble when
77
+// debugging things.
78
+func registerLogCallback() {
79
+	LogWithErrnoInit()
80
+}
81
+
82
+func init() {
83
+	// Use the default logger by default. We only allow LogLevelFatal by
84
+	// default, because internally we mask a lot of libdm errors by retrying
85
+	// and similar tricks. Also, libdm is very chatty and we don't want to
86
+	// worry users for no reason.
87
+	dmLogger = DefaultLogger{
88
+		Level: LogLevelFatal,
89
+	}
90
+
91
+	// Register as early as possible so we don't miss anything.
92
+	registerLogCallback()
93
+}