├── go.mod ├── examples ├── ConsoleLogWriter_Manual.go ├── XMLConfigurationExample.go ├── SocketLogWriter_Manual.go ├── SimpleNetLogServer.go ├── FileLogWriter_Manual.go └── example.xml ├── README ├── LICENSE ├── termlog.go ├── socklog.go ├── pattlog.go ├── filelog.go ├── config.go ├── wrapper.go ├── log4go.go ├── log4go_test.go └── go.sum /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blackbeans/log4go 2 | 3 | go 1.16 4 | 5 | require github.com/prometheus/client_golang v1.14.0 6 | -------------------------------------------------------------------------------- /examples/ConsoleLogWriter_Manual.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | import l4g "github.com/blackbeans/log4go" 8 | 9 | func main() { 10 | log := l4g.NewLogger() 11 | log.AddFilter("stdout", l4g.DEBUG, l4g.NewConsoleLogWriter()) 12 | log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02")) 13 | } 14 | -------------------------------------------------------------------------------- /examples/XMLConfigurationExample.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import l4g "github.com/blackbeans/log4go" 4 | 5 | func main() { 6 | // Load the configuration (isn't this easy?) 7 | l4g.LoadConfiguration("example.xml") 8 | 9 | // And now we're ready! 10 | l4g.Finest("This will only go to those of you really cool UDP kids! If you change enabled=true.") 11 | l4g.Debug("Oh no! %d + %d = %d!", 2, 2, 2+2) 12 | l4g.Info("About that time, eh chaps?") 13 | } 14 | -------------------------------------------------------------------------------- /examples/SocketLogWriter_Manual.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | import l4g "github.com/blackbeans/log4go" 8 | 9 | func main() { 10 | log := l4g.NewLogger() 11 | log.AddFilter("network", l4g.FINEST, l4g.NewSocketLogWriter("udp", "192.168.1.255:12124")) 12 | 13 | // Run `nc -u -l -p 12124` or similar before you run this to see the following message 14 | log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02")) 15 | 16 | // This makes sure the output stream buffer is written 17 | log.Close() 18 | } 19 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Please see http://log4go.googlecode.com/ 2 | 3 | Installation: 4 | - Run `go install log4go.googlecode.com/hg` 5 | 6 | Usage: 7 | - Add the following import: 8 | import l4g "log4go.googlecode.com/hg" 9 | 10 | Acknowledgements: 11 | - pomack 12 | For providing awesome patches to bring log4go up to the latest Go spec 13 | 14 | 15 | More Feature: 16 | - log to a file as you define 17 | 18 | log.LoadConfiguration("log.xml") 19 | exp: 20 | log.DebugLog("kite_handler",format,args...) 21 | log.InfoLog("kite_handler",format,args...) 22 | log.WarnLog("kite_handler",format,args...) 23 | log.ErrorLog("kite_handler",format,args...) 24 | 25 | - log rolling by day 26 | 27 | true 28 | -------------------------------------------------------------------------------- /examples/SimpleNetLogServer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "net" 7 | "os" 8 | ) 9 | 10 | var ( 11 | port = flag.String("p", "12124", "Port number to listen on") 12 | ) 13 | 14 | func e(err error) { 15 | if err != nil { 16 | fmt.Printf("Erroring out: %s\n", err) 17 | os.Exit(1) 18 | } 19 | } 20 | 21 | func main() { 22 | flag.Parse() 23 | 24 | // Bind to the port 25 | bind, err := net.ResolveUDPAddr("udp", "0.0.0.0:"+*port) 26 | e(err) 27 | 28 | // Create listener 29 | listener, err := net.ListenUDP("udp", bind) 30 | e(err) 31 | 32 | fmt.Printf("Listening to port %s...\n", *port) 33 | for { 34 | // read into a new buffer 35 | buffer := make([]byte, 1024) 36 | _, _, err := listener.ReadFrom(buffer) 37 | e(err) 38 | 39 | // log to standard output 40 | fmt.Println(string(buffer)) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010, Kyle Lemons . All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 5 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the 6 | documentation and/or other materials provided with the distribution. 7 | 8 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 9 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 10 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 11 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 12 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 13 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 14 | -------------------------------------------------------------------------------- /termlog.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2010, Kyle Lemons . All rights reserved. 2 | 3 | package log4go 4 | 5 | import ( 6 | "io" 7 | "os" 8 | "fmt" 9 | ) 10 | 11 | var stdout io.Writer = os.Stdout 12 | 13 | // This is the standard writer that prints to standard output. 14 | type ConsoleLogWriter chan *LogRecord 15 | 16 | // This creates a new ConsoleLogWriter 17 | func NewConsoleLogWriter() ConsoleLogWriter { 18 | records := make(ConsoleLogWriter, LogBufferLength) 19 | go records.run(stdout) 20 | return records 21 | } 22 | 23 | func (w ConsoleLogWriter) run(out io.Writer) { 24 | var timestr string 25 | var timestrAt int64 26 | 27 | for rec := range w { 28 | if at := rec.Created.UnixNano() / 1e9; at != timestrAt { 29 | timestr, timestrAt = rec.Created.Format("01/02/06 15:04:05"), at 30 | } 31 | fmt.Fprint(out, "[", timestr, "] [", levelStrings[rec.Level], "] ", rec.Message, "\n") 32 | } 33 | } 34 | 35 | // This is the ConsoleLogWriter's output method. This will block if the output 36 | // buffer is full. 37 | func (w ConsoleLogWriter) LogWrite(rec *LogRecord) { 38 | w <- rec 39 | } 40 | 41 | // Close stops the logger from sending messages to standard output. Attempts to 42 | // send log messages to this logger after a Close have undefined behavior. 43 | func (w ConsoleLogWriter) Close() { 44 | close(w) 45 | } 46 | -------------------------------------------------------------------------------- /socklog.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2010, Kyle Lemons . All rights reserved. 2 | 3 | package log4go 4 | 5 | import ( 6 | "encoding/json" 7 | "fmt" 8 | "net" 9 | "os" 10 | ) 11 | 12 | // This log writer sends output to a socket 13 | type SocketLogWriter chan *LogRecord 14 | 15 | // This is the SocketLogWriter's output method 16 | func (w SocketLogWriter) LogWrite(rec *LogRecord) { 17 | w <- rec 18 | } 19 | 20 | func (w SocketLogWriter) Close() { 21 | close(w) 22 | } 23 | 24 | func NewSocketLogWriter(proto, hostport string) SocketLogWriter { 25 | sock, err := net.Dial(proto, hostport) 26 | if err != nil { 27 | fmt.Fprintf(os.Stderr, "NewSocketLogWriter(%q): %s\n", hostport, err) 28 | return nil 29 | } 30 | 31 | w := SocketLogWriter(make(chan *LogRecord, LogBufferLength)) 32 | 33 | go func() { 34 | defer func() { 35 | if sock != nil && proto == "tcp" { 36 | sock.Close() 37 | } 38 | }() 39 | 40 | for rec := range w { 41 | // Marshall into JSON 42 | js, err := json.Marshal(rec) 43 | if err != nil { 44 | fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err) 45 | return 46 | } 47 | 48 | _, err = sock.Write(js) 49 | if err != nil { 50 | fmt.Fprint(os.Stderr, "SocketLogWriter(%q): %s", hostport, err) 51 | return 52 | } 53 | } 54 | }() 55 | 56 | return w 57 | } 58 | -------------------------------------------------------------------------------- /examples/FileLogWriter_Manual.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "os" 8 | "time" 9 | ) 10 | 11 | import l4g "github.com/blackbeans/log4go" 12 | 13 | const ( 14 | filename = "flw.log" 15 | ) 16 | 17 | func main() { 18 | // Get a new logger instance 19 | log := l4g.NewLogger() 20 | 21 | // Create a default logger that is logging messages of FINE or higher 22 | log.AddFilter("file", l4g.FINE, l4g.NewFileLogWriter(filename, false, true)) 23 | log.Close() 24 | 25 | /* Can also specify manually via the following: (these are the defaults) */ 26 | flw := l4g.NewFileLogWriter(filename, false, true) 27 | flw.SetFormat("[%D %T] [%L] (%S) %M") 28 | flw.SetRotate(false) 29 | flw.SetRotateSize(0) 30 | flw.SetRotateLines(0) 31 | flw.SetRotateDaily(false) 32 | log.AddFilter("file", l4g.FINE, flw) 33 | 34 | // Log some experimental messages 35 | log.Finest("Everything is created now (notice that I will not be printing to the file)") 36 | log.Info("The time is now: %s", time.Now().Format("15:04:05 MST 2006/01/02")) 37 | log.Critical("Time to close out!") 38 | 39 | // Close the log 40 | log.Close() 41 | 42 | // Print what was logged to the file (yes, I know I'm skipping error checking) 43 | fd, _ := os.Open(filename) 44 | in := bufio.NewReader(fd) 45 | fmt.Print("Messages logged to file were: (line numbers not included)\n") 46 | for lineno := 1; ; lineno++ { 47 | line, err := in.ReadString('\n') 48 | if err == io.EOF { 49 | break 50 | } 51 | fmt.Printf("%3d:\t%s", lineno, line) 52 | } 53 | fd.Close() 54 | 55 | // Remove the file so it's not lying around 56 | os.Remove(filename) 57 | } 58 | -------------------------------------------------------------------------------- /examples/example.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | stdout 4 | console 5 | 6 | DEBUG 7 | 8 | 9 | file 10 | file 11 | FINEST 12 | test.log 13 | 24 | [%D %T] [%L] (%S) %M 25 | false 26 | 0M 27 | 0K 28 | true 29 | 30 | 31 | xmllog 32 | xml 33 | TRACE 34 | trace.xml 35 | true 36 | 100M 37 | 6K 38 | false 39 | 40 | 41 | donotopen 42 | socket 43 | FINEST 44 | 192.168.1.255:12124 45 | udp 46 | 47 | 48 | -------------------------------------------------------------------------------- /pattlog.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2010, Kyle Lemons . All rights reserved. 2 | 3 | package log4go 4 | 5 | import ( 6 | "bytes" 7 | "fmt" 8 | "io" 9 | ) 10 | 11 | const ( 12 | FORMAT_DEFAULT = "[%D %T] [%L] (%S) %M" 13 | FORMAT_SHORT = "[%t %d] [%L] %M" 14 | FORMAT_ABBREV = "[%L] %M" 15 | ) 16 | 17 | type formatCacheType struct { 18 | LastUpdateSeconds int64 19 | shortTime, shortDate string 20 | longTime, longDate string 21 | } 22 | 23 | var formatCache = &formatCacheType{} 24 | 25 | // Known format codes: 26 | // %T - Time (15:04:05 MST) 27 | // %t - Time (15:04) 28 | // %D - Date (2006/01/02) 29 | // %d - Date (01/02/06) 30 | // %L - Level (FNST, FINE, DEBG, TRAC, WARN, EROR, CRIT) 31 | // %S - Source 32 | // %M - Message 33 | // Ignores unknown formats 34 | // Recommended: "[%D %T] [%L] (%S) %M" 35 | func FormatLogRecord(format string, rec *LogRecord) string { 36 | if rec == nil { 37 | return "" 38 | } 39 | if len(format) == 0 { 40 | return "" 41 | } 42 | 43 | out := bytes.NewBuffer(make([]byte, 0, 64)) 44 | secs := rec.Created.UnixNano() / 1e9 45 | 46 | cache := *formatCache 47 | if cache.LastUpdateSeconds != secs { 48 | month, day, year := rec.Created.Month(), rec.Created.Day(), rec.Created.Year() 49 | hour, minute, second := rec.Created.Hour(), rec.Created.Minute(), rec.Created.Second() 50 | zone, _ := rec.Created.Zone() 51 | updated := &formatCacheType{ 52 | LastUpdateSeconds: secs, 53 | shortTime: fmt.Sprintf("%02d:%02d", hour, minute), 54 | shortDate: fmt.Sprintf("%02d/%02d/%02d", month, day, year%100), 55 | longTime: fmt.Sprintf("%02d:%02d:%02d %s", hour, minute, second, zone), 56 | longDate: fmt.Sprintf("%04d/%02d/%02d", year, month, day), 57 | } 58 | cache = *updated 59 | formatCache = updated 60 | } 61 | 62 | // Split the string into pieces by % signs 63 | pieces := bytes.Split([]byte(format), []byte{'%'}) 64 | 65 | // Iterate over the pieces, replacing known formats 66 | for i, piece := range pieces { 67 | if i > 0 && len(piece) > 0 { 68 | switch piece[0] { 69 | case 'T': 70 | out.WriteString(cache.longTime) 71 | case 't': 72 | out.WriteString(cache.shortTime) 73 | case 'D': 74 | out.WriteString(cache.longDate) 75 | case 'd': 76 | out.WriteString(cache.shortDate) 77 | case 'L': 78 | out.WriteString(levelStrings[rec.Level]) 79 | case 'S': 80 | out.WriteString(rec.Source) 81 | case 'M': 82 | out.WriteString(rec.Message) 83 | } 84 | if len(piece) > 1 { 85 | out.Write(piece[1:]) 86 | } 87 | } else if len(piece) > 0 { 88 | out.Write(piece) 89 | } 90 | } 91 | out.WriteByte('\n') 92 | 93 | return out.String() 94 | } 95 | 96 | // This is the standard writer that prints to standard output. 97 | type FormatLogWriter chan *LogRecord 98 | 99 | // This creates a new FormatLogWriter 100 | func NewFormatLogWriter(out io.Writer, format string) FormatLogWriter { 101 | records := make(FormatLogWriter, LogBufferLength) 102 | go records.run(out, format) 103 | return records 104 | } 105 | 106 | func (w FormatLogWriter) run(out io.Writer, format string) { 107 | for rec := range w { 108 | fmt.Fprint(out, FormatLogRecord(format, rec)) 109 | } 110 | } 111 | 112 | // This is the FormatLogWriter's output method. This will block if the output 113 | // buffer is full. 114 | func (w FormatLogWriter) LogWrite(rec *LogRecord) { 115 | w <- rec 116 | } 117 | 118 | // Close stops the logger from sending messages to standard output. Attempts to 119 | // send log messages to this logger after a Close have undefined behavior. 120 | func (w FormatLogWriter) Close() { 121 | close(w) 122 | } 123 | -------------------------------------------------------------------------------- /filelog.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2010, Kyle Lemons . All rights reserved. 2 | 3 | package log4go 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | // This log writer sends output to a file 13 | type FileLogWriter struct { 14 | rec chan *LogRecord 15 | rot chan bool 16 | 17 | // The opened file 18 | filename string 19 | file *os.File 20 | 21 | // The logging format 22 | format string 23 | 24 | // File header/trailer 25 | header, trailer string 26 | 27 | // Rotate at linecount 28 | maxlines int 29 | maxlines_curlines int 30 | 31 | // Rotate at size 32 | maxsize int 33 | maxsize_cursize int 34 | 35 | // Rotate daily 36 | daily bool 37 | daily_opendate int 38 | 39 | // Keep old logfiles (.001, .002, etc) 40 | rotate bool 41 | } 42 | 43 | // This is the FileLogWriter's output method 44 | func (w *FileLogWriter) LogWrite(rec *LogRecord) { 45 | w.rec <- rec 46 | } 47 | 48 | func (w *FileLogWriter) Close() { 49 | close(w.rec) 50 | } 51 | 52 | // NewFileLogWriter creates a new LogWriter which writes to the given file and 53 | // has rotation enabled if rotate is true. 54 | // 55 | // If rotate is true, any time a new log file is opened, the old one is renamed 56 | // with a .### extension to preserve it. The various Set* methods can be used 57 | // to configure log rotation based on lines, size, and daily. 58 | // 59 | // The standard log-line format is: 60 | // [%D %T] [%L] (%S) %M 61 | func NewFileLogWriter(fname string, rotate bool, daily bool) *FileLogWriter { 62 | w := &FileLogWriter{ 63 | rec: make(chan *LogRecord, LogBufferLength), 64 | rot: make(chan bool), 65 | filename: fname, 66 | daily_opendate: time.Now().Day(), 67 | format: "[%D %T] [%L] (%S) %M", 68 | rotate: rotate, 69 | daily: daily} 70 | 71 | // open the file for the first time 72 | // if err := w.intRotate(); err != nil { 73 | // fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) 74 | // return nil 75 | // } 76 | 77 | fpath := fname[:strings.LastIndex(fname, "/")] 78 | 79 | //check path 80 | _, err := os.Lstat(fpath) 81 | if nil != err { 82 | os.MkdirAll(fpath, os.ModePerm) 83 | } 84 | 85 | // Open the log file 86 | fd, err := os.OpenFile(fname, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660) 87 | if err != nil { 88 | panic(err) 89 | } 90 | w.file = fd 91 | 92 | now := time.Now() 93 | 94 | // Set the daily open date to the current date 95 | w.daily_opendate = now.Day() 96 | 97 | fi, err := fd.Stat() 98 | if nil == err && nil != fi { 99 | w.maxsize_cursize = int(fi.Size()) 100 | now = fi.ModTime() 101 | } 102 | // initialize rotation values 103 | w.maxlines_curlines = 0 104 | 105 | fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now})) 106 | 107 | go func() { 108 | defer func() { 109 | if w.file != nil { 110 | fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()})) 111 | w.file.Close() 112 | } 113 | }() 114 | 115 | for { 116 | select { 117 | case <-w.rot: 118 | if err := w.intRotate(); err != nil { 119 | fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) 120 | return 121 | } 122 | case rec, ok := <-w.rec: 123 | if !ok { 124 | return 125 | } 126 | 127 | if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) || 128 | (w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) { 129 | if err := w.intRotate(); err != nil { 130 | fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) 131 | return 132 | } 133 | } 134 | 135 | now := time.Now() 136 | //如果是开启了并且按天滚动,并且已经换了一天需要重建 137 | if w.daily { 138 | if now.Day() != w.daily_opendate { 139 | if err := w.intRotate(); err != nil { 140 | fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) 141 | return 142 | } 143 | } 144 | } 145 | 146 | // Perform the write 147 | n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec)) 148 | if err != nil { 149 | fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) 150 | return 151 | } 152 | 153 | // Update the counts 154 | w.maxlines_curlines++ 155 | w.maxsize_cursize += n 156 | } 157 | } 158 | }() 159 | 160 | return w 161 | } 162 | 163 | // Request that the logs rotate 164 | func (w *FileLogWriter) Rotate() { 165 | w.rot <- true 166 | } 167 | 168 | // If this is called in a threaded context, it MUST be synchronized 169 | func (w *FileLogWriter) intRotate() error { 170 | // Close any log file that may be open 171 | if w.file != nil { 172 | fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()})) 173 | w.file.Close() 174 | } 175 | 176 | // If we are keeping log files, move it to the next available number 177 | if w.rotate { 178 | _, err := os.Lstat(w.filename) 179 | if err == nil { // file exists 180 | // Find the next available number 181 | num := 1 182 | fname := w.filename 183 | filename := strings.TrimSuffix(w.filename, ".log") 184 | for ; err == nil && num <= 999; num++ { 185 | if w.daily { 186 | if time.Now().Day() != w.daily_opendate { 187 | t := time.Now().Add(-24 * time.Hour).Format("2006-01-02") 188 | fname = fmt.Sprintf("%s.%s-%03d.log", filename, t, num) 189 | } else { 190 | t := time.Now().Format("2006-01-02") 191 | fname = fmt.Sprintf("%s.%s-%03d.log", filename, t, num) 192 | } 193 | } else { 194 | fname = fmt.Sprintf("%s.%03d.log", filename, num) 195 | } 196 | 197 | _, err = os.Lstat(fname) 198 | } 199 | // return error if the last file checked still existed 200 | if err == nil { 201 | return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename) 202 | } 203 | 204 | // Rename the file to its newfound home 205 | err = os.Rename(w.filename, fname) 206 | if err != nil { 207 | return fmt.Errorf("Rotate: %s\n", err) 208 | } 209 | } 210 | } 211 | 212 | // Open the log file 213 | fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660) 214 | if err != nil { 215 | return err 216 | } 217 | w.file = fd 218 | 219 | now := time.Now() 220 | fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now})) 221 | 222 | // Set the daily open date to the current date 223 | w.daily_opendate = now.Day() 224 | 225 | // initialize rotation values 226 | w.maxlines_curlines = 0 227 | w.maxsize_cursize = 0 228 | return nil 229 | } 230 | 231 | // Set the logging format (chainable). Must be called before the first log 232 | // message is written. 233 | func (w *FileLogWriter) SetFormat(format string) *FileLogWriter { 234 | w.format = format 235 | return w 236 | } 237 | 238 | // Set the logfile header and footer (chainable). Must be called before the first log 239 | // message is written. These are formatted similar to the FormatLogRecord (e.g. 240 | // you can use %D and %T in your header/footer for date and time). 241 | func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter { 242 | w.header, w.trailer = head, foot 243 | if w.maxlines_curlines == 0 { 244 | fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()})) 245 | } 246 | return w 247 | } 248 | 249 | // Set rotate at linecount (chainable). Must be called before the first log 250 | // message is written. 251 | func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter { 252 | //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines) 253 | w.maxlines = maxlines 254 | return w 255 | } 256 | 257 | // Set rotate at size (chainable). Must be called before the first log message 258 | // is written. 259 | func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter { 260 | //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize) 261 | w.maxsize = maxsize 262 | return w 263 | } 264 | 265 | // Set rotate daily (chainable). Must be called before the first log message is 266 | // written. 267 | func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter { 268 | //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily) 269 | w.daily = daily 270 | return w 271 | } 272 | 273 | // SetRotate changes whether or not the old logs are kept. (chainable) Must be 274 | // called before the first log message is written. If rotate is false, the 275 | // files are overwritten; otherwise, they are rotated to another file before the 276 | // new log is opened. 277 | func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter { 278 | //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate) 279 | w.rotate = rotate 280 | return w 281 | } 282 | 283 | // NewXMLLogWriter is a utility method for creating a FileLogWriter set up to 284 | // output XML record log messages instead of line-based ones. 285 | func NewXMLLogWriter(fname string, rotate bool, daily bool) *FileLogWriter { 286 | return NewFileLogWriter(fname, rotate, daily).SetFormat( 287 | ` 288 | %D %T 289 | %S 290 | %M 291 | `).SetHeadFoot("", "") 292 | } 293 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2010, Kyle Lemons . All rights reserved. 2 | 3 | package log4go 4 | 5 | import ( 6 | "encoding/xml" 7 | "fmt" 8 | "io/ioutil" 9 | "os" 10 | "strconv" 11 | "strings" 12 | ) 13 | 14 | type xmlProperty struct { 15 | Name string `xml:"name,attr"` 16 | Value string `xml:",chardata"` 17 | } 18 | 19 | type xmlFilter struct { 20 | Enabled string `xml:"enabled,attr"` 21 | Tag string `xml:"tag"` 22 | Level string `xml:"level"` 23 | Type string `xml:"type"` 24 | Property []xmlProperty `xml:"property"` 25 | } 26 | 27 | type xmlLoggerConfig struct { 28 | Filter []xmlFilter `xml:"filter"` 29 | } 30 | 31 | // Load XML configuration; see examples/example.xml for documentation 32 | func (log Logger) LoadConfiguration(filename string) { 33 | log.Close() 34 | 35 | // Open the configuration file 36 | fd, err := os.Open(filename) 37 | if err != nil { 38 | fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not open %q for reading: %s\n", filename, err) 39 | os.Exit(1) 40 | } 41 | 42 | contents, err := ioutil.ReadAll(fd) 43 | if err != nil { 44 | fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not read %q: %s\n", filename, err) 45 | os.Exit(1) 46 | } 47 | 48 | xc := new(xmlLoggerConfig) 49 | if err := xml.Unmarshal(contents, xc); err != nil { 50 | fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not parse XML configuration in %q: %s\n", filename, err) 51 | os.Exit(1) 52 | } 53 | 54 | for _, xmlfilt := range xc.Filter { 55 | var filt LogWriter 56 | var lvl level 57 | bad, good, enabled := false, true, false 58 | 59 | // Check required children 60 | if len(xmlfilt.Enabled) == 0 { 61 | fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required attribute %s for filter missing in %s\n", "enabled", filename) 62 | bad = true 63 | } else { 64 | enabled = xmlfilt.Enabled != "false" 65 | } 66 | if len(xmlfilt.Tag) == 0 { 67 | fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "tag", filename) 68 | bad = true 69 | } 70 | if len(xmlfilt.Type) == 0 { 71 | fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "type", filename) 72 | bad = true 73 | } 74 | if len(xmlfilt.Level) == 0 { 75 | fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter missing in %s\n", "level", filename) 76 | bad = true 77 | } 78 | 79 | switch xmlfilt.Level { 80 | case "FINEST": 81 | lvl = FINEST 82 | case "FINE": 83 | lvl = FINE 84 | case "DEBUG": 85 | lvl = DEBUG 86 | case "TRACE": 87 | lvl = TRACE 88 | case "INFO": 89 | lvl = INFO 90 | case "WARNING": 91 | lvl = WARNING 92 | case "ERROR": 93 | lvl = ERROR 94 | case "CRITICAL": 95 | lvl = CRITICAL 96 | default: 97 | fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required child <%s> for filter has unknown value in %s: %s\n", "level", filename, xmlfilt.Level) 98 | bad = true 99 | } 100 | 101 | // Just so all of the required attributes are errored at the same time if missing 102 | if bad { 103 | os.Exit(1) 104 | } 105 | 106 | file := "./logs/" 107 | switch xmlfilt.Type { 108 | case "console": 109 | filt, good = xmlToConsoleLogWriter(filename, xmlfilt.Property, enabled) 110 | case "file": 111 | filt, file, good = xmlToFileLogWriter(filename, xmlfilt.Property, enabled) 112 | case "xml": 113 | filt, file, good = xmlToXMLLogWriter(filename, xmlfilt.Property, enabled) 114 | case "socket": 115 | filt, good = xmlToSocketLogWriter(filename, xmlfilt.Property, enabled) 116 | default: 117 | fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Could not load XML configuration in %s: unknown filter type \"%s\"\n", filename, xmlfilt.Type) 118 | os.Exit(1) 119 | } 120 | 121 | // Just so all of the required params are errored at the same time if wrong 122 | if !good { 123 | os.Exit(1) 124 | } 125 | 126 | // If we're disabled (syntax and correctness checks only), don't add to logger 127 | if !enabled { 128 | continue 129 | } 130 | 131 | log[xmlfilt.Tag] = &Filter{lvl, file, filt} 132 | } 133 | } 134 | 135 | func xmlToConsoleLogWriter(filename string, props []xmlProperty, enabled bool) (ConsoleLogWriter, bool) { 136 | // Parse properties 137 | for _, prop := range props { 138 | switch prop.Name { 139 | default: 140 | fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for console filter in %s\n", prop.Name, filename) 141 | } 142 | } 143 | 144 | // If it's disabled, we're just checking syntax 145 | if !enabled { 146 | return nil, true 147 | } 148 | 149 | return NewConsoleLogWriter(), true 150 | } 151 | 152 | // Parse a number with K/M/G suffixes based on thousands (1000) or 2^10 (1024) 153 | func strToNumSuffix(str string, mult int) int { 154 | num := 1 155 | if len(str) > 1 { 156 | switch str[len(str)-1] { 157 | case 'G', 'g': 158 | num *= mult 159 | fallthrough 160 | case 'M', 'm': 161 | num *= mult 162 | fallthrough 163 | case 'K', 'k': 164 | num *= mult 165 | str = str[0 : len(str)-1] 166 | } 167 | } 168 | parsed, _ := strconv.Atoi(str) 169 | return parsed * num 170 | } 171 | func xmlToFileLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, string, bool) { 172 | file := "" 173 | format := "[%D %T] [%L] (%S) %M" 174 | maxlines := 0 175 | maxsize := 0 176 | daily := false 177 | rotate := false 178 | 179 | // Parse properties 180 | for _, prop := range props { 181 | switch prop.Name { 182 | case "filename": 183 | file = strings.Trim(prop.Value, " \r\n") 184 | case "format": 185 | format = strings.Trim(prop.Value, " \r\n") 186 | case "maxlines": 187 | maxlines = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000) 188 | case "maxsize": 189 | maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024) 190 | case "daily": 191 | daily = strings.Trim(prop.Value, " \r\n") != "false" 192 | case "rotate": 193 | rotate = strings.Trim(prop.Value, " \r\n") != "false" 194 | default: 195 | fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename) 196 | } 197 | } 198 | 199 | // Check properties 200 | if len(file) == 0 { 201 | fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "filename", filename) 202 | return nil, file, false 203 | } 204 | 205 | // If it's disabled, we're just checking syntax 206 | if !enabled { 207 | return nil, file, true 208 | } 209 | 210 | flw := NewFileLogWriter(file, rotate, daily) 211 | flw.SetFormat(format) 212 | flw.SetRotateLines(maxlines) 213 | flw.SetRotateSize(maxsize) 214 | return flw, file, true 215 | } 216 | 217 | func xmlToXMLLogWriter(filename string, props []xmlProperty, enabled bool) (*FileLogWriter, string, bool) { 218 | file := "" 219 | maxrecords := 0 220 | maxsize := 0 221 | daily := false 222 | rotate := false 223 | 224 | // Parse properties 225 | for _, prop := range props { 226 | switch prop.Name { 227 | case "filename": 228 | file = strings.Trim(prop.Value, " \r\n") 229 | case "maxrecords": 230 | maxrecords = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1000) 231 | case "maxsize": 232 | maxsize = strToNumSuffix(strings.Trim(prop.Value, " \r\n"), 1024) 233 | case "daily": 234 | daily = strings.Trim(prop.Value, " \r\n") != "false" 235 | case "rotate": 236 | rotate = strings.Trim(prop.Value, " \r\n") != "false" 237 | default: 238 | fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for xml filter in %s\n", prop.Name, filename) 239 | } 240 | } 241 | 242 | // Check properties 243 | if len(file) == 0 { 244 | fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for xml filter missing in %s\n", "filename", filename) 245 | return nil, file, false 246 | } 247 | 248 | // If it's disabled, we're just checking syntax 249 | if !enabled { 250 | return nil, file, true 251 | } 252 | 253 | xlw := NewXMLLogWriter(file, rotate, daily) 254 | xlw.SetRotateLines(maxrecords) 255 | xlw.SetRotateSize(maxsize) 256 | return xlw, file, true 257 | } 258 | 259 | func xmlToSocketLogWriter(filename string, props []xmlProperty, enabled bool) (SocketLogWriter, bool) { 260 | endpoint := "" 261 | protocol := "udp" 262 | 263 | // Parse properties 264 | for _, prop := range props { 265 | switch prop.Name { 266 | case "endpoint": 267 | endpoint = strings.Trim(prop.Value, " \r\n") 268 | case "protocol": 269 | protocol = strings.Trim(prop.Value, " \r\n") 270 | default: 271 | fmt.Fprintf(os.Stderr, "LoadConfiguration: Warning: Unknown property \"%s\" for file filter in %s\n", prop.Name, filename) 272 | } 273 | } 274 | 275 | // Check properties 276 | if len(endpoint) == 0 { 277 | fmt.Fprintf(os.Stderr, "LoadConfiguration: Error: Required property \"%s\" for file filter missing in %s\n", "endpoint", filename) 278 | return nil, false 279 | } 280 | 281 | // If it's disabled, we're just checking syntax 282 | if !enabled { 283 | return nil, true 284 | } 285 | 286 | return NewSocketLogWriter(protocol, endpoint), true 287 | } 288 | -------------------------------------------------------------------------------- /wrapper.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2010, Kyle Lemons . All rights reserved. 2 | 3 | package log4go 4 | 5 | import ( 6 | "errors" 7 | "fmt" 8 | "os" 9 | "strings" 10 | ) 11 | 12 | var ( 13 | Global Logger 14 | ) 15 | 16 | func init() { 17 | Global = NewDefaultLogger(DEBUG) 18 | // innerInit() 19 | } 20 | 21 | // Wrapper for (*Logger).LoadConfiguration 22 | func LoadConfiguration(filename string) { 23 | Global.LoadConfiguration(filename) 24 | 25 | //check defualt logger 26 | _, ok := Global["stdout"] 27 | if !ok { 28 | Global["stdout"] = &Filter{INFO, "./logs/", NewConsoleLogWriter()} 29 | } 30 | } 31 | 32 | // Wrapper for (*Logger).AddFilter 33 | func AddFilter(name string, lvl level, writer LogWriter) { 34 | Global.AddFilter(name, lvl, writer) 35 | 36 | } 37 | 38 | // Wrapper for (*Logger).Close (closes and removes all logwriters) 39 | func Close() { 40 | Global.Close() 41 | } 42 | 43 | func Crash(args ...interface{}) { 44 | if len(args) > 0 { 45 | Global.intLogf(CRITICAL, strings.Repeat(" %v", len(args))[1:], args...) 46 | } 47 | panic(args) 48 | } 49 | 50 | // Logs the given message and crashes the program 51 | func Crashf(format string, args ...interface{}) { 52 | Global.intLogf(CRITICAL, format, args...) 53 | Global.Close() // so that hopefully the messages get logged 54 | panic(fmt.Sprintf(format, args...)) 55 | } 56 | 57 | // Compatibility with `log` 58 | func Exit(args ...interface{}) { 59 | if len(args) > 0 { 60 | Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...) 61 | } 62 | Global.Close() // so that hopefully the messages get logged 63 | os.Exit(0) 64 | } 65 | 66 | // Compatibility with `log` 67 | func Exitf(format string, args ...interface{}) { 68 | Global.intLogf(ERROR, format, args...) 69 | Global.Close() // so that hopefully the messages get logged 70 | os.Exit(0) 71 | } 72 | 73 | // Compatibility with `log` 74 | func Stderr(args ...interface{}) { 75 | if len(args) > 0 { 76 | Global.intLogf(ERROR, strings.Repeat(" %v", len(args))[1:], args...) 77 | } 78 | } 79 | 80 | // Compatibility with `log` 81 | func Stderrf(format string, args ...interface{}) { 82 | Global.intLogf(ERROR, format, args...) 83 | } 84 | 85 | // Compatibility with `log` 86 | func Stdout(args ...interface{}) { 87 | if len(args) > 0 { 88 | Global.intLogf(INFO, strings.Repeat(" %v", len(args))[1:], args...) 89 | } 90 | } 91 | 92 | // Compatibility with `log` 93 | func Stdoutf(format string, args ...interface{}) { 94 | Global.intLogf(INFO, format, args...) 95 | } 96 | 97 | // Send a log message manually 98 | // Wrapper for (*Logger).Log 99 | func Log(lvl level, source, message string) { 100 | Global.Log(lvl, source, message) 101 | } 102 | 103 | // Send a formatted log message easily 104 | // Wrapper for (*Logger).Logf 105 | func Logf(lvl level, format string, args ...interface{}) { 106 | Global.intLogf(lvl, format, args...) 107 | } 108 | 109 | // Send a closure log message 110 | // Wrapper for (*Logger).Logc 111 | func Logc(lvl level, closure func() string) { 112 | Global.intLogc(lvl, closure) 113 | } 114 | 115 | // Utility for finest log messages (see Debug() for parameter explanation) 116 | // Wrapper for (*Logger).Finest 117 | func Finest(arg0 interface{}, args ...interface{}) { 118 | const ( 119 | lvl = FINEST 120 | ) 121 | switch first := arg0.(type) { 122 | case string: 123 | // Use the string as a format string 124 | Global.intLogf(lvl, first, args...) 125 | case func() string: 126 | // Log the closure (no other arguments used) 127 | Global.intLogc(lvl, first) 128 | default: 129 | // Build a format string so that it will be similar to Sprint 130 | Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) 131 | } 132 | } 133 | 134 | // Utility for fine log messages (see Debug() for parameter explanation) 135 | // Wrapper for (*Logger).Fine 136 | func Fine(arg0 interface{}, args ...interface{}) { 137 | const ( 138 | lvl = FINE 139 | ) 140 | switch first := arg0.(type) { 141 | case string: 142 | // Use the string as a format string 143 | Global.intLogf(lvl, first, args...) 144 | case func() string: 145 | // Log the closure (no other arguments used) 146 | Global.intLogc(lvl, first) 147 | default: 148 | // Build a format string so that it will be similar to Sprint 149 | Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) 150 | } 151 | } 152 | 153 | // Utility for debug log messages 154 | // When given a string as the first argument, this behaves like Logf but with the DEBUG log level (e.g. the first argument is interpreted as a format for the latter arguments) 155 | // When given a closure of type func()string, this logs the string returned by the closure iff it will be logged. The closure runs at most one time. 156 | // When given anything else, the log message will be each of the arguments formatted with %v and separated by spaces (ala Sprint). 157 | // Wrapper for (*Logger).Debug 158 | func Debug(arg0 interface{}, args ...interface{}) { 159 | const ( 160 | lvl = DEBUG 161 | ) 162 | switch first := arg0.(type) { 163 | case string: 164 | // Use the string as a format string 165 | Global.intLogf(lvl, first, args...) 166 | case func() string: 167 | // Log the closure (no other arguments used) 168 | Global.intLogc(lvl, first) 169 | default: 170 | // Build a format string so that it will be similar to Sprint 171 | Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) 172 | } 173 | } 174 | 175 | // Utility for error log messages (returns an error for easy function returns) (see Debug() for parameter explanation) 176 | // These functions will execute a closure exactly once, to build the error message for the return 177 | // Wrapper for (*Logger).Error 178 | func DebugLog(logname string, arg0 interface{}, args ...interface{}) error { 179 | const ( 180 | lvl = DEBUG 181 | ) 182 | switch first := arg0.(type) { 183 | case string: 184 | // Use the string as a format string 185 | Global.intLogNamef(logname, lvl, first, args...) 186 | case func() string: 187 | // Log the closure (no other arguments used) 188 | Global.intLogNamec(logname, lvl, first) 189 | default: 190 | // Build a format string so that it will be similar to Sprint 191 | Global.intLogNamef(logname, lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) 192 | } 193 | return nil 194 | } 195 | 196 | // Utility for trace log messages (see Debug() for parameter explanation) 197 | // Wrapper for (*Logger).Trace 198 | func Trace(arg0 interface{}, args ...interface{}) { 199 | const ( 200 | lvl = TRACE 201 | ) 202 | switch first := arg0.(type) { 203 | case string: 204 | // Use the string as a format string 205 | Global.intLogf(lvl, first, args...) 206 | case func() string: 207 | // Log the closure (no other arguments used) 208 | Global.intLogc(lvl, first) 209 | default: 210 | // Build a format string so that it will be similar to Sprint 211 | Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) 212 | } 213 | } 214 | 215 | func TraceLog(logname string, arg0 interface{}, args ...interface{}) { 216 | const ( 217 | lvl = TRACE 218 | ) 219 | switch first := arg0.(type) { 220 | case string: 221 | // Use the string as a format string 222 | Global.intLogNamef(logname, lvl, first, args...) 223 | case func() string: 224 | // Log the closure (no other arguments used) 225 | Global.intLogNamec(logname, lvl, first) 226 | default: 227 | // Build a format string so that it will be similar to Sprint 228 | Global.intLogNamef(logname, lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) 229 | } 230 | 231 | } 232 | 233 | // Utility for info log messages (see Debug() for parameter explanation) 234 | // Wrapper for (*Logger).Info 235 | func Info(arg0 interface{}, args ...interface{}) { 236 | const ( 237 | lvl = INFO 238 | ) 239 | switch first := arg0.(type) { 240 | case string: 241 | // Use the string as a format string 242 | Global.intLogf(lvl, first, args...) 243 | case func() string: 244 | // Log the closure (no other arguments used) 245 | Global.intLogc(lvl, first) 246 | default: 247 | // Build a format string so that it will be similar to Sprint 248 | Global.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) 249 | } 250 | } 251 | 252 | func InfoLog(logname string, arg0 interface{}, args ...interface{}) { 253 | const ( 254 | lvl = INFO 255 | ) 256 | switch first := arg0.(type) { 257 | case string: 258 | // Use the string as a format string 259 | Global.intLogNamef(logname, lvl, first, args...) 260 | case func() string: 261 | // Log the closure (no other arguments used) 262 | Global.intLogNamec(logname, lvl, first) 263 | default: 264 | // Build a format string so that it will be similar to Sprint 265 | Global.intLogNamef(logname, lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) 266 | } 267 | 268 | } 269 | 270 | // Utility for warn log messages (returns an error for easy function returns) (see Debug() for parameter explanation) 271 | // These functions will execute a closure exactly once, to build the error message for the return 272 | // Wrapper for (*Logger).Warn 273 | func Warn(arg0 interface{}, args ...interface{}) error { 274 | const ( 275 | lvl = WARNING 276 | ) 277 | switch first := arg0.(type) { 278 | case string: 279 | // Use the string as a format string 280 | Global.intLogf(lvl, first, args...) 281 | return errors.New(fmt.Sprintf(first, args...)) 282 | case func() string: 283 | // Log the closure (no other arguments used) 284 | str := first() 285 | Global.intLogf(lvl, "%s", str) 286 | return errors.New(str) 287 | default: 288 | // Build a format string so that it will be similar to Sprint 289 | Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) 290 | return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...)) 291 | } 292 | return nil 293 | } 294 | 295 | func WarnLog(logname string, arg0 interface{}, args ...interface{}) error { 296 | const ( 297 | lvl = WARNING 298 | ) 299 | switch first := arg0.(type) { 300 | case string: 301 | // Use the string as a format string 302 | Global.intLogNamef(logname, lvl, first, args...) 303 | return errors.New(fmt.Sprintf(first, args...)) 304 | case func() string: 305 | // Log the closure (no other arguments used) 306 | str := first() 307 | Global.intLogNamef(logname, lvl, "%s", str) 308 | return errors.New(str) 309 | default: 310 | // Build a format string so that it will be similar to Sprint 311 | Global.intLogNamef(logname, lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) 312 | return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...)) 313 | } 314 | return nil 315 | } 316 | 317 | // Utility for error log messages (returns an error for easy function returns) (see Debug() for parameter explanation) 318 | // These functions will execute a closure exactly once, to build the error message for the return 319 | // Wrapper for (*Logger).Error 320 | func Error(arg0 interface{}, args ...interface{}) error { 321 | const ( 322 | lvl = ERROR 323 | ) 324 | switch first := arg0.(type) { 325 | case string: 326 | // Use the string as a format string 327 | Global.intLogf(lvl, first, args...) 328 | return errors.New(fmt.Sprintf(first, args...)) 329 | case func() string: 330 | // Log the closure (no other arguments used) 331 | str := first() 332 | Global.intLogf(lvl, "%s", str) 333 | return errors.New(str) 334 | default: 335 | // Build a format string so that it will be similar to Sprint 336 | Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) 337 | return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...)) 338 | } 339 | return nil 340 | } 341 | 342 | // Utility for error log messages (returns an error for easy function returns) (see Debug() for parameter explanation) 343 | // These functions will execute a closure exactly once, to build the error message for the return 344 | // Wrapper for (*Logger).Error 345 | func ErrorLog(logname string, arg0 interface{}, args ...interface{}) error { 346 | const ( 347 | lvl = ERROR 348 | ) 349 | switch first := arg0.(type) { 350 | case string: 351 | // Use the string as a format string 352 | Global.intLogNamef(logname, lvl, first, args...) 353 | return errors.New(fmt.Sprintf(first, args...)) 354 | case func() string: 355 | // Log the closure (no other arguments used) 356 | str := first() 357 | Global.intLogNamef(logname, lvl, "%s", str) 358 | return errors.New(str) 359 | default: 360 | // Build a format string so that it will be similar to Sprint 361 | Global.intLogNamef(logname, lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) 362 | return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...)) 363 | } 364 | return nil 365 | } 366 | 367 | // Utility for critical log messages (returns an error for easy function returns) (see Debug() for parameter explanation) 368 | // These functions will execute a closure exactly once, to build the error message for the return 369 | // Wrapper for (*Logger).Critical 370 | func Critical(arg0 interface{}, args ...interface{}) error { 371 | const ( 372 | lvl = CRITICAL 373 | ) 374 | switch first := arg0.(type) { 375 | case string: 376 | // Use the string as a format string 377 | Global.intLogf(lvl, first, args...) 378 | return errors.New(fmt.Sprintf(first, args...)) 379 | case func() string: 380 | // Log the closure (no other arguments used) 381 | str := first() 382 | Global.intLogf(lvl, "%s", str) 383 | return errors.New(str) 384 | default: 385 | // Build a format string so that it will be similar to Sprint 386 | Global.intLogf(lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) 387 | return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...)) 388 | } 389 | return nil 390 | } 391 | 392 | func CriticalLog(logname string, arg0 interface{}, args ...interface{}) error { 393 | const ( 394 | lvl = CRITICAL 395 | ) 396 | switch first := arg0.(type) { 397 | case string: 398 | // Use the string as a format string 399 | Global.intLogNamef(logname, lvl, first, args...) 400 | return errors.New(fmt.Sprintf(first, args...)) 401 | case func() string: 402 | // Log the closure (no other arguments used) 403 | str := first() 404 | Global.intLogNamef(logname, lvl, "%s", str) 405 | return errors.New(str) 406 | default: 407 | // Build a format string so that it will be similar to Sprint 408 | Global.intLogNamef(logname, lvl, fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) 409 | return errors.New(fmt.Sprint(first) + fmt.Sprintf(strings.Repeat(" %v", len(args)), args...)) 410 | } 411 | return nil 412 | } 413 | -------------------------------------------------------------------------------- /log4go.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2010, Kyle Lemons . All rights reserved. 2 | 3 | // Package log4go provides level-based and highly configurable logging. 4 | // 5 | // Enhanced Logging 6 | // 7 | // This is inspired by the logging functionality in Java. Essentially, you create a Logger 8 | // object and create output filters for it. You can send whatever you want to the Logger, 9 | // and it will filter that based on your settings and send it to the outputs. This way, you 10 | // can put as much debug code in your program as you want, and when you're done you can filter 11 | // out the mundane messages so only the important ones show up. 12 | // 13 | // Utility functions are provided to make life easier. Here is some example code to get started: 14 | // 15 | // log := log4go.NewLogger() 16 | // log.AddFilter("stdout", log4go.DEBUG, log4go.NewConsoleLogWriter()) 17 | // log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true)) 18 | // log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02")) 19 | // 20 | // The first two lines can be combined with the utility NewDefaultLogger: 21 | // 22 | // log := log4go.NewDefaultLogger(log4go.DEBUG) 23 | // log.AddFilter("log", log4go.FINE, log4go.NewFileLogWriter("example.log", true)) 24 | // log.Info("The time is now: %s", time.LocalTime().Format("15:04:05 MST 2006/01/02")) 25 | // 26 | // Usage notes: 27 | // - The ConsoleLogWriter does not display the source of the message to standard 28 | // output, but the FileLogWriter does. 29 | // - The utility functions (Info, Debug, Warn, etc) derive their source from the 30 | // calling function, and this incurs extra overhead. 31 | // 32 | // Changes from 2.0: 33 | // - The external interface has remained mostly stable, but a lot of the 34 | // internals have been changed, so if you depended on any of this or created 35 | // your own LogWriter, then you will probably have to update your code. In 36 | // particular, Logger is now a map and ConsoleLogWriter is now a channel 37 | // behind-the-scenes, and the LogWrite method no longer has return values. 38 | // 39 | // Future work: (please let me know if you think I should work on any of these particularly) 40 | // - Log file rotation 41 | // - Logging configuration files ala log4j 42 | // - Have the ability to remove filters? 43 | // - Have GetInfoChannel, GetDebugChannel, etc return a chan string that allows 44 | // for another method of logging 45 | // - Add an XML filter type 46 | package log4go 47 | 48 | import ( 49 | "errors" 50 | "fmt" 51 | "github.com/prometheus/client_golang/prometheus" 52 | "os" 53 | "runtime" 54 | "strings" 55 | "time" 56 | ) 57 | 58 | // Version information 59 | const ( 60 | L4G_VERSION = "log4go-v3.0.1" 61 | L4G_MAJOR = 3 62 | L4G_MINOR = 0 63 | L4G_BUILD = 1 64 | ) 65 | 66 | var ( 67 | loglevelCounter *prometheus.CounterVec 68 | ) 69 | 70 | func init() { 71 | loglevelCounter = prometheus.NewCounterVec(prometheus.CounterOpts{ 72 | Name: "log_level_total", 73 | Help: "Total number of a specified loglevel", 74 | }, []string{"level"}) 75 | 76 | for _, levelString := range levelStrings { 77 | loglevelCounter.WithLabelValues(levelString) 78 | } 79 | err := prometheus.Register(loglevelCounter) 80 | if err != nil { 81 | panic(err) 82 | } 83 | } 84 | 85 | /****** Constants ******/ 86 | 87 | // These are the integer logging levels used by the logger 88 | type level int 89 | 90 | const ( 91 | FINEST level = iota 92 | FINE 93 | DEBUG 94 | TRACE 95 | INFO 96 | WARNING 97 | ERROR 98 | CRITICAL 99 | ) 100 | 101 | // Logging level strings 102 | var ( 103 | levelStrings = [...]string{"FNST", "FINE", "DEBG", "TRAC", "INFO", "WARN", "EROR", "CRIT"} 104 | ) 105 | 106 | func (l level) String() string { 107 | if l < 0 || int(l) > len(levelStrings) { 108 | return "UNKNOWN" 109 | } 110 | return levelStrings[int(l)] 111 | } 112 | 113 | /****** Variables ******/ 114 | var ( 115 | // LogBufferLength specifies how many log messages a particular log4go 116 | // logger can buffer at a time before writing them. 117 | LogBufferLength = 32 118 | ) 119 | 120 | /****** LogRecord ******/ 121 | 122 | // A LogRecord contains all of the pertinent information for each message 123 | type LogRecord struct { 124 | Level level // The log level 125 | Created time.Time // The time at which the log message was created (nanoseconds) 126 | Source string // The message source 127 | Message string // The log message 128 | } 129 | 130 | /****** LogWriter ******/ 131 | 132 | // This is an interface for anything that should be able to write logs 133 | type LogWriter interface { 134 | // This will be called to log a LogRecord message. 135 | LogWrite(rec *LogRecord) 136 | 137 | // This should clean up anything lingering about the LogWriter, as it is called before 138 | // the LogWriter is removed. LogWrite should not be called after Close. 139 | Close() 140 | } 141 | 142 | /****** Logger ******/ 143 | 144 | // A Filter represents the log level below which no log records are written to 145 | // the associated LogWriter. 146 | type Filter struct { 147 | Level level 148 | Path string 149 | LogWriter 150 | } 151 | 152 | // A Logger represents a collection of Filters through which log messages are 153 | // written. 154 | type Logger map[string]*Filter 155 | 156 | // Create a new logger. 157 | // 158 | // DEPRECATED: Use make(Logger) instead. 159 | func NewLogger() Logger { 160 | os.Stderr.WriteString("warning: use of deprecated NewLogger\n") 161 | return make(Logger) 162 | } 163 | 164 | // Create a new logger with a "stdout" filter configured to send log messages at 165 | // or above lvl to standard output. 166 | // 167 | // DEPRECATED: use NewDefaultLogger instead. 168 | func NewConsoleLogger(lvl level) Logger { 169 | os.Stderr.WriteString("warning: use of deprecated NewConsoleLogger\n") 170 | return Logger{ 171 | "stdout": &Filter{lvl, "./logs/stdout.log", NewConsoleLogWriter()}, 172 | } 173 | } 174 | 175 | // Create a new logger with a "stdout" filter configured to send log messages at 176 | // or above lvl to standard output. 177 | func NewDefaultLogger(lvl level) Logger { 178 | return Logger{ 179 | "stdout": &Filter{lvl, "./logs/stdout.log", NewConsoleLogWriter()}, 180 | } 181 | } 182 | 183 | // Closes all log writers in preparation for exiting the program or a 184 | // reconfiguration of logging. Calling this is not really imperative, unless 185 | // you want to guarantee that all log messages are written. Close removes 186 | // all filters (and thus all LogWriters) from the logger. 187 | func (log Logger) Close() { 188 | // Close all open loggers 189 | for name, filt := range log { 190 | filt.Close() 191 | delete(log, name) 192 | } 193 | } 194 | 195 | // Add a new LogWriter to the Logger which will only log messages at lvl or 196 | // higher. This function should not be called from multiple goroutines. 197 | // Returns the logger for chaining. 198 | func (log Logger) AddFilter(name string, lvl level, writer LogWriter) Logger { 199 | log[name] = &Filter{lvl, "./logs/" + name + ".log", writer} 200 | return log 201 | } 202 | 203 | /******* Logging *******/ 204 | // Send a formatted log message internally 205 | func (log Logger) intLogf(lvl level, format string, args ...interface{}) { 206 | log.intLogNamef(logName(lvl), lvl, format, args...) 207 | } 208 | 209 | // Send a closure log message internally 210 | func (log Logger) intLogc(lvl level, closure func() string) { 211 | 212 | log.intLogNamec(logName(lvl), lvl, closure) 213 | } 214 | 215 | func logName(lvl level) string { 216 | return "stdout" 217 | } 218 | 219 | // Send a log message with manual level, source, and message. 220 | func (log Logger) Log(lvl level, source, message string) { 221 | log.intLogNamef(logName(lvl), lvl, message) 222 | } 223 | 224 | func (log Logger) getLogger(logname string, lvl level) (*Filter, bool) { 225 | l, ok := log[logname] 226 | if !ok { 227 | //use stdout 228 | l, ok = log["stdout"] 229 | } 230 | return l, ok 231 | } 232 | 233 | // Send a formatted log message internally 234 | func (log Logger) intLogNamef(logname string, lvl level, format string, args ...interface{}) { 235 | 236 | loglevelCounter.WithLabelValues(lvl.String()).Inc() 237 | 238 | l, ok := log.getLogger(logname, lvl) 239 | //log level less than filter level ignored 240 | if !ok || lvl < l.Level { 241 | return 242 | } 243 | 244 | // Determine caller func 245 | pc, _, lineno, ok := runtime.Caller(2) 246 | src := "" 247 | if ok { 248 | src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno) 249 | } 250 | 251 | msg := format 252 | if len(args) > 0 { 253 | msg = fmt.Sprintf(format, args...) 254 | } 255 | 256 | // Make the log record 257 | rec := &LogRecord{ 258 | Level: lvl, 259 | Created: time.Now(), 260 | Source: src, 261 | Message: msg, 262 | } 263 | 264 | // Dispatch the logs 265 | l.LogWrite(rec) 266 | } 267 | 268 | // Send a closure log message internally 269 | func (log Logger) intLogNamec(logname string, lvl level, closure func() string) { 270 | l, ok := log.getLogger(logname, lvl) 271 | 272 | //log level less than filter level ignored 273 | if !ok || lvl < l.Level { 274 | return 275 | } 276 | 277 | // Determine caller func 278 | pc, _, lineno, ok := runtime.Caller(2) 279 | src := "" 280 | if ok { 281 | src = fmt.Sprintf("%s:%d", runtime.FuncForPC(pc).Name(), lineno) 282 | } 283 | 284 | // Make the log record 285 | rec := &LogRecord{ 286 | Level: lvl, 287 | Created: time.Now(), 288 | Source: src, 289 | Message: closure(), 290 | } 291 | 292 | // Dispatch the logs 293 | l.LogWrite(rec) 294 | } 295 | 296 | // Logf logs a formatted log message at the given log level, using the caller as 297 | // its source. 298 | func (log Logger) Logf(lvl level, format string, args ...interface{}) { 299 | log.intLogf(lvl, format, args...) 300 | } 301 | 302 | // Logc logs a string returned by the closure at the given log level, using the caller as 303 | // its source. If no log message would be written, the closure is never called. 304 | func (log Logger) Logc(lvl level, closure func() string) { 305 | log.intLogc(lvl, closure) 306 | } 307 | 308 | // Finest logs a message at the finest log level. 309 | // See Debug for an explanation of the arguments. 310 | func (log Logger) Finest(arg0 interface{}, args ...interface{}) { 311 | const ( 312 | lvl = FINEST 313 | ) 314 | switch first := arg0.(type) { 315 | case string: 316 | // Use the string as a format string 317 | log.intLogf(lvl, first, args...) 318 | case func() string: 319 | // Log the closure (no other arguments used) 320 | log.intLogc(lvl, first) 321 | default: 322 | // Build a format string so that it will be similar to Sprint 323 | log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) 324 | } 325 | } 326 | 327 | // Fine logs a message at the fine log level. 328 | // See Debug for an explanation of the arguments. 329 | func (log Logger) Fine(arg0 interface{}, args ...interface{}) { 330 | const ( 331 | lvl = FINE 332 | ) 333 | switch first := arg0.(type) { 334 | case string: 335 | // Use the string as a format string 336 | log.intLogf(lvl, first, args...) 337 | case func() string: 338 | // Log the closure (no other arguments used) 339 | log.intLogc(lvl, first) 340 | default: 341 | // Build a format string so that it will be similar to Sprint 342 | log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) 343 | } 344 | } 345 | 346 | // Debug is a utility method for debug log messages. 347 | // The behavior of Debug depends on the first argument: 348 | // - arg0 is a string 349 | // When given a string as the first argument, this behaves like Logf but with 350 | // the DEBUG log level: the first argument is interpreted as a format for the 351 | // latter arguments. 352 | // - arg0 is a func()string 353 | // When given a closure of type func()string, this logs the string returned by 354 | // the closure iff it will be logged. The closure runs at most one time. 355 | // - arg0 is interface{} 356 | // When given anything else, the log message will be each of the arguments 357 | // formatted with %v and separated by spaces (ala Sprint). 358 | func (log Logger) Debug(arg0 interface{}, args ...interface{}) { 359 | const ( 360 | lvl = DEBUG 361 | ) 362 | switch first := arg0.(type) { 363 | case string: 364 | // Use the string as a format string 365 | log.intLogf(lvl, first, args...) 366 | case func() string: 367 | // Log the closure (no other arguments used) 368 | log.intLogc(lvl, first) 369 | default: 370 | // Build a format string so that it will be similar to Sprint 371 | log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) 372 | } 373 | } 374 | 375 | // Trace logs a message at the trace log level. 376 | // See Debug for an explanation of the arguments. 377 | func (log Logger) Trace(arg0 interface{}, args ...interface{}) { 378 | const ( 379 | lvl = TRACE 380 | ) 381 | switch first := arg0.(type) { 382 | case string: 383 | // Use the string as a format string 384 | log.intLogf(lvl, first, args...) 385 | case func() string: 386 | // Log the closure (no other arguments used) 387 | log.intLogc(lvl, first) 388 | default: 389 | // Build a format string so that it will be similar to Sprint 390 | log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) 391 | } 392 | } 393 | 394 | // Info logs a message at the info log level. 395 | // See Debug for an explanation of the arguments. 396 | func (log Logger) Info(arg0 interface{}, args ...interface{}) { 397 | const ( 398 | lvl = INFO 399 | ) 400 | switch first := arg0.(type) { 401 | case string: 402 | // Use the string as a format string 403 | log.intLogf(lvl, first, args...) 404 | case func() string: 405 | // Log the closure (no other arguments used) 406 | log.intLogc(lvl, first) 407 | default: 408 | // Build a format string so that it will be similar to Sprint 409 | log.intLogf(lvl, fmt.Sprint(arg0)+strings.Repeat(" %v", len(args)), args...) 410 | } 411 | } 412 | 413 | // Warn logs a message at the warning log level and returns the formatted error. 414 | // At the warning level and higher, there is no performance benefit if the 415 | // message is not actually logged, because all formats are processed and all 416 | // closures are executed to format the error message. 417 | // See Debug for further explanation of the arguments. 418 | func (log Logger) Warn(arg0 interface{}, args ...interface{}) error { 419 | const ( 420 | lvl = WARNING 421 | ) 422 | var msg string 423 | switch first := arg0.(type) { 424 | case string: 425 | // Use the string as a format string 426 | msg = fmt.Sprintf(first, args...) 427 | case func() string: 428 | // Log the closure (no other arguments used) 429 | msg = first() 430 | default: 431 | // Build a format string so that it will be similar to Sprint 432 | msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) 433 | } 434 | log.intLogf(lvl, msg) 435 | return errors.New(msg) 436 | } 437 | 438 | // Error logs a message at the error log level and returns the formatted error, 439 | // See Warn for an explanation of the performance and Debug for an explanation 440 | // of the parameters. 441 | func (log Logger) Error(arg0 interface{}, args ...interface{}) error { 442 | const ( 443 | lvl = ERROR 444 | ) 445 | var msg string 446 | switch first := arg0.(type) { 447 | case string: 448 | // Use the string as a format string 449 | msg = fmt.Sprintf(first, args...) 450 | case func() string: 451 | // Log the closure (no other arguments used) 452 | msg = first() 453 | default: 454 | // Build a format string so that it will be similar to Sprint 455 | msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) 456 | } 457 | log.intLogf(lvl, msg) 458 | return errors.New(msg) 459 | } 460 | 461 | // Critical logs a message at the critical log level and returns the formatted error, 462 | // See Warn for an explanation of the performance and Debug for an explanation 463 | // of the parameters. 464 | func (log Logger) Critical(arg0 interface{}, args ...interface{}) error { 465 | const ( 466 | lvl = CRITICAL 467 | ) 468 | var msg string 469 | switch first := arg0.(type) { 470 | case string: 471 | // Use the string as a format string 472 | msg = fmt.Sprintf(first, args...) 473 | case func() string: 474 | // Log the closure (no other arguments used) 475 | msg = first() 476 | default: 477 | // Build a format string so that it will be similar to Sprint 478 | msg = fmt.Sprintf(fmt.Sprint(first)+strings.Repeat(" %v", len(args)), args...) 479 | } 480 | log.intLogf(lvl, msg) 481 | return errors.New(msg) 482 | } 483 | -------------------------------------------------------------------------------- /log4go_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2010, Kyle Lemons . All rights reserved. 2 | 3 | package log4go 4 | 5 | import ( 6 | "crypto/md5" 7 | "encoding/hex" 8 | "fmt" 9 | "io" 10 | "io/ioutil" 11 | "os" 12 | "runtime" 13 | "testing" 14 | "time" 15 | ) 16 | 17 | const testLogFile = "_logtest.log" 18 | 19 | var now time.Time = time.Unix(0, 1234567890123456789).In(time.UTC) 20 | 21 | func newLogRecord(lvl level, src string, msg string) *LogRecord { 22 | return &LogRecord{ 23 | Level: lvl, 24 | Source: src, 25 | Created: now, 26 | Message: msg, 27 | } 28 | } 29 | 30 | func TestELog(t *testing.T) { 31 | fmt.Printf("Testing %s\n", L4G_VERSION) 32 | lr := newLogRecord(CRITICAL, "source", "message") 33 | if lr.Level != CRITICAL { 34 | t.Errorf("Incorrect level: %d should be %d", lr.Level, CRITICAL) 35 | } 36 | if lr.Source != "source" { 37 | t.Errorf("Incorrect source: %s should be %s", lr.Source, "source") 38 | } 39 | if lr.Message != "message" { 40 | t.Errorf("Incorrect message: %s should be %s", lr.Source, "message") 41 | } 42 | } 43 | 44 | var formatTests = []struct { 45 | Test string 46 | Record *LogRecord 47 | Formats map[string]string 48 | }{ 49 | { 50 | Test: "Standard formats", 51 | Record: &LogRecord{ 52 | Level: ERROR, 53 | Source: "source", 54 | Message: "message", 55 | Created: now, 56 | }, 57 | Formats: map[string]string{ 58 | // TODO(kevlar): How can I do this so it'll work outside of PST? 59 | FORMAT_DEFAULT: "[2009/02/13 23:31:30 UTC] [EROR] (source) message\n", 60 | FORMAT_SHORT: "[23:31 02/13/09] [EROR] message\n", 61 | FORMAT_ABBREV: "[EROR] message\n", 62 | }, 63 | }, 64 | } 65 | 66 | func TestFormatLogRecord(t *testing.T) { 67 | for _, test := range formatTests { 68 | name := test.Test 69 | for fmt, want := range test.Formats { 70 | if got := FormatLogRecord(fmt, test.Record); got != want { 71 | t.Errorf("%s - %s:", name, fmt) 72 | t.Errorf(" got %q", got) 73 | t.Errorf(" want %q", want) 74 | } 75 | } 76 | } 77 | } 78 | 79 | var logRecordWriteTests = []struct { 80 | Test string 81 | Record *LogRecord 82 | Console string 83 | }{ 84 | { 85 | Test: "Normal message", 86 | Record: &LogRecord{ 87 | Level: CRITICAL, 88 | Source: "source", 89 | Message: "message", 90 | Created: now, 91 | }, 92 | Console: "[02/13/09 23:31:30] [CRIT] message\n", 93 | }, 94 | } 95 | 96 | func TestConsoleLogWriter(t *testing.T) { 97 | console := make(ConsoleLogWriter) 98 | 99 | r, w := io.Pipe() 100 | go console.run(w) 101 | defer console.Close() 102 | 103 | buf := make([]byte, 1024) 104 | 105 | for _, test := range logRecordWriteTests { 106 | name := test.Test 107 | 108 | console.LogWrite(test.Record) 109 | n, _ := r.Read(buf) 110 | 111 | if got, want := string(buf[:n]), test.Console; got != want { 112 | t.Errorf("%s: got %q", name, got) 113 | t.Errorf("%s: want %q", name, want) 114 | } 115 | } 116 | } 117 | 118 | func TestFileLogWriter(t *testing.T) { 119 | defer func(buflen int) { 120 | LogBufferLength = buflen 121 | }(LogBufferLength) 122 | LogBufferLength = 0 123 | 124 | w := NewFileLogWriter(testLogFile, false, true) 125 | if w == nil { 126 | t.Fatalf("Invalid return: w should not be nil") 127 | } 128 | defer os.Remove(testLogFile) 129 | 130 | w.LogWrite(newLogRecord(CRITICAL, "source", "message")) 131 | w.Close() 132 | runtime.Gosched() 133 | 134 | if contents, err := ioutil.ReadFile(testLogFile); err != nil { 135 | t.Errorf("read(%q): %s", testLogFile, err) 136 | } else if len(contents) != 50 { 137 | t.Errorf("malformed filelog: %q (%d bytes)", string(contents), len(contents)) 138 | } 139 | } 140 | 141 | func TestXMLLogWriter(t *testing.T) { 142 | defer func(buflen int) { 143 | LogBufferLength = buflen 144 | }(LogBufferLength) 145 | LogBufferLength = 0 146 | 147 | w := NewXMLLogWriter(testLogFile, false, true) 148 | if w == nil { 149 | t.Fatalf("Invalid return: w should not be nil") 150 | } 151 | defer os.Remove(testLogFile) 152 | 153 | w.LogWrite(newLogRecord(CRITICAL, "source", "message")) 154 | w.Close() 155 | runtime.Gosched() 156 | 157 | if contents, err := ioutil.ReadFile(testLogFile); err != nil { 158 | t.Errorf("read(%q): %s", testLogFile, err) 159 | } else if len(contents) != 185 { 160 | t.Errorf("malformed xmllog: %q (%d bytes)", string(contents), len(contents)) 161 | } 162 | } 163 | 164 | func TestLogger(t *testing.T) { 165 | sl := NewDefaultLogger(WARNING) 166 | if sl == nil { 167 | t.Fatalf("NewDefaultLogger should never return nil") 168 | } 169 | if lw, exist := sl["stdout"]; lw == nil || exist != true { 170 | t.Fatalf("NewDefaultLogger produced invalid logger (DNE or nil)") 171 | } 172 | if sl["stdout"].Level != WARNING { 173 | t.Fatalf("NewDefaultLogger produced invalid logger (incorrect level)") 174 | } 175 | if len(sl) != 1 { 176 | t.Fatalf("NewDefaultLogger produced invalid logger (incorrect map count)") 177 | } 178 | 179 | //func (l *Logger) AddFilter(name string, level int, writer LogWriter) {} 180 | l := make(Logger) 181 | l.AddFilter("stdout", DEBUG, NewConsoleLogWriter()) 182 | if lw, exist := l["stdout"]; lw == nil || exist != true { 183 | t.Fatalf("AddFilter produced invalid logger (DNE or nil)") 184 | } 185 | if l["stdout"].Level != DEBUG { 186 | t.Fatalf("AddFilter produced invalid logger (incorrect level)") 187 | } 188 | if len(l) != 1 { 189 | t.Fatalf("AddFilter produced invalid logger (incorrect map count)") 190 | } 191 | 192 | //func (l *Logger) Warn(format string, args ...interface{}) error {} 193 | if err := l.Warn("%s %d %#v", "Warning:", 1, []int{}); err.Error() != "Warning: 1 []int{}" { 194 | t.Errorf("Warn returned invalid error: %s", err) 195 | } 196 | 197 | //func (l *Logger) Error(format string, args ...interface{}) error {} 198 | if err := l.Error("%s %d %#v", "Error:", 10, []string{}); err.Error() != "Error: 10 []string{}" { 199 | t.Errorf("Error returned invalid error: %s", err) 200 | } 201 | 202 | //func (l *Logger) Critical(format string, args ...interface{}) error {} 203 | if err := l.Critical("%s %d %#v", "Critical:", 100, []int64{}); err.Error() != "Critical: 100 []int64{}" { 204 | t.Errorf("Critical returned invalid error: %s", err) 205 | } 206 | 207 | // Already tested or basically untestable 208 | //func (l *Logger) Log(level int, source, message string) {} 209 | //func (l *Logger) Logf(level int, format string, args ...interface{}) {} 210 | //func (l *Logger) intLogf(level int, format string, args ...interface{}) string {} 211 | //func (l *Logger) Finest(format string, args ...interface{}) {} 212 | //func (l *Logger) Fine(format string, args ...interface{}) {} 213 | //func (l *Logger) Debug(format string, args ...interface{}) {} 214 | //func (l *Logger) Trace(format string, args ...interface{}) {} 215 | //func (l *Logger) Info(format string, args ...interface{}) {} 216 | } 217 | 218 | func TestLogOutput(t *testing.T) { 219 | const ( 220 | expected = "fdf3e51e444da56b4cb400f30bc47424" 221 | ) 222 | 223 | // Unbuffered output 224 | defer func(buflen int) { 225 | LogBufferLength = buflen 226 | }(LogBufferLength) 227 | LogBufferLength = 0 228 | 229 | l := make(Logger) 230 | 231 | // Delete and open the output log without a timestamp (for a constant md5sum) 232 | l.AddFilter("file", FINEST, NewFileLogWriter(testLogFile, false, true).SetFormat("[%L] %M")) 233 | defer os.Remove(testLogFile) 234 | 235 | // Send some log messages 236 | l.Log(CRITICAL, "testsrc1", fmt.Sprintf("This message is level %d", int(CRITICAL))) 237 | l.Logf(ERROR, "This message is level %v", ERROR) 238 | l.Logf(WARNING, "This message is level %s", WARNING) 239 | l.Logc(INFO, func() string { return "This message is level INFO" }) 240 | l.Trace("This message is level %d", int(TRACE)) 241 | l.Debug("This message is level %s", DEBUG) 242 | l.Fine(func() string { return fmt.Sprintf("This message is level %v", FINE) }) 243 | l.Finest("This message is level %v", FINEST) 244 | l.Finest(FINEST, "is also this message's level") 245 | 246 | l.Close() 247 | 248 | contents, err := ioutil.ReadFile(testLogFile) 249 | if err != nil { 250 | t.Fatalf("Could not read output log: %s", err) 251 | } 252 | 253 | sum := md5.New() 254 | sum.Write(contents) 255 | if sumstr := hex.EncodeToString(sum.Sum(nil)); sumstr != expected { 256 | t.Errorf("--- Log Contents:\n%s---", string(contents)) 257 | t.Fatalf("Checksum does not match: %s (expecting %s)", sumstr, expected) 258 | } 259 | } 260 | 261 | func TestCountMallocs(t *testing.T) { 262 | const N = 1 263 | var m runtime.MemStats 264 | getMallocs := func() uint64 { 265 | runtime.ReadMemStats(&m) 266 | return m.Mallocs 267 | } 268 | 269 | // Console logger 270 | sl := NewDefaultLogger(INFO) 271 | mallocs := 0 - getMallocs() 272 | for i := 0; i < N; i++ { 273 | sl.Log(WARNING, "here", "This is a WARNING message") 274 | } 275 | mallocs += getMallocs() 276 | fmt.Printf("mallocs per sl.Log((WARNING, \"here\", \"This is a log message\"): %d\n", mallocs/N) 277 | 278 | // Console logger formatted 279 | mallocs = 0 - getMallocs() 280 | for i := 0; i < N; i++ { 281 | sl.Logf(WARNING, "%s is a log message with level %d", "This", WARNING) 282 | } 283 | mallocs += getMallocs() 284 | fmt.Printf("mallocs per sl.Logf(WARNING, \"%%s is a log message with level %%d\", \"This\", WARNING): %d\n", mallocs/N) 285 | 286 | // Console logger (not logged) 287 | sl = NewDefaultLogger(INFO) 288 | mallocs = 0 - getMallocs() 289 | for i := 0; i < N; i++ { 290 | sl.Log(DEBUG, "here", "This is a DEBUG log message") 291 | } 292 | mallocs += getMallocs() 293 | fmt.Printf("mallocs per unlogged sl.Log((WARNING, \"here\", \"This is a log message\"): %d\n", mallocs/N) 294 | 295 | // Console logger formatted (not logged) 296 | mallocs = 0 - getMallocs() 297 | for i := 0; i < N; i++ { 298 | sl.Logf(DEBUG, "%s is a log message with level %d", "This", DEBUG) 299 | } 300 | mallocs += getMallocs() 301 | fmt.Printf("mallocs per unlogged sl.Logf(WARNING, \"%%s is a log message with level %%d\", \"This\", WARNING): %d\n", mallocs/N) 302 | } 303 | 304 | func TestXMLConfig(t *testing.T) { 305 | const ( 306 | configfile = "example.xml" 307 | ) 308 | 309 | fd, err := os.Create(configfile) 310 | if err != nil { 311 | t.Fatalf("Could not open %s for writing: %s", configfile, err) 312 | } 313 | 314 | fmt.Fprintln(fd, "") 315 | fmt.Fprintln(fd, " ") 316 | fmt.Fprintln(fd, " stdout") 317 | fmt.Fprintln(fd, " console") 318 | fmt.Fprintln(fd, " ") 319 | fmt.Fprintln(fd, " DEBUG") 320 | fmt.Fprintln(fd, " ") 321 | fmt.Fprintln(fd, " ") 322 | fmt.Fprintln(fd, " file") 323 | fmt.Fprintln(fd, " file") 324 | fmt.Fprintln(fd, " FINEST") 325 | fmt.Fprintln(fd, " test.log") 326 | fmt.Fprintln(fd, " ") 337 | fmt.Fprintln(fd, " [%D %T] [%L] (%S) %M") 338 | fmt.Fprintln(fd, " false ") 339 | fmt.Fprintln(fd, " 0M ") 340 | fmt.Fprintln(fd, " 0K ") 341 | fmt.Fprintln(fd, " true ") 342 | fmt.Fprintln(fd, " ") 343 | fmt.Fprintln(fd, " ") 344 | fmt.Fprintln(fd, " xmllog") 345 | fmt.Fprintln(fd, " xml") 346 | fmt.Fprintln(fd, " TRACE") 347 | fmt.Fprintln(fd, " trace.xml") 348 | fmt.Fprintln(fd, " true ") 349 | fmt.Fprintln(fd, " 100M ") 350 | fmt.Fprintln(fd, " 6K ") 351 | fmt.Fprintln(fd, " false ") 352 | fmt.Fprintln(fd, " ") 353 | fmt.Fprintln(fd, " ") 354 | fmt.Fprintln(fd, " donotopen") 355 | fmt.Fprintln(fd, " socket") 356 | fmt.Fprintln(fd, " FINEST") 357 | fmt.Fprintln(fd, " 192.168.1.255:12124 ") 358 | fmt.Fprintln(fd, " udp ") 359 | fmt.Fprintln(fd, " ") 360 | fmt.Fprintln(fd, "") 361 | fd.Close() 362 | 363 | log := make(Logger) 364 | log.LoadConfiguration(configfile) 365 | defer os.Remove("trace.xml") 366 | defer os.Remove("test.log") 367 | defer log.Close() 368 | 369 | // Make sure we got all loggers 370 | if len(log) != 3 { 371 | t.Fatalf("XMLConfig: Expected 3 filters, found %d", len(log)) 372 | } 373 | 374 | // Make sure they're the right keys 375 | if _, ok := log["stdout"]; !ok { 376 | t.Errorf("XMLConfig: Expected stdout logger") 377 | } 378 | if _, ok := log["file"]; !ok { 379 | t.Fatalf("XMLConfig: Expected file logger") 380 | } 381 | if _, ok := log["xmllog"]; !ok { 382 | t.Fatalf("XMLConfig: Expected xmllog logger") 383 | } 384 | 385 | // Make sure they're the right type 386 | if _, ok := log["stdout"].LogWriter.(ConsoleLogWriter); !ok { 387 | t.Fatalf("XMLConfig: Expected stdout to be ConsoleLogWriter, found %T", log["stdout"].LogWriter) 388 | } 389 | if _, ok := log["file"].LogWriter.(*FileLogWriter); !ok { 390 | t.Fatalf("XMLConfig: Expected file to be *FileLogWriter, found %T", log["file"].LogWriter) 391 | } 392 | if _, ok := log["xmllog"].LogWriter.(*FileLogWriter); !ok { 393 | t.Fatalf("XMLConfig: Expected xmllog to be *FileLogWriter, found %T", log["xmllog"].LogWriter) 394 | } 395 | 396 | // Make sure levels are set 397 | if lvl := log["stdout"].Level; lvl != DEBUG { 398 | t.Errorf("XMLConfig: Expected stdout to be set to level %d, found %d", DEBUG, lvl) 399 | } 400 | if lvl := log["file"].Level; lvl != FINEST { 401 | t.Errorf("XMLConfig: Expected file to be set to level %d, found %d", FINEST, lvl) 402 | } 403 | if lvl := log["xmllog"].Level; lvl != TRACE { 404 | t.Errorf("XMLConfig: Expected xmllog to be set to level %d, found %d", TRACE, lvl) 405 | } 406 | 407 | // Make sure the w is open and points to the right file 408 | if fname := log["file"].LogWriter.(*FileLogWriter).file.Name(); fname != "test.log" { 409 | t.Errorf("XMLConfig: Expected file to have opened %s, found %s", "test.log", fname) 410 | } 411 | 412 | // Make sure the XLW is open and points to the right file 413 | if fname := log["xmllog"].LogWriter.(*FileLogWriter).file.Name(); fname != "trace.xml" { 414 | t.Errorf("XMLConfig: Expected xmllog to have opened %s, found %s", "trace.xml", fname) 415 | } 416 | 417 | // Move XML log file 418 | os.Rename(configfile, "examples/"+configfile) // Keep this so that an example with the documentation is available 419 | } 420 | 421 | func BenchmarkFormatLogRecord(b *testing.B) { 422 | const updateEvery = 1 423 | rec := &LogRecord{ 424 | Level: CRITICAL, 425 | Created: now, 426 | Source: "source", 427 | Message: "message", 428 | } 429 | for i := 0; i < b.N; i++ { 430 | rec.Created = rec.Created.Add(1 * time.Second / updateEvery) 431 | if i%2 == 0 { 432 | FormatLogRecord(FORMAT_DEFAULT, rec) 433 | } else { 434 | FormatLogRecord(FORMAT_SHORT, rec) 435 | } 436 | } 437 | } 438 | 439 | func BenchmarkConsoleLog(b *testing.B) { 440 | /* This doesn't seem to work on OS X 441 | sink, err := os.Open(os.DevNull) 442 | if err != nil { 443 | panic(err) 444 | } 445 | if err := syscall.Dup2(int(sink.Fd()), syscall.Stdout); err != nil { 446 | panic(err) 447 | } 448 | */ 449 | 450 | stdout = ioutil.Discard 451 | sl := NewDefaultLogger(INFO) 452 | for i := 0; i < b.N; i++ { 453 | sl.Log(WARNING, "here", "This is a log message") 454 | } 455 | } 456 | 457 | func BenchmarkConsoleNotLogged(b *testing.B) { 458 | sl := NewDefaultLogger(INFO) 459 | for i := 0; i < b.N; i++ { 460 | sl.Log(DEBUG, "here", "This is a log message") 461 | } 462 | } 463 | 464 | func BenchmarkConsoleUtilLog(b *testing.B) { 465 | sl := NewDefaultLogger(INFO) 466 | for i := 0; i < b.N; i++ { 467 | sl.Info("%s is a log message", "This") 468 | } 469 | } 470 | 471 | func BenchmarkConsoleUtilNotLog(b *testing.B) { 472 | sl := NewDefaultLogger(INFO) 473 | for i := 0; i < b.N; i++ { 474 | sl.Debug("%s is a log message", "This") 475 | } 476 | } 477 | 478 | func BenchmarkFileLog(b *testing.B) { 479 | sl := make(Logger) 480 | b.StopTimer() 481 | sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false, true)) 482 | b.StartTimer() 483 | for i := 0; i < b.N; i++ { 484 | sl.Log(WARNING, "here", "This is a log message") 485 | } 486 | b.StopTimer() 487 | os.Remove("benchlog.log") 488 | } 489 | 490 | func BenchmarkFileNotLogged(b *testing.B) { 491 | sl := make(Logger) 492 | b.StopTimer() 493 | sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false, true)) 494 | b.StartTimer() 495 | for i := 0; i < b.N; i++ { 496 | sl.Log(DEBUG, "here", "This is a log message") 497 | } 498 | b.StopTimer() 499 | os.Remove("benchlog.log") 500 | } 501 | 502 | func BenchmarkFileUtilLog(b *testing.B) { 503 | sl := make(Logger) 504 | b.StopTimer() 505 | sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false, true)) 506 | b.StartTimer() 507 | for i := 0; i < b.N; i++ { 508 | sl.Info("%s is a log message", "This") 509 | } 510 | b.StopTimer() 511 | os.Remove("benchlog.log") 512 | } 513 | 514 | func BenchmarkFileUtilNotLog(b *testing.B) { 515 | sl := make(Logger) 516 | b.StopTimer() 517 | sl.AddFilter("file", INFO, NewFileLogWriter("benchlog.log", false, true)) 518 | b.StartTimer() 519 | for i := 0; i < b.N; i++ { 520 | sl.Debug("%s is a log message", "This") 521 | } 522 | b.StopTimer() 523 | os.Remove("benchlog.log") 524 | } 525 | 526 | // Benchmark results (darwin amd64 6g) 527 | //elog.BenchmarkConsoleLog 100000 22819 ns/op 528 | //elog.BenchmarkConsoleNotLogged 2000000 879 ns/op 529 | //elog.BenchmarkConsoleUtilLog 50000 34380 ns/op 530 | //elog.BenchmarkConsoleUtilNotLog 1000000 1339 ns/op 531 | //elog.BenchmarkFileLog 100000 26497 ns/op 532 | //elog.BenchmarkFileNotLogged 2000000 821 ns/op 533 | //elog.BenchmarkFileUtilLog 50000 33945 ns/op 534 | //elog.BenchmarkFileUtilNotLog 1000000 1258 ns/op 535 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 9 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 10 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 11 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 12 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 13 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 14 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 15 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 16 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 17 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 18 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 19 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 20 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 21 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 22 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 23 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 24 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 25 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 26 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 27 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 28 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 29 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 30 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 31 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 32 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 33 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 34 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 35 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 36 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 37 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 38 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 39 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 40 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 41 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 42 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 43 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 44 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 45 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 46 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 47 | github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= 48 | github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 49 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 50 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 51 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 52 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 53 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 54 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 55 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 56 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 57 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 58 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 59 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 60 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 61 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 62 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 63 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 64 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 65 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 66 | github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= 67 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 68 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 69 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 70 | github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= 71 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 72 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 73 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 74 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 75 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 76 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 77 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 78 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 79 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 80 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 81 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 82 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 83 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 84 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 85 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 86 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 87 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 88 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 89 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 90 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 91 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 92 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 93 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 94 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 95 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 96 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 97 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 98 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 99 | github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= 100 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 101 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 102 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 103 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 104 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 105 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 106 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 107 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 108 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 109 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 110 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 111 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 112 | github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= 113 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 114 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 115 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 116 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 117 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 118 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 119 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 120 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 121 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 122 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 123 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 124 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 125 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 126 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 127 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 128 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 129 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 130 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 131 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 132 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 133 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 134 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 135 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 136 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 137 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 138 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 139 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 140 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 141 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 142 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 143 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 144 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 145 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 146 | github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= 147 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 148 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 149 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 150 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 151 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 152 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 153 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 154 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 155 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 156 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 157 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 158 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 159 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 160 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 161 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 162 | github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= 163 | github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= 164 | github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= 165 | github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= 166 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 167 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 168 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 169 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 170 | github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= 171 | github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= 172 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 173 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 174 | github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= 175 | github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= 176 | github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= 177 | github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= 178 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 179 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 180 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 181 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 182 | github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 183 | github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= 184 | github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= 185 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 186 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 187 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 188 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 189 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 190 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 191 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 192 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 193 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 194 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 195 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 196 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 197 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 198 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 199 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 200 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 201 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 202 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 203 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 204 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 205 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 206 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 207 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 208 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 209 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 210 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 211 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 212 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 213 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 214 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 215 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 216 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 217 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 218 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 219 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 220 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 221 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 222 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 223 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 224 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 225 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 226 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 227 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 228 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 229 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 230 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 231 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 232 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 233 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 234 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 235 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 236 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 237 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 238 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 239 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 240 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 241 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 242 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 243 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 244 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 245 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 246 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 247 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 248 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 249 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 250 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 251 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 252 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 253 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 254 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 255 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 256 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 257 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 258 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 259 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 260 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 261 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 262 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 263 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 264 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 265 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 266 | golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 267 | golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 268 | golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 269 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 270 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 271 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 272 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 273 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 274 | golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 275 | golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 276 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 277 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 278 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 279 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 280 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 281 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 282 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 283 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 284 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 285 | golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 286 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 287 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 288 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 289 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 290 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 291 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 292 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 293 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 294 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 295 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 296 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 297 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 298 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 299 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 300 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 301 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 302 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 303 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 304 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 305 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 306 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 307 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 308 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 309 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 310 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 311 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 312 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 313 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 314 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 315 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 316 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 317 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 318 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 319 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 320 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 321 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 322 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 323 | golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 324 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= 325 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 326 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 327 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 328 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 329 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 330 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 331 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 332 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 333 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 334 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 335 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 336 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 337 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 338 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 339 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 340 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 341 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 342 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 343 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 344 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 345 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 346 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 347 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 348 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 349 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 350 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 351 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 352 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 353 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 354 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 355 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 356 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 357 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 358 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 359 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 360 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 361 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 362 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 363 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 364 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 365 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 366 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 367 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 368 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 369 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 370 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 371 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 372 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 373 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 374 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 375 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 376 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 377 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 378 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 379 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 380 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 381 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 382 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 383 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 384 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 385 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 386 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 387 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 388 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 389 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 390 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 391 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 392 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 393 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 394 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 395 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 396 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 397 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 398 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 399 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 400 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 401 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 402 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 403 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 404 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 405 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 406 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 407 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 408 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 409 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 410 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 411 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 412 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 413 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 414 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 415 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 416 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 417 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 418 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 419 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 420 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 421 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 422 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 423 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 424 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 425 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 426 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 427 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 428 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 429 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 430 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 431 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 432 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 433 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 434 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 435 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 436 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 437 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 438 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 439 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 440 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 441 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 442 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 443 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 444 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 445 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 446 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 447 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 448 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 449 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 450 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 451 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 452 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 453 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 454 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 455 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 456 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 457 | google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= 458 | google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 459 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 460 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 461 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 462 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 463 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 464 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 465 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 466 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 467 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 468 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 469 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 470 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 471 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 472 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 473 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 474 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 475 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 476 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 477 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 478 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 479 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 480 | --------------------------------------------------------------------------------