]> Cypherpunks repositories - gostls13.git/commitdiff
log: generalize getting and setting flags and prefix.
authorRob Pike <r@golang.org>
Wed, 6 Apr 2011 18:48:03 +0000 (11:48 -0700)
committerRob Pike <r@golang.org>
Wed, 6 Apr 2011 18:48:03 +0000 (11:48 -0700)
- used to be only for standard log, not for user-built.
- there were no getters.
Also rearrange the code a little so we can avoid allocating
a buffer on every call.  Logging is expensive but we should
avoid unnecessary cost.

This should have no effect on existing code.

R=rsc
CC=golang-dev
https://golang.org/cl/4363045

src/pkg/log/log.go
src/pkg/log/log_test.go

index 658e3bd94b1b3b8835e860f37bfdf2a305d9063c..33140ee08afa08e358a2d100645bd1b330c6e69e 100644 (file)
@@ -28,11 +28,12 @@ const (
        // order they appear (the order listed here) or the format they present (as
        // described in the comments).  A colon appears after these items:
        //      2009/0123 01:23:23.123123 /a/b/c/d.go:23: message
-       Ldate         = 1 << iota // the date: 2009/0123
-       Ltime                     // the time: 01:23:23
-       Lmicroseconds             // microsecond resolution: 01:23:23.123123.  assumes Ltime.
-       Llongfile                 // full file name and line number: /a/b/c/d.go:23
-       Lshortfile                // final file name element and line number: d.go:23. overrides Llongfile
+       Ldate         = 1 << iota     // the date: 2009/0123
+       Ltime                         // the time: 01:23:23
+       Lmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime.
+       Llongfile                     // full file name and line number: /a/b/c/d.go:23
+       Lshortfile                    // final file name element and line number: d.go:23. overrides Llongfile
+       LstdFlags     = Ldate | Ltime // initial values for the standard logger
 )
 
 // A Logger represents an active logging object that generates lines of
@@ -40,10 +41,11 @@ const (
 // 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
-       out    io.Writer  // destination for output
-       prefix string     // prefix to write at beginning of each line
-       flag   int        // properties
+       prefix string       // prefix to write at beginning of each line
+       flag   int          // properties
+       mu     sync.Mutex   // ensures atomic writes; protects the following fields
+       out    io.Writer    // destination for output
+       buf    bytes.Buffer // for accumulating text to write
 }
 
 // New creates a new Logger.   The out variable sets the
@@ -54,7 +56,7 @@ func New(out io.Writer, prefix string, flag int) *Logger {
        return &Logger{out: out, prefix: prefix, flag: flag}
 }
 
-var std = New(os.Stderr, "", Ldate|Ltime)
+var std = New(os.Stderr, "", LstdFlags)
 
 // Cheap integer to fixed-width decimal ASCII.  Give a negative width to avoid zero-padding.
 // Knows the buffer has capacity.
@@ -81,7 +83,7 @@ func itoa(buf *bytes.Buffer, i int, wid int) {
        }
 }
 
