mirror of
https://github.com/golang/go
synced 2024-11-20 02:14:46 -07:00
log: generalize getting and setting flags and prefix.
- 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
This commit is contained in:
parent
41971434d1
commit
5ee13c0d59
@ -33,6 +33,7 @@ const (
|
||||
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
|
||||
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,8 +109,6 @@ 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-- {
|
||||
@ -119,10 +119,6 @@ func (l *Logger) formatHeader(buf *bytes.Buffer, ns int64, calldepth int) {
|
||||
}
|
||||
file = short
|
||||
}
|
||||
} else {
|
||||
file = "???"
|
||||
line = 0
|
||||
}
|
||||
buf.WriteString(file)
|
||||
buf.WriteByte(':')
|
||||
itoa(buf, line, -1)
|
||||
@ -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.
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user