Gerrit Bot has uploaded this change for review.
log: improve logger implementation
The current implementation of the log.Logger provides us with
limited flexibility. With the new changes, it will provide more
flexibility. The changes introduce new features such as Log Level,
Formatter, Root Logger, Context support etc. but the changes made
won't break any existing functionalities or affect any libraries
which use log package. Thanks to the changes made, any custom logger
implementation won't be needed and developer needs will be met.
Instead, the formatter can be used to format the output.
Fixes #13182
Change-Id: I21043238c84dfc28777f5f4c5d4fa771355422bd
GitHub-Last-Rev: 20c4f5b4ae2ad7a6b9ce32fde52bb3fa62186447
GitHub-Pull-Request: golang/go#48464
---
A src/log/entry.go
A src/log/formatter.go
A src/log/formatter_test.go
M src/log/log.go
4 files changed, 804 insertions(+), 44 deletions(-)
diff --git a/src/log/entry.go b/src/log/entry.go
new file mode 100644
index 0000000..1cde941
--- /dev/null
+++ b/src/log/entry.go
@@ -0,0 +1,242 @@
+package log
+
+import (
+ "context"
+ "fmt"
+ "os"
+)
+
+// The Entry is a logging entry that contains context set by the user and needed data
+// for the log.
+// Entry fields are used by a custom formatter while formatting output.
+type Entry struct {
+ logger *Logger // logger which will be used to log the entry
+ context context.Context // context set by the user
+ calldepth int // calldepth is the count of the number of frames to skip
+ level *Level // level of the entry
+ message string // message contains the text to print
+}
+
+// NewEntry creates a new Entry. The logger variable sets the
+// the logger which will be used to log the entry.
+func NewEntry(logger *Logger) *Entry {
+ return &Entry{
+ logger: logger,
+ }
+}
+
+// Logger returns the logger which will write entry to the output destination.
+func (e *Entry) Logger() *Logger {
+ return e.logger
+}
+
+// Context returns the context set by the user for entry.
+func (e *Entry) Context() context.Context {
+ return e.context
+}
+
+// LogLevel returns the log level for entry.
+func (e *Entry) LogLevel() *Level {
+ return e.level
+}
+
+// Message returns the log message for entry.
+func (e *Entry) Message() string {
+ return e.message
+}
+
+// CallDepth returns the calldepth for entry.
+func (e *Entry) CallDepth() int {
+ return e.calldepth
+}
+
+// Print calls e.Output to print to the logger.
+// Arguments are handled in the manner of fmt.Print.
+func (e *Entry) Print(v ...interface{}) {
+ e.Output(2, fmt.Sprint(v...))
+}
+
+// Printf calls e.Output to print to the logger.
+// Arguments are handled in the manner of fmt.Printf.
+func (e *Entry) Printf(format string, v ...interface{}) {
+ e.Output(2, fmt.Sprintf(format, v...))
+}
+
+// Println calls e.Output to print to the logger.
+// Arguments are handled in the manner of fmt.Println.
+func (e *Entry) Println(v ...interface{}) {
+ e.Output(2, fmt.Sprintln(v...))
+}
+
+// Fatal is equivalent to Print() followed by a call to os.Exit(1).
+func (e *Entry) Fatal(v ...interface{}) {
+ e.Output(2, fmt.Sprint(v...), FatalLevel)
+ os.Exit(1)
+}
+
+// Fatalf is equivalent to Printf() followed by a call to os.Exit(1).
+func (e *Entry) Fatalf(format string, v ...interface{}) {
+ e.Output(2, fmt.Sprintf(format, v...), FatalLevel)
+ os.Exit(1)
+}
+
+// Fatalln is equivalent to Println() followed by a call to os.Exit(1).
+func (e *Entry) Fatalln(v ...interface{}) {
+ e.Output(2, fmt.Sprintln(v...), FatalLevel)
+ os.Exit(1)
+}
+
+// Panic is equivalent to Print() and logs the message at level Error
+// followed by a call to panic().
+func (e *Entry) Panic(v ...interface{}) {
+ s := fmt.Sprint(v...)
+ e.Output(2, s, PanicLevel)
+ panic(s)
+}
+
+// Panicf is equivalent to Printf() and logs the message at level Error
+// followed by a call to panic().
+func (e *Entry) Panicf(format string, v ...interface{}) {
+ s := fmt.Sprintf(format, v...)
+ e.Output(2, s, PanicLevel)
+ panic(s)
+}
+
+// Panicln is equivalent to Println() and logs the message at level Error
+// followed by a call to panic().
+func (e *Entry) Panicln(v ...interface{}) {
+ s := fmt.Sprintln(v...)
+ e.Output(2, s, PanicLevel)
+ panic(s)
+}
+
+// Error is equivalent to Print() and logs the message at level Error.
+func (e *Entry) Error(v ...interface{}) {
+ e.Output(2, fmt.Sprint(v...), ErrorLevel)
+}
+
+// Errorf is equivalent to Printf() and logs the message at level Error.
+func (e *Entry) Errorf(format string, v ...interface{}) {
+ e.Output(2, fmt.Sprintf(format, v...), ErrorLevel)
+}
+
+// Errorln is equivalent to Println() and logs the message at level Error.
+func (e *Entry) Errorln(v ...interface{}) {
+ e.Output(2, fmt.Sprintln(v...), ErrorLevel)
+}
+
+// Warn is equivalent to Print() and logs the message at level Warning.
+func (e *Entry) Warn(v ...interface{}) {
+ e.Output(2, fmt.Sprint(v...), WarnLevel)
+}
+
+// Warnf is equivalent to Printf() and logs the message at level Warning.
+func (e *Entry) Warnf(format string, v ...interface{}) {
+ e.Output(2, fmt.Sprintf(format, v...), WarnLevel)
+}
+
+// Warnln is equivalent to Println() and logs the message at level Warning.
+func (e *Entry) Warnln(v ...interface{}) {
+ e.Output(2, fmt.Sprintln(v...), WarnLevel)
+}
+
+// Info is equivalent to Print() and logs the message at level Info.
+func (e *Entry) Info(v ...interface{}) {
+ e.Output(2, fmt.Sprint(v...), InfoLevel)
+}
+
+// Infof is equivalent to Printf() and logs the message at level Info.
+func (e *Entry) Infof(format string, v ...interface{}) {
+ e.Output(2, fmt.Sprintf(format, v...), InfoLevel)
+}
+
+// Infoln is equivalent to Println() and logs the message at level Info.
+func (e *Entry) Infoln(v ...interface{}) {
+ e.Output(2, fmt.Sprintln(v...), InfoLevel)
+}
+
+// Debug is equivalent to Print() and logs the message at level Debug.
+func (e *Entry) Debug(v ...interface{}) {
+ e.Output(2, fmt.Sprint(v...), DebugLevel)
+}
+
+// Debugf is equivalent to Printf() and logs the message at level Debug.
+func (e *Entry) Debugf(format string, v ...interface{}) {
+ e.Output(2, fmt.Sprintf(format, v...), DebugLevel)
+}
+
+// Debugln is equivalent to Println() and logs the message at level Debug.
+func (e *Entry) Debugln(v ...interface{}) {
+ e.Output(2, fmt.Sprintln(v...), DebugLevel)
+}
+
+// Trace is equivalent to Print() and logs the message at level Trace.
+func (e *Entry) Trace(v ...interface{}) {
+ e.Output(2, fmt.Sprint(v...), TraceLevel)
+}
+
+// Tracef is equivalent to Printf() and logs the message at level Trace.
+func (e *Entry) Tracef(format string, v ...interface{}) {
+ e.Output(2, fmt.Sprintf(format, v...), TraceLevel)
+}
+
+// Traceln is equivalent to Println() and logs the message at level Trace.
+func (e *Entry) Traceln(v ...interface{}) {
+ e.Output(2, fmt.Sprintln(v...), TraceLevel)
+}
+
+// Output writes the output for a logging event. The string s contains
+// the text to print after the prefix specified by the flags of the
+// Logger. A newline is appended if the last character of s is not
+// already a newline. Calldepth is the count of the number of
+// frames to skip when computing the file name and line number
+// if Llongfile or Lshortfile is set; a value of 1 will print the details
+// for the caller of Output. Level is the log level for the output.
+// If any formatter is configured for the logger, it will be used to format
+// the output.
+func (e *Entry) Output(calldepth int, s string, level ...Level) error {
+ var formatter LoggerFormatter
+
+ e.logger.mu.Lock()
+ if e.logger.rootLogger != nil {
+ e.logger.rootLogger.formatterMu.Lock()
+ formatter = e.logger.rootLogger.formatter
+ e.logger.rootLogger.formatterMu.Unlock()
+ }
+ if formatter == nil {
+ formatter = e.logger.formatter
+ }
+ e.logger.mu.Unlock()
+
+ if formatter != nil {
+ // +1 for this frame.
+ e.calldepth = calldepth + 1
+ e.message = s
+
+ if level != nil {
+ e.level = &level[0]
+ } else {
+ e.level = nil
+ }
+
+ serialized, err := formatter.Format(e)
+
+ if err == nil && serialized != nil {
+ // if the logger has got a root logger, use the output
+ // destination of the root logger.
+ if e.logger.rootLogger != nil {
+ e.logger.rootLogger.mu.Lock()
+ _, err = e.logger.rootLogger.out.Write(serialized)
+ e.logger.rootLogger.mu.Unlock()
+ } else {
+ e.logger.mu.Lock()
+ _, err = e.logger.out.Write(serialized)
+ e.logger.mu.Unlock()
+ }
+ }
+
+ return err
+ }
+
+ return e.logger.Output(calldepth+1, s, level...) // +1 for this frame.
+}
diff --git a/src/log/formatter.go b/src/log/formatter.go
new file mode 100644
index 0000000..b3a8459
--- /dev/null
+++ b/src/log/formatter.go
@@ -0,0 +1,7 @@
+package log
+
+// The LoggerFormatter interface is used to implement a custom formatter.
+// So the log output can be customized by implementing this interface.
+type LoggerFormatter interface {
+ Format(entry *Entry) ([]byte, error)
+}
diff --git a/src/log/formatter_test.go b/src/log/formatter_test.go
new file mode 100644
index 0000000..12b4ac9
--- /dev/null
+++ b/src/log/formatter_test.go
@@ -0,0 +1,40 @@
+package log
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+)
+
+type testFormatter struct {
+}
+
+func (f *testFormatter) Format(entry *Entry) ([]byte, error) {
+ testString := "formatter message: " + entry.Message()
+ return []byte(testString), nil
+}
+
+func ExampleLoggerWithFormatter() {
+ var (
+ buf bytes.Buffer
+ logger = New(&buf, "logger: ", Lshortfile)
+ )
+ logger.SetFormatter(&testFormatter{})
+ logger.Info("Hello, log file!")
+
+ fmt.Print(&buf)
+ // Output:
+ // formatter message: Hello, log file!
+}
+
+func BenchmarkPrintlnWithFormatter(b *testing.B) {
+ const testString = "test"
+ var buf bytes.Buffer
+ l := New(&buf, "", 0)
+ l.SetFormatter(&testFormatter{})
+
+ for i := 0; i < b.N; i++ {
+ buf.Reset()
+ l.Println(testString)
+ }
+}
diff --git a/src/log/log.go b/src/log/log.go
index 3172384..1b677ec 100644
--- a/src/log/log.go
+++ b/src/log/log.go
@@ -15,6 +15,7 @@
package log
import (
+ "context"
"fmt"
"io"
"os"
@@ -43,20 +44,62 @@
Lshortfile // final file name element and line number: d.go:23. overrides Llongfile
LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone
Lmsgprefix // move the "prefix" from the beginning of the line to before the message
+ Llevel // logger level
LstdFlags = Ldate | Ltime // initial values for the standard logger
)
+type Level uint32
+
+// These are the different logger levels. You can set the logger level
+// on your instance of logger
+const (
+ PanicLevel Level = iota
+ FatalLevel
+ ErrorLevel
+ WarnLevel
+ InfoLevel
+ DebugLevel
+ TraceLevel
+)
+
+// Print methods do not have any log level, but they are considered as INFO by default.
+// This is the level text for the print methods.
+const defaultLogLevelText = "INFO"
+
+// These are level texts corresponding to logger levels.
+var levelText = map[Level]string{
+ PanicLevel: "PANIC",
+ FatalLevel: "FATAL",
+ ErrorLevel: "ERROR",
+ WarnLevel: "WARN",
+ InfoLevel: "INFO",
+ DebugLevel: "DEBUG",
+ TraceLevel: "TRACE",
+}
+
+// LevelText returns a text for the logger level. It returns the empty
+// string if the level is unknown.
+func LevelText(code Level) string {
+ return levelText[code]
+}
+
// A Logger represents an active logging object that generates lines of
// output to an io.Writer. Each logging operation makes a single call to
// the Writer's Write method. A Logger can be used simultaneously from
// multiple goroutines; it guarantees to serialize access to the Writer.
type Logger struct {
- mu sync.Mutex // ensures atomic writes; protects the following fields
- prefix string // prefix on each line to identify the logger (but see Lmsgprefix)
- flag int // properties
- out io.Writer // destination for output
- buf []byte // for accumulating text to write
- isDiscard int32 // atomic boolean: whether out == io.Discard
+ mu sync.Mutex // ensures atomic writes; protects the following fields
+ prefix string // prefix on each line to identify the logger (but see Lmsgprefix)
+ flag int // properties
+ out io.Writer // destination for output
+ buf []byte // for accumulating text to write
+ isDiscard int32 // atomic boolean: whether out == io.Discard
+ level Level // logger level
+ ctx context.Context // logger context
+ formatter LoggerFormatter // logger formatter to format the log output
+ formatterMu sync.Mutex // protects the formatter
+ rootLogger *Logger // root logger for logger
+ entryPool sync.Pool // entry pool
}
// New creates a new Logger. The out variable sets the
@@ -65,23 +108,54 @@
// after the log header if the Lmsgprefix flag is provided.
// The flag argument defines the logging properties.
func New(out io.Writer, prefix string, flag int) *Logger {
- l := &Logger{out: out, prefix: prefix, flag: flag}
+ l := &Logger{out: out, prefix: prefix, flag: flag, level: DebugLevel}
if out == io.Discard {
l.isDiscard = 1
}
return l
}
+// GetLogger returns a new Logger with a root logger and a different prefix.
+// The logger will write the same output destination as the root logger.
+// However, it has got a separate context and a logger level that can be configured.
+func (l *Logger) GetLogger(prefix string) *Logger {
+ rootLogger := l.RootLogger()
+
+ if rootLogger == nil {
+ rootLogger = l
+ }
+
+ return &Logger{prefix: prefix, rootLogger: rootLogger, level: DebugLevel}
+}
+
+// RootLogger returns the root logger for the logger.
+func (l *Logger) RootLogger() *Logger {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ return l.rootLogger
+}
+
+// WithContext creates an entry from the logger and adds a context to it.
+func (l *Logger) WithContext(ctx context.Context) *Entry {
+ entry := l.newEntry()
+ entry.context = ctx
+ return entry
+}
+
// SetOutput sets the output destination for the logger.
+// If the logger has got a root logger, the output destination will not be set
+// because the output destination of the root logger is used.
func (l *Logger) SetOutput(w io.Writer) {
l.mu.Lock()
defer l.mu.Unlock()
- l.out = w
- isDiscard := int32(0)
- if w == io.Discard {
- isDiscard = 1
+ if l.rootLogger == nil {
+ l.out = w
+ isDiscard := int32(0)
+ if w == io.Discard {
+ isDiscard = 1
+ }
+ atomic.StoreInt32(&l.isDiscard, isDiscard)
}
- atomic.StoreInt32(&l.isDiscard, isDiscard)
}
var std = New(os.Stderr, "", LstdFlags)
@@ -109,17 +183,18 @@
// formatHeader writes log header to buf in following order:
// * l.prefix (if it's not blank and Lmsgprefix is unset),
// * date and/or time (if corresponding flags are provided),
+// * log level (if corresponding flags are provided)
// * file and line number (if corresponding flags are provided),
// * l.prefix (if it's not blank and Lmsgprefix is set).
-func (l *Logger) formatHeader(buf *[]byte, t time.Time, file string, line int) {
- if l.flag&Lmsgprefix == 0 {
+func (l *Logger) formatHeader(flag int, buf *[]byte, t time.Time, file string, line int, level ...Level) {
+ if flag&Lmsgprefix == 0 {
*buf = append(*buf, l.prefix...)
}
- if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
- if l.flag&LUTC != 0 {
+ if flag&(Ldate|Ltime|Lmicroseconds) != 0 {
+ if flag&LUTC != 0 {
t = t.UTC()
}
- if l.flag&Ldate != 0 {
+ if flag&Ldate != 0 {
year, month, day := t.Date()
itoa(buf, year, 4)
*buf = append(*buf, '/')
@@ -128,22 +203,29 @@
itoa(buf, day, 2)
*buf = append(*buf, ' ')
}
- if l.flag&(Ltime|Lmicroseconds) != 0 {
+ if flag&(Ltime|Lmicroseconds) != 0 {
hour, min, sec := t.Clock()
itoa(buf, hour, 2)
*buf = append(*buf, ':')
itoa(buf, min, 2)
*buf = append(*buf, ':')
itoa(buf, sec, 2)
- if l.flag&Lmicroseconds != 0 {
+ if flag&Lmicroseconds != 0 {
*buf = append(*buf, '.')
itoa(buf, t.Nanosecond()/1e3, 6)
}
*buf = append(*buf, ' ')
}
}
- if l.flag&(Lshortfile|Llongfile) != 0 {
- if l.flag&Lshortfile != 0 {
+ if flag&Llevel != 0 {
+ if level == nil {
+ *buf = append(*buf, fmt.Sprintf("%-5s ", defaultLogLevelText)...)
+ } else {
+ *buf = append(*buf, fmt.Sprintf("%-5s ", levelText[level[0]])...)
+ }
+ }
+ if flag&(Lshortfile|Llongfile) != 0 {
+ if flag&Lshortfile != 0 {
short := file
for i := len(file) - 1; i > 0; i-- {
if file[i] == '/' {
@@ -158,7 +240,7 @@
itoa(buf, line, -1)
*buf = append(*buf, ": "...)
}
- if l.flag&Lmsgprefix != 0 {
+ if flag&Lmsgprefix != 0 {
*buf = append(*buf, l.prefix...)
}
}
@@ -168,14 +250,79 @@
// Logger. A newline is appended if the last character of s is not
// already a newline. Calldepth is used to recover the PC and is
// provided for generality, although at the moment on all pre-defined
-// paths it will be 2.
-func (l *Logger) Output(calldepth int, s string) error {
+// paths it will be 2. Level is the log level for the output.
+// If any formatter is configured for the logger, it will be used to format
+// the output.
+func (l *Logger) Output(calldepth int, s string, level ...Level) error {
+ var formatter LoggerFormatter
+
+ l.mu.Lock()
+ if l.rootLogger != nil {
+ l.rootLogger.formatterMu.Lock()
+ formatter = l.rootLogger.formatter
+ l.rootLogger.formatterMu.Unlock()
+ }
+ if formatter == nil {
+ formatter = l.formatter
+ }
+ l.mu.Unlock()
+
+ if formatter != nil {
+ entry := l.newEntry()
+ entry.calldepth = calldepth + 1
+ entry.message = s
+ entry.logger = l
+ entry.context = nil
+
+ if level != nil {
+ entry.level = &level[0]
+ } else {
+ entry.level = nil
+ }
+
+ serialized, err := formatter.Format(entry)
+
+ if err == nil && serialized != nil {
+ if l.rootLogger != nil {
+ l.rootLogger.mu.Lock()
+ _, err = l.rootLogger.out.Write(serialized)
+ l.rootLogger.mu.Unlock()
+ } else {
+ l.mu.Lock()
+ _, err = l.out.Write(serialized)
+ l.mu.Unlock()
+ }
+ }
+
+ l.releaseEntry(entry)
+ return err
+ }
+
now := time.Now() // get this early.
var file string
var line int
l.mu.Lock()
defer l.mu.Unlock()
- if l.flag&(Lshortfile|Llongfile) != 0 {
+
+ var flag int
+
+ if l.rootLogger != nil {
+ l.rootLogger.mu.Lock()
+ flag = l.rootLogger.flag
+ l.rootLogger.mu.Unlock()
+ } else {
+ flag = l.flag
+ }
+
+ if flag&Llevel != 0 {
+ if level == nil && l.level < InfoLevel {
+ return nil
+ } else if level != nil && l.level < level[0] {
+ return nil
+ }
+ }
+
+ if flag&(Lshortfile|Llongfile) != 0 {
// Release lock while getting caller info - it's expensive.
l.mu.Unlock()
var ok bool
@@ -187,12 +334,20 @@
l.mu.Lock()
}
l.buf = l.buf[:0]
- l.formatHeader(&l.buf, now, file, line)
+ l.formatHeader(flag, &l.buf, now, file, line, level...)
l.buf = append(l.buf, s...)
if len(s) == 0 || s[len(s)-1] != '\n' {
l.buf = append(l.buf, '\n')
}
- _, err := l.out.Write(l.buf)
+
+ var err error
+ if l.rootLogger != nil {
+ l.rootLogger.mu.Lock()
+ _, err = l.rootLogger.out.Write(l.buf)
+ l.rootLogger.mu.Unlock()
+ } else {
+ _, err = l.out.Write(l.buf)
+ }
return err
}
@@ -225,57 +380,153 @@
// Fatal is equivalent to l.Print() followed by a call to os.Exit(1).
func (l *Logger) Fatal(v ...interface{}) {
- l.Output(2, fmt.Sprint(v...))
+ l.Output(2, fmt.Sprint(v...), FatalLevel)
os.Exit(1)
}
// Fatalf is equivalent to l.Printf() followed by a call to os.Exit(1).
func (l *Logger) Fatalf(format string, v ...interface{}) {
- l.Output(2, fmt.Sprintf(format, v...))
+ l.Output(2, fmt.Sprintf(format, v...), FatalLevel)
os.Exit(1)
}
// Fatalln is equivalent to l.Println() followed by a call to os.Exit(1).
func (l *Logger) Fatalln(v ...interface{}) {
- l.Output(2, fmt.Sprintln(v...))
+ l.Output(2, fmt.Sprintln(v...), FatalLevel)
os.Exit(1)
}
// Panic is equivalent to l.Print() followed by a call to panic().
func (l *Logger) Panic(v ...interface{}) {
s := fmt.Sprint(v...)
- l.Output(2, s)
+ l.Output(2, s, PanicLevel)
panic(s)
}
// Panicf is equivalent to l.Printf() followed by a call to panic().
func (l *Logger) Panicf(format string, v ...interface{}) {
s := fmt.Sprintf(format, v...)
- l.Output(2, s)
+ l.Output(2, s, PanicLevel)
panic(s)
}
// Panicln is equivalent to l.Println() followed by a call to panic().
func (l *Logger) Panicln(v ...interface{}) {
s := fmt.Sprintln(v...)
- l.Output(2, s)
+ l.Output(2, s, PanicLevel)
panic(s)
}
+// Error is equivalent to Print() and logs the message at level Error.
+func (l *Logger) Error(v ...interface{}) {
+ s := fmt.Sprint(v...)
+ l.Output(2, s, ErrorLevel)
+}
+
+// Errorf is equivalent to Printf() and logs the message at level Error.
+func (l *Logger) Errorf(format string, v ...interface{}) {
+ s := fmt.Sprintf(format, v...)
+ l.Output(2, s, ErrorLevel)
+}
+
+// Errorln is equivalent to Println() and logs the message at level Error.
+func (l *Logger) Errorln(v ...interface{}) {
+ l.Output(2, fmt.Sprintln(v...), ErrorLevel)
+}
+
+// Warn is equivalent to Print() and logs the message at level Warning.
+func (l *Logger) Warn(v ...interface{}) {
+ s := fmt.Sprint(v...)
+ l.Output(2, s, WarnLevel)
+}
+
+// Warnf is equivalent to Printf() and logs the message at level Warning.
+func (l *Logger) Warnf(format string, v ...interface{}) {
+ s := fmt.Sprintf(format, v...)
+ l.Output(2, s, WarnLevel)
+}
+
+// Warnln is equivalent to Println() and logs the message at level Warning.
+func (l *Logger) Warnln(v ...interface{}) {
+ l.Output(2, fmt.Sprintln(v...), WarnLevel)
+}
+
+// Info is equivalent to Print() and logs the message at level Info.
+func (l *Logger) Info(v ...interface{}) {
+ s := fmt.Sprint(v...)
+ l.Output(2, s, InfoLevel)
+}
+
+// Infof is equivalent to Printf() and logs the message at level Info.
+func (l *Logger) Infof(format string, v ...interface{}) {
+ s := fmt.Sprintf(format, v...)
+ l.Output(2, s, InfoLevel)
+}
+
+// Infoln is equivalent to Println() and logs the message at level Info.
+func (l *Logger) Infoln(v ...interface{}) {
+ l.Output(2, fmt.Sprintln(v...), InfoLevel)
+}
+
+// Debug is equivalent to Print() and logs the message at level Debug.
+func (l *Logger) Debug(v ...interface{}) {
+ s := fmt.Sprint(v...)
+ l.Output(2, s, DebugLevel)
+}
+
+// Debugf is equivalent to Printf() and logs the message at level Debug.
+func (l *Logger) Debugf(format string, v ...interface{}) {
+ s := fmt.Sprintf(format, v...)
+ l.Output(2, s, DebugLevel)
+}
+
+// Debugln is equivalent to Println() and logs the message at level Debug.
+func (l *Logger) Debugln(v ...interface{}) {
+ l.Output(2, fmt.Sprintln(v...), DebugLevel)
+}
+
+// Trace is equivalent to Print() and logs the message at level Trace.
+func (l *Logger) Trace(v ...interface{}) {
+ s := fmt.Sprint(v...)
+ l.Output(2, s, TraceLevel)
+}
+
+// Tracef is equivalent to Printf() and logs the message at level Trace.
+func (l *Logger) Tracef(format string, v ...interface{}) {
+ s := fmt.Sprintf(format, v...)
+ l.Output(2, s, TraceLevel)
+}
+
+// Traceln is equivalent to Println() and logs the message at level Trace.
+func (l *Logger) Traceln(v ...interface{}) {
+ l.Output(2, fmt.Sprintln(v...), TraceLevel)
+}
+
// Flags returns the output flags for the logger.
// The flag bits are Ldate, Ltime, and so on.
+// If the logger has got a root logger, the output flags of the root
+// logger are returned.
func (l *Logger) Flags() int {
l.mu.Lock()
defer l.mu.Unlock()
+ if l.rootLogger != nil {
+ l.rootLogger.mu.Lock()
+ defer l.rootLogger.mu.Unlock()
+ return l.rootLogger.flag
+ }
return l.flag
}
// SetFlags sets the output flags for the logger.
// The flag bits are Ldate, Ltime, and so on.
+// If the logger has got a root logger, the output flags will not be set
+// because the output flags of the root logger is used.
func (l *Logger) SetFlags(flag int) {
l.mu.Lock()
defer l.mu.Unlock()
- l.flag = flag
+ if l.rootLogger == nil {
+ l.flag = flag
+ }
}
// Prefix returns the output prefix for the logger.
@@ -292,16 +543,114 @@
l.prefix = prefix
}
+// LoggerLevel returns the log level for the logger.
+func (l *Logger) LoggerLevel() Level {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ return l.level
+}
+
+// SetLoggerLevel sets the log level for the logger.
+func (l *Logger) SetLoggerLevel(level Level) {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ l.level = level
+}
+
+// Context returns the context for the logger.
+func (l *Logger) Context() context.Context {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ return l.ctx
+}
+
+// SetContext sets the context for the logger.
+func (l *Logger) SetContext(ctx context.Context) {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ l.ctx = ctx
+}
+
+// Formatter returns the formatter for the logger.
+// If the logger has got a root logger, the formatter of the root
+// logger is returned.
+func (l *Logger) Formatter() LoggerFormatter {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+
+ if l.rootLogger != nil {
+ l.rootLogger.formatterMu.Lock()
+ defer l.rootLogger.formatterMu.Unlock()
+ return l.rootLogger.formatter
+ }
+
+ l.formatterMu.Lock()
+ defer l.formatterMu.Unlock()
+ return l.formatter
+}
+
+// SetFormatter sets the formatter for the logger.
+// If the logger has got a root logger, the formatter will not be set
+// because the formatter of the root logger is used.
+func (l *Logger) SetFormatter(formatter LoggerFormatter) {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+
+ if l.rootLogger == nil {
+ l.formatterMu.Lock()
+ defer l.formatterMu.Unlock()
+ l.formatter = formatter
+ }
+}
+
// Writer returns the output destination for the logger.
+// If the logger has got a root logger, the output destination
+// of the root logger is returned.
func (l *Logger) Writer() io.Writer {
l.mu.Lock()
defer l.mu.Unlock()
+ if l.rootLogger != nil {
+ return l.rootLogger.Writer()
+ }
return l.out
}
+// newEntry either gets an entry from the pool or creates a new entry.
+func (l *Logger) newEntry() *Entry {
+ entry, ok := l.entryPool.Get().(*Entry)
+ if ok {
+ return entry
+ }
+ return NewEntry(l)
+}
+
+// releaseEntry puts the entry into the pool.
+func (l *Logger) releaseEntry(entry *Entry) {
+ l.entryPool.Put(entry)
+}
+
+// GetLogger returns a new Logger whose root logger is the standard logger.
+// The logger will write the same output destination as the standard logger.
+// However, it has got a separate context and a logger level that can be configured.
+func GetLogger(prefix string) *Logger {
+ return std.GetLogger(prefix)
+}
+
+// RootLogger returns the root logger of the standard logger.
+func RootLogger() *Logger {
+ return std.RootLogger()
+}
+
+// WithContext creates an entry from the standard logger and adds a context to it.
+func WithContext(ctx context.Context) *Entry {
+ return std.WithContext(ctx)
+}
+
// SetOutput sets the output destination for the standard logger.
func SetOutput(w io.Writer) {
- std.SetOutput(w)
+ std.mu.Lock()
+ defer std.mu.Unlock()
+ std.out = w
}
// Flags returns the output flags for the standard logger.
@@ -326,6 +675,36 @@
std.SetPrefix(prefix)
}
+// LoggerLevel returns the logger level for the standard logger.
+func LoggerLevel() Level {
+ return std.LoggerLevel()
+}
+
+// SetLoggerLevel sets the logger level for the standard logger.
+func SetLoggerLevel(level Level) {
+ std.SetLoggerLevel(level)
+}
+
+// Context returns the context for the standard logger.
+func Context() context.Context {
+ return std.Context()
+}
+
+// SetContext sets the context for the standard logger.
+func SetContext(ctx context.Context) {
+ std.SetContext(ctx)
+}
+
+// Formatter returns the logger formatter for the standard logger.
+func Formatter() LoggerFormatter {
+ return std.Formatter()
+}
+
+// SetFormatter sets the logger formatter for the standard logger.
+func SetFormatter(formatter LoggerFormatter) {
+ std.SetFormatter(formatter)
+}
+
// Writer returns the output destination for the standard logger.
func Writer() io.Writer {
return std.Writer()
@@ -362,50 +741,142 @@
// Fatal is equivalent to Print() followed by a call to os.Exit(1).
func Fatal(v ...interface{}) {
- std.Output(2, fmt.Sprint(v...))
+ std.Output(2, fmt.Sprint(v...), FatalLevel)
os.Exit(1)
}
// Fatalf is equivalent to Printf() followed by a call to os.Exit(1).
func Fatalf(format string, v ...interface{}) {
- std.Output(2, fmt.Sprintf(format, v...))
+ std.Output(2, fmt.Sprintf(format, v...), FatalLevel)
os.Exit(1)
}
// Fatalln is equivalent to Println() followed by a call to os.Exit(1).
func Fatalln(v ...interface{}) {
- std.Output(2, fmt.Sprintln(v...))
+ std.Output(2, fmt.Sprintln(v...), FatalLevel)
os.Exit(1)
}
// Panic is equivalent to Print() followed by a call to panic().
func Panic(v ...interface{}) {
s := fmt.Sprint(v...)
- std.Output(2, s)
+ std.Output(2, s, PanicLevel)
panic(s)
}
// Panicf is equivalent to Printf() followed by a call to panic().
func Panicf(format string, v ...interface{}) {
s := fmt.Sprintf(format, v...)
- std.Output(2, s)
+ std.Output(2, s, PanicLevel)
panic(s)
}
// Panicln is equivalent to Println() followed by a call to panic().
func Panicln(v ...interface{}) {
s := fmt.Sprintln(v...)
- std.Output(2, s)
+ std.Output(2, s, PanicLevel)
panic(s)
}
+// Error logs a message at level Error on the standard logger.
+func Error(v ...interface{}) {
+ s := fmt.Sprint(v...)
+ std.Output(2, s, ErrorLevel)
+}
+
+// Errorf logs a message at level Error on the standard logger.
+func Errorf(format string, v ...interface{}) {
+ s := fmt.Sprintf(format, v...)
+ std.Output(2, s, ErrorLevel)
+}
+
+// Errorln logs a message at level Error on the standard logger.
+func Errorln(v ...interface{}) {
+ s := fmt.Sprintln(v...)
+ std.Output(2, s, ErrorLevel)
+}
+
+// Warn logs a message at level Warning on the standard logger.
+func Warn(v ...interface{}) {
+ s := fmt.Sprint(v...)
+ std.Output(2, s, WarnLevel)
+}
+
+// Warnf logs a message at level Warning on the standard logger.
+func Warnf(format string, v ...interface{}) {
+ s := fmt.Sprintf(format, v...)
+ std.Output(2, s, WarnLevel)
+}
+
+// Warnln logs a message at level Warning on the standard logger.
+func Warnln(v ...interface{}) {
+ s := fmt.Sprintln(v...)
+ std.Output(2, s, WarnLevel)
+}
+
+// Info logs a message at level Info on the standard logger.
+func Info(v ...interface{}) {
+ s := fmt.Sprint(v...)
+ std.Output(2, s, InfoLevel)
+}
+
+// Infof logs a message at level Info on the standard logger.
+func Infof(format string, v ...interface{}) {
+ s := fmt.Sprintf(format, v...)
+ std.Output(2, s, InfoLevel)
+}
+
+// Infoln logs a message at level Info on the standard logger.
+func Infoln(v ...interface{}) {
+ s := fmt.Sprintln(v...)
+ std.Output(2, s, InfoLevel)
+}
+
+// Debug logs a message at level Debug on the standard logger.
+func Debug(v ...interface{}) {
+ s := fmt.Sprint(v...)
+ std.Output(2, s, DebugLevel)
+}
+
+// Debugf logs a message at level Debug on the standard logger.
+func Debugf(format string, v ...interface{}) {
+ s := fmt.Sprintf(format, v...)
+ std.Output(2, s, DebugLevel)
+}
+
+// Debugln logs a message at level Debug on the standard logger.
+func Debugln(v ...interface{}) {
+ s := fmt.Sprintln(v...)
+ std.Output(2, s, DebugLevel)
+}
+
+// Trace logs a message at level Trace on the standard logger.
+func Trace(v ...interface{}) {
+ s := fmt.Sprint(v...)
+ std.Output(2, s, TraceLevel)
+}
+
+// Tracef logs a message at level Trace on the standard logger.
+func Tracef(format string, v ...interface{}) {
+ s := fmt.Sprintf(format, v...)
+ std.Output(2, s, TraceLevel)
+}
+
+// Traceln logs a message at level Trace on the standard logger.
+func Traceln(v ...interface{}) {
+ s := fmt.Sprintln(v...)
+ std.Output(2, s, TraceLevel)
+}
+
// Output writes the output for a logging event. The string s contains
// the text to print after the prefix specified by the flags of the
// Logger. A newline is appended if the last character of s is not
// already a newline. Calldepth is the count of the number of
// frames to skip when computing the file name and line number
// if Llongfile or Lshortfile is set; a value of 1 will print the details
-// for the caller of Output.
-func Output(calldepth int, s string) error {
- return std.Output(calldepth+1, s) // +1 for this frame.
+// for the caller of Output. Level is the log level for the output.
+// If any formatter is configured for the logger, it will be used to format
+// the output.
+func Output(calldepth int, s string, level ...Level) error {
+ return std.Output(calldepth+1, s, level...) // +1 for this frame.
}
To view, visit change 350869. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Rob Pike.
Gerrit Bot uploaded patch set #2 to this change.
log: improve logger implementation
The current implementation of the log.Logger provides us with
limited flexibility. With the new changes, it will provide more
flexibility. The changes introduce new features such as Log Level,
Formatter, Root Logger, Context support etc. but the changes made
won't break any existing functionalities or affect any libraries
which use log package. Thanks to the changes made, any custom logger
implementation won't be needed and developer needs will be met.
Instead, the formatter can be used to format the output.
Fixes #13182
Change-Id: I21043238c84dfc28777f5f4c5d4fa771355422bd
GitHub-Last-Rev: c3570003987004d8ff9aaa333aa8688652c719b1
GitHub-Pull-Request: golang/go#48464
---
A src/log/entry.go
A src/log/formatter.go
A src/log/formatter_test.go
M src/log/log.go
4 files changed, 799 insertions(+), 44 deletions(-)
To view, visit change 350869. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Rob Pike.
Gerrit Bot uploaded patch set #3 to this change.
log: improve logger implementation
The current implementation of the log.Logger provides us with
limited flexibility. With the new changes, it will provide more
flexibility. The changes introduce new features such as Log Level,
Formatter, Root Logger, Context support etc. but the changes made
won't break any existing functionalities or affect any libraries
which use log package. Thanks to the changes made, any custom logger
implementation won't be needed and developer needs will be met.
Instead, the formatter can be used to format the output.
Fixes #13182
Change-Id: I21043238c84dfc28777f5f4c5d4fa771355422bd
GitHub-Last-Rev: ffc9536abc3237a45e0512e4661cdb63c97fa55c
GitHub-Pull-Request: golang/go#48464
---
A src/log/entry.go
A src/log/formatter.go
A src/log/formatter_test.go
M src/log/log.go
M src/log/log_test.go
5 files changed, 858 insertions(+), 49 deletions(-)
To view, visit change 350869. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Rob Pike.
Patch set 3:Code-Review -2
1 comment:
Patchset:
Thanks. This is a lot of new API, and for that we use a proposal process for review: see https://golang.org/s/proposal. Marking -2 temporarily until a proposal has been accepted.
Note that there are a number of third party logging packages. There has been no consensus on what features should be shared by all logging packages. See also the discussion on https://golang.org/issue/28412.
To view, visit change 350869. To unsubscribe, or for help writing mail filters, visit settings.
Attention is currently required from: Rob Pike.
Gerrit Bot uploaded patch set #4 to this change.
log: improve logger implementation
The current implementation of the log.Logger provides us with
limited flexibility. With the new changes, it will provide more
flexibility. The changes introduce new features such as Log Level,
Formatter, Root Logger, Context support etc. but the changes made
won't break any existing functionalities or affect any libraries
which use log package. Thanks to the changes made, any custom logger
implementation won't be needed and developer needs will be met.
Instead, the formatter can be used to format the output.
Fixes #13182
Change-Id: I21043238c84dfc28777f5f4c5d4fa771355422bd
GitHub-Last-Rev: 2fe9a32da17ae9b74ff76c689ba92b3f10e87136
GitHub-Pull-Request: golang/go#48464
---
A src/log/formatter_test.go
A src/log/entry.go
A src/log/formatter.go
M src/log/log.go
M src/log/log_test.go
5 files changed, 898 insertions(+), 57 deletions(-)
To view, visit change 350869. To unsubscribe, or for help writing mail filters, visit settings.