ref/lib/logger: provide an implementation of v23/logger.

MultiPart: 2/3
Change-Id: Idc9f5e90863ee40f336be058d9384b8a44a5f2d3
diff --git a/internal/logger/logger.go b/internal/logger/logger.go
new file mode 100644
index 0000000..22749a2
--- /dev/null
+++ b/internal/logger/logger.go
@@ -0,0 +1,65 @@
+// 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 logger provides access to an implementation of v23/logger.Logging
+// for use within a runtime implementation. There is pre-created "global" logger
+// and the ability to create new loggers.
+package logger
+
+import (
+	"v.io/x/lib/vlog"
+
+	"v.io/v23/logging"
+)
+
+// Global returns the global logger.
+func Global() logging.Logger {
+	return vlog.Log
+}
+
+// NewLogger creates a new logger with the supplied name.
+func NewLogger(name string) logging.Logger {
+	return vlog.NewLogger(name)
+}
+
+// ManageLog defines the methods for managing and configuring a logger.
+type ManageLog interface {
+
+	// LogDir returns the directory where the log files are written.
+	LogDir() string
+
+	// Stats returns stats on how many lines/bytes haven been written to
+	// this set of logs.
+	Stats() (Info, Error struct{ Lines, Bytes int64 })
+
+	// ConfigureLoggerFromFlags will configure the supplied logger using
+	// command line flags.
+	ConfigureFromFlags() error
+
+	// ExplicitlySetFlags returns a map of the logging command line flags and their
+	// values formatted as strings.  Only the flags that were explicitly set are
+	// returned. This is intended for use when an application needs to know what
+	// value the flags were set to, for example when creating subprocesses.
+	ExplicitlySetFlags() map[string]string
+}
+
+type dummy struct{}
+
+func (*dummy) LogDir() string { return "" }
+func (*dummy) Stats() (Info, Error struct{ Lines, Bytes int64 }) {
+	return struct{ Lines, Bytes int64 }{0, 0}, struct{ Lines, Bytes int64 }{0, 0}
+}
+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
+// 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.
+func Manager(logger logging.Logger) ManageLog {
+	if vl, ok := logger.(ManageLog); ok {
+		return vl
+	}
+	return &dummy{}
+}
diff --git a/internal/logger/logger_test.go b/internal/logger/logger_test.go
new file mode 100644
index 0000000..ecfad73
--- /dev/null
+++ b/internal/logger/logger_test.go
@@ -0,0 +1,31 @@
+// 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 logger_test
+
+import (
+	"testing"
+
+	"v.io/x/lib/vlog"
+
+	"v.io/v23/logging"
+
+	"v.io/x/ref/internal/logger"
+)
+
+func TestManager(t *testing.T) {
+	global := logger.Global()
+	if _, ok := global.(*vlog.Logger); !ok {
+		t.Fatalf("global logger is not a vlog.Logger")
+	}
+
+	manager := logger.Manager(logger.Global())
+	if _, ok := manager.(*vlog.Logger); !ok {
+		t.Fatalf("logger.Manager does not return a vlog.Logger")
+	}
+
+	// Make sure vlog.Log satisfies the logging interfaces
+	var _ logger.ManageLog = vlog.Log
+	var _ logging.Logger = vlog.Log
+}
diff --git a/lib/apilog/apilog.go b/lib/apilog/apilog.go
index c75f8cb..8e76855 100644
--- a/lib/apilog/apilog.go
+++ b/lib/apilog/apilog.go
@@ -21,12 +21,13 @@
 	"v.io/x/lib/vlog"
 
 	"v.io/v23/context"
+	"v.io/v23/logging"
 )
 
 // logCallLogLevel is the log level beyond which calls are logged.
 const logCallLogLevel = 1
 
-var logger vlog.Logger
+var logger logging.Logger
 
 func init() {
 	logger = vlog.Log
@@ -45,8 +46,6 @@
 	return funcName
 }
 
-// TODO(cnicolaou): remove LogCall from vlog.
-
 // 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
diff --git a/lib/apilog/util_test.go b/lib/apilog/util_test.go
index 9d2ea7d..252ea2a 100644
--- a/lib/apilog/util_test.go
+++ b/lib/apilog/util_test.go
@@ -4,12 +4,13 @@
 
 package apilog
 
-import "v.io/x/lib/vlog"
+import "v.io/v23/logging"
 