-func (l *Logger) formatHeader(buf *bytes.Buffer, ns int64, calldepth int) {
+func (l *Logger) formatHeader(buf *bytes.Buffer, ns int64, file string, line int) {
        buf.WriteString(l.prefix)
        if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
                t := time.SecondsToLocalTime(ns / 1e9)
@@ -107,21 +109,15 @@ func (l *Logger) formatHeader(buf *bytes.Buffer, ns int64, calldepth int) {
                }
        }
        if l.flag&(Lshortfile|Llongfile) != 0 {
-               _, file, line, ok := runtime.Caller(calldepth)
-               if ok {
-                       if l.flag&Lshortfile != 0 {
-                               short := file
-                               for i := len(file) - 1; i > 0; i-- {
-                                       if file[i] == '/' {
-                                               short = file[i+1:]
-                                               break
-                                       }
+               if l.flag&Lshortfile != 0 {
+                       short := file
+                       for i := len(file) - 1; i > 0; i-- {
+                               if file[i] == '/' {
+                                       short = file[i+1:]
+                                       break
                                }
-                               file = short
                        }
-               } else {
-                       file = "???"
-                       line = 0
+                       file = short
                }
                buf.WriteString(file)
                buf.WriteByte(':')
@@ -138,15 +134,26 @@ func (l *Logger) formatHeader(buf *bytes.Buffer, ns int64, calldepth int) {
 // paths it will be 2.
 func (l *Logger) Output(calldepth int, s string) os.Error {
        now := time.Nanoseconds() // get this early.
-       buf := new(bytes.Buffer)
-       l.formatHeader(buf, now, calldepth+1)
-       buf.WriteString(s)
-       if len(s) > 0 && s[len(s)-1] != '\n' {
-               buf.WriteByte('\n')
+       // get caller info (if required) before locking - it's expensive.
+       var file string
+       var line int
+       if l.flag&(Lshortfile|Llongfile) != 0 {
+               var ok bool
+               _, file, line, ok = runtime.Caller(calldepth)
+               if !ok {
+                       file = "???"
+                       line = 0
+               }
        }
        l.mu.Lock()
        defer l.mu.Unlock()
-       _, err := l.out.Write(buf.Bytes())
+       l.buf.Reset()
+       l.formatHeader(&l.buf, now, file, line)
+       l.buf.WriteString(s)
+       if len(s) > 0 && s[len(s)-1] != '\n' {
+               l.buf.WriteByte('\n')
+       }
+       _, err := l.out.Write(l.buf.Bytes())
        return err
 }
 
@@ -203,19 +210,49 @@ func (l *Logger) Panicln(v ...interface{}) {
        panic(s)
 }
 
+// Flags returns the output flags for the logger.
+func (l *Logger) Flags() int {
+       return l.flag
+}
+
+// SetFlags sets the output flags for the logger.
+func (l *Logger) SetFlags(flag int) {
+       l.flag = flag
+}
+
+// Prefix returns the output prefix for the logger.
+func (l *Logger) Prefix() string {
+       return l.prefix
+}
+
+// SetPrefix sets the output prefix for the logger.
+func (l *Logger) SetPrefix(prefix string) {
+       l.prefix = prefix
+}
+
 // SetOutput sets the output destination for the standard logger.
 func SetOutput(w io.Writer) {
        std.out = w
 }
 
+// Flags returns the output flags for the standard logger.
+func Flags() int {
+       return std.Flags()
+}
+
 // SetFlags sets the output flags for the standard logger.
 func SetFlags(flag int) {
-       std.flag = flag
+       std.SetFlags(flag)
+}
+
+// Prefix returns the output prefix for the standard logger.
+func Prefix() string {
+       return std.Prefix()
 }
 
 // SetPrefix sets the output prefix for the standard logger.
 func SetPrefix(prefix string) {
-       std.prefix = prefix
+       std.SetPrefix(prefix)
 }
 
 // These functions write to the standard logger.
index f99070afb4fad85b81c010bf9a19e149177ec67a..158c3d93c7e2fb8262579d855767cabf381517f0 100644 (file)
@@ -84,3 +84,36 @@ func TestOutput(t *testing.T) {
                t.Errorf("log output should match %q is %q", expect, b.String())
        }
 }
+
+func TestFlagAndPrefixSetting(t *testing.T) {
+       var b bytes.Buffer
+       l := New(&b, "Test:", LstdFlags)
+       f := l.Flags()
+       if f != LstdFlags {
+               t.Errorf("Flags 1: expected %x got %x", LstdFlags, f)
+       }
+       l.SetFlags(f | Lmicroseconds)
+       f = l.Flags()
+       if f != LstdFlags|Lmicroseconds {
+               t.Errorf("Flags 2: expected %x got %x", LstdFlags|Lmicroseconds, f)
+       }
+       p := l.Prefix()
+       if p != "Test:" {
+               t.Errorf(`Prefix: expected "Test:" got %q`, p)
+       }
+       l.SetPrefix("Reality:")
+       p = l.Prefix()
+       if p != "Reality:" {
+               t.Errorf(`Prefix: expected "Reality:" got %q`, p)
+       }
+       // Verify a log message looks right, with our prefix and microseconds present.
+       l.Print("hello")
+       pattern := "^Reality:" + Rdate + " " + Rtime + Rmicroseconds + " hello\n"
+       matched, err := regexp.Match(pattern, b.Bytes())
+       if err != nil {
+               t.Fatalf("pattern %q did not compile: %s", pattern, err)
+       }
+       if !matched {
+               t.Error("message did not match pattern")
+       }
+}