summaryrefslogtreecommitdiff
path: root/src/pkg/log/log.go
blob: ac996a38daee33d8336380674cedc72274fd52a4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Rudimentary logging package. Defines a type, Logger, with simple
// methods for formatting output to one or two destinations. Also has
// predefined Loggers accessible through helper functions Stdout[f],
// Stderr[f], Exit[f], and Crash[f], which are easier to use than creating
// a Logger manually.
// Exit exits when written to.
// Crash causes a crash when written to.
package log

import (
	"fmt"
	"io"
	"runtime"
	"os"
	"time"
)

// These flags define the properties of the Logger and the output they produce.
const (
	// Flags
	Lok    = iota
	Lexit  // terminate execution when written
	Lcrash // crash (panic) when written
	// Bits or'ed together to control what's printed. There is no control over the
	// 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
	lAllBits      = Ldate | Ltime | Lmicroseconds | Llongfile | Lshortfile
)

// Logger represents an active logging object.
type Logger struct {
	out0   io.Writer // first destination for output
	out1   io.Writer // second destination for output; may be nil
	prefix string    // prefix to write at beginning of each line
	flag   int       // properties
}

// New creates a new Logger.   The out0 and out1 variables set the
// destinations to which log data will be written; out1 may be nil.
// The prefix appears at the beginning of each generated log line.
// The flag argument defines the logging properties.
func New(out0, out1 io.Writer, prefix string, flag int) *Logger {
	return &Logger{out0, out1, prefix, flag}
}

var (
	stdout = New(os.Stdout, nil, "", Lok|Ldate|Ltime)
	stderr = New(os.Stderr, nil, "", Lok|Ldate|Ltime)
	exit   = New(os.Stderr, nil, "", Lexit|Ldate|Ltime)
	crash  = New(os.Stderr, nil, "", Lcrash|Ldate|Ltime)
)

var shortnames = make(map[string]string) // cache of short names to avoid allocation.

// Cheap integer to fixed-width decimal ASCII.  Use a negative width to avoid zero-padding
func itoa(i int, wid int) string {
	var u uint = uint(i)
	if u == 0 && wid <= 1 {
		return "0"
	}

	// Assemble decimal in reverse order.
	var b [32]byte
	bp := len(b)
	for ; u > 0 || wid > 0; u /= 10 {
		bp--
		wid--
		b[bp] = byte(u%10) + '0'
	}

	return string(b[bp:])
}

func (l *Logger) formatHeader(ns int64, calldepth int) string {
	h := l.prefix
	if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
		t := time.SecondsToLocalTime(ns / 1e9)
		if l.flag&(Ldate) != 0 {
			h += itoa(int(t.Year), 4) + "/" + itoa(t.Month, 2) + "/" + itoa(t.Day, 2) + " "
		}
		if l.flag&(Ltime|Lmicroseconds) != 0 {
			h += itoa(t.Hour, 2) + ":" + itoa(t.Minute, 2) + ":" + itoa(t.Second, 2)
			if l.flag&Lmicroseconds != 0 {
				h += "." + itoa(int(ns%1e9)/1e3, 6)
			}
			h += " "
		}
	}
	if l.flag&(Lshortfile|Llongfile) != 0 {
		_, file, line, ok := runtime.Caller(calldepth)
		if ok {
			if l.flag&Lshortfile != 0 {
				short, ok := shortnames[file]
				if !ok {
					short = file
					for i := len(file) - 1; i > 0; i-- {
						if file[i] == '/' {
							short = file[i+1:]
							break
						}
					}
					shortnames[file] = short
				}
				file = short
			}
		} else {
			file = "???"
			line = 0
		}
		h += file + ":" + itoa(line, -1) + ": "
	}
	return h
}

// Output writes the output for a logging event.  The string s contains the text to print after
// the time stamp;  calldepth is used to recover the PC.  It 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) os.Error {
	now := time.Nanoseconds() // get this early.
	newline := "\n"
	if len(s) > 0 && s[len(s)-1] == '\n' {
		newline = ""
	}
	s = l.formatHeader(now, calldepth+1) + s + newline
	_, err := io.WriteString(l.out0, s)
	if l.out1 != nil {
		_, err1 := io.WriteString(l.out1, s)
		if err == nil && err1 != nil {
			err = err1
		}
	}
	switch l.flag & ^lAllBits {
	case Lcrash:
		panic("log: fatal error")
	case Lexit:
		os.Exit(1)
	}
	return err
}

// Logf is analogous to Printf() for a Logger.
func (l *Logger) Logf(format string, v ...) { l.Output(2, fmt.Sprintf(format, v)) }

// Log is analogous to Print() for a Logger.
func (l *Logger) Log(v ...) { l.Output(2, fmt.Sprintln(v)) }

// Stdout is a helper function for easy logging to stdout. It is analogous to Print().
func Stdout(v ...) { stdout.Output(2, fmt.Sprint(v)) }

// Stderr is a helper function for easy logging to stderr. It is analogous to Fprint(os.Stderr).
func Stderr(v ...) { stderr.Output(2, fmt.Sprintln(v)) }

// Stdoutf is a helper functions for easy formatted logging to stdout. It is analogous to Printf().
func Stdoutf(format string, v ...) { stdout.Output(2, fmt.Sprintf(format, v)) }

// Stderrf is a helper function for easy formatted logging to stderr. It is analogous to Fprintf(os.Stderr).
func Stderrf(format string, v ...) { stderr.Output(2, fmt.Sprintf(format, v)) }

// Exit is equivalent to Stderr() followed by a call to os.Exit(1).
func Exit(v ...) { exit.Output(2, fmt.Sprintln(v)) }

// Exitf is equivalent to Stderrf() followed by a call to os.Exit(1).
func Exitf(format string, v ...) { exit.Output(2, fmt.Sprintf(format, v)) }

// Crash is equivalent to Stderr() followed by a call to panic().
func Crash(v ...) { crash.Output(2, fmt.Sprintln(v)) }

// Crashf is equivalent to Stderrf() followed by a call to panic().
func Crashf(format string, v ...) { crash.Output(2, fmt.Sprintf(format, v)) }