-func SetLog(l vlog.Logger) {
+// TODO(cnicolaou): remove these when we are using the context for logging.
+func SetLog(l logging.Logger) {
 	logger = l
 }
 
-func Log() vlog.Logger {
+func Log() logging.Logger {
 	return logger
 }
diff --git a/runtime/factories/fake/runtime.go b/runtime/factories/fake/runtime.go
index 2812aaf..16ee8a4 100644
--- a/runtime/factories/fake/runtime.go
+++ b/runtime/factories/fake/runtime.go
@@ -9,7 +9,7 @@
 	"v.io/v23/context"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
-	"v.io/x/ref/lib/apilog"
+
 	vsecurity "v.io/x/ref/lib/security"
 )
 
@@ -34,28 +34,28 @@
 }
 
 func (r *Runtime) Init(ctx *context.T) error {
-	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
+	// nologcall
 	return nil
 }
 
 func (r *Runtime) WithPrincipal(ctx *context.T, principal security.Principal) (*context.T, error) {
-	defer apilog.LogCallf(ctx, "principal=")(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
+	// nologcall
 	return context.WithValue(ctx, principalKey, principal), nil
 }
 
 func (r *Runtime) GetPrincipal(ctx *context.T) security.Principal {
-	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
+	// nologcall
 	p, _ := ctx.Value(principalKey).(security.Principal)
 	return p
 }
 
 func (r *Runtime) GetAppCycle(ctx *context.T) v23.AppCycle {
-	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
+	// nologcall
 	panic("unimplemented")
 }
 
 func (r *Runtime) WithBackgroundContext(ctx *context.T) *context.T {
-	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
+	// nologcall
 	// Note we add an extra context with a nil value here.
 	// This prevents users from travelling back through the
 	// chain of background contexts.
@@ -64,7 +64,7 @@
 }
 
 func (r *Runtime) GetBackgroundContext(ctx *context.T) *context.T {
-	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
+	// nologcall
 	bctx, _ := ctx.Value(backgroundKey).(*context.T)
 	if bctx == nil {
 		// There should always be a background context.  If we don't find
@@ -77,11 +77,11 @@
 }
 
 func (*Runtime) WithReservedNameDispatcher(ctx *context.T, d rpc.Dispatcher) *context.T {
-	defer apilog.LogCallf(ctx, "d=")(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
+	// nologcall
 	panic("unimplemented")
 }
 
 func (*Runtime) GetReservedNameDispatcher(ctx *context.T) rpc.Dispatcher {
-	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
+	// nologcall
 	panic("unimplmeneted")
 }
diff --git a/runtime/factories/roaming/roaming.go b/runtime/factories/roaming/roaming.go
index 9f6c8ef..40d82e0 100644
--- a/runtime/factories/roaming/roaming.go
+++ b/runtime/factories/roaming/roaming.go
@@ -26,6 +26,7 @@
 	"v.io/v23/context"
 	"v.io/v23/rpc"
 
+	"v.io/x/ref/internal/logger"
 	"v.io/x/ref/lib/flags"
 	"v.io/x/ref/lib/security/securityflag"
 	"v.io/x/ref/runtime/internal"
@@ -62,14 +63,14 @@
 		Addrs: rpc.ListenAddrs(lf.Addrs),
 		Proxy: lf.ListenProxy,
 	}
-	reservedDispatcher := debuglib.NewDispatcher(vlog.Log.LogDir, securityflag.NewAuthorizerOrDie())
+	reservedDispatcher := debuglib.NewDispatcher(logger.Manager(logger.Global()).LogDir, securityflag.NewAuthorizerOrDie())
 
 	ac := appcycle.New()
 
 	// Our address is private, so we test for running on GCE and for its
 	// 1:1 NAT configuration.
-	if !internal.HasPublicIP(vlog.Log) {
-		if addr := internal.GCEPublicAddress(vlog.Log); addr != nil {
+	if !internal.HasPublicIP(logger.Global()) {
+		if addr := internal.GCEPublicAddress(logger.Global()); addr != nil {
 			listenSpec.AddressChooser = func(string, []net.Addr) ([]net.Addr, error) {
 				// TODO(cnicolaou): the protocol at least should
 				// be configurable, or maybe there's a RuntimeFactory specific
diff --git a/runtime/factories/static/static.go b/runtime/factories/static/static.go
index c49bd32..c0873bb 100644
--- a/runtime/factories/static/static.go
+++ b/runtime/factories/static/static.go
@@ -9,12 +9,11 @@
 	"flag"
 	"net"
 
-	"v.io/x/lib/vlog"
-
 	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/rpc"
 
+	"v.io/x/ref/internal/logger"
 	"v.io/x/ref/lib/flags"
 	"v.io/x/ref/lib/security/securityflag"
 	"v.io/x/ref/runtime/internal"
@@ -45,15 +44,15 @@
 		Addrs: rpc.ListenAddrs(lf.Addrs),
 		Proxy: lf.ListenProxy,
 	}
-	reservedDispatcher := debuglib.NewDispatcher(vlog.Log.LogDir, securityflag.NewAuthorizerOrDie())
+	reservedDispatcher := debuglib.NewDispatcher(logger.Manager(logger.Global()).LogDir, securityflag.NewAuthorizerOrDie())
 
 	ac := appcycle.New()
 
 	// Our address is private, so we test for running on GCE and for its 1:1 NAT
 	// configuration. GCEPublicAddress returns a non-nil addr if we are
 	// running on GCE.
-	if !internal.HasPublicIP(vlog.Log) {
-		if addr := internal.GCEPublicAddress(vlog.Log); addr != nil {
+	if !internal.HasPublicIP(logger.Global()) {
+		if addr := internal.GCEPublicAddress(logger.Global()); addr != nil {
 			listenSpec.AddressChooser = func(string, []net.Addr) ([]net.Addr, error) {
 				return []net.Addr{addr}, nil
 			}
diff --git a/runtime/internal/gce_linux.go b/runtime/internal/gce_linux.go
index 2041da9..aaf489f 100644
--- a/runtime/internal/gce_linux.go
+++ b/runtime/internal/gce_linux.go
@@ -9,7 +9,7 @@
 import (
 	"net"
 
-	"v.io/x/lib/vlog"
+	"v.io/v23/logging"
 
 	"v.io/x/ref/runtime/internal/gce"
 )
@@ -17,7 +17,7 @@
 // GCEPublicAddress returns the public IP address of the GCE instance
 // it is run from, or nil if run from anywhere else. The returned address
 // is the public address of a 1:1 NAT tunnel to this host.
-func GCEPublicAddress(log vlog.Logger) *net.IPAddr {
+func GCEPublicAddress(log logging.Logger) *net.IPAddr {
 	if !gce.RunningOnGCE() {
 		return nil
 	}
diff --git a/runtime/internal/gce_other.go b/runtime/internal/gce_other.go
index 63db7e3..4aea338 100644
--- a/runtime/internal/gce_other.go
+++ b/runtime/internal/gce_other.go
@@ -9,12 +9,12 @@
 import (
 	"net"
 
-	"v.io/x/lib/vlog"
+	"v.io/v23/logging"
 )
 
 // GCEPublicAddress returns the public IP address of the GCE instance
 // it is run from, or nil if run from anywhere else. The returned address
 // is the public address of a 1:1 NAT tunnel to this host.
-func GCEPublicAddress(vlog.Logger) *net.IPAddr {
+func GCEPublicAddress(logging.Logger) *net.IPAddr {
 	return nil
 }
diff --git a/runtime/internal/naming/endpoint.go b/runtime/internal/naming/endpoint.go
index 014b179..5b0552b 100644
--- a/runtime/internal/naming/endpoint.go
+++ b/runtime/internal/naming/endpoint.go
@@ -14,7 +14,6 @@
 
 	"v.io/v23/naming"
 	"v.io/x/lib/metadata"
-	"v.io/x/ref/lib/apilog"
 )
 
 const (
@@ -157,7 +156,7 @@
 var defaultVersion = 5
 
 func (ep *Endpoint) VersionedString(version int) string {
-	defer apilog.LogCallf(nil, "version=%v", version)(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
+	// nologcall
 	switch version {
 	default:
 		return ep.VersionedString(defaultVersion)
diff --git a/runtime/internal/rpc/stream/proxy/proxy.go b/runtime/internal/rpc/stream/proxy/proxy.go
index b623365..c1f140f 100644
--- a/runtime/internal/rpc/stream/proxy/proxy.go
+++ b/runtime/internal/rpc/stream/proxy/proxy.go
@@ -12,15 +12,16 @@
 	"time"
 
 	"v.io/x/lib/netstate"
+	"v.io/x/lib/vlog"
 
 	"v.io/v23"
 	"v.io/v23/context"
+	"v.io/v23/logging"
 	"v.io/v23/naming"
 	"v.io/v23/rpc"
 	"v.io/v23/security"
 	"v.io/v23/verror"
 	"v.io/v23/vom"
-	"v.io/x/lib/vlog"
 
 	"v.io/x/ref/runtime/internal/lib/bqueue"
 	"v.io/x/ref/runtime/internal/lib/bqueue/drrqueue"
@@ -819,10 +820,10 @@
 }
 
 // Convenience functions to assist with the logging convention.
-func proxyLog() vlog.InfoLog   { return vlog.VI(1) }
-func processLog() vlog.InfoLog { return vlog.VI(2) }
-func vcLog() vlog.InfoLog      { return vlog.VI(3) }
-func msgLog() vlog.InfoLog     { return vlog.VI(4) }
+func proxyLog() logging.InfoLog   { return vlog.VI(1) }
+func processLog() logging.InfoLog { return vlog.VI(2) }
+func vcLog() logging.InfoLog      { return vlog.VI(3) }
+func msgLog() logging.InfoLog     { return vlog.VI(4) }
 func packIDs(vci id.VC, fid id.Flow) bqueue.ID {
 	return bqueue.ID(message.MakeCounterID(vci, fid))
 }
diff --git a/runtime/internal/rt/rt_test.go b/runtime/internal/rt/rt_test.go
index b3b3e29..e695718 100644
--- a/runtime/internal/rt/rt_test.go
+++ b/runtime/internal/rt/rt_test.go
@@ -12,10 +12,12 @@
 	"testing"
 	"time"
 
+	"v.io/x/lib/vlog"
+
 	"v.io/v23"
 	"v.io/v23/context"
 	"v.io/v23/security"
-	"v.io/x/lib/vlog"
+
 	"v.io/x/ref"
 	vsecurity "v.io/x/ref/lib/security"
 	"v.io/x/ref/test"
@@ -33,9 +35,9 @@
 	l := vlog.Log
 	fmt.Println(l)
 	args := fmt.Sprintf("%s", l)
-	expected := regexp.MustCompile("name=vanadium logdirs=\\[/tmp\\] logtostderr=true|false alsologtostderr=false|true max_stack_buf_size=4292608 v=[0-9] stderrthreshold=2 vmodule= log_backtrace_at=:0")
+	expected := regexp.MustCompile("name=vlog logdirs=\\[/tmp\\] logtostderr=true|false alsologtostderr=false|true max_stack_buf_size=4292608 v=[0-9] stderrthreshold=2 vmodule= log_backtrace_at=:0")
 	if !expected.MatchString(args) {
-		t.Errorf("unexpected default args: %s", args)
+		t.Errorf("unexpected default args: %s, want %s", args, expected)
 	}
 	p := v23.GetPrincipal(ctx)
 	if p == nil {
@@ -74,7 +76,7 @@
 	if err != nil {
 		t.Fatalf("unexpected error: %s", err)
 	}
-	h.Expect(fmt.Sprintf("name=vanadium "+
+	h.Expect(fmt.Sprintf("name=vlog "+
 		"logdirs=[%s] "+
 		"logtostderr=true "+
 		"alsologtostderr=true "+
diff --git a/runtime/internal/rt/runtime.go b/runtime/internal/rt/runtime.go
index 1d006f4..09d1b70 100644
--- a/runtime/internal/rt/runtime.go
+++ b/runtime/internal/rt/runtime.go
@@ -13,7 +13,9 @@
 	"syscall"
 	"time"
 
+	"v.io/x/lib/metadata"
 	"v.io/x/lib/pubsub"
+	"v.io/x/lib/vlog"
 
 	"v.io/v23"
 	"v.io/v23/context"
@@ -25,8 +27,8 @@
 	"v.io/v23/security"
 	"v.io/v23/verror"
 	"v.io/v23/vtrace"
-	"v.io/x/lib/metadata"
-	"v.io/x/lib/vlog"
+
+	"v.io/x/ref/internal/logger"
 	"v.io/x/ref/lib/apilog"
 	"v.io/x/ref/lib/flags"
 	"v.io/x/ref/lib/stats"
@@ -94,10 +96,14 @@
 		ctx = context.WithValue(ctx, reservedNameKey, reservedDispatcher)
 	}
 
-	err := vlog.ConfigureLibraryLoggerFromFlags()
+	err := logger.Manager(logger.Global()).ConfigureFromFlags()
 	if err != nil && err != vlog.ErrConfigured {
 		return nil, nil, nil, err
 	}
+
+	// Configure the context to use the global logger.
+	ctx = context.WithLogger(ctx, logger.Global())
+
 	// We want to print out metadata only into the log files, to avoid
 	// spamming stderr, see #1246.
 	//
@@ -107,8 +113,8 @@
 	// 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.
-	if vlog.Log.LogDir() != os.TempDir() {
-		vlog.Infof(metadata.ToXML())
+	if logger.Manager(ctx).LogDir() != os.TempDir() {
+		ctx.Infof(metadata.ToXML())
 	}
 
 	// Setup the initial trace.
@@ -215,7 +221,7 @@
 }
 
 func (*Runtime) NewEndpoint(ep string) (naming.Endpoint, error) {
-	defer apilog.LogCallf(nil, "ep=%.10s...", ep)(nil, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
+	// nologcall
 	return inaming.NewEndpoint(ep)
 }
 
@@ -351,7 +357,7 @@
 }
 
 func (*Runtime) GetPrincipal(ctx *context.T) security.Principal {
-	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
+	// nologcall
 	p, _ := ctx.Value(principalKey).(security.Principal)
 	return p
 }
@@ -384,7 +390,7 @@
 }
 
 func (*Runtime) GetClient(ctx *context.T) rpc.Client {
-	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
+	// nologcall
 	cl, _ := ctx.Value(clientKey).(rpc.Client)
 	return cl
 }
@@ -442,7 +448,7 @@
 }
 
 func (*Runtime) WithBackgroundContext(ctx *context.T) *context.T {
-	defer apilog.LogCall(ctx)(ctx) // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
+	// nologcall
 	// Note we add an extra context with a nil value here.
 	// This prevents users from travelling back through the
 	// chain of background contexts.
@@ -464,7 +470,7 @@
 }
 
 func (*Runtime) WithReservedNameDispatcher(ctx *context.T, d rpc.Dispatcher) *context.T {
-	defer apilog.LogCallf(ctx, "d=")(ctx, "") // gologcop: DO NOT EDIT, MUST BE FIRST STATEMENT
+	// nologcall
 	return context.WithValue(ctx, reservedNameKey, d)
 }
 
diff --git a/runtime/internal/util.go b/runtime/internal/util.go
index 58894d7..6a49e50 100644
--- a/runtime/internal/util.go
+++ b/runtime/internal/util.go
@@ -10,10 +10,11 @@
 	"os"
 	"strings"
 
-	"v.io/v23/verror"
-	"v.io/x/lib/vlog"
-
 	"v.io/x/lib/netstate"
+
+	"v.io/v23/logging"
+	"v.io/v23/verror"
+
 	"v.io/x/ref/lib/exec"
 	"v.io/x/ref/lib/flags"
 )
@@ -80,7 +81,7 @@
 }
 
 // HasPublicIP returns true if the host has at least one public IP address.
-func HasPublicIP(log vlog.Logger) bool {
+func HasPublicIP(log logging.Logger) bool {
 	state, err := netstate.GetAccessibleIPs()
 	if err != nil {
 		log.Infof("failed to determine network state: %s", err)
diff --git a/test/init.go b/test/init.go
index d86b452..071d9c1 100644
--- a/test/init.go
+++ b/test/init.go
@@ -10,11 +10,10 @@
 	"runtime"
 	"sync"
 
-	"v.io/x/lib/vlog"
-
 	"v.io/v23"
 	"v.io/v23/context"
 
+	"v.io/x/ref/internal/logger"
 	"v.io/x/ref/lib/flags"
 	"v.io/x/ref/test/testutil"
 )
@@ -57,7 +56,7 @@
 		// This will be the case if this is called from the init()
 		// function of a _test.go file.
 		flag.Parse()
-		vlog.ConfigureLibraryLoggerFromFlags()
+		logger.Manager(logger.Global()).ConfigureFromFlags()
 	}
 	once.Do(init)
 }
diff --git a/test/modules/exec.go b/test/modules/exec.go
index c85a2c9..055c1dd 100644
--- a/test/modules/exec.go
+++ b/test/modules/exec.go
@@ -12,9 +12,12 @@
 	"sync"
 	"time"
 
-	"v.io/v23/verror"
 	"v.io/x/lib/envvar"
 	"v.io/x/lib/vlog"
+
+	"v.io/v23/verror"
+
+	"v.io/x/ref/internal/logger"
 	vexec "v.io/x/ref/lib/exec"
 	"v.io/x/ref/lib/mgmt"
 	"v.io/x/ref/services/agent/agentlib"
@@ -41,7 +44,8 @@
 func testFlags() []string {
 	var fl []string
 	// pass logging flags to any subprocesses
-	for fname, fval := range vlog.Log.ExplicitlySetFlags() {
+	flags := logger.Manager(logger.Global()).ExplicitlySetFlags()
+	for fname, fval := range flags {
 		fl = append(fl, "--"+fname+"="+fval)
 	}
 	timeout := flag.Lookup("test.timeout")