This is an automated email from the ASF dual-hosted git repository.
rawlin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
The following commit(s) were added to refs/heads/master by this push:
new 6d1aa80 Fix TO log format for 3rd party libs (#3589)
6d1aa80 is described below
commit 6d1aa8030c832f0c5420c69a5dda7590438eace9
Author: Robert Butts <[email protected]>
AuthorDate: Thu May 30 16:06:22 2019 -0600
Fix TO log format for 3rd party libs (#3589)
Fixes TO Go logging of Riak and ReverseProxy, which weren't properly
adding the prefix and date format of lib/go-log.
Fixes it by adding a func to create a new wrapper Logger, which
writes to the given logger with the standard log.Printf funcs,
properly setting the time and prefix.
Fixes #3587
---
CHANGELOG.md | 1 +
lib/go-log/log.go | 70 +++++++++---------------
lib/go-log/standardlogger.go | 48 ++++++++++++++++
lib/go-log/standardlogger_test.go | 69 +++++++++++++++++++++++
traffic_ops/traffic_ops_golang/routing/routes.go | 35 ++++--------
5 files changed, 153 insertions(+), 70 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 97dedc2..e1d0a6c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -49,6 +49,7 @@ The format is based on [Keep a
Changelog](http://keepachangelog.com/en/1.0.0/).
- Upgraded Traffic Portal to AngularJS 1.7.8
- Issue 3275: Improved the snapshot diff performance and experience.
- Issue #3605: Fixed Traffic Monitor custom ports in health polling URL.
+- Issue 3587: Fixed Traffic Ops Golang reverse proxy and Riak logs to be
consistent with the format of other error logs.
## [3.0.0] - 2018-10-30
### Added
diff --git a/lib/go-log/log.go b/lib/go-log/log.go
index 593b46a..1470a2e 100644
--- a/lib/go-log/log.go
+++ b/lib/go-log/log.go
@@ -72,56 +72,36 @@ func Init(eventW, errW, warnW, infoW, debugW
io.WriteCloser) {
initLogger(&Event, &eventCloser, eventW, "", 0)
}
-const timeFormat = time.RFC3339Nano
-const stackFrame = 3
-
-func Errorf(format string, v ...interface{}) {
- if Error == nil {
- return
- }
- Error.Output(stackFrame, time.Now().Format(timeFormat)+":
"+fmt.Sprintf(format, v...))
-}
-func Errorln(v ...interface{}) {
- if Error == nil {
- return
- }
- Error.Output(stackFrame, time.Now().Format(timeFormat)+":
"+fmt.Sprintln(v...))
-}
-func Warnf(format string, v ...interface{}) {
- if Warning == nil {
+func logf(logger *log.Logger, format string, v ...interface{}) {
+ if logger == nil {
return
}
- Warning.Output(stackFrame, time.Now().Format(timeFormat)+":
"+fmt.Sprintf(format, v...))
+ logger.Output(stackFrame, time.Now().Format(timeFormat)+":
"+fmt.Sprintf(format, v...))
}
-func Warnln(v ...interface{}) {
- if Warning == nil {
- return
- }
- Warning.Output(stackFrame, time.Now().Format(timeFormat)+":
"+fmt.Sprintln(v...))
-}
-func Infof(format string, v ...interface{}) {
- if Info == nil {
- return
- }
- Info.Output(stackFrame, time.Now().Format(timeFormat)+":
"+fmt.Sprintf(format, v...))
-}
-func Infoln(v ...interface{}) {
- if Info == nil {
- return
- }
- Info.Output(stackFrame, time.Now().Format(timeFormat)+":
"+fmt.Sprintln(v...))
-}
-func Debugf(format string, v ...interface{}) {
- if Debug == nil {
+
+func logln(logger *log.Logger, v ...interface{}) {
+ if logger == nil {
return
}
- Debug.Output(stackFrame, time.Now().Format(timeFormat)+":
"+fmt.Sprintf(format, v...))
+ logger.Output(stackFrame, time.Now().Format(timeFormat)+":
"+fmt.Sprintln(v...))
}
-func Debugln(v ...interface{}) {
- if Debug == nil {
- return
- }
- Debug.Output(stackFrame, time.Now().Format(timeFormat)+":
"+fmt.Sprintln(v...))
+
+const timeFormat = time.RFC3339Nano
+const stackFrame = 3
+
+func Errorf(format string, v ...interface{}) { logf(Error, format, v...) }
+func Errorln(v ...interface{}) { logln(Error, v...) }
+func Warnf(format string, v ...interface{}) { logf(Warning, format, v...) }
+func Warnln(v ...interface{}) { logln(Warning, v...) }
+func Infof(format string, v ...interface{}) { logf(Info, format, v...) }
+func Infoln(v ...interface{}) { logln(Info, v...) }
+func Debugf(format string, v ...interface{}) { logf(Debug, format, v...) }
+func Debugln(v ...interface{}) { logln(Debug, v...) }
+
+const eventFormat = "%.3f %s"
+
+func eventTime(t time.Time) float64 {
+ return float64(t.Unix()) + (float64(t.Nanosecond()) / 1e9)
}
// event log entries (TM event.log, TR access.log, etc)
@@ -130,7 +110,7 @@ func Eventf(t time.Time, format string, v ...interface{}) {
return
}
// 1484001185.287 ...
- Event.Printf("%.3f %s",
float64(t.Unix())+(float64(t.Nanosecond())/1e9), fmt.Sprintf(format, v...))
+ Event.Printf(eventFormat, eventTime(t), fmt.Sprintf(format, v...))
}
// EventfRaw writes to the event log with no prefix.
diff --git a/lib/go-log/standardlogger.go b/lib/go-log/standardlogger.go
new file mode 100644
index 0000000..71c775d
--- /dev/null
+++ b/lib/go-log/standardlogger.go
@@ -0,0 +1,48 @@
+package log
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+ "log"
+ "strings"
+)
+
+// StandardLogger returns a new Logger which will write the appropriate prefix
for standard log Printf calls.
+// This allows the given logger to prefix correctly when passed to third-party
or standard library functions which only know about the standard Logger
interface.
+// It does this by wrapping logger's error print as a writer, and sets that as
the new Logger's io.Writer.
+//
+// prefix is a prefix to add, which will be added immediately before messages,
but after any existing prefix on logger and the timestamp.
+//
+func StandardLogger(logger *log.Logger, prefix string) *log.Logger {
+ return log.New(&standardLoggerWriter{realLogger: logger, prefix:
prefix}, "", 0)
+}
+
+type standardLoggerWriter struct {
+ realLogger *log.Logger
+ prefix string
+}
+
+// Write writes to writer's underlying log, in the standard log format (note
this is not the Event log format).
+// The writer.realLogger may be nil, in which case this does nothing.
+// This always returns len(p) and nil, claiming it successfully wrote even if
it didn't.
+func (writer *standardLoggerWriter) Write(p []byte) (n int, err error) {
+ logln(writer.realLogger, writer.prefix+strings.TrimSpace(string(p)))
+ return len(p), nil
+}
diff --git a/lib/go-log/standardlogger_test.go
b/lib/go-log/standardlogger_test.go
new file mode 100644
index 0000000..bf2422b
--- /dev/null
+++ b/lib/go-log/standardlogger_test.go
@@ -0,0 +1,69 @@
+package log
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+ "bytes"
+ "log"
+ "strings"
+ "testing"
+ "time"
+)
+
+func TestStandardLogger(t *testing.T) {
+ buf := &bytes.Buffer{}
+ realLogPrefix := "a-real-log-prefix: "
+ logger := log.New(buf, realLogPrefix, log.Lshortfile)
+
+ sPrefix := "a-standard-logger-prefix: "
+ sLogger := StandardLogger(logger, sPrefix)
+
+ msg := "a-message"
+ sLogger.Printf(msg + "\n") // newline, to verify a double-newline isn't
printed.
+
+ actual := buf.String()
+
+ if !strings.HasPrefix(actual, realLogPrefix) {
+ t.Errorf("expected prefix '%v' actual '%v'\n", realLogPrefix,
actual)
+ }
+
+ actualFields := strings.Fields(actual)
+ if len(actualFields) < 4 {
+ t.Fatalf("expected fields >4 (prefix, line, time, msg} actual
%v '''%v'''\n", len(actualFields), actual)
+ }
+
+ timeField := actualFields[2]
+ if len(timeField) > 0 {
+ timeField = timeField[:len(timeField)-1] // timestamp ends with
:, strip it for parsing
+ }
+
+ actualTime, err := time.Parse(time.RFC3339Nano, timeField)
+ if err != nil {
+ t.Fatalf("expected 3rd field is RFC3339 nano format, actual
'%v'\n", timeField)
+ }
+ if actualTime.After(time.Now().Add(time.Minute)) ||
actualTime.Before(time.Now().Add(time.Minute*-1)) {
+ t.Errorf("expected 3rd field is RFC3339 nano format around now
'%v', actual '%v'\n", time.Now(), actualTime)
+ }
+}
+
+func TestStandardLoggerNil(t *testing.T) {
+ // test that a nil logger doesn't panic
+ StandardLogger(nil, "").Println("foo")
+}
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go
b/traffic_ops/traffic_ops_golang/routing/routes.go
index 532d73c..fe59806 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -23,15 +23,13 @@ import (
"crypto/tls"
"encoding/json"
"errors"
- "io"
- "log"
"net"
"net/http"
"net/http/httputil"
"runtime"
"time"
- tclog "github.com/apache/trafficcontrol/lib/go-log"
+ "github.com/apache/trafficcontrol/lib/go-log"
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/about"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
@@ -56,6 +54,7 @@ import (
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/federations"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/hwinfo"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/login"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/origin"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/parameter"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/physlocation"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ping"
@@ -73,7 +72,6 @@ import (
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/urisigning"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/user"
- "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/origin"
"github.com/basho/riak-go-client"
"github.com/jmoiron/sqlx"
)
@@ -451,7 +449,7 @@ func MemoryStatsHandler() http.HandlerFunc {
bytes, err := json.Marshal(stats)
if err != nil {
- tclog.Errorln("unable to marshal stats: " + err.Error())
+ log.Errorln("unable to marshal stats: " + err.Error())
handleErrs(http.StatusInternalServerError,
errors.New("marshalling error"))
return
}
@@ -467,7 +465,7 @@ func DBStatsHandler(db *sqlx.DB) http.HandlerFunc {
bytes, err := json.Marshal(stats)
if err != nil {
- tclog.Errorln("unable to marshal stats: " + err.Error())
+ log.Errorln("unable to marshal stats: " + err.Error())
handleErrs(http.StatusInternalServerError,
errors.New("marshalling error"))
return
}
@@ -492,25 +490,12 @@ func rootHandler(d ServerData) http.Handler {
rp := httputil.NewSingleHostReverseProxy(d.URL)
rp.Transport = tr
- var errorLogger interface{}
- errorLogger, err := tclog.GetLogWriter(d.Config.ErrorLog())
- if err != nil {
- tclog.Errorln("could not create error log writer for proxy: ",
err)
- }
- if errorLogger != nil {
- rp.ErrorLog = log.New(errorLogger.(io.Writer), "proxy error: ",
log.Ldate|log.Ltime|log.Lmicroseconds|log.LUTC) //if we don't provide a logger
to the reverse proxy it logs to stdout/err and is lost when ran by a script.
- riak.SetErrorLogger(log.New(errorLogger.(io.Writer), "riak
error: ", log.Ldate|log.Ltime|log.Lmicroseconds|log.LUTC))
- }
- var infoLogger interface{}
- infoLogger, err = tclog.GetLogWriter(d.Config.InfoLog())
- if err != nil {
- tclog.Errorln("could not create info log writer for proxy: ",
err)
- }
- if infoLogger != nil {
- riak.SetLogger(log.New(infoLogger.(io.Writer), "riak info: ",
log.Ldate|log.Ltime|log.Lmicroseconds|log.LUTC))
- }
- tclog.Debugf("our reverseProxy: %++v\n", rp)
- tclog.Debugf("our reverseProxy's transport: %++v\n", tr)
+ rp.ErrorLog = log.StandardLogger(log.Error, "proxy error: ")
+ riak.SetErrorLogger(log.StandardLogger(log.Error, "riak error: "))
+ riak.SetLogger(log.StandardLogger(log.Info, "riak info: "))
+
+ log.Debugf("our reverseProxy: %++v\n", rp)
+ log.Debugf("our reverseProxy's transport: %++v\n", tr)
loggingProxyHandler := wrapAccessLog(d.Secrets[0], rp)
managerHandler := CreateThrottledHandler(loggingProxyHandler,
d.BackendMaxConnections["mojolicious"])