devmapper_log.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. // +build linux,cgo
  2. package devicemapper
  3. import "C"
  4. import (
  5. "fmt"
  6. "strings"
  7. "github.com/sirupsen/logrus"
  8. )
  9. // DevmapperLogger defines methods required to register as a callback for
  10. // logging events received from devicemapper. Note that devicemapper will send
  11. // *all* logs regardless to callbacks (including debug logs) so it's
  12. // recommended to not spam the console with the outputs.
  13. type DevmapperLogger interface {
  14. // DMLog is the logging callback containing all of the information from
  15. // devicemapper. The interface is identical to the C libdm counterpart.
  16. DMLog(level int, file string, line int, dmError int, message string)
  17. }
  18. // dmLogger is the current logger in use that is being forwarded our messages.
  19. var dmLogger DevmapperLogger
  20. // LogInit changes the logging callback called after processing libdm logs for
  21. // error message information. The default logger simply forwards all logs to
  22. // logrus. Calling LogInit(nil) disables the calling of callbacks.
  23. func LogInit(logger DevmapperLogger) {
  24. dmLogger = logger
  25. }
  26. // Due to the way cgo works this has to be in a separate file, as devmapper.go has
  27. // definitions in the cgo block, which is incompatible with using "//export"
  28. // DevmapperLogCallback exports the devmapper log callback for cgo. Note that
  29. // because we are using callbacks, this function will be called for *every* log
  30. // in libdm (even debug ones because there's no way of setting the verbosity
  31. // level for an external logging callback).
  32. //export DevmapperLogCallback
  33. func DevmapperLogCallback(level C.int, file *C.char, line, dmErrnoOrClass C.int, message *C.char) {
  34. msg := C.GoString(message)
  35. // Track what errno libdm saw, because the library only gives us 0 or 1.
  36. if level < LogLevelDebug {
  37. if strings.Contains(msg, "busy") {
  38. dmSawBusy = true
  39. }
  40. if strings.Contains(msg, "File exists") {
  41. dmSawExist = true
  42. }
  43. if strings.Contains(msg, "No such device or address") {
  44. dmSawEnxio = true
  45. }
  46. }
  47. if dmLogger != nil {
  48. dmLogger.DMLog(int(level), C.GoString(file), int(line), int(dmErrnoOrClass), msg)
  49. }
  50. }
  51. // DefaultLogger is the default logger used by pkg/devicemapper. It forwards
  52. // all logs that are of higher or equal priority to the given level to the
  53. // corresponding logrus level.
  54. type DefaultLogger struct {
  55. // Level corresponds to the highest libdm level that will be forwarded to
  56. // logrus. In order to change this, register a new DefaultLogger.
  57. Level int
  58. }
  59. // DMLog is the logging callback containing all of the information from
  60. // devicemapper. The interface is identical to the C libdm counterpart.
  61. func (l DefaultLogger) DMLog(level int, file string, line, dmError int, message string) {
  62. if level <= l.Level {
  63. // Forward the log to the correct logrus level, if allowed by dmLogLevel.
  64. logMsg := fmt.Sprintf("libdevmapper(%d): %s:%d (%d) %s", level, file, line, dmError, message)
  65. switch level {
  66. case LogLevelFatal, LogLevelErr:
  67. logrus.Error(logMsg)
  68. case LogLevelWarn:
  69. logrus.Warn(logMsg)
  70. case LogLevelNotice, LogLevelInfo:
  71. logrus.Info(logMsg)
  72. case LogLevelDebug:
  73. logrus.Debug(logMsg)
  74. default:
  75. // Don't drop any "unknown" levels.
  76. logrus.Info(logMsg)
  77. }
  78. }
  79. }
  80. // registerLogCallback registers our own logging callback function for libdm
  81. // (which is DevmapperLogCallback).
  82. //
  83. // Because libdm only gives us {0,1} error codes we need to parse the logs
  84. // produced by libdm (to set dmSawBusy and so on). Note that by registering a
  85. // callback using DevmapperLogCallback, libdm will no longer output logs to
  86. // stderr so we have to log everything ourselves. None of this handling is
  87. // optional because we depend on log callbacks to parse the logs, and if we
  88. // don't forward the log information we'll be in a lot of trouble when
  89. // debugging things.
  90. func registerLogCallback() {
  91. LogWithErrnoInit()
  92. }
  93. func init() {
  94. // Use the default logger by default. We only allow LogLevelFatal by
  95. // default, because internally we mask a lot of libdm errors by retrying
  96. // and similar tricks. Also, libdm is very chatty and we don't want to
  97. // worry users for no reason.
  98. dmLogger = DefaultLogger{
  99. Level: LogLevelFatal,
  100. }
  101. // Register as early as possible so we don't miss anything.
  102. registerLogCallback()
  103. }