devicemapper: rework logging and add --storage-opt dm.libdm_log_level
| ... | ... |
@@ -34,17 +34,13 @@ import ( |
| 34 | 34 |
) |
| 35 | 35 |
|
| 36 | 36 |
var ( |
| 37 |
- defaultDataLoopbackSize int64 = 100 * 1024 * 1024 * 1024 |
|
| 38 |
- defaultMetaDataLoopbackSize int64 = 2 * 1024 * 1024 * 1024 |
|
| 39 |
- defaultBaseFsSize uint64 = 10 * 1024 * 1024 * 1024 |
|
| 40 |
- defaultThinpBlockSize uint32 = 128 // 64K = 128 512b sectors |
|
| 41 |
- defaultUdevSyncOverride = false |
|
| 42 |
- maxDeviceID = 0xffffff // 24 bit, pool limit |
|
| 43 |
- deviceIDMapSz = (maxDeviceID + 1) / 8 |
|
| 44 |
- // We retry device removal so many a times that even error messages |
|
| 45 |
- // will fill up console during normal operation. So only log Fatal |
|
| 46 |
- // messages by default. |
|
| 47 |
- logLevel = devicemapper.LogLevelFatal |
|
| 37 |
+ defaultDataLoopbackSize int64 = 100 * 1024 * 1024 * 1024 |
|
| 38 |
+ defaultMetaDataLoopbackSize int64 = 2 * 1024 * 1024 * 1024 |
|
| 39 |
+ defaultBaseFsSize uint64 = 10 * 1024 * 1024 * 1024 |
|
| 40 |
+ defaultThinpBlockSize uint32 = 128 // 64K = 128 512b sectors |
|
| 41 |
+ defaultUdevSyncOverride = false |
|
| 42 |
+ maxDeviceID = 0xffffff // 24 bit, pool limit |
|
| 43 |
+ deviceIDMapSz = (maxDeviceID + 1) / 8 |
|
| 48 | 44 |
driverDeferredRemovalSupport = false |
| 49 | 45 |
enableDeferredRemoval = false |
| 50 | 46 |
enableDeferredDeletion = false |
| ... | ... |
@@ -1272,26 +1268,6 @@ func setCloseOnExec(name string) {
|
| 1272 | 1272 |
} |
| 1273 | 1273 |
} |
| 1274 | 1274 |
|
| 1275 |
-// DMLog implements logging using DevMapperLogger interface. |
|
| 1276 |
-func (devices *DeviceSet) DMLog(level int, file string, line int, dmError int, message string) {
|
|
| 1277 |
- // By default libdm sends us all the messages including debug ones. |
|
| 1278 |
- // We need to filter out messages here and figure out which one |
|
| 1279 |
- // should be printed. |
|
| 1280 |
- if level > logLevel {
|
|
| 1281 |
- return |
|
| 1282 |
- } |
|
| 1283 |
- |
|
| 1284 |
- // FIXME(vbatts) push this back into ./pkg/devicemapper/ |
|
| 1285 |
- if level <= devicemapper.LogLevelErr {
|
|
| 1286 |
- logrus.Errorf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
|
|
| 1287 |
- } else if level <= devicemapper.LogLevelInfo {
|
|
| 1288 |
- logrus.Infof("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
|
|
| 1289 |
- } else {
|
|
| 1290 |
- // FIXME(vbatts) push this back into ./pkg/devicemapper/ |
|
| 1291 |
- logrus.Debugf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
|
|
| 1292 |
- } |
|
| 1293 |
-} |
|
| 1294 |
- |
|
| 1295 | 1275 |
func major(device uint64) uint64 {
|
| 1296 | 1276 |
return (device >> 8) & 0xfff |
| 1297 | 1277 |
} |
| ... | ... |
@@ -1689,9 +1665,6 @@ func (devices *DeviceSet) enableDeferredRemovalDeletion() error {
|
| 1689 | 1689 |
} |
| 1690 | 1690 |
|
| 1691 | 1691 |
func (devices *DeviceSet) initDevmapper(doInit bool) (retErr error) {
|
| 1692 |
- // give ourselves to libdm as a log handler |
|
| 1693 |
- devicemapper.LogInit(devices) |
|
| 1694 |
- |
|
| 1695 | 1692 |
if err := devices.enableDeferredRemovalDeletion(); err != nil {
|
| 1696 | 1693 |
return err |
| 1697 | 1694 |
} |
| ... | ... |
@@ -2804,6 +2777,18 @@ func NewDeviceSet(root string, doInit bool, options []string, uidMaps, gidMaps [ |
| 2804 | 2804 |
return nil, errors.New("dm.thinp_autoextend_threshold must be greater than 0 and less than 100")
|
| 2805 | 2805 |
} |
| 2806 | 2806 |
lvmSetupConfig.AutoExtendThreshold = per |
| 2807 |
+ case "dm.libdm_log_level": |
|
| 2808 |
+ level, err := strconv.ParseInt(val, 10, 32) |
|
| 2809 |
+ if err != nil {
|
|
| 2810 |
+ return nil, errors.Wrapf(err, "could not parse `dm.libdm_log_level=%s`", val) |
|
| 2811 |
+ } |
|
| 2812 |
+ if level < devicemapper.LogLevelFatal || level > devicemapper.LogLevelDebug {
|
|
| 2813 |
+ return nil, errors.Errorf("dm.libdm_log_level must be in range [%d,%d]", devicemapper.LogLevelFatal, devicemapper.LogLevelDebug)
|
|
| 2814 |
+ } |
|
| 2815 |
+ // Register a new logging callback with the specified level. |
|
| 2816 |
+ devicemapper.LogInit(devicemapper.DefaultLogger{
|
|
| 2817 |
+ Level: int(level), |
|
| 2818 |
+ }) |
|
| 2807 | 2819 |
default: |
| 2808 | 2820 |
return nil, fmt.Errorf("devmapper: Unknown option %s\n", key)
|
| 2809 | 2821 |
} |
| ... | ... |
@@ -13,11 +13,6 @@ import ( |
| 13 | 13 |
"golang.org/x/sys/unix" |
| 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,19 +259,6 @@ func UdevWait(cookie *uint) error {
|
| 264 | 264 |
return nil |
| 265 | 265 |
} |
| 266 | 266 |
|
| 267 |
-// LogInitVerbose is an interface to initialize the verbose logger for the device mapper library. |
|
| 268 |
-func LogInitVerbose(level int) {
|
|
| 269 |
- DmLogInitVerbose(level) |
|
| 270 |
-} |
|
| 271 |
- |
|
| 272 |
-var dmLogger DevmapperLogger |
|
| 273 |
- |
|
| 274 |
-// LogInit initializes the logger for the device mapper library. |
|
| 275 |
-func LogInit(logger DevmapperLogger) {
|
|
| 276 |
- dmLogger = logger |
|
| 277 |
- LogWithErrnoInit() |
|
| 278 |
-} |
|
| 279 |
- |
|
| 280 | 267 |
// SetDevDir sets the dev folder for the device mapper library (usually /dev). |
| 281 | 268 |
func SetDevDir(dir string) error {
|
| 282 | 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 |
+} |
| ... | ... |
@@ -4,6 +4,7 @@ package devicemapper |
| 4 | 4 |
|
| 5 | 5 |
/* |
| 6 | 6 |
#cgo LDFLAGS: -L. -ldevmapper |
| 7 |
+#define _GNU_SOURCE |
|
| 7 | 8 |
#include <libdevmapper.h> |
| 8 | 9 |
#include <linux/fs.h> // FIXME: present only for BLKGETSIZE64, maybe we can remove it? |
| 9 | 10 |
|
| ... | ... |
@@ -12,19 +13,20 @@ extern void DevmapperLogCallback(int level, char *file, int line, int dm_errno_o |
| 12 | 12 |
|
| 13 | 13 |
static void log_cb(int level, const char *file, int line, int dm_errno_or_class, const char *f, ...) |
| 14 | 14 |
{
|
| 15 |
- char buffer[256]; |
|
| 16 |
- va_list ap; |
|
| 15 |
+ char *buffer = NULL; |
|
| 16 |
+ va_list ap; |
|
| 17 | 17 |
|
| 18 |
- va_start(ap, f); |
|
| 19 |
- vsnprintf(buffer, 256, f, ap); |
|
| 20 |
- va_end(ap); |
|
| 18 |
+ va_start(ap, f); |
|
| 19 |
+ vasprintf(&buffer, f, ap); |
|
| 20 |
+ va_end(ap); |
|
| 21 | 21 |
|
| 22 |
- DevmapperLogCallback(level, (char *)file, line, dm_errno_or_class, buffer); |
|
| 22 |
+ DevmapperLogCallback(level, (char *)file, line, dm_errno_or_class, buffer); |
|
| 23 |
+ free(buffer); |
|
| 23 | 24 |
} |
| 24 | 25 |
|
| 25 | 26 |
static void log_with_errno_init() |
| 26 | 27 |
{
|
| 27 |
- dm_log_with_errno_init(log_cb); |
|
| 28 |
+ dm_log_with_errno_init(log_cb); |
|
| 28 | 29 |
} |
| 29 | 30 |
*/ |
| 30 | 31 |
import "C" |
| ... | ... |
@@ -56,7 +58,6 @@ const ( |
| 56 | 56 |
var ( |
| 57 | 57 |
DmGetLibraryVersion = dmGetLibraryVersionFct |
| 58 | 58 |
DmGetNextTarget = dmGetNextTargetFct |
| 59 |
- DmLogInitVerbose = dmLogInitVerboseFct |
|
| 60 | 59 |
DmSetDevDir = dmSetDevDirFct |
| 61 | 60 |
DmTaskAddTarget = dmTaskAddTargetFct |
| 62 | 61 |
DmTaskCreate = dmTaskCreateFct |
| ... | ... |
@@ -226,10 +227,6 @@ func dmCookieSupportedFct() int {
|
| 226 | 226 |
return int(C.dm_cookie_supported()) |
| 227 | 227 |
} |
| 228 | 228 |
|
| 229 |
-func dmLogInitVerboseFct(level int) {
|
|
| 230 |
- C.dm_log_init_verbose(C.int(level)) |
|
| 231 |
-} |
|
| 232 |
- |
|
| 233 | 229 |
func logWithErrnoInitFct() {
|
| 234 | 230 |
C.log_with_errno_init() |
| 235 | 231 |
} |