log,cloud/https: Suppress HTTP log messages unless -log=debug

Suppress benign HTTP errors except when -log=debug is set. Configure
http.Server's ErrorLog field with the default debug logger.
Additionally use the same debug logger for http.Servers in
cmd/{upspinfs,cacheserver}.

Fixes #263.

Change-Id: I8b0bcbb7a959b02d71002665149189dca4cdfb8e
Reviewed-on: https://upspin-review.googlesource.com/18700
Reviewed-by: Andrew Gerrand <adg@golang.org>
diff --git a/cloud/https/https.go b/cloud/https/https.go
index d19e68f..3b8b501 100644
--- a/cloud/https/https.go
+++ b/cloud/https/https.go
@@ -213,6 +213,7 @@
 	}
 	shutdown.Handle(func() { ln.Close() })
 
+	httpLogger := log.NewStdLogger(log.Info)
 	if manager.Cache != nil {
 		// If we're using LetsEncrypt then we need to serve the http-01
 		// challenge by plain HTTP. We also serve a redirect to HTTPS
@@ -222,7 +223,10 @@
 			log.Fatalf("https: %v", err)
 		}
 		shutdown.Handle(func() { httpLn.Close() })
-		httpServer := &http.Server{Handler: manager.HTTPHandler(nil)}
+		httpServer := &http.Server{
+			Handler:  manager.HTTPHandler(nil),
+			ErrorLog: httpLogger,
+		}
 		go func() {
 			err := httpServer.Serve(httpLn)
 			log.Printf("https: %v", err)
@@ -250,6 +254,7 @@
 		WriteTimeout: 0,
 		IdleTimeout:  60 * time.Second,
 		TLSConfig:    config,
+		ErrorLog:     httpLogger,
 	}
 	// TODO(adg): enable HTTP/2 once it's fast enough
 	//err := http2.ConfigureServer(server, nil)
diff --git a/cmd/cacheserver/serve.go b/cmd/cacheserver/serve.go
index 85d059c..87c24f1 100644
--- a/cmd/cacheserver/serve.go
+++ b/cmd/cacheserver/serve.go
@@ -67,7 +67,10 @@
 	// Use our own ServerMux so that we can run in the same
 	// process as a server using the default one.
 	mux := &http.ServeMux{}
-	httpServer := &http.Server{Handler: mux}
+	httpServer := &http.Server{
+		Handler:  mux,
+		ErrorLog: log.NewStdLogger(log.Debug),
+	}
 
 	mux.Handle("/api/Store/", ss)
 	mux.Handle("/api/Dir/", ds)
diff --git a/cmd/upspinfs/main.go b/cmd/upspinfs/main.go
index e3a3c32..c086157 100644
--- a/cmd/upspinfs/main.go
+++ b/cmd/upspinfs/main.go
@@ -88,7 +88,9 @@
 	if err != nil {
 		log.Fatal(err)
 	}
-	srv := &http.Server{}
+	srv := &http.Server{
+		ErrorLog: log.NewStdLogger(log.Debug),
+	}
 	go func() {
 		log.Fatal(srv.Serve(ln))
 	}()
diff --git a/log/log.go b/log/log.go
index a73abe8..ae4644b 100644
--- a/log/log.go
+++ b/log/log.go
@@ -7,6 +7,7 @@
 package log // import "upspin.io/log"
 
 import (
+	"bytes"
 	"fmt"
 	"io"
 	"log"
@@ -80,6 +81,38 @@
 	return log.New(w, "", log.Ldate|log.Ltime|log.LUTC|log.Lmicroseconds)
 }
 
+// logBridge augments the Logger type with the io.Writer interface enabling
+// NewStdLogger to connect Go's standard library logger to the logger provided
+// by this package.
+type logBridge struct {
+	Logger
+}
+
+// Write parses the standard logging line (configured with log.Lshortfile) and
+// passes its message component to the logger provided by this package.
+func (lb logBridge) Write(b []byte) (n int, err error) {
+	var message string
+	// Split "f.go:42: message" into "f.go", "42", and "message".
+	parts := bytes.SplitN(b, []byte{':'}, 3)
+	if len(parts) != 3 || len(parts[0]) < 1 || len(parts[2]) < 1 {
+		message = fmt.Sprintf("bad log format: %s", b)
+	} else {
+		message = string(parts[2][1:]) // Skip leading space.
+	}
+	lb.Print(message)
+	return len(b), nil
+}
+
+// NewStdLogger creates a *log.Logger ("log" is from the Go standard library)
+// that forwards messages to the provided upspin logger using a logBridge. The
+// standard logger is configured with log.Lshortfile, this log line
+// format which is parsed to extract the log message (skipping the filename,
+// line number) to forward it to the provided upspin logger.
+func NewStdLogger(l Logger) *log.Logger {
+	lb := logBridge{l}
+	return log.New(lb, "", log.Lshortfile)
+}
+
 // Register connects an ExternalLogger to the default logger. This may only be
 // called once.
 func Register(e ExternalLogger) {