Support setting a logger for fuse errors (but not other debug info).

For GoogleCloudPlatform/gcsfuse#60.
geesefs-0-30-9
Aaron Jacobs 2015-05-25 14:23:25 +10:00
commit c1f7e56cab
5 changed files with 75 additions and 29 deletions

View File

@ -40,7 +40,8 @@ var fTraceByPID = flag.Bool(
// A connection to the fuse kernel process. // A connection to the fuse kernel process.
type Connection struct { type Connection struct {
logger *log.Logger debugLogger *log.Logger
errorLogger *log.Logger
wrapped *bazilfuse.Conn wrapped *bazilfuse.Conn
opsInFlight sync.WaitGroup opsInFlight sync.WaitGroup
@ -68,10 +69,12 @@ type Connection struct {
// result. You must call c.close() eventually. // result. You must call c.close() eventually.
func newConnection( func newConnection(
parentCtx context.Context, parentCtx context.Context,
logger *log.Logger, debugLogger *log.Logger,
errorLogger *log.Logger,
wrapped *bazilfuse.Conn) (c *Connection, err error) { wrapped *bazilfuse.Conn) (c *Connection, err error) {
c = &Connection{ c = &Connection{
logger: logger, debugLogger: debugLogger,
errorLogger: errorLogger,
wrapped: wrapped, wrapped: wrapped,
parentCtx: parentCtx, parentCtx: parentCtx,
cancelFuncs: make(map[bazilfuse.RequestID]func()), cancelFuncs: make(map[bazilfuse.RequestID]func()),
@ -83,7 +86,7 @@ func newConnection(
// Log information for an operation with the given ID. calldepth is the depth // Log information for an operation with the given ID. calldepth is the depth
// to use when recovering file:line information with runtime.Caller. // to use when recovering file:line information with runtime.Caller.
func (c *Connection) log( func (c *Connection) debugLog(
opID uint32, opID uint32,
calldepth int, calldepth int,
format string, format string,
@ -108,7 +111,7 @@ func (c *Connection) log(
fmt.Sprintf(format, v...)) fmt.Sprintf(format, v...))
// Print it. // Print it.
c.logger.Println(msg) c.debugLogger.Println(msg)
} }
// LOCKS_EXCLUDED(c.mu) // LOCKS_EXCLUDED(c.mu)
@ -316,13 +319,13 @@ func (c *Connection) ReadOp() (op fuseops.Op, err error) {
c.nextOpID++ c.nextOpID++
// Log the receipt of the operation. // Log the receipt of the operation.
c.log(opID, 1, "<- %v", bfReq) c.debugLog(opID, 1, "<- %v", bfReq)
// Special case: responding to statfs is required to make mounting work on // Special case: responding to statfs is required to make mounting work on
// OS X. We don't currently expose the capability for the file system to // OS X. We don't currently expose the capability for the file system to
// intercept this. // intercept this.
if statfsReq, ok := bfReq.(*bazilfuse.StatfsRequest); ok { if statfsReq, ok := bfReq.(*bazilfuse.StatfsRequest); ok {
c.log(opID, 1, "-> (Statfs) OK") c.debugLog(opID, 1, "-> (Statfs) OK")
statfsReq.Respond(&bazilfuse.StatfsResponse{}) statfsReq.Respond(&bazilfuse.StatfsResponse{})
continue continue
} }
@ -336,13 +339,19 @@ func (c *Connection) ReadOp() (op fuseops.Op, err error) {
// Set up op dependencies. // Set up op dependencies.
opCtx := c.beginOp(bfReq) opCtx := c.beginOp(bfReq)
logForOp := func(calldepth int, format string, v ...interface{}) { debugLogForOp := func(calldepth int, format string, v ...interface{}) {
c.log(opID, calldepth+1, format, v...) c.debugLog(opID, calldepth+1, format, v...)
} }
finished := func(err error) { c.finishOp(bfReq) } finished := func(err error) { c.finishOp(bfReq) }
op = fuseops.Convert(opCtx, bfReq, logForOp, finished) op = fuseops.Convert(
opCtx,
bfReq,
debugLogForOp,
c.errorLogger,
finished)
return return
} }
} }

View File

@ -28,12 +28,12 @@ var fEnableDebug = flag.Bool(
false, false,
"Write FUSE debugging messages to stderr.") "Write FUSE debugging messages to stderr.")
var gLogger *log.Logger var gDebugLogger *log.Logger
var gLoggerOnce sync.Once var gDebugLoggerOnce sync.Once
func initLogger() { func initDebugDebugLogger() {
if !flag.Parsed() { if !flag.Parsed() {
panic("initLogger called before flags available.") panic("initDebugDebugLogger called before flags available.")
} }
var writer io.Writer = ioutil.Discard var writer io.Writer = ioutil.Discard
@ -42,10 +42,10 @@ func initLogger() {
} }
const flags = log.Ldate | log.Ltime | log.Lmicroseconds const flags = log.Ldate | log.Ltime | log.Lmicroseconds
gLogger = log.New(writer, "", flags) gDebugLogger = log.New(writer, "", flags)
} }
func getLogger() *log.Logger { func getDebugLogger() *log.Logger {
gLoggerOnce.Do(initLogger) gDebugLoggerOnce.Do(initDebugDebugLogger)
return gLogger return gDebugLogger
} }

View File

@ -16,6 +16,7 @@ package fuseops
import ( import (
"fmt" "fmt"
"log"
"reflect" "reflect"
"strings" "strings"
@ -46,9 +47,12 @@ type commonOp struct {
// The underlying bazilfuse request for this op. // The underlying bazilfuse request for this op.
bazilReq bazilfuse.Request bazilReq bazilfuse.Request
// A function that can be used to log information about the op. The first // A function that can be used to log debug information about the op. The
// argument is a call depth. // first argument is a call depth.
log func(int, string, ...interface{}) debugLog func(int, string, ...interface{})
// A logger to be used for logging exceptional errors.
errorLogger *log.Logger
// A function that is invoked with the error given to Respond, for use in // A function that is invoked with the error given to Respond, for use in
// closing off traces and reporting back to the connection. // closing off traces and reporting back to the connection.
@ -76,13 +80,15 @@ func (o *commonOp) init(
ctx context.Context, ctx context.Context,
op internalOp, op internalOp,
bazilReq bazilfuse.Request, bazilReq bazilfuse.Request,
log func(int, string, ...interface{}), debugLog func(int, string, ...interface{}),
errorLogger *log.Logger,
finished func(error)) { finished func(error)) {
// Initialize basic fields. // Initialize basic fields.
o.ctx = ctx o.ctx = ctx
o.op = op o.op = op
o.bazilReq = bazilReq o.bazilReq = bazilReq
o.log = log o.debugLog = debugLog
o.errorLogger = errorLogger
o.finished = finished o.finished = finished
// Set up a trace span for this op. // Set up a trace span for this op.
@ -111,7 +117,7 @@ func (o *commonOp) Context() context.Context {
func (o *commonOp) Logf(format string, v ...interface{}) { func (o *commonOp) Logf(format string, v ...interface{}) {
const calldepth = 2 const calldepth = 2
o.log(calldepth, format, v...) o.debugLog(calldepth, format, v...)
} }
func (o *commonOp) Respond(err error) { func (o *commonOp) Respond(err error) {
@ -130,6 +136,11 @@ func (o *commonOp) Respond(err error) {
o.op.ShortDesc(), o.op.ShortDesc(),
err) err)
o.errorLogger.Printf(
"(%s) error: %v",
o.op.ShortDesc(),
err)
// Send a response to the kernel. // Send a response to the kernel.
o.bazilReq.RespondError(err) o.bazilReq.RespondError(err)
} }

View File

@ -15,6 +15,7 @@
package fuseops package fuseops
import ( import (
"log"
"time" "time"
"golang.org/x/net/context" "golang.org/x/net/context"
@ -34,7 +35,8 @@ import (
func Convert( func Convert(
opCtx context.Context, opCtx context.Context,
r bazilfuse.Request, r bazilfuse.Request,
logForOp func(int, string, ...interface{}), debugLogForOp func(int, string, ...interface{}),
errorLogger *log.Logger,
finished func(error)) (o Op) { finished func(error)) (o Op) {
var co *commonOp var co *commonOp
@ -239,7 +241,13 @@ func Convert(
co = &to.commonOp co = &to.commonOp
} }
co.init(opCtx, io, r, logForOp, finished) co.init(
opCtx,
io,
r,
debugLogForOp,
errorLogger,
finished)
o = io o = io
return return

View File

@ -16,6 +16,8 @@ package fuse
import ( import (
"fmt" "fmt"
"io/ioutil"
"log"
"runtime" "runtime"
"github.com/jacobsa/bazilfuse" "github.com/jacobsa/bazilfuse"
@ -75,6 +77,11 @@ type MountConfig struct {
// chtimes, etc. will fail. // chtimes, etc. will fail.
ReadOnly bool ReadOnly bool
// A logger to use for logging errors. All errors are logged, with the
// exception of a few blacklisted errors that are expected. If nil, no error
// logging is performed.
ErrorLogger *log.Logger
// OS X only. // OS X only.
// //
// Normally on OS X we mount with the novncache option // Normally on OS X we mount with the novncache option
@ -148,7 +155,7 @@ func Mount(
dir string, dir string,
server Server, server Server,
config *MountConfig) (mfs *MountedFileSystem, err error) { config *MountConfig) (mfs *MountedFileSystem, err error) {
logger := getLogger() debugLogger := getDebugLogger()
// Initialize the struct. // Initialize the struct.
mfs = &MountedFileSystem{ mfs = &MountedFileSystem{
@ -157,7 +164,7 @@ func Mount(
} }
// Open a bazilfuse connection. // Open a bazilfuse connection.
logger.Println("Opening a bazilfuse connection.") debugLogger.Println("Opening a bazilfuse connection.")
bfConn, err := bazilfuse.Mount(mfs.dir, config.bazilfuseOptions()...) bfConn, err := bazilfuse.Mount(mfs.dir, config.bazilfuseOptions()...)
if err != nil { if err != nil {
err = fmt.Errorf("bazilfuse.Mount: %v", err) err = fmt.Errorf("bazilfuse.Mount: %v", err)
@ -170,8 +177,19 @@ func Mount(
opContext = context.Background() opContext = context.Background()
} }
// Create a /dev/null error logger if necessary.
errorLogger := config.ErrorLogger
if errorLogger == nil {
errorLogger = log.New(ioutil.Discard, "", 0)
}
// Create our own Connection object wrapping it. // Create our own Connection object wrapping it.
connection, err := newConnection(opContext, logger, bfConn) connection, err := newConnection(
opContext,
debugLogger,
errorLogger,
bfConn)
if err != nil { if err != nil {
bfConn.Close() bfConn.Close()
err = fmt.Errorf("newConnection: %v", err) err = fmt.Errorf("newConnection: %v", err)