lib/apilog: use ctx based logging and sundry bug fixes.
- context.T now wraps rather than embeds logging.Logger
to avoid an annoying go'ism, namely if a struct
with an embedded struct/interface is converted to
an interface then a 'autogenerated' call frame is
inserted in order to create the interface instance. This
messes up the 'InfoDepth' usage.
- logger.Manager needs to test for context.T and extract its
underlying implementation in order to work correctly.
This was the root cause of the bug fixed by: v.io/c/12543
- expand the unit tests to hopefully catch any regressions.
MultiPart: 1/2
Change-Id: I76b1723d31d394be481b4c2947838a1a5096df73
diff --git a/internal/logger/logger.go b/internal/logger/logger.go
index 22749a2..4f07b0d 100644
--- a/internal/logger/logger.go
+++ b/internal/logger/logger.go
@@ -10,6 +10,7 @@
import (
"v.io/x/lib/vlog"
+ "v.io/v23/context"
"v.io/v23/logging"
)
@@ -53,7 +54,7 @@
func (*dummy) ConfigureFromFlags() error { return nil }
func (*dummy) ExplicitlySetFlags() map[string]string { return nil }
-// Manager determines if the supplied logger implements ManageLog and if so
+// Manager determines if the supplied logging.Logger implements ManageLog and if so
// returns an instance of it. If it doesn't implement ManageLog then Manager
// will return a dummy implementation that is essentially a no-op. It is
// always safe to use it as: logger.Manager(logger.Global()).LogDir() for example.
@@ -61,5 +62,13 @@
if vl, ok := logger.(ManageLog); ok {
return vl
}
+ // If the logger is a context.T then ask it for its implementation
+ // of logging.Logger and look for ManageLog being implemented by it,
+ // since context.T can never implement ManageLog itself.
+ if ctx, ok := logger.(*context.T); ok {
+ if l, ok := ctx.LoggerImplementation().(logging.Logger); ok {
+ return Manager(l)
+ }
+ }
return &dummy{}
}
diff --git a/internal/logger/logger_test.go b/internal/logger/logger_test.go
index ecfad73..617df60 100644
--- a/internal/logger/logger_test.go
+++ b/internal/logger/logger_test.go
@@ -9,6 +9,7 @@
"v.io/x/lib/vlog"
+ "v.io/v23/context"
"v.io/v23/logging"
"v.io/x/ref/internal/logger"
@@ -28,4 +29,17 @@
// Make sure vlog.Log satisfies the logging interfaces
var _ logger.ManageLog = vlog.Log
var _ logging.Logger = vlog.Log
+
+ // Make sure context.T implements logging.T
+ ctx, _ := context.RootContext()
+ var _ logging.Logger = ctx
+
+ // Make sure that logger.Manager can extract the appropriate management
+ // interface from a context.
+ nl := vlog.NewLogger("test")
+ ctx = context.WithLogger(ctx, nl)
+ manager = logger.Manager(ctx)
+ if _, ok := manager.(*vlog.Logger); !ok {
+ t.Errorf("failed to extract correct manager type")
+ }
}
diff --git a/lib/apilog/apilog.go b/lib/apilog/apilog.go
index 8e76855..a5d3d91 100644
--- a/lib/apilog/apilog.go
+++ b/lib/apilog/apilog.go
@@ -18,21 +18,15 @@
"runtime"
"sync/atomic"
- "v.io/x/lib/vlog"
-
"v.io/v23/context"
"v.io/v23/logging"
+
+ "v.io/x/ref/internal/logger"
)
// logCallLogLevel is the log level beyond which calls are logged.
const logCallLogLevel = 1
-var logger logging.Logger
-
-func init() {
- logger = vlog.Log
-}
-
func callerLocation() string {
var funcName string
const stackSkip = 1
@@ -46,6 +40,13 @@
return funcName
}
+func getLogger(ctx *context.T) logging.Logger {
+ if ctx == nil {
+ return logger.Global()
+ }
+ return ctx
+}
+
// LogCall logs that its caller has been called given the arguments
// passed to it. It returns a function that is supposed to be called
// when the caller returns, logging the caller’s return along with the
@@ -98,7 +99,8 @@
// }
//
func LogCall(ctx *context.T, v ...interface{}) func(*context.T, ...interface{}) {
- if !logger.V(logCallLogLevel) { // TODO(mattr): add call to vtrace.
+ l := getLogger(ctx)
+ if !l.V(logCallLogLevel) { // TODO(mattr): add call to vtrace.
return func(*context.T, ...interface{}) {}
}
callerLocation := callerLocation()
@@ -109,7 +111,7 @@
} else {
output = fmt.Sprintf("call[%s %s]", callerLocation, invocationId)
}
- logger.InfoDepth(1, output)
+ l.InfoDepth(1, output)
// TODO(mattr): annotate vtrace span.
return func(ctx *context.T, v ...interface{}) {
@@ -119,7 +121,7 @@
} else {
output = fmt.Sprintf("return[%s %s]", callerLocation, invocationId)
}
- logger.InfoDepth(1, output)
+ getLogger(ctx).InfoDepth(1, output)
// TODO(mattr): annotate vtrace span.
}
}
@@ -134,17 +136,18 @@
// }
//
func LogCallf(ctx *context.T, format string, v ...interface{}) func(*context.T, string, ...interface{}) {
- if !logger.V(logCallLogLevel) { // TODO(mattr): add call to vtrace.
+ l := getLogger(ctx)
+ if !l.V(logCallLogLevel) { // TODO(mattr): add call to vtrace.
return func(*context.T, string, ...interface{}) {}
}
callerLocation := callerLocation()
invocationId := newInvocationIdentifier()
output := fmt.Sprintf("call[%s %s]: %s", callerLocation, invocationId, fmt.Sprintf(format, v...))
- logger.InfoDepth(1, output)
+ l.InfoDepth(1, output)
// TODO(mattr): annotate vtrace span.
return func(ctx *context.T, format string, v ...interface{}) {
output := fmt.Sprintf("return[%s %s]: %v", callerLocation, invocationId, fmt.Sprintf(format, derefSlice(v)...))
- logger.InfoDepth(1, output)
+ getLogger(ctx).InfoDepth(1, output)
// TODO(mattr): annotate vtrace span.
}
}
diff --git a/lib/apilog/apilog_test.go b/lib/apilog/apilog_test.go
index 7debc4c..e440cb1 100644
--- a/lib/apilog/apilog_test.go
+++ b/lib/apilog/apilog_test.go
@@ -14,6 +14,8 @@
"v.io/x/lib/vlog"
+ "v.io/v23/context"
+
"v.io/x/ref/lib/apilog"
)
@@ -42,9 +44,9 @@
return contents, nil
}
-func myLoggedFunc() {
- f := apilog.LogCall(nil, "entry")
- f(nil, "exit")
+func myLoggedFunc(ctx *context.T) {
+ f := apilog.LogCall(ctx, "entry")
+ f(ctx, "exit")
}
func TestLogCall(t *testing.T) {
@@ -53,17 +55,36 @@
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
- logger := vlog.NewLogger("testHeader")
- logger.Configure(vlog.LogDir(dir), vlog.Level(2))
- saveLog := apilog.Log()
- defer func() { apilog.SetLog(saveLog) }()
- apilog.SetLog(logger)
- myLoggedFunc()
- logger.FlushLog()
- contents, err := readLogFiles(dir)
+ l := vlog.NewLogger("testHeader")
+ l.Configure(vlog.LogDir(dir), vlog.Level(2))
+ ctx, _ := context.RootContext()
+ ctx = context.WithLogger(ctx, l)
+ myLoggedFunc(ctx)
+ ctx.FlushLog()
+ testLogOutput(t, dir)
+}
+
+func TestLogCallNoContext(t *testing.T) {
+ dir, err := ioutil.TempDir("", "logtest")
+ defer os.RemoveAll(dir)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
+ l := vlog.NewLogger("testHeader")
+ l.Configure(vlog.LogDir(dir), vlog.Level(2))
+ saved := vlog.Log
+ vlog.Log = l
+ defer func() { vlog.Log = saved }()
+ myLoggedFunc(nil)
+ vlog.FlushLog()
+ testLogOutput(t, dir)
+}
+
+func testLogOutput(t *testing.T, dir string) {
+ contents, err := readLogFiles(dir)
+ if err != nil {
+ t.Fatalf("unexpected error: %q %s", dir, err)
+ }
if want, got := 2, len(contents); want != got {
t.Errorf("Expected %d info lines, got %d instead", want, got)
}
diff --git a/lib/apilog/util_test.go b/lib/apilog/util_test.go
deleted file mode 100644
index 252ea2a..0000000
--- a/lib/apilog/util_test.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package apilog
-
-import "v.io/v23/logging"
-
-// TODO(cnicolaou): remove these when we are using the context for logging.
-func SetLog(l logging.Logger) {
- logger = l
-}
-
-func Log() logging.Logger {
- return logger
-}
diff --git a/runtime/internal/rpc/test/client_test.go b/runtime/internal/rpc/test/client_test.go
index a42a2a9..bd29251 100644
--- a/runtime/internal/rpc/test/client_test.go
+++ b/runtime/internal/rpc/test/client_test.go
@@ -22,7 +22,9 @@
"v.io/v23/security"
"v.io/v23/vdlroot/signature"
"v.io/v23/verror"
+
"v.io/x/ref"
+ "v.io/x/ref/internal/logger"
_ "v.io/x/ref/runtime/factories/generic"
inaming "v.io/x/ref/runtime/internal/naming"
irpc "v.io/x/ref/runtime/internal/rpc"
@@ -289,6 +291,7 @@
}
emptyCtx := &context.T{}
+ emptyCtx = context.WithLogger(emptyCtx, logger.Global())
_, err := client.StartCall(emptyCtx, "noname", "nomethod", nil)
if verror.ErrorID(err) != verror.ErrBadArg.ID {
t.Fatalf("wrong error: %s", err)
diff --git a/runtime/internal/rt/runtime.go b/runtime/internal/rt/runtime.go
index 6940890..09d1b70 100644
--- a/runtime/internal/rt/runtime.go
+++ b/runtime/internal/rt/runtime.go
@@ -13,6 +13,7 @@
"syscall"
"time"
+ "v.io/x/lib/metadata"
"v.io/x/lib/pubsub"
"v.io/x/lib/vlog"
@@ -112,14 +113,9 @@
// log_dir for the program. It's a hack, but it gets us the metadata
// to device manager-run apps and avoids it for command-lines, which is
// a good enough approximation.
-
- // TODO(caprita): Disabled on 6/11/2015 because changes in the logging
- // system caused this to be always printed. Re-enable in a different
- // way.
-
- // if logger.Manager(ctx).LogDir() != os.TempDir() {
- // ctx.Infof(metadata.ToXML())
- // }
+ if logger.Manager(ctx).LogDir() != os.TempDir() {
+ ctx.Infof(metadata.ToXML())
+ }
// Setup the initial trace.
ctx, err = ivtrace.Init(ctx, flags.Vtrace)