@@ -37,12 +37,43 @@ import (
3737 "fmt"
3838 "io"
3939 "io/ioutil"
40+ "os"
41+ "runtime"
4042 "strings"
4143 "sync"
4244 "sync/atomic"
4345 "time"
4446)
4547
48+ // defaultFlags specifies changes to the default logger behavior. It is set
49+ // during package init and configured using the LOGFLAGS environment variable.
50+ // New logger backends can override these default flags using WithFlags.
51+ var defaultFlags uint32
52+
53+ // Flags to modify Backend's behavior.
54+ const (
55+ // Llongfile modifies the logger output to include full path and line number
56+ // of the logging callsite, e.g. /a/b/c/main.go:123.
57+ Llongfile uint32 = 1 << iota
58+
59+ // Lshortfile modifies the logger output to include filename and line number
60+ // of the logging callsite, e.g. main.go:123. Overrides Llongfile.
61+ Lshortfile
62+ )
63+
64+ // Read logger flags from the LOGFLAGS environment variable. Multiple flags can
65+ // be set at once, separated by commas.
66+ func init () {
67+ for _ , f := range strings .Split (os .Getenv ("LOGFLAGS" ), "," ) {
68+ switch f {
69+ case "longfile" :
70+ defaultFlags |= Llongfile
71+ case "shortfile" :
72+ defaultFlags |= Lshortfile
73+ }
74+ }
75+ }
76+
4677// Level is the level at which a logger is configured. All messages sent
4778// to a level which is below the current level are filtered.
4879type Level uint32
@@ -95,16 +126,33 @@ func (l Level) String() string {
95126}
96127
97128// NewBackend creates a logger backend from a Writer.
98- func NewBackend (w io.Writer ) * Backend {
99- return & Backend {w : w }
129+ func NewBackend (w io.Writer , opts ... BackendOption ) * Backend {
130+ b := & Backend {w : w , flag : defaultFlags }
131+ for _ , o := range opts {
132+ o (b )
133+ }
134+ return b
100135}
101136
102137// Backend is a logging backend. Subsystems created from the backend write to
103138// the backend's Writer. Backend provides atomic writes to the Writer from all
104139// subsystems.
105140type Backend struct {
106- w io.Writer
107- mu sync.Mutex // ensures atomic writes
141+ w io.Writer
142+ mu sync.Mutex // ensures atomic writes
143+ flag uint32
144+ }
145+
146+ // BackendOption is a function used to modify the behavior of a Backend.
147+ type BackendOption func (b * Backend )
148+
149+ // WithFlags configures a Backend to use the specified flags rather than using
150+ // the package's defaults as determined through the LOGFLAGS environment
151+ // variable.
152+ func WithFlags (flags uint32 ) BackendOption {
153+ return func (b * Backend ) {
154+ b .flag = flags
155+ }
108156}
109157
110158// bufferPool defines a concurrent safe free list of byte slices used to provide
@@ -150,8 +198,10 @@ func itoa(buf *[]byte, i int, wid int) {
150198 * buf = append (* buf , b [bp :]... )
151199}
152200
153- // Appends a header in the format 'YYYY-MM-DD hh:mm:ss.sss [LVL] TAG: '.
154- func formatHeader (buf * []byte , t time.Time , lvl , tag string ) {
201+ // Appends a header in the default format 'YYYY-MM-DD hh:mm:ss.sss [LVL] TAG: '.
202+ // If either of the Lshortfile or Llongfile flags are specified, the file named
203+ // and line number are included after the tag and before the final colon.
204+ func formatHeader (buf * []byte , t time.Time , lvl , tag string , file string , line int ) {
155205 year , month , day := t .Date ()
156206 hour , min , sec := t .Clock ()
157207 ms := t .Nanosecond () / 1e6
@@ -173,9 +223,41 @@ func formatHeader(buf *[]byte, t time.Time, lvl, tag string) {
173223 * buf = append (* buf , lvl ... )
174224 * buf = append (* buf , "] " ... )
175225 * buf = append (* buf , tag ... )
226+ if file != "" {
227+ * buf = append (* buf , ' ' )
228+ * buf = append (* buf , file ... )
229+ * buf = append (* buf , ':' )
230+ itoa (buf , line , - 1 )
231+ }
176232 * buf = append (* buf , ": " ... )
177233}
178234
235+ // calldepth is the call depth of the callsite function relative to the
236+ // caller of the subsystem logger. It is used to recover the filename and line
237+ // number of the logging call if either the short or long file flags are
238+ // specified.
239+ const calldepth = 3
240+
241+ // callsite returns the file name and line number of the callsite to the
242+ // subsystem logger.
243+ func callsite (flag uint32 ) (string , int ) {
244+ _ , file , line , ok := runtime .Caller (calldepth )
245+ if ! ok {
246+ return "???" , 0
247+ }
248+ if flag & Lshortfile != 0 {
249+ short := file
250+ for i := len (file ) - 1 ; i > 0 ; i -- {
251+ if os .IsPathSeparator (file [i ]) {
252+ short = file [i + 1 :]
253+ break
254+ }
255+ }
256+ file = short
257+ }
258+ return file , line
259+ }
260+
179261// print outputs a log message to the writer associated with the backend after
180262// creating a prefix for the given level and tag according to the formatHeader
181263// function and formatting the provided arguments using the default formatting
@@ -185,7 +267,13 @@ func (b *Backend) print(lvl, tag string, args ...interface{}) {
185267
186268 bytebuf := buffer ()
187269
188- formatHeader (bytebuf , t , lvl , tag )
270+ var file string
271+ var line int
272+ if b .flag & (Lshortfile | Llongfile ) != 0 {
273+ file , line = callsite (b .flag )
274+ }
275+
276+ formatHeader (bytebuf , t , lvl , tag , file , line )
189277 buf := bytes .NewBuffer (* bytebuf )
190278 fmt .Fprintln (buf , args ... )
191279 * bytebuf = buf .Bytes ()
@@ -206,7 +294,13 @@ func (b *Backend) printf(lvl, tag string, format string, args ...interface{}) {
206294
207295 bytebuf := buffer ()
208296
209- formatHeader (bytebuf , t , lvl , tag )
297+ var file string
298+ var line int
299+ if b .flag & (Lshortfile | Llongfile ) != 0 {
300+ file , line = callsite (b .flag )
301+ }
302+
303+ formatHeader (bytebuf , t , lvl , tag , file , line )
210304 buf := bytes .NewBuffer (* bytebuf )
211305 fmt .Fprintf (buf , format , args ... )
212306 * bytebuf = append (buf .Bytes (), '\n' )
0 commit comments