git » log » commit 7d576e7

Allow the user to control whether to log time, level and caller

author Alberto Bertogli
2020-05-22 16:16:52 UTC
committer Alberto Bertogli
2020-05-22 16:53:51 UTC
parent dba4a5e2fa67674862122fcac488378b49ee5abc

Allow the user to control whether to log time, level and caller

This patch allows the user to control whether a logger includes
timestamp, log level, and caller information on each message.

log.go +44 -21
log_test.go +40 -5

diff --git a/log.go b/log.go
index 2c6a226..87bca24 100644
--- a/log.go
+++ b/log.go
@@ -81,11 +81,25 @@ type Logger struct {
 	// could change in the future.
 	Level Level
 
+	// Include timestamp in the log message.
+	// The use of this field should be considered EXPERIMENTAL, the API for it
+	// could change in the future.
+	LogTime bool
+
+	// Include the log level in the log message.
+	// The use of this field should be considered EXPERIMENTAL, the API for it
+	// could change in the future.
+	LogLevel bool
+
+	// Include the caller in the log message.
+	// The use of this field should be considered EXPERIMENTAL, the API for it
+	// could change in the future.
+	LogCaller bool
+
 	// File name, if this logger is backed by a file. It's used to implement
 	// reopening.
 	fname string
 
-	logTime    bool
 	callerSkip int
 	w          io.WriteCloser
 	sync.Mutex
@@ -97,7 +111,9 @@ func New(w io.WriteCloser) *Logger {
 		w:          w,
 		callerSkip: 0,
 		Level:      Info,
-		logTime:    true,
+		LogTime:    true,
+		LogLevel:   true,
+		LogCaller:  true,
 	}
 }
 
@@ -116,7 +132,6 @@ func NewFile(path string) (*Logger, error) {
 	}
 
 	l := New(f)
-	l.logTime = true
 	l.fname = path
 	return l, nil
 }
@@ -130,7 +145,7 @@ func NewSyslog(priority syslog.Priority, tag string) (*Logger, error) {
 	}
 
 	l := New(w)
-	l.logTime = false
+	l.LogTime = false
 	return l, nil
 }
 
@@ -182,25 +197,29 @@ func (l *Logger) Log(level Level, skip int, format string, a ...interface{}) err
 	msg := fmt.Sprintf(format, a...)
 
 	// Caller.
-	_, file, line, ok := runtime.Caller(1 + l.callerSkip + skip)
-	if !ok {
-		file = "unknown"
-	}
-	fl := fmt.Sprintf("%s:%-4d", filepath.Base(file), line)
-	if len(fl) > 18 {
-		fl = fl[len(fl)-18:]
+	if l.LogCaller {
+		_, file, line, ok := runtime.Caller(1 + l.callerSkip + skip)
+		if !ok {
+			file = "unknown"
+		}
+		fl := fmt.Sprintf("%s:%-4d", filepath.Base(file), line)
+		if len(fl) > 18 {
+			fl = fl[len(fl)-18:]
+		}
+		msg = fmt.Sprintf("%-18s", fl) + " " + msg
 	}
-	msg = fmt.Sprintf("%-18s", fl) + " " + msg
 
 	// Level.
-	letter, ok := levelToLetter[level]
-	if !ok {
-		letter = strconv.Itoa(int(level))
+	if l.LogLevel {
+		letter, ok := levelToLetter[level]
+		if !ok {
+			letter = strconv.Itoa(int(level))
+		}
+		msg = letter + " " + msg
 	}
-	msg = letter + " " + msg
 
 	// Time.
-	if l.logTime {
+	if l.LogTime {
 		msg = time.Now().Format("2006-01-02 15:04:05.000000 ") + msg
 	}
 
@@ -241,10 +260,14 @@ func (l *Logger) Fatalf(format string, a ...interface{}) {
 
 // The default logger, used by the top-level functions below.
 var Default = &Logger{
-	w:          os.Stderr,
+	w: os.Stderr,
+
+	Level: Info,
+
 	callerSkip: 1,
-	Level:      Info,
-	logTime:    false,
+	LogCaller:  true,
+	LogLevel:   true,
+	LogTime:    false,
 }
 
 // Initialize the default logger, based on the command-line flags.
@@ -271,7 +294,7 @@ func Init() {
 
 	Default.callerSkip = 1
 	Default.Level = Level(*vLevel)
-	Default.logTime = *logTime
+	Default.LogTime = *logTime
 }
 
 // V is a convenient wrapper to Default.V.
diff --git a/log_test.go b/log_test.go
index 7d235cb..1ce9883 100644
--- a/log_test.go
+++ b/log_test.go
@@ -36,7 +36,7 @@ func checkContentsMatch(t *testing.T, name, path, expected string) {
 }
 
 func testLogger(t *testing.T, fname string, l *Logger) {
-	l.logTime = false
+	l.LogTime = false
 	l.Infof("message %d", 1)
 	checkContentsMatch(t, "info-no-time", fname,
 		"^_ log_test.go:....   message 1\n")
@@ -47,13 +47,13 @@ func testLogger(t *testing.T, fname string, l *Logger) {
 		"^_ log_test.go:....   message 1\n")
 
 	os.Truncate(fname, 0)
-	l.logTime = true
+	l.LogTime = true
 	l.Infof("message %d", 1)
 	checkContentsMatch(t, "info-with-time", fname,
 		`^....-..-.. ..:..:..\.\d{6} _ log_test.go:....   message 1\n`)
 
 	os.Truncate(fname, 0)
-	l.logTime = false
+	l.LogTime = false
 	l.Errorf("error %d", 1)
 	checkContentsMatch(t, "error", fname, `^E log_test.go:....   error 1\n`)
 
@@ -62,7 +62,7 @@ func testLogger(t *testing.T, fname string, l *Logger) {
 	}
 
 	os.Truncate(fname, 0)
-	l.logTime = false
+	l.LogTime = false
 	l.Debugf("debug %d", 1)
 	checkContentsMatch(t, "debug-no-log", fname, `^$`)
 
@@ -87,6 +87,41 @@ func testLogger(t *testing.T, fname string, l *Logger) {
 	l.Log(Fatal, 0, "log fatal %d", 1)
 	checkContentsMatch(t, "log", fname,
 		`^☠ log_test.go:....   log fatal 1\n`)
+
+	// Test some combinations of options.
+	cases := []struct {
+		name      string
+		logTime   bool
+		logLevel  bool
+		logCaller bool
+		expected  string
+	}{
+		{
+			"show everything",
+			true, true, true,
+			`^....-..-.. ..:..:..\.\d{6} _ log_test.go:....   message 1\n`,
+		}, {
+			"caller+level",
+			false, true, true,
+			`^_ log_test.go:....   message 1\n`,
+		}, {
+			"time",
+			true, false, false,
+			`^....-..-.. ..:..:..\.\d{6} message 1\n`,
+		}, {
+			"none",
+			false, false, false,
+			`message 1\n`,
+		},
+	}
+	for _, c := range cases {
+		os.Truncate(fname, 0)
+		l.LogTime = c.logTime
+		l.LogLevel = c.logLevel
+		l.LogCaller = c.logCaller
+		l.Infof("message %d", 1)
+		checkContentsMatch(t, c.name, fname, c.expected)
+	}
 }
 
 func TestBasic(t *testing.T) {
@@ -113,7 +148,7 @@ func TestReopen(t *testing.T) {
 	fname, l := mustNewFile(t)
 	defer l.Close()
 	defer os.Remove(fname)
-	l.logTime = false
+	l.LogTime = false
 
 	l.Infof("pre rename")
 	checkContentsMatch(t, "r", fname, `^_ log_test.go:....   pre rename\n`)