Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 1 | // Copyright 2009 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | // Rudimentary logging package. Defines a type, Logger, with simple |
| 6 | // methods for formatting output to one or two destinations. Also has |
| 7 | // predefined Loggers accessible through helper functions Stdout[f], |
Rob Pike | 26cb4df | 2009-03-04 22:46:44 -0800 | [diff] [blame] | 8 | // Stderr[f], Exit[f], and Crash[f], which are easier to use than creating |
| 9 | // a Logger manually. |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 10 | // Exit exits when written to. |
| 11 | // Crash causes a crash when written to. |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 12 | package log |
| 13 | |
| 14 | import ( |
| 15 | "fmt"; |
| 16 | "io"; |
Russ Cox | 918afd94 | 2009-05-08 15:21:41 -0700 | [diff] [blame] | 17 | "runtime"; |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 18 | "os"; |
| 19 | "time"; |
| 20 | ) |
| 21 | |
Rob Pike | 26cb4df | 2009-03-04 22:46:44 -0800 | [diff] [blame] | 22 | // These flags define the properties of the Logger and the output they produce. |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 23 | const ( |
Rob Pike | 646b3b5 | 2009-01-26 15:34:16 -0800 | [diff] [blame] | 24 | // Flags |
Russ Cox | c2ec958 | 2009-10-06 19:41:51 -0700 | [diff] [blame] | 25 | Lok = iota; |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 26 | Lexit; // terminate execution when written |
| 27 | Lcrash; // crash (panic) when written |
Rob Pike | 646b3b5 | 2009-01-26 15:34:16 -0800 | [diff] [blame] | 28 | // Bits or'ed together to control what's printed. There is no control over the |
| 29 | // order they appear (the order listed here) or the format they present (as |
| 30 | // described in the comments). A colon appears after these items: |
| 31 | // 2009/0123 01:23:23.123123 /a/b/c/d.go:23: message |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 32 | Ldate = 1 << iota; // the date: 2009/0123 |
Russ Cox | c2ec958 | 2009-10-06 19:41:51 -0700 | [diff] [blame] | 33 | Ltime; // the time: 01:23:23 |
Rob Pike | 646b3b5 | 2009-01-26 15:34:16 -0800 | [diff] [blame] | 34 | Lmicroseconds; // microsecond resolution: 01:23:23.123123. assumes Ltime. |
| 35 | Llongfile; // full file name and line number: /a/b/c/d.go:23 |
| 36 | Lshortfile; // final file name element and line number: d.go:23. overrides Llongfile |
Russ Cox | c2ec958 | 2009-10-06 19:41:51 -0700 | [diff] [blame] | 37 | lAllBits = Ldate | Ltime | Lmicroseconds | Llongfile | Lshortfile; |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 38 | ) |
| 39 | |
Rob Pike | 26cb4df | 2009-03-04 22:46:44 -0800 | [diff] [blame] | 40 | // Logger represents an active logging object. |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 41 | type Logger struct { |
Rob Pike | c8b47c6 | 2009-05-08 11:22:57 -0700 | [diff] [blame] | 42 | out0 io.Writer; // first destination for output |
| 43 | out1 io.Writer; // second destination for output; may be nil |
Russ Cox | c2ec958 | 2009-10-06 19:41:51 -0700 | [diff] [blame] | 44 | prefix string; // prefix to write at beginning of each line |
| 45 | flag int; // properties |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 46 | } |
| 47 | |
Rob Pike | c54699c | 2009-05-15 17:22:30 -0700 | [diff] [blame] | 48 | // New creates a new Logger. The out0 and out1 variables set the |
Rob Pike | 26cb4df | 2009-03-04 22:46:44 -0800 | [diff] [blame] | 49 | // destinations to which log data will be written; out1 may be nil. |
| 50 | // The prefix appears at the beginning of each generated log line. |
| 51 | // The flag argument defines the logging properties. |
Rob Pike | c54699c | 2009-05-15 17:22:30 -0700 | [diff] [blame] | 52 | func New(out0, out1 io.Writer, prefix string, flag int) *Logger { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 53 | return &Logger{out0, out1, prefix, flag} |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 54 | } |
| 55 | |
| 56 | var ( |
Russ Cox | c2ec958 | 2009-10-06 19:41:51 -0700 | [diff] [blame] | 57 | stdout = New(os.Stdout, nil, "", Lok|Ldate|Ltime); |
| 58 | stderr = New(os.Stderr, nil, "", Lok|Ldate|Ltime); |
| 59 | exit = New(os.Stderr, nil, "", Lexit|Ldate|Ltime); |
| 60 | crash = New(os.Stderr, nil, "", Lcrash|Ldate|Ltime); |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 61 | ) |
| 62 | |
Russ Cox | c2ec958 | 2009-10-06 19:41:51 -0700 | [diff] [blame] | 63 | var shortnames = make(map[string]string) // cache of short names to avoid allocation. |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 64 | |
Rob Pike | 646b3b5 | 2009-01-26 15:34:16 -0800 | [diff] [blame] | 65 | // Cheap integer to fixed-width decimal ASCII. Use a negative width to avoid zero-padding |
| 66 | func itoa(i int, wid int) string { |
| 67 | var u uint = uint(i); |
| 68 | if u == 0 && wid <= 1 { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 69 | return "0" |
Rob Pike | 646b3b5 | 2009-01-26 15:34:16 -0800 | [diff] [blame] | 70 | } |
| 71 | |
| 72 | // Assemble decimal in reverse order. |
| 73 | var b [32]byte; |
| 74 | bp := len(b); |
| 75 | for ; u > 0 || wid > 0; u /= 10 { |
| 76 | bp--; |
| 77 | wid--; |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 78 | b[bp] = byte(u%10) + '0'; |
Rob Pike | 646b3b5 | 2009-01-26 15:34:16 -0800 | [diff] [blame] | 79 | } |
| 80 | |
Russ Cox | 9ac4449 | 2009-11-20 11:45:05 -0800 | [diff] [blame] | 81 | return string(b[bp:]); |
Rob Pike | 646b3b5 | 2009-01-26 15:34:16 -0800 | [diff] [blame] | 82 | } |
| 83 | |
| 84 | func (l *Logger) formatHeader(ns int64, calldepth int) string { |
| 85 | h := l.prefix; |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 86 | if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 { |
| 87 | t := time.SecondsToLocalTime(ns / 1e9); |
| 88 | if l.flag&(Ldate) != 0 { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 89 | h += itoa(int(t.Year), 4) + "/" + itoa(t.Month, 2) + "/" + itoa(t.Day, 2) + " " |
Rob Pike | 646b3b5 | 2009-01-26 15:34:16 -0800 | [diff] [blame] | 90 | } |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 91 | if l.flag&(Ltime|Lmicroseconds) != 0 { |
Rob Pike | 646b3b5 | 2009-01-26 15:34:16 -0800 | [diff] [blame] | 92 | h += itoa(t.Hour, 2) + ":" + itoa(t.Minute, 2) + ":" + itoa(t.Second, 2); |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 93 | if l.flag&Lmicroseconds != 0 { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 94 | h += "." + itoa(int(ns%1e9)/1e3, 6) |
Rob Pike | 646b3b5 | 2009-01-26 15:34:16 -0800 | [diff] [blame] | 95 | } |
| 96 | h += " "; |
| 97 | } |
| 98 | } |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 99 | if l.flag&(Lshortfile|Llongfile) != 0 { |
Russ Cox | ca6a0fe | 2009-09-15 09:41:59 -0700 | [diff] [blame] | 100 | _, file, line, ok := runtime.Caller(calldepth); |
Rob Pike | 646b3b5 | 2009-01-26 15:34:16 -0800 | [diff] [blame] | 101 | if ok { |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 102 | if l.flag&Lshortfile != 0 { |
Rob Pike | 646b3b5 | 2009-01-26 15:34:16 -0800 | [diff] [blame] | 103 | short, ok := shortnames[file]; |
| 104 | if !ok { |
| 105 | short = file; |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 106 | for i := len(file) - 1; i > 0; i-- { |
Rob Pike | 646b3b5 | 2009-01-26 15:34:16 -0800 | [diff] [blame] | 107 | if file[i] == '/' { |
Russ Cox | 9ac4449 | 2009-11-20 11:45:05 -0800 | [diff] [blame] | 108 | short = file[i+1:]; |
Rob Pike | 646b3b5 | 2009-01-26 15:34:16 -0800 | [diff] [blame] | 109 | break; |
| 110 | } |
| 111 | } |
| 112 | shortnames[file] = short; |
| 113 | } |
| 114 | file = short; |
| 115 | } |
| 116 | } else { |
| 117 | file = "???"; |
| 118 | line = 0; |
| 119 | } |
| 120 | h += file + ":" + itoa(line, -1) + ": "; |
| 121 | } |
| 122 | return h; |
| 123 | } |
| 124 | |
Rob Pike | 26cb4df | 2009-03-04 22:46:44 -0800 | [diff] [blame] | 125 | // Output writes the output for a logging event. The string s contains the text to print after |
| 126 | // the time stamp; calldepth is used to recover the PC. It is provided for generality, although |
| 127 | // at the moment on all pre-defined paths it will be 2. |
Rob Pike | 646b3b5 | 2009-01-26 15:34:16 -0800 | [diff] [blame] | 128 | func (l *Logger) Output(calldepth int, s string) { |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 129 | now := time.Nanoseconds(); // get this early. |
| 130 | newline := "\n"; |
| 131 | if len(s) > 0 && s[len(s)-1] == '\n' { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 132 | newline = "" |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 133 | } |
Robert Griesemer | 3bb0032 | 2009-11-09 21:23:52 -0800 | [diff] [blame] | 134 | s = l.formatHeader(now, calldepth+1) + s + newline; |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 135 | io.WriteString(l.out0, s); |
| 136 | if l.out1 != nil { |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 137 | io.WriteString(l.out1, s) |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 138 | } |
Rob Pike | 646b3b5 | 2009-01-26 15:34:16 -0800 | [diff] [blame] | 139 | switch l.flag & ^lAllBits { |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 140 | case Lcrash: |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 141 | panic("log: fatal error") |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 142 | case Lexit: |
Robert Griesemer | 40621d5 | 2009-11-09 12:07:39 -0800 | [diff] [blame] | 143 | os.Exit(1) |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 144 | } |
| 145 | } |
| 146 | |
Rob Pike | 26cb4df | 2009-03-04 22:46:44 -0800 | [diff] [blame] | 147 | // Logf is analogous to Printf() for a Logger. |
Robert Griesemer | 368f8cb | 2009-11-06 14:24:38 -0800 | [diff] [blame] | 148 | func (l *Logger) Logf(format string, v ...) { l.Output(2, fmt.Sprintf(format, v)) } |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 149 | |
David Symonds | 6ea866c | 2009-11-06 18:43:57 -0800 | [diff] [blame] | 150 | // Log is analogous to Print() for a Logger. |
Robert Griesemer | 368f8cb | 2009-11-06 14:24:38 -0800 | [diff] [blame] | 151 | func (l *Logger) Log(v ...) { l.Output(2, fmt.Sprintln(v)) } |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 152 | |
Rob Pike | 26cb4df | 2009-03-04 22:46:44 -0800 | [diff] [blame] | 153 | // Stdout is a helper function for easy logging to stdout. It is analogous to Print(). |
Robert Griesemer | 368f8cb | 2009-11-06 14:24:38 -0800 | [diff] [blame] | 154 | func Stdout(v ...) { stdout.Output(2, fmt.Sprint(v)) } |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 155 | |
Rob Pike | 26cb4df | 2009-03-04 22:46:44 -0800 | [diff] [blame] | 156 | // Stderr is a helper function for easy logging to stderr. It is analogous to Fprint(os.Stderr). |
Robert Griesemer | 368f8cb | 2009-11-06 14:24:38 -0800 | [diff] [blame] | 157 | func Stderr(v ...) { stderr.Output(2, fmt.Sprintln(v)) } |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 158 | |
Rob Pike | 26cb4df | 2009-03-04 22:46:44 -0800 | [diff] [blame] | 159 | // Stdoutf is a helper functions for easy formatted logging to stdout. It is analogous to Printf(). |
Robert Griesemer | 368f8cb | 2009-11-06 14:24:38 -0800 | [diff] [blame] | 160 | func Stdoutf(format string, v ...) { stdout.Output(2, fmt.Sprintf(format, v)) } |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 161 | |
Rob Pike | 26cb4df | 2009-03-04 22:46:44 -0800 | [diff] [blame] | 162 | // Stderrf is a helper function for easy formatted logging to stderr. It is analogous to Fprintf(os.Stderr). |
Robert Griesemer | 368f8cb | 2009-11-06 14:24:38 -0800 | [diff] [blame] | 163 | func Stderrf(format string, v ...) { stderr.Output(2, fmt.Sprintf(format, v)) } |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 164 | |
Russ Cox | 918afd94 | 2009-05-08 15:21:41 -0700 | [diff] [blame] | 165 | // Exit is equivalent to Stderr() followed by a call to os.Exit(1). |
Robert Griesemer | 368f8cb | 2009-11-06 14:24:38 -0800 | [diff] [blame] | 166 | func Exit(v ...) { exit.Output(2, fmt.Sprintln(v)) } |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 167 | |
Russ Cox | 918afd94 | 2009-05-08 15:21:41 -0700 | [diff] [blame] | 168 | // Exitf is equivalent to Stderrf() followed by a call to os.Exit(1). |
Robert Griesemer | 368f8cb | 2009-11-06 14:24:38 -0800 | [diff] [blame] | 169 | func Exitf(format string, v ...) { exit.Output(2, fmt.Sprintf(format, v)) } |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 170 | |
Kai Backman | 8149a8c | 2009-08-13 15:14:41 -0700 | [diff] [blame] | 171 | // Crash is equivalent to Stderr() followed by a call to panic(). |
Robert Griesemer | 368f8cb | 2009-11-06 14:24:38 -0800 | [diff] [blame] | 172 | func Crash(v ...) { crash.Output(2, fmt.Sprintln(v)) } |
Rob Pike | 806d00f | 2009-01-26 09:28:07 -0800 | [diff] [blame] | 173 | |
Rob Pike | 26cb4df | 2009-03-04 22:46:44 -0800 | [diff] [blame] | 174 | // Crashf is equivalent to Stderrf() followed by a call to panic(). |
Robert Griesemer | 368f8cb | 2009-11-06 14:24:38 -0800 | [diff] [blame] | 175 | func Crashf(format string, v ...) { crash.Output(2, fmt.Sprintf(format, v)) } |