├── README.md ├── LICENSE └── log.go /README.md: -------------------------------------------------------------------------------- 1 | log 2 | ============== 3 | log is a simple and useful logging tool of Go. see [API](https://godoc.org/github.com/imroc/log). 4 | 5 | ## Features 6 | * light weight 7 | * easy to use 8 | 9 | ## Note 10 | there is only 2 log level(Debug,Info) the philosophy of this can be found in [here](https://dave.cheney.net/2015/11/05/lets-talk-about-logging) 11 | 12 | ## Quick Start 13 | 14 | ##### Installation 15 | ``` sh 16 | go get github.com/imroc/log 17 | ``` 18 | ##### Simple Usage 19 | ``` go 20 | import ( 21 | "github.com/imroc/log" 22 | ) 23 | 24 | func main() { 25 | log.Debug("debug ", "message") 26 | log.Infof("%s message", "info") 27 | } 28 | ``` 29 | output: 30 | ``` 31 | 2016/10/04 14:38:38 [DEBG] main.go:7 debug message 32 | 2016/10/04 14:38:38 [INFO] main.go:8 info message 33 | ``` 34 | ##### More Control 35 | ``` go 36 | log.SetFilename("test.log") 37 | log.SetFlag(log.Ldate | log.Ltime | log.Llongfile) 38 | log.SetDebug(false) 39 | ``` 40 | ## LICENSE 41 | log is is distributed under the terms of the MIT License. 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 roc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "runtime" 8 | "sync" 9 | "time" 10 | ) 11 | 12 | // These flags define which text to prefix to each log entry generated by the Logger. 13 | const ( 14 | // Bits or'ed together to control what's printed. 15 | // There is no control over the order they appear (the order listed 16 | // here) or the format they present (as described in the comments). 17 | // The prefix is followed by a colon only when Llongfile or Lshortfile 18 | // is specified. 19 | // For example, flags Ldate | Ltime (or LstdFlags) produce, 20 | // 2009/01/23 01:23:23 message 21 | // while flags Ldate | Ltime | Lmicroseconds | Llongfile produce, 22 | // 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message 23 | Ldate = 1 << iota // the date in the local time zone: 2009/01/23 24 | Ltime // the time in the local time zone: 01:23:23 25 | Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime. 26 | Llongfile // full file name and line number: /a/b/c/d.go:23 27 | Lshortfile // final file name element and line number: d.go:23. overrides Llongfile 28 | LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone 29 | LstdFlags = Ldate | Ltime | Lshortfile // initial values for the standard logger 30 | ) 31 | 32 | var debug = true 33 | var flag int = LstdFlags 34 | var out io.Writer = os.Stderr // destination for output 35 | var mu sync.Mutex // ensures atomic writes; protects the following fields 36 | var buf []byte // for accumulating text to write 37 | 38 | // Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid zero-padding. 39 | func itoa(i int, wid int) { 40 | // Assemble decimal in reverse order. 41 | var b [20]byte 42 | bp := len(b) - 1 43 | for i >= 10 || wid > 1 { 44 | wid-- 45 | q := i / 10 46 | b[bp] = byte('0' + i - q*10) 47 | bp-- 48 | i = q 49 | } 50 | // i < 10 51 | b[bp] = byte('0' + i) 52 | buf = append(buf, b[bp:]...) 53 | } 54 | 55 | func formatHeader(t time.Time, level string, file string, line int) { 56 | if flag&LUTC != 0 { 57 | t = t.UTC() 58 | } 59 | if flag&(Ldate|Ltime|Lmicroseconds) != 0 { 60 | if flag&Ldate != 0 { 61 | year, month, day := t.Date() 62 | itoa(year, 4) 63 | buf = append(buf, '/') 64 | itoa(int(month), 2) 65 | buf = append(buf, '/') 66 | itoa(day, 2) 67 | buf = append(buf, ' ') 68 | } 69 | if flag&(Ltime|Lmicroseconds) != 0 { 70 | hour, min, sec := t.Clock() 71 | itoa(hour, 2) 72 | buf = append(buf, ':') 73 | itoa(min, 2) 74 | buf = append(buf, ':') 75 | itoa(sec, 2) 76 | if flag&Lmicroseconds != 0 { 77 | buf = append(buf, '.') 78 | itoa(t.Nanosecond()/1e3, 6) 79 | } 80 | buf = append(buf, ' ') 81 | } 82 | } 83 | 84 | buf = append(buf, level...) 85 | buf = append(buf, ' ') 86 | 87 | if flag&(Lshortfile|Llongfile) != 0 { 88 | if flag&Lshortfile != 0 { 89 | short := file 90 | for i := len(file) - 1; i > 0; i-- { 91 | if file[i] == '/' { 92 | short = file[i+1:] 93 | break 94 | } 95 | } 96 | file = short 97 | } 98 | buf = append(buf, file...) 99 | buf = append(buf, ':') 100 | itoa(line, -1) 101 | buf = append(buf, ": "...) 102 | } 103 | } 104 | 105 | // output outputs the string of with level to the writer. 106 | func output(level string, s string) error { 107 | now := time.Now() // get this early. 108 | var file string 109 | var line int 110 | mu.Lock() 111 | defer mu.Unlock() 112 | if flag&(Lshortfile|Llongfile) != 0 { 113 | // release lock while getting caller info - it's expensive. 114 | mu.Unlock() 115 | var ok bool 116 | _, file, line, ok = runtime.Caller(2) 117 | if !ok { 118 | file = "???" 119 | line = 0 120 | } 121 | mu.Lock() 122 | } 123 | buf = buf[:0] // clear buffer 124 | formatHeader(now, level, file, line) 125 | buf = append(buf, s...) 126 | if len(s) == 0 || s[len(s)-1] != '\n' { 127 | buf = append(buf, '\n') 128 | } 129 | _, err := out.Write(buf) 130 | return err 131 | } 132 | 133 | // SetDebug enable or disable debug. 134 | func SetDebug(d bool) { 135 | debug = d 136 | } 137 | 138 | // SetFlag set the output flag. 139 | func SetFlag(f int) { 140 | flag = f 141 | } 142 | 143 | // SetOutput set the output. 144 | func SetOutput(o io.Writer) { 145 | out = o 146 | } 147 | 148 | // SetFilename set the ouput filename. 149 | func SetFilename(name string) (err error) { 150 | file, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666) 151 | if err != nil { 152 | return 153 | } 154 | SetOutput(file) 155 | return 156 | } 157 | 158 | // Debug output the debug info if DEBUG is set to true. 159 | func Debug(a ...interface{}) { 160 | if !debug { 161 | return 162 | } 163 | output("[DEBG]", fmt.Sprint(a...)) 164 | } 165 | 166 | // Debugf output the formated debug info if DEBUG is set to true. 167 | func Debugf(format string, a ...interface{}) { 168 | if !debug { 169 | return 170 | } 171 | output("[DEBG]", fmt.Sprintf(format, a...)) 172 | } 173 | 174 | // Info output the info. 175 | func Info(a ...interface{}) { 176 | output("[INFO]", fmt.Sprint(a...)) 177 | } 178 | 179 | // Infof output the formated info. 180 | func Infof(format string, a ...interface{}) { 181 | output("[INFO]", fmt.Sprintf(format, a...)) 182 | } 183 | --------------------------------------------------------------------------------