├── README.md ├── glog_file.go ├── LICENSE ├── glog_test.go └── glog.go /README.md: -------------------------------------------------------------------------------- 1 | glog 2 | ==== 3 | 4 | 在[golang/glog](https://github.com/golang/glog)的基础上做了一些修改。 5 | 6 | ## 修改的地方: 7 | 1. 增加每天切割日志文件的功能,程序运行时指定 --dailyRolling=true参数即可 8 | 2. 将日志等级由原来的INFO WARN ERROR FATAL改为DEBUG INFO ERROR FATAL 9 | 3. 增加日志输出等级设置,当日志信息等级低于输出等级时则不输出日志信息 10 | 4. 将默认的刷新缓冲区时间由20s改为5s 11 | 12 | ##使用示例 13 | ``` 14 | func main() { 15 | //初始化命令行参数 16 | flag.Parse() 17 | //退出时调用,确保日志写入文件中 18 | defer glog.Flush() 19 | 20 | //一般在测试环境下设置输出等级为DEBUG,线上环境设置为INFO 21 | glog.SetLevelString("DEBUG") 22 | 23 | glog.Info("hello, glog") 24 | glog.Warning("warning glog") 25 | glog.Error("error glog") 26 | 27 | glog.Infof("info %d", 1) 28 | glog.Warningf("warning %d", 2) 29 | glog.Errorf("error %d", 3) 30 | } 31 | 32 | //假设编译后的可执行程序名为demo,运行时指定log_dir参数将日志文件保存到特定的目录 33 | // ./demo --log_dir=./log --dailyRolling=true 34 | ``` 35 | -------------------------------------------------------------------------------- /glog_file.go: -------------------------------------------------------------------------------- 1 | // Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ 2 | // 3 | // Copyright 2013 Google Inc. All Rights Reserved. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | // File I/O for logs. 18 | 19 | package glog 20 | 21 | import ( 22 | "errors" 23 | "flag" 24 | "fmt" 25 | "os" 26 | "os/user" 27 | "path/filepath" 28 | "strings" 29 | "sync" 30 | "time" 31 | ) 32 | 33 | // MaxSize is the maximum size of a log file in bytes. 34 | var MaxSize uint64 = 1024 * 1024 * 1800 35 | 36 | // logDirs lists the candidate directories for new log files. 37 | var logDirs []string 38 | 39 | // If non-empty, overrides the choice of directory in which to write logs. 40 | // See createLogDirs for the full list of possible destinations. 41 | var logDir = flag.String("log_dir", "", "If non-empty, write log files in this directory") 42 | 43 | func createLogDirs() { 44 | if *logDir != "" { 45 | logDirs = append(logDirs, *logDir) 46 | } 47 | logDirs = append(logDirs, os.TempDir()) 48 | } 49 | 50 | var ( 51 | pid = os.Getpid() 52 | program = filepath.Base(os.Args[0]) 53 | host = "unknownhost" 54 | userName = "unknownuser" 55 | ) 56 | 57 | func init() { 58 | h, err := os.Hostname() 59 | if err == nil { 60 | host = shortHostname(h) 61 | } 62 | 63 | current, err := user.Current() 64 | if err == nil { 65 | userName = current.Username 66 | } 67 | 68 | // Sanitize userName since it may contain filepath separators on Windows. 69 | userName = strings.Replace(userName, `\`, "_", -1) 70 | } 71 | 72 | // shortHostname returns its argument, truncating at the first period. 73 | // For instance, given "www.google.com" it returns "www". 74 | func shortHostname(hostname string) string { 75 | if i := strings.Index(hostname, "."); i >= 0 { 76 | return hostname[:i] 77 | } 78 | return hostname 79 | } 80 | 81 | // logName returns a new log file name containing tag, with start time t, and 82 | // the name for the symlink for tag. 83 | func logName(tag string, t time.Time) (name, link string) { 84 | name = fmt.Sprintf("%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d", 85 | program, 86 | host, 87 | userName, 88 | tag, 89 | t.Year(), 90 | t.Month(), 91 | t.Day(), 92 | t.Hour(), 93 | t.Minute(), 94 | t.Second(), 95 | pid) 96 | return name, program + "." + tag 97 | } 98 | 99 | var onceLogDirs sync.Once 100 | 101 | // create creates a new log file and returns the file and its filename, which 102 | // contains tag ("INFO", "FATAL", etc.) and t. If the file is created 103 | // successfully, create also attempts to update the symlink for that tag, ignoring 104 | // errors. 105 | func create(tag string, t time.Time) (f *os.File, filename string, err error) { 106 | onceLogDirs.Do(createLogDirs) 107 | if len(logDirs) == 0 { 108 | return nil, "", errors.New("log: no log dirs") 109 | } 110 | name, link := logName(tag, t) 111 | var lastErr error 112 | for _, dir := range logDirs { 113 | fname := filepath.Join(dir, name) 114 | f, err := os.Create(fname) 115 | if err == nil { 116 | symlink := filepath.Join(dir, link) 117 | os.Remove(symlink) // ignore err 118 | os.Symlink(name, symlink) // ignore err 119 | return f, fname, nil 120 | } 121 | lastErr = err 122 | } 123 | return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr) 124 | } 125 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "[]" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright [yyyy] [name of copyright owner] 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /glog_test.go: -------------------------------------------------------------------------------- 1 | // Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ 2 | // 3 | // Copyright 2013 Google Inc. All Rights Reserved. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package glog 18 | 19 | import ( 20 | "bytes" 21 | "fmt" 22 | stdLog "log" 23 | "path/filepath" 24 | "runtime" 25 | "strconv" 26 | "strings" 27 | "testing" 28 | "time" 29 | ) 30 | 31 | // Test that shortHostname works as advertised. 32 | func TestShortHostname(t *testing.T) { 33 | for hostname, expect := range map[string]string{ 34 | "": "", 35 | "host": "host", 36 | "host.google.com": "host", 37 | } { 38 | if got := shortHostname(hostname); expect != got { 39 | t.Errorf("shortHostname(%q): expected %q, got %q", hostname, expect, got) 40 | } 41 | } 42 | } 43 | 44 | // flushBuffer wraps a bytes.Buffer to satisfy flushSyncWriter. 45 | type flushBuffer struct { 46 | bytes.Buffer 47 | } 48 | 49 | func (f *flushBuffer) Flush() error { 50 | return nil 51 | } 52 | 53 | func (f *flushBuffer) Sync() error { 54 | return nil 55 | } 56 | 57 | // swap sets the log writers and returns the old array. 58 | func (l *loggingT) swap(writers [numSeverity]flushSyncWriter) (old [numSeverity]flushSyncWriter) { 59 | l.mu.Lock() 60 | defer l.mu.Unlock() 61 | old = l.file 62 | for i, w := range writers { 63 | logging.file[i] = w 64 | } 65 | return 66 | } 67 | 68 | // newBuffers sets the log writers to all new byte buffers and returns the old array. 69 | func (l *loggingT) newBuffers() [numSeverity]flushSyncWriter { 70 | return l.swap([numSeverity]flushSyncWriter{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)}) 71 | } 72 | 73 | // contents returns the specified log value as a string. 74 | func contents(s severity) string { 75 | return logging.file[s].(*flushBuffer).String() 76 | } 77 | 78 | // contains reports whether the string is contained in the log. 79 | func contains(s severity, str string, t *testing.T) bool { 80 | return strings.Contains(contents(s), str) 81 | } 82 | 83 | // setFlags configures the logging flags how the test expects them. 84 | func setFlags() { 85 | logging.toStderr = false 86 | } 87 | 88 | func TestOutputSeverity(t *testing.T) { 89 | setFlags() 90 | defer logging.swap(logging.newBuffers()) 91 | Debug("test") 92 | if contains(debugLog, "test", t) { 93 | t.Error("OutputSeverity failed") 94 | } 95 | 96 | SetLevelString("DEBUG") 97 | Debug("test") 98 | if !contains(debugLog, "test", t) { 99 | t.Error("OutputSeverity failed") 100 | } 101 | } 102 | 103 | // Test that Info works as advertised. 104 | func TestInfo(t *testing.T) { 105 | setFlags() 106 | defer logging.swap(logging.newBuffers()) 107 | Info("test") 108 | if !contains(infoLog, "I", t) { 109 | t.Errorf("Info has wrong character: %q", contents(infoLog)) 110 | } 111 | if !contains(infoLog, "test", t) { 112 | t.Error("Info failed") 113 | } 114 | } 115 | 116 | func TestInfoDepth(t *testing.T) { 117 | setFlags() 118 | defer logging.swap(logging.newBuffers()) 119 | 120 | f := func() { InfoDepth(1, "depth-test1") } 121 | 122 | // The next three lines must stay together 123 | _, _, wantLine, _ := runtime.Caller(0) 124 | InfoDepth(0, "depth-test0") 125 | f() 126 | 127 | msgs := strings.Split(strings.TrimSuffix(contents(infoLog), "\n"), "\n") 128 | if len(msgs) != 2 { 129 | t.Fatalf("Got %d lines, expected 2", len(msgs)) 130 | } 131 | 132 | for i, m := range msgs { 133 | if !strings.HasPrefix(m, "I") { 134 | t.Errorf("InfoDepth[%d] has wrong character: %q", i, m) 135 | } 136 | w := fmt.Sprintf("depth-test%d", i) 137 | if !strings.Contains(m, w) { 138 | t.Errorf("InfoDepth[%d] missing %q: %q", i, w, m) 139 | } 140 | 141 | // pull out the line number (between : and ]) 142 | msg := m[strings.LastIndex(m, ":")+1:] 143 | x := strings.Index(msg, "]") 144 | if x < 0 { 145 | t.Errorf("InfoDepth[%d]: missing ']': %q", i, m) 146 | continue 147 | } 148 | line, err := strconv.Atoi(msg[:x]) 149 | if err != nil { 150 | t.Errorf("InfoDepth[%d]: bad line number: %q", i, m) 151 | continue 152 | } 153 | wantLine++ 154 | if wantLine != line { 155 | t.Errorf("InfoDepth[%d]: got line %d, want %d", i, line, wantLine) 156 | } 157 | } 158 | } 159 | 160 | func init() { 161 | CopyStandardLogTo("INFO") 162 | } 163 | 164 | // Test that CopyStandardLogTo panics on bad input. 165 | func TestCopyStandardLogToPanic(t *testing.T) { 166 | defer func() { 167 | if s, ok := recover().(string); !ok || !strings.Contains(s, "LOG") { 168 | t.Errorf(`CopyStandardLogTo("LOG") should have panicked: %v`, s) 169 | } 170 | }() 171 | CopyStandardLogTo("LOG") 172 | } 173 | 174 | // Test that using the standard log package logs to INFO. 175 | func TestStandardLog(t *testing.T) { 176 | setFlags() 177 | defer logging.swap(logging.newBuffers()) 178 | stdLog.Print("test") 179 | if !contains(infoLog, "I", t) { 180 | t.Errorf("Info has wrong character: %q", contents(infoLog)) 181 | } 182 | if !contains(infoLog, "test", t) { 183 | t.Error("Info failed") 184 | } 185 | } 186 | 187 | // Test that the header has the correct format. 188 | func TestHeader(t *testing.T) { 189 | setFlags() 190 | defer logging.swap(logging.newBuffers()) 191 | defer func(previous func() time.Time) { timeNow = previous }(timeNow) 192 | timeNow = func() time.Time { 193 | return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local) 194 | } 195 | pid = 1234 196 | Info("test") 197 | var line int 198 | format := "I0102 15:04:05.067890 1234 glog_test.go:%d] test\n" 199 | n, err := fmt.Sscanf(contents(infoLog), format, &line) 200 | if n != 1 || err != nil { 201 | t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog)) 202 | } 203 | // Scanf treats multiple spaces as equivalent to a single space, 204 | // so check for correct space-padding also. 205 | want := fmt.Sprintf(format, line) 206 | if contents(infoLog) != want { 207 | t.Errorf("log format error: got:\n\t%q\nwant:\t%q", contents(infoLog), want) 208 | } 209 | } 210 | 211 | // Test that an Error log goes to Warning and Info. 212 | // Even in the Info log, the source character will be E, so the data should 213 | // all be identical. 214 | func TestError(t *testing.T) { 215 | setFlags() 216 | defer logging.swap(logging.newBuffers()) 217 | Error("test") 218 | if !contains(errorLog, "E", t) { 219 | t.Errorf("Error has wrong character: %q", contents(errorLog)) 220 | } 221 | if !contains(errorLog, "test", t) { 222 | t.Error("Error failed") 223 | } 224 | str := contents(errorLog) 225 | if !contains(warningLog, str, t) { 226 | t.Error("Warning failed") 227 | } 228 | if !contains(infoLog, str, t) { 229 | t.Error("Info failed") 230 | } 231 | if !contains(debugLog, str, t) { 232 | t.Error("Debug failed") 233 | } 234 | } 235 | 236 | // Test that a Warning log goes to Info. 237 | // Even in the Info log, the source character will be W, so the data should 238 | // all be identical. 239 | func TestWarning(t *testing.T) { 240 | setFlags() 241 | defer logging.swap(logging.newBuffers()) 242 | Warning("test") 243 | if !contains(warningLog, "W", t) { 244 | t.Errorf("Warning has wrong character: %q", contents(warningLog)) 245 | } 246 | if !contains(warningLog, "test", t) { 247 | t.Error("Warning failed") 248 | } 249 | str := contents(warningLog) 250 | if !contains(infoLog, str, t) { 251 | t.Error("Info failed") 252 | } 253 | } 254 | 255 | // Test that a V log goes to Info. 256 | func TestV(t *testing.T) { 257 | setFlags() 258 | defer logging.swap(logging.newBuffers()) 259 | logging.verbosity.Set("2") 260 | defer logging.verbosity.Set("0") 261 | V(2).Info("test") 262 | if !contains(infoLog, "I", t) { 263 | t.Errorf("Info has wrong character: %q", contents(infoLog)) 264 | } 265 | if !contains(infoLog, "test", t) { 266 | t.Error("Info failed") 267 | } 268 | } 269 | 270 | // Test that a vmodule enables a log in this file. 271 | func TestVmoduleOn(t *testing.T) { 272 | setFlags() 273 | defer logging.swap(logging.newBuffers()) 274 | logging.vmodule.Set("glog_test=2") 275 | defer logging.vmodule.Set("") 276 | if !V(1) { 277 | t.Error("V not enabled for 1") 278 | } 279 | if !V(2) { 280 | t.Error("V not enabled for 2") 281 | } 282 | if V(3) { 283 | t.Error("V enabled for 3") 284 | } 285 | V(2).Info("test") 286 | if !contains(infoLog, "I", t) { 287 | t.Errorf("Info has wrong character: %q", contents(infoLog)) 288 | } 289 | if !contains(infoLog, "test", t) { 290 | t.Error("Info failed") 291 | } 292 | } 293 | 294 | // Test that a vmodule of another file does not enable a log in this file. 295 | func TestVmoduleOff(t *testing.T) { 296 | setFlags() 297 | defer logging.swap(logging.newBuffers()) 298 | logging.vmodule.Set("notthisfile=2") 299 | defer logging.vmodule.Set("") 300 | for i := 1; i <= 3; i++ { 301 | if V(Level(i)) { 302 | t.Errorf("V enabled for %d", i) 303 | } 304 | } 305 | V(2).Info("test") 306 | if contents(infoLog) != "" { 307 | t.Error("V logged incorrectly") 308 | } 309 | } 310 | 311 | // vGlobs are patterns that match/don't match this file at V=2. 312 | var vGlobs = map[string]bool{ 313 | // Easy to test the numeric match here. 314 | "glog_test=1": false, // If -vmodule sets V to 1, V(2) will fail. 315 | "glog_test=2": true, 316 | "glog_test=3": true, // If -vmodule sets V to 1, V(3) will succeed. 317 | // These all use 2 and check the patterns. All are true. 318 | "*=2": true, 319 | "?l*=2": true, 320 | "????_*=2": true, 321 | "??[mno]?_*t=2": true, 322 | // These all use 2 and check the patterns. All are false. 323 | "*x=2": false, 324 | "m*=2": false, 325 | "??_*=2": false, 326 | "?[abc]?_*t=2": false, 327 | } 328 | 329 | // Test that vmodule globbing works as advertised. 330 | func testVmoduleGlob(pat string, match bool, t *testing.T) { 331 | setFlags() 332 | defer logging.swap(logging.newBuffers()) 333 | defer logging.vmodule.Set("") 334 | logging.vmodule.Set(pat) 335 | if V(2) != Verbose(match) { 336 | t.Errorf("incorrect match for %q: got %t expected %t", pat, V(2), match) 337 | } 338 | } 339 | 340 | // Test that a vmodule globbing works as advertised. 341 | func TestVmoduleGlob(t *testing.T) { 342 | for glob, match := range vGlobs { 343 | testVmoduleGlob(glob, match, t) 344 | } 345 | } 346 | 347 | func TestRollover(t *testing.T) { 348 | setFlags() 349 | var err error 350 | defer func(previous func(error)) { logExitFunc = previous }(logExitFunc) 351 | logExitFunc = func(e error) { 352 | err = e 353 | } 354 | defer func(previous uint64) { MaxSize = previous }(MaxSize) 355 | MaxSize = 512 356 | 357 | Info("x") // Be sure we have a file. 358 | info, ok := logging.file[infoLog].(*syncBuffer) 359 | if !ok { 360 | t.Fatal("info wasn't created") 361 | } 362 | if err != nil { 363 | t.Fatalf("info has initial error: %v", err) 364 | } 365 | fname0 := info.file.Name() 366 | Info(strings.Repeat("x", int(MaxSize))) // force a rollover 367 | if err != nil { 368 | t.Fatalf("info has error after big write: %v", err) 369 | } 370 | 371 | // Make sure the next log file gets a file name with a different 372 | // time stamp. 373 | // 374 | // TODO: determine whether we need to support subsecond log 375 | // rotation. C++ does not appear to handle this case (nor does it 376 | // handle Daylight Savings Time properly). 377 | time.Sleep(1 * time.Second) 378 | 379 | Info("x") // create a new file 380 | if err != nil { 381 | t.Fatalf("error after rotation: %v", err) 382 | } 383 | fname1 := info.file.Name() 384 | if fname0 == fname1 { 385 | t.Errorf("info.f.Name did not change: %v", fname0) 386 | } 387 | if info.nbytes >= MaxSize { 388 | t.Errorf("file size was not reset: %d", info.nbytes) 389 | } 390 | } 391 | 392 | func TestLogBacktraceAt(t *testing.T) { 393 | setFlags() 394 | defer logging.swap(logging.newBuffers()) 395 | // The peculiar style of this code simplifies line counting and maintenance of the 396 | // tracing block below. 397 | var infoLine string 398 | setTraceLocation := func(file string, line int, ok bool, delta int) { 399 | if !ok { 400 | t.Fatal("could not get file:line") 401 | } 402 | _, file = filepath.Split(file) 403 | infoLine = fmt.Sprintf("%s:%d", file, line+delta) 404 | err := logging.traceLocation.Set(infoLine) 405 | if err != nil { 406 | t.Fatal("error setting log_backtrace_at: ", err) 407 | } 408 | } 409 | { 410 | // Start of tracing block. These lines know about each other's relative position. 411 | _, file, line, ok := runtime.Caller(0) 412 | setTraceLocation(file, line, ok, +2) // Two lines between Caller and Info calls. 413 | Info("we want a stack trace here") 414 | } 415 | numAppearances := strings.Count(contents(infoLog), infoLine) 416 | if numAppearances < 2 { 417 | // Need 2 appearances, one in the log header and one in the trace: 418 | // log_test.go:281: I0511 16:36:06.952398 02238 log_test.go:280] we want a stack trace here 419 | // ... 420 | // github.com/glog/glog_test.go:280 (0x41ba91) 421 | // ... 422 | // We could be more precise but that would require knowing the details 423 | // of the traceback format, which may not be dependable. 424 | t.Fatal("got no trace back; log is ", contents(infoLog)) 425 | } 426 | } 427 | 428 | func BenchmarkHeader(b *testing.B) { 429 | for i := 0; i < b.N; i++ { 430 | buf, _, _ := logging.header(infoLog, 0) 431 | logging.putBuffer(buf) 432 | } 433 | } 434 | -------------------------------------------------------------------------------- /glog.go: -------------------------------------------------------------------------------- 1 | // Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ 2 | // 3 | // Copyright 2013 Google Inc. All Rights Reserved. 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | // Package glog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup. 18 | // It provides functions Info, Warning, Error, Fatal, plus formatting variants such as 19 | // Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags. 20 | // 21 | // Basic examples: 22 | // 23 | // glog.Info("Prepare to repel boarders") 24 | // 25 | // glog.Fatalf("Initialization failed: %s", err) 26 | // 27 | // See the documentation for the V function for an explanation of these examples: 28 | // 29 | // if glog.V(2) { 30 | // glog.Info("Starting transaction...") 31 | // } 32 | // 33 | // glog.V(2).Infoln("Processed", nItems, "elements") 34 | // 35 | // Log output is buffered and written periodically using Flush. Programs 36 | // should call Flush before exiting to guarantee all log output is written. 37 | // 38 | // By default, all log statements write to files in a temporary directory. 39 | // This package provides several flags that modify this behavior. 40 | // As a result, flag.Parse must be called before any logging is done. 41 | // 42 | // -logtostderr=false 43 | // Logs are written to standard error instead of to files. 44 | // -alsologtostderr=false 45 | // Logs are written to standard error as well as to files. 46 | // -stderrthreshold=ERROR 47 | // Log events at or above this severity are logged to standard 48 | // error as well as to files. 49 | // -log_dir="" 50 | // Log files will be written to this directory instead of the 51 | // default temporary directory. 52 | // 53 | // Other flags provide aids to debugging. 54 | // 55 | // -log_backtrace_at="" 56 | // When set to a file and line number holding a logging statement, 57 | // such as 58 | // -log_backtrace_at=gopherflakes.go:234 59 | // a stack trace will be written to the Info log whenever execution 60 | // hits that statement. (Unlike with -vmodule, the ".go" must be 61 | // present.) 62 | // -v=0 63 | // Enable V-leveled logging at the specified level. 64 | // -vmodule="" 65 | // The syntax of the argument is a comma-separated list of pattern=N, 66 | // where pattern is a literal file name (minus the ".go" suffix) or 67 | // "glob" pattern and N is a V level. For instance, 68 | // -vmodule=gopher*=3 69 | // sets the V level to 3 in all Go files whose names begin "gopher". 70 | // 71 | package glog 72 | 73 | import ( 74 | "bufio" 75 | "bytes" 76 | "errors" 77 | "flag" 78 | "fmt" 79 | "io" 80 | stdLog "log" 81 | "os" 82 | "path/filepath" 83 | "runtime" 84 | "strconv" 85 | "strings" 86 | "sync" 87 | "sync/atomic" 88 | "time" 89 | ) 90 | 91 | // severity identifies the sort of log: info, warning etc. It also implements 92 | // the flag.Value interface. The -stderrthreshold flag is of type severity and 93 | // should be modified only through the flag.Value interface. The values match 94 | // the corresponding constants in C++. 95 | type severity int32 // sync/atomic int32 96 | var outputSeverity severity 97 | 98 | // These constants identify the log levels in order of increasing severity. 99 | // A message written to a high-severity log file is also written to each 100 | // lower-severity log file. 101 | const ( 102 | debugLog severity = iota 103 | infoLog 104 | errorLog 105 | fatalLog 106 | numSeverity = 5 107 | ) 108 | 109 | const severityChar = "DIEF" 110 | 111 | var severityName = []string{ 112 | debugLog: "DEBUG", 113 | infoLog: "INFO", 114 | errorLog: "ERROR", 115 | fatalLog: "FATAL", 116 | } 117 | 118 | //测试tag 119 | // get returns the value of the severity. 120 | func (s *severity) get() severity { 121 | return severity(atomic.LoadInt32((*int32)(s))) 122 | } 123 | 124 | // set sets the value of the severity. 125 | func (s *severity) set(val severity) { 126 | atomic.StoreInt32((*int32)(s), int32(val)) 127 | } 128 | 129 | // String is part of the flag.Value interface. 130 | func (s *severity) String() string { 131 | return strconv.FormatInt(int64(*s), 10) 132 | } 133 | 134 | // Get is part of the flag.Value interface. 135 | func (s *severity) Get() interface{} { 136 | return *s 137 | } 138 | 139 | // Set is part of the flag.Value interface. 140 | func (s *severity) Set(value string) error { 141 | var threshold severity 142 | // Is it a known name? 143 | if v, ok := severityByName(value); ok { 144 | threshold = v 145 | } else { 146 | v, err := strconv.Atoi(value) 147 | if err != nil { 148 | return err 149 | } 150 | threshold = severity(v) 151 | } 152 | logging.stderrThreshold.set(threshold) 153 | return nil 154 | } 155 | 156 | func severityByName(s string) (severity, bool) { 157 | s = strings.ToUpper(s) 158 | for i, name := range severityName { 159 | if name == s { 160 | return severity(i), true 161 | } 162 | } 163 | return 0, false 164 | } 165 | 166 | func SetLevelString(outputLevel string) { 167 | severity, ok := severityByName(outputLevel) 168 | if !ok { 169 | panic(fmt.Errorf("cannot find severity name %s", outputLevel)) 170 | } 171 | outputSeverity = severity 172 | } 173 | 174 | // OutputStats tracks the number of output lines and bytes written. 175 | type OutputStats struct { 176 | lines int64 177 | bytes int64 178 | } 179 | 180 | // Lines returns the number of lines written. 181 | func (s *OutputStats) Lines() int64 { 182 | return atomic.LoadInt64(&s.lines) 183 | } 184 | 185 | // Bytes returns the number of bytes written. 186 | func (s *OutputStats) Bytes() int64 { 187 | return atomic.LoadInt64(&s.bytes) 188 | } 189 | 190 | // Stats tracks the number of lines of output and number of bytes 191 | // per severity level. Values must be read with atomic.LoadInt64. 192 | var Stats struct { 193 | Debug, Info, Error OutputStats 194 | } 195 | 196 | var severityStats = [numSeverity]*OutputStats{ 197 | debugLog: &Stats.Debug, 198 | infoLog: &Stats.Info, 199 | errorLog: &Stats.Error, 200 | } 201 | 202 | // Level is exported because it appears in the arguments to V and is 203 | // the type of the v flag, which can be set programmatically. 204 | // It's a distinct type because we want to discriminate it from logType. 205 | // Variables of type level are only changed under logging.mu. 206 | // The -v flag is read only with atomic ops, so the state of the logging 207 | // module is consistent. 208 | 209 | // Level is treated as a sync/atomic int32. 210 | 211 | // Level specifies a level of verbosity for V logs. *Level implements 212 | // flag.Value; the -v flag is of type Level and should be modified 213 | // only through the flag.Value interface. 214 | type Level int32 215 | 216 | // get returns the value of the Level. 217 | func (l *Level) get() Level { 218 | return Level(atomic.LoadInt32((*int32)(l))) 219 | } 220 | 221 | // set sets the value of the Level. 222 | func (l *Level) set(val Level) { 223 | atomic.StoreInt32((*int32)(l), int32(val)) 224 | } 225 | 226 | // String is part of the flag.Value interface. 227 | func (l *Level) String() string { 228 | return strconv.FormatInt(int64(*l), 10) 229 | } 230 | 231 | // Get is part of the flag.Value interface. 232 | func (l *Level) Get() interface{} { 233 | return *l 234 | } 235 | 236 | // Set is part of the flag.Value interface. 237 | func (l *Level) Set(value string) error { 238 | v, err := strconv.Atoi(value) 239 | if err != nil { 240 | return err 241 | } 242 | logging.mu.Lock() 243 | defer logging.mu.Unlock() 244 | logging.setVState(Level(v), logging.vmodule.filter, false) 245 | return nil 246 | } 247 | 248 | // moduleSpec represents the setting of the -vmodule flag. 249 | type moduleSpec struct { 250 | filter []modulePat 251 | } 252 | 253 | // modulePat contains a filter for the -vmodule flag. 254 | // It holds a verbosity level and a file pattern to match. 255 | type modulePat struct { 256 | pattern string 257 | literal bool // The pattern is a literal string 258 | level Level 259 | } 260 | 261 | // match reports whether the file matches the pattern. It uses a string 262 | // comparison if the pattern contains no metacharacters. 263 | func (m *modulePat) match(file string) bool { 264 | if m.literal { 265 | return file == m.pattern 266 | } 267 | match, _ := filepath.Match(m.pattern, file) 268 | return match 269 | } 270 | 271 | func (m *moduleSpec) String() string { 272 | // Lock because the type is not atomic. TODO: clean this up. 273 | logging.mu.Lock() 274 | defer logging.mu.Unlock() 275 | var b bytes.Buffer 276 | for i, f := range m.filter { 277 | if i > 0 { 278 | b.WriteRune(',') 279 | } 280 | fmt.Fprintf(&b, "%s=%d", f.pattern, f.level) 281 | } 282 | return b.String() 283 | } 284 | 285 | // Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the 286 | // struct is not exported. 287 | func (m *moduleSpec) Get() interface{} { 288 | return nil 289 | } 290 | 291 | var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N") 292 | 293 | // Syntax: -vmodule=recordio=2,file=1,gfs*=3 294 | func (m *moduleSpec) Set(value string) error { 295 | var filter []modulePat 296 | for _, pat := range strings.Split(value, ",") { 297 | if len(pat) == 0 { 298 | // Empty strings such as from a trailing comma can be ignored. 299 | continue 300 | } 301 | patLev := strings.Split(pat, "=") 302 | if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 { 303 | return errVmoduleSyntax 304 | } 305 | pattern := patLev[0] 306 | v, err := strconv.Atoi(patLev[1]) 307 | if err != nil { 308 | return errors.New("syntax error: expect comma-separated list of filename=N") 309 | } 310 | if v < 0 { 311 | return errors.New("negative value for vmodule level") 312 | } 313 | if v == 0 { 314 | continue // Ignore. It's harmless but no point in paying the overhead. 315 | } 316 | // TODO: check syntax of filter? 317 | filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)}) 318 | } 319 | logging.mu.Lock() 320 | defer logging.mu.Unlock() 321 | logging.setVState(logging.verbosity, filter, true) 322 | return nil 323 | } 324 | 325 | // isLiteral reports whether the pattern is a literal string, that is, has no metacharacters 326 | // that require filepath.Match to be called to match the pattern. 327 | func isLiteral(pattern string) bool { 328 | return !strings.ContainsAny(pattern, `\*?[]`) 329 | } 330 | 331 | // traceLocation represents the setting of the -log_backtrace_at flag. 332 | type traceLocation struct { 333 | file string 334 | line int 335 | } 336 | 337 | // isSet reports whether the trace location has been specified. 338 | // logging.mu is held. 339 | func (t *traceLocation) isSet() bool { 340 | return t.line > 0 341 | } 342 | 343 | // match reports whether the specified file and line matches the trace location. 344 | // The argument file name is the full path, not the basename specified in the flag. 345 | // logging.mu is held. 346 | func (t *traceLocation) match(file string, line int) bool { 347 | if t.line != line { 348 | return false 349 | } 350 | if i := strings.LastIndex(file, "/"); i >= 0 { 351 | file = file[i+1:] 352 | } 353 | return t.file == file 354 | } 355 | 356 | func (t *traceLocation) String() string { 357 | // Lock because the type is not atomic. TODO: clean this up. 358 | logging.mu.Lock() 359 | defer logging.mu.Unlock() 360 | return fmt.Sprintf("%s:%d", t.file, t.line) 361 | } 362 | 363 | // Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the 364 | // struct is not exported 365 | func (t *traceLocation) Get() interface{} { 366 | return nil 367 | } 368 | 369 | var errTraceSyntax = errors.New("syntax error: expect file.go:234") 370 | 371 | // Syntax: -log_backtrace_at=gopherflakes.go:234 372 | // Note that unlike vmodule the file extension is included here. 373 | func (t *traceLocation) Set(value string) error { 374 | if value == "" { 375 | // Unset. 376 | t.line = 0 377 | t.file = "" 378 | } 379 | fields := strings.Split(value, ":") 380 | if len(fields) != 2 { 381 | return errTraceSyntax 382 | } 383 | file, line := fields[0], fields[1] 384 | if !strings.Contains(file, ".") { 385 | return errTraceSyntax 386 | } 387 | v, err := strconv.Atoi(line) 388 | if err != nil { 389 | return errTraceSyntax 390 | } 391 | if v <= 0 { 392 | return errors.New("negative or zero value for level") 393 | } 394 | logging.mu.Lock() 395 | defer logging.mu.Unlock() 396 | t.line = v 397 | t.file = file 398 | return nil 399 | } 400 | 401 | // flushSyncWriter is the interface satisfied by logging destinations. 402 | type flushSyncWriter interface { 403 | Flush() error 404 | Sync() error 405 | io.Writer 406 | } 407 | 408 | func init() { 409 | flag.BoolVar(&logging.toStderr, "logtostderr", false, "log to standard error instead of files") 410 | flag.BoolVar(&logging.dailyRolling, "dailyRolling", false, " weather to handle log files daily") 411 | flag.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files") 412 | flag.Var(&logging.verbosity, "v", "log level for V logs") 413 | flag.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr") 414 | flag.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging") 415 | flag.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace") 416 | 417 | // Default stderrThreshold is ERROR. 418 | logging.stderrThreshold = errorLog 419 | 420 | //Default outputSeverity is INFO. 421 | outputSeverity = infoLog 422 | 423 | logging.setVState(0, nil, false) 424 | go logging.flushDaemon() 425 | } 426 | 427 | // Flush flushes all pending log I/O. 428 | func Flush() { 429 | logging.lockAndFlushAll() 430 | } 431 | 432 | // loggingT collects all the global state of the logging setup. 433 | type loggingT struct { 434 | // Boolean flags. Not handled atomically because the flag.Value interface 435 | // does not let us avoid the =true, and that shorthand is necessary for 436 | // compatibility. TODO: does this matter enough to fix? Seems unlikely. 437 | toStderr bool // The -logtostderr flag. 438 | alsoToStderr bool // The -alsologtostderr flag. 439 | 440 | dailyRolling bool 441 | 442 | // Level flag. Handled atomically. 443 | stderrThreshold severity // The -stderrthreshold flag. 444 | 445 | // freeList is a list of byte buffers, maintained under freeListMu. 446 | freeList *buffer 447 | // freeListMu maintains the free list. It is separate from the main mutex 448 | // so buffers can be grabbed and printed to without holding the main lock, 449 | // for better parallelization. 450 | freeListMu sync.Mutex 451 | 452 | // mu protects the remaining elements of this structure and is 453 | // used to synchronize logging. 454 | mu sync.Mutex 455 | // file holds writer for each of the log types. 456 | file [numSeverity]flushSyncWriter 457 | // pcs is used in V to avoid an allocation when computing the caller's PC. 458 | pcs [1]uintptr 459 | // vmap is a cache of the V Level for each V() call site, identified by PC. 460 | // It is wiped whenever the vmodule flag changes state. 461 | vmap map[uintptr]Level 462 | // filterLength stores the length of the vmodule filter chain. If greater 463 | // than zero, it means vmodule is enabled. It may be read safely 464 | // using sync.LoadInt32, but is only modified under mu. 465 | filterLength int32 466 | // traceLocation is the state of the -log_backtrace_at flag. 467 | traceLocation traceLocation 468 | // These flags are modified only under lock, although verbosity may be fetched 469 | // safely using atomic.LoadInt32. 470 | vmodule moduleSpec // The state of the -vmodule flag. 471 | verbosity Level // V logging level, the value of the -v flag/ 472 | } 473 | 474 | // buffer holds a byte Buffer for reuse. The zero value is ready for use. 475 | type buffer struct { 476 | bytes.Buffer 477 | tmp [64]byte // temporary byte array for creating headers. 478 | next *buffer 479 | } 480 | 481 | var logging loggingT 482 | 483 | // setVState sets a consistent state for V logging. 484 | // l.mu is held. 485 | func (l *loggingT) setVState(verbosity Level, filter []modulePat, setFilter bool) { 486 | // Turn verbosity off so V will not fire while we are in transition. 487 | logging.verbosity.set(0) 488 | // Ditto for filter length. 489 | atomic.StoreInt32(&logging.filterLength, 0) 490 | 491 | // Set the new filters and wipe the pc->Level map if the filter has changed. 492 | if setFilter { 493 | logging.vmodule.filter = filter 494 | logging.vmap = make(map[uintptr]Level) 495 | } 496 | 497 | // Things are consistent now, so enable filtering and verbosity. 498 | // They are enabled in order opposite to that in V. 499 | atomic.StoreInt32(&logging.filterLength, int32(len(filter))) 500 | logging.verbosity.set(verbosity) 501 | } 502 | 503 | // getBuffer returns a new, ready-to-use buffer. 504 | func (l *loggingT) getBuffer() *buffer { 505 | l.freeListMu.Lock() 506 | b := l.freeList 507 | if b != nil { 508 | l.freeList = b.next 509 | } 510 | l.freeListMu.Unlock() 511 | if b == nil { 512 | b = new(buffer) 513 | } else { 514 | b.next = nil 515 | b.Reset() 516 | } 517 | return b 518 | } 519 | 520 | // putBuffer returns a buffer to the free list. 521 | func (l *loggingT) putBuffer(b *buffer) { 522 | if b.Len() >= 256 { 523 | // Let big buffers die a natural death. 524 | return 525 | } 526 | l.freeListMu.Lock() 527 | b.next = l.freeList 528 | l.freeList = b 529 | l.freeListMu.Unlock() 530 | } 531 | 532 | var timeNow = time.Now // Stubbed out for testing. 533 | 534 | /* 535 | header formats a log header as defined by the C++ implementation. 536 | It returns a buffer containing the formatted header and the user's file and line number. 537 | The depth specifies how many stack frames above lives the source line to be identified in the log message. 538 | 539 | Log lines have this form: 540 | Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg... 541 | where the fields are defined as follows: 542 | L A single character, representing the log level (eg 'I' for INFO) 543 | mm The month (zero padded; ie May is '05') 544 | dd The day (zero padded) 545 | hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds 546 | threadid The space-padded thread ID as returned by GetTID() 547 | file The file name 548 | line The line number 549 | msg The user-supplied message 550 | */ 551 | func (l *loggingT) header(s severity, depth int) (*buffer, string, int) { 552 | _, file, line, ok := runtime.Caller(3 + depth) 553 | if !ok { 554 | file = "???" 555 | line = 1 556 | } else { 557 | slash := strings.LastIndex(file, "/") 558 | if slash >= 0 { 559 | file = file[slash+1:] 560 | } 561 | } 562 | return l.formatHeader(s, file, line), file, line 563 | } 564 | 565 | // formatHeader formats a log header using the provided file name and line number. 566 | func (l *loggingT) formatHeader(s severity, file string, line int) *buffer { 567 | now := timeNow() 568 | if line < 0 { 569 | line = 0 // not a real line number, but acceptable to someDigits 570 | } 571 | if s > fatalLog { 572 | s = infoLog // for safety. 573 | } 574 | buf := l.getBuffer() 575 | 576 | // Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand. 577 | // It's worth about 3X. Fprintf is hard. 578 | _, month, day := now.Date() 579 | hour, minute, second := now.Clock() 580 | // Lmmdd hh:mm:ss.uuuuuu threadid file:line] 581 | buf.tmp[0] = severityChar[s] 582 | buf.twoDigits(1, int(month)) 583 | buf.twoDigits(3, day) 584 | buf.tmp[5] = ' ' 585 | buf.twoDigits(6, hour) 586 | buf.tmp[8] = ':' 587 | buf.twoDigits(9, minute) 588 | buf.tmp[11] = ':' 589 | buf.twoDigits(12, second) 590 | buf.tmp[14] = '.' 591 | buf.nDigits(6, 15, now.Nanosecond()/1000, '0') 592 | buf.tmp[21] = ' ' 593 | buf.nDigits(7, 22, pid, ' ') // TODO: should be TID 594 | buf.tmp[29] = ' ' 595 | buf.Write(buf.tmp[:30]) 596 | buf.WriteString(file) 597 | buf.tmp[0] = ':' 598 | n := buf.someDigits(1, line) 599 | buf.tmp[n+1] = ']' 600 | buf.tmp[n+2] = ' ' 601 | buf.Write(buf.tmp[:n+3]) 602 | return buf 603 | } 604 | 605 | // Some custom tiny helper functions to print the log header efficiently. 606 | 607 | const digits = "0123456789" 608 | 609 | // twoDigits formats a zero-prefixed two-digit integer at buf.tmp[i]. 610 | func (buf *buffer) twoDigits(i, d int) { 611 | buf.tmp[i+1] = digits[d%10] 612 | d /= 10 613 | buf.tmp[i] = digits[d%10] 614 | } 615 | 616 | // nDigits formats an n-digit integer at buf.tmp[i], 617 | // padding with pad on the left. 618 | // It assumes d >= 0. 619 | func (buf *buffer) nDigits(n, i, d int, pad byte) { 620 | j := n - 1 621 | for ; j >= 0 && d > 0; j-- { 622 | buf.tmp[i+j] = digits[d%10] 623 | d /= 10 624 | } 625 | for ; j >= 0; j-- { 626 | buf.tmp[i+j] = pad 627 | } 628 | } 629 | 630 | // someDigits formats a zero-prefixed variable-width integer at buf.tmp[i]. 631 | func (buf *buffer) someDigits(i, d int) int { 632 | // Print into the top, then copy down. We know there's space for at least 633 | // a 10-digit number. 634 | j := len(buf.tmp) 635 | for { 636 | j-- 637 | buf.tmp[j] = digits[d%10] 638 | d /= 10 639 | if d == 0 { 640 | break 641 | } 642 | } 643 | return copy(buf.tmp[i:], buf.tmp[j:]) 644 | } 645 | 646 | func (l *loggingT) println(s severity, args ...interface{}) { 647 | if s < outputSeverity { 648 | return 649 | } 650 | buf, file, line := l.header(s, 0) 651 | fmt.Fprintln(buf, args...) 652 | l.output(s, buf, file, line, false) 653 | } 654 | 655 | func (l *loggingT) print(s severity, args ...interface{}) { 656 | l.printDepth(s, 1, args...) 657 | } 658 | 659 | func (l *loggingT) printDepth(s severity, depth int, args ...interface{}) { 660 | if s < outputSeverity { 661 | return 662 | } 663 | buf, file, line := l.header(s, depth) 664 | fmt.Fprint(buf, args...) 665 | if buf.Bytes()[buf.Len()-1] != '\n' { 666 | buf.WriteByte('\n') 667 | } 668 | l.output(s, buf, file, line, false) 669 | } 670 | 671 | func (l *loggingT) printf(s severity, format string, args ...interface{}) { 672 | if s < outputSeverity { 673 | return 674 | } 675 | buf, file, line := l.header(s, 0) 676 | fmt.Fprintf(buf, format, args...) 677 | if buf.Bytes()[buf.Len()-1] != '\n' { 678 | buf.WriteByte('\n') 679 | } 680 | l.output(s, buf, file, line, false) 681 | } 682 | 683 | // printWithFileLine behaves like print but uses the provided file and line number. If 684 | // alsoLogToStderr is true, the log message always appears on standard error; it 685 | // will also appear in the log file unless --logtostderr is set. 686 | func (l *loggingT) printWithFileLine(s severity, file string, line int, alsoToStderr bool, args ...interface{}) { 687 | if s < outputSeverity { 688 | return 689 | } 690 | buf := l.formatHeader(s, file, line) 691 | fmt.Fprint(buf, args...) 692 | if buf.Bytes()[buf.Len()-1] != '\n' { 693 | buf.WriteByte('\n') 694 | } 695 | l.output(s, buf, file, line, alsoToStderr) 696 | } 697 | 698 | // output writes the data to the log files and releases the buffer. 699 | func (l *loggingT) output(s severity, buf *buffer, file string, line int, alsoToStderr bool) { 700 | l.mu.Lock() 701 | if l.traceLocation.isSet() { 702 | if l.traceLocation.match(file, line) { 703 | buf.Write(stacks(false)) 704 | } 705 | } 706 | data := buf.Bytes() 707 | if !flag.Parsed() { 708 | os.Stderr.Write([]byte("ERROR: logging before flag.Parse: ")) 709 | os.Stderr.Write(data) 710 | } else if l.toStderr { 711 | os.Stderr.Write(data) 712 | } else { 713 | if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() { 714 | os.Stderr.Write(data) 715 | } 716 | if l.file[s] == nil { 717 | if err := l.createFiles(s); err != nil { 718 | os.Stderr.Write(data) // Make sure the message appears somewhere. 719 | l.exit(err) 720 | } 721 | } 722 | switch s { 723 | case fatalLog: 724 | l.file[fatalLog].Write(data) 725 | fallthrough 726 | case errorLog: 727 | l.file[errorLog].Write(data) 728 | fallthrough 729 | case infoLog: 730 | l.file[infoLog].Write(data) 731 | fallthrough 732 | case debugLog: 733 | l.file[debugLog].Write(data) 734 | } 735 | } 736 | if s == fatalLog { 737 | // If we got here via Exit rather than Fatal, print no stacks. 738 | if atomic.LoadUint32(&fatalNoStacks) > 0 { 739 | l.mu.Unlock() 740 | timeoutFlush(10 * time.Second) 741 | os.Exit(1) 742 | } 743 | // Dump all goroutine stacks before exiting. 744 | // First, make sure we see the trace for the current goroutine on standard error. 745 | // If -logtostderr has been specified, the loop below will do that anyway 746 | // as the first stack in the full dump. 747 | if !l.toStderr { 748 | os.Stderr.Write(stacks(false)) 749 | } 750 | // Write the stack trace for all goroutines to the files. 751 | trace := stacks(true) 752 | logExitFunc = func(error) {} // If we get a write error, we'll still exit below. 753 | for log := fatalLog; log >= debugLog; log-- { 754 | if f := l.file[log]; f != nil { // Can be nil if -logtostderr is set. 755 | f.Write(trace) 756 | } 757 | } 758 | l.mu.Unlock() 759 | timeoutFlush(10 * time.Second) 760 | os.Exit(255) // C++ uses -1, which is silly because it's anded with 255 anyway. 761 | } 762 | l.putBuffer(buf) 763 | l.mu.Unlock() 764 | if stats := severityStats[s]; stats != nil { 765 | atomic.AddInt64(&stats.lines, 1) 766 | atomic.AddInt64(&stats.bytes, int64(len(data))) 767 | } 768 | } 769 | 770 | // timeoutFlush calls Flush and returns when it completes or after timeout 771 | // elapses, whichever happens first. This is needed because the hooks invoked 772 | // by Flush may deadlock when glog.Fatal is called from a hook that holds 773 | // a lock. 774 | func timeoutFlush(timeout time.Duration) { 775 | done := make(chan bool, 1) 776 | go func() { 777 | Flush() // calls logging.lockAndFlushAll() 778 | done <- true 779 | }() 780 | select { 781 | case <-done: 782 | case <-time.After(timeout): 783 | fmt.Fprintln(os.Stderr, "glog: Flush took longer than", timeout) 784 | } 785 | } 786 | 787 | // stacks is a wrapper for runtime.Stack that attempts to recover the data for all goroutines. 788 | func stacks(all bool) []byte { 789 | // We don't know how big the traces are, so grow a few times if they don't fit. Start large, though. 790 | n := 10000 791 | if all { 792 | n = 100000 793 | } 794 | var trace []byte 795 | for i := 0; i < 5; i++ { 796 | trace = make([]byte, n) 797 | nbytes := runtime.Stack(trace, all) 798 | if nbytes < len(trace) { 799 | return trace[:nbytes] 800 | } 801 | n *= 2 802 | } 803 | return trace 804 | } 805 | 806 | // logExitFunc provides a simple mechanism to override the default behavior 807 | // of exiting on error. Used in testing and to guarantee we reach a required exit 808 | // for fatal logs. Instead, exit could be a function rather than a method but that 809 | // would make its use clumsier. 810 | var logExitFunc func(error) 811 | 812 | // exit is called if there is trouble creating or writing log files. 813 | // It flushes the logs and exits the program; there's no point in hanging around. 814 | // l.mu is held. 815 | func (l *loggingT) exit(err error) { 816 | fmt.Fprintf(os.Stderr, "log: exiting because of error: %s\n", err) 817 | // If logExitFunc is set, we do that instead of exiting. 818 | if logExitFunc != nil { 819 | logExitFunc(err) 820 | return 821 | } 822 | l.flushAll() 823 | os.Exit(2) 824 | } 825 | 826 | // syncBuffer joins a bufio.Writer to its underlying file, providing access to the 827 | // file's Sync method and providing a wrapper for the Write method that provides log 828 | // file rotation. There are conflicting methods, so the file cannot be embedded. 829 | // l.mu is held for all its methods. 830 | type syncBuffer struct { 831 | logger *loggingT 832 | *bufio.Writer 833 | file *os.File 834 | sev severity 835 | nbytes uint64 // The number of bytes written to this file 836 | createdDate string 837 | } 838 | 839 | func (sb *syncBuffer) Sync() error { 840 | return sb.file.Sync() 841 | } 842 | 843 | func (sb *syncBuffer) Write(p []byte) (n int, err error) { 844 | if logging.dailyRolling { 845 | if sb.createdDate != string(p[1:5]) { 846 | if err := sb.rotateFile(time.Now()); err != nil { 847 | sb.logger.exit(err) 848 | } 849 | } 850 | } 851 | 852 | if sb.nbytes+uint64(len(p)) >= MaxSize { 853 | if err := sb.rotateFile(time.Now()); err != nil { 854 | sb.logger.exit(err) 855 | } 856 | } 857 | n, err = sb.Writer.Write(p) 858 | sb.nbytes += uint64(n) 859 | if err != nil { 860 | sb.logger.exit(err) 861 | } 862 | return 863 | } 864 | 865 | // rotateFile closes the syncBuffer's file and starts a new one. 866 | func (sb *syncBuffer) rotateFile(now time.Time) error { 867 | if sb.file != nil { 868 | sb.Flush() 869 | sb.file.Close() 870 | } 871 | var err error 872 | sb.file, _, err = create(severityName[sb.sev], now) 873 | sb.nbytes = 0 874 | if err != nil { 875 | return err 876 | } 877 | 878 | sb.Writer = bufio.NewWriterSize(sb.file, bufferSize) 879 | _, month, day := now.Date() 880 | sb.createdDate = fmt.Sprintf("%02d%02d", month, day) 881 | 882 | // Write header. 883 | var buf bytes.Buffer 884 | fmt.Fprintf(&buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05")) 885 | fmt.Fprintf(&buf, "Running on machine: %s\n", host) 886 | fmt.Fprintf(&buf, "Binary: Built with %s %s for %s/%s\n", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH) 887 | fmt.Fprintf(&buf, "Log line format: [DIEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg\n") 888 | n, err := sb.file.Write(buf.Bytes()) 889 | sb.nbytes += uint64(n) 890 | return err 891 | } 892 | 893 | // bufferSize sizes the buffer associated with each log file. It's large 894 | // so that log records can accumulate without the logging thread blocking 895 | // on disk I/O. The flushDaemon will block instead. 896 | const bufferSize = 256 * 1024 897 | 898 | // createFiles creates all the log files for severity from sev down to infoLog. 899 | // l.mu is held. 900 | func (l *loggingT) createFiles(sev severity) error { 901 | now := time.Now() 902 | // Files are created in decreasing severity order, so as soon as we find one 903 | // has already been created, we can stop. 904 | for s := sev; s >= debugLog && l.file[s] == nil; s-- { 905 | sb := &syncBuffer{ 906 | logger: l, 907 | sev: s, 908 | } 909 | if err := sb.rotateFile(now); err != nil { 910 | return err 911 | } 912 | l.file[s] = sb 913 | } 914 | return nil 915 | } 916 | 917 | var flushInterval time.Duration = 5 * time.Second 918 | 919 | // flushDaemon periodically flushes the log file buffers. 920 | func (l *loggingT) flushDaemon() { 921 | for _ = range time.NewTicker(flushInterval).C { 922 | l.lockAndFlushAll() 923 | } 924 | } 925 | 926 | // lockAndFlushAll is like flushAll but locks l.mu first. 927 | func (l *loggingT) lockAndFlushAll() { 928 | l.mu.Lock() 929 | l.flushAll() 930 | l.mu.Unlock() 931 | } 932 | 933 | // flushAll flushes all the logs and attempts to "sync" their data to disk. 934 | // l.mu is held. 935 | func (l *loggingT) flushAll() { 936 | // Flush from fatal down, in case there's trouble flushing. 937 | for s := fatalLog; s >= debugLog; s-- { 938 | file := l.file[s] 939 | if file != nil { 940 | file.Flush() // ignore error 941 | file.Sync() // ignore error 942 | } 943 | } 944 | } 945 | 946 | // CopyStandardLogTo arranges for messages written to the Go "log" package's 947 | // default logs to also appear in the Google logs for the named and lower 948 | // severities. Subsequent changes to the standard log's default output location 949 | // or format may break this behavior. 950 | // 951 | // Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not 952 | // recognized, CopyStandardLogTo panics. 953 | func CopyStandardLogTo(name string) { 954 | sev, ok := severityByName(name) 955 | if !ok { 956 | panic(fmt.Sprintf("log.CopyStandardLogTo(%q): unrecognized severity name", name)) 957 | } 958 | // Set a log format that captures the user's file and line: 959 | // d.go:23: message 960 | stdLog.SetFlags(stdLog.Lshortfile) 961 | stdLog.SetOutput(logBridge(sev)) 962 | } 963 | 964 | // logBridge provides the Write method that enables CopyStandardLogTo to connect 965 | // Go's standard logs to the logs provided by this package. 966 | type logBridge severity 967 | 968 | // Write parses the standard logging line and passes its components to the 969 | // logger for severity(lb). 970 | func (lb logBridge) Write(b []byte) (n int, err error) { 971 | var ( 972 | file = "???" 973 | line = 1 974 | text string 975 | ) 976 | // Split "d.go:23: message" into "d.go", "23", and "message". 977 | if parts := bytes.SplitN(b, []byte{':'}, 3); len(parts) != 3 || len(parts[0]) < 1 || len(parts[2]) < 1 { 978 | text = fmt.Sprintf("bad log format: %s", b) 979 | } else { 980 | file = string(parts[0]) 981 | text = string(parts[2][1:]) // skip leading space 982 | line, err = strconv.Atoi(string(parts[1])) 983 | if err != nil { 984 | text = fmt.Sprintf("bad line number: %s", b) 985 | line = 1 986 | } 987 | } 988 | // printWithFileLine with alsoToStderr=true, so standard log messages 989 | // always appear on standard error. 990 | logging.printWithFileLine(severity(lb), file, line, true, text) 991 | return len(b), nil 992 | } 993 | 994 | // setV computes and remembers the V level for a given PC 995 | // when vmodule is enabled. 996 | // File pattern matching takes the basename of the file, stripped 997 | // of its .go suffix, and uses filepath.Match, which is a little more 998 | // general than the *? matching used in C++. 999 | // l.mu is held. 1000 | func (l *loggingT) setV(pc uintptr) Level { 1001 | fn := runtime.FuncForPC(pc) 1002 | file, _ := fn.FileLine(pc) 1003 | // The file is something like /a/b/c/d.go. We want just the d. 1004 | if strings.HasSuffix(file, ".go") { 1005 | file = file[:len(file)-3] 1006 | } 1007 | if slash := strings.LastIndex(file, "/"); slash >= 0 { 1008 | file = file[slash+1:] 1009 | } 1010 | for _, filter := range l.vmodule.filter { 1011 | if filter.match(file) { 1012 | l.vmap[pc] = filter.level 1013 | return filter.level 1014 | } 1015 | } 1016 | l.vmap[pc] = 0 1017 | return 0 1018 | } 1019 | 1020 | // Verbose is a boolean type that implements Infof (like Printf) etc. 1021 | // See the documentation of V for more information. 1022 | type Verbose bool 1023 | 1024 | // V reports whether verbosity at the call site is at least the requested level. 1025 | // The returned value is a boolean of type Verbose, which implements Info, Infoln 1026 | // and Infof. These methods will write to the Info log if called. 1027 | // Thus, one may write either 1028 | // if glog.V(2) { glog.Info("log this") } 1029 | // or 1030 | // glog.V(2).Info("log this") 1031 | // The second form is shorter but the first is cheaper if logging is off because it does 1032 | // not evaluate its arguments. 1033 | // 1034 | // Whether an individual call to V generates a log record depends on the setting of 1035 | // the -v and --vmodule flags; both are off by default. If the level in the call to 1036 | // V is at least the value of -v, or of -vmodule for the source file containing the 1037 | // call, the V call will log. 1038 | func V(level Level) Verbose { 1039 | // This function tries hard to be cheap unless there's work to do. 1040 | // The fast path is two atomic loads and compares. 1041 | 1042 | // Here is a cheap but safe test to see if V logging is enabled globally. 1043 | if logging.verbosity.get() >= level { 1044 | return Verbose(true) 1045 | } 1046 | 1047 | // It's off globally but it vmodule may still be set. 1048 | // Here is another cheap but safe test to see if vmodule is enabled. 1049 | if atomic.LoadInt32(&logging.filterLength) > 0 { 1050 | // Now we need a proper lock to use the logging structure. The pcs field 1051 | // is shared so we must lock before accessing it. This is fairly expensive, 1052 | // but if V logging is enabled we're slow anyway. 1053 | logging.mu.Lock() 1054 | defer logging.mu.Unlock() 1055 | if runtime.Callers(2, logging.pcs[:]) == 0 { 1056 | return Verbose(false) 1057 | } 1058 | v, ok := logging.vmap[logging.pcs[0]] 1059 | if !ok { 1060 | v = logging.setV(logging.pcs[0]) 1061 | } 1062 | return Verbose(v >= level) 1063 | } 1064 | return Verbose(false) 1065 | } 1066 | 1067 | // Info is equivalent to the global Info function, guarded by the value of v. 1068 | // See the documentation of V for usage. 1069 | func (v Verbose) Info(args ...interface{}) { 1070 | if v { 1071 | logging.print(infoLog, args...) 1072 | } 1073 | } 1074 | 1075 | // Infoln is equivalent to the global Infoln function, guarded by the value of v. 1076 | // See the documentation of V for usage. 1077 | func (v Verbose) Infoln(args ...interface{}) { 1078 | if v { 1079 | logging.println(infoLog, args...) 1080 | } 1081 | } 1082 | 1083 | // Infof is equivalent to the global Infof function, guarded by the value of v. 1084 | // See the documentation of V for usage. 1085 | func (v Verbose) Infof(format string, args ...interface{}) { 1086 | if v { 1087 | logging.printf(infoLog, format, args...) 1088 | } 1089 | } 1090 | 1091 | func Debug(args ...interface{}) { 1092 | logging.print(debugLog, args...) 1093 | } 1094 | 1095 | func DebugDepth(depth int, args ...interface{}) { 1096 | logging.printDepth(debugLog, depth, args...) 1097 | } 1098 | 1099 | func Debugln(args ...interface{}) { 1100 | logging.println(debugLog, args...) 1101 | } 1102 | 1103 | func Debugf(format string, args ...interface{}) { 1104 | logging.printf(debugLog, format, args...) 1105 | } 1106 | 1107 | // Info logs to the INFO log. 1108 | // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. 1109 | func Info(args ...interface{}) { 1110 | logging.print(infoLog, args...) 1111 | } 1112 | 1113 | // InfoDepth acts as Info but uses depth to determine which call frame to log. 1114 | // InfoDepth(0, "msg") is the same as Info("msg"). 1115 | func InfoDepth(depth int, args ...interface{}) { 1116 | logging.printDepth(infoLog, depth, args...) 1117 | } 1118 | 1119 | // Infoln logs to the INFO log. 1120 | // Arguments are handled in the manner of fmt.Println; a newline is appended if missing. 1121 | func Infoln(args ...interface{}) { 1122 | logging.println(infoLog, args...) 1123 | } 1124 | 1125 | // Infof logs to the INFO log. 1126 | // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. 1127 | func Infof(format string, args ...interface{}) { 1128 | logging.printf(infoLog, format, args...) 1129 | } 1130 | 1131 | // Error logs to the ERROR, WARNING, and INFO logs. 1132 | // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. 1133 | func Error(args ...interface{}) { 1134 | logging.print(errorLog, args...) 1135 | } 1136 | 1137 | // ErrorDepth acts as Error but uses depth to determine which call frame to log. 1138 | // ErrorDepth(0, "msg") is the same as Error("msg"). 1139 | func ErrorDepth(depth int, args ...interface{}) { 1140 | logging.printDepth(errorLog, depth, args...) 1141 | } 1142 | 1143 | // Errorln logs to the ERROR, WARNING, and INFO logs. 1144 | // Arguments are handled in the manner of fmt.Println; a newline is appended if missing. 1145 | func Errorln(args ...interface{}) { 1146 | logging.println(errorLog, args...) 1147 | } 1148 | 1149 | // Errorf logs to the ERROR, WARNING, and INFO logs. 1150 | // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. 1151 | func Errorf(format string, args ...interface{}) { 1152 | logging.printf(errorLog, format, args...) 1153 | } 1154 | 1155 | // Fatal logs to the FATAL, ERROR, WARNING, and INFO logs, 1156 | // including a stack trace of all running goroutines, then calls os.Exit(255). 1157 | // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. 1158 | func Fatal(args ...interface{}) { 1159 | logging.print(fatalLog, args...) 1160 | } 1161 | 1162 | // FatalDepth acts as Fatal but uses depth to determine which call frame to log. 1163 | // FatalDepth(0, "msg") is the same as Fatal("msg"). 1164 | func FatalDepth(depth int, args ...interface{}) { 1165 | logging.printDepth(fatalLog, depth, args...) 1166 | } 1167 | 1168 | // Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs, 1169 | // including a stack trace of all running goroutines, then calls os.Exit(255). 1170 | // Arguments are handled in the manner of fmt.Println; a newline is appended if missing. 1171 | func Fatalln(args ...interface{}) { 1172 | logging.println(fatalLog, args...) 1173 | } 1174 | 1175 | // Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs, 1176 | // including a stack trace of all running goroutines, then calls os.Exit(255). 1177 | // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. 1178 | func Fatalf(format string, args ...interface{}) { 1179 | logging.printf(fatalLog, format, args...) 1180 | } 1181 | 1182 | // fatalNoStacks is non-zero if we are to exit without dumping goroutine stacks. 1183 | // It allows Exit and relatives to use the Fatal logs. 1184 | var fatalNoStacks uint32 1185 | 1186 | // Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). 1187 | // Arguments are handled in the manner of fmt.Print; a newline is appended if missing. 1188 | func Exit(args ...interface{}) { 1189 | atomic.StoreUint32(&fatalNoStacks, 1) 1190 | logging.print(fatalLog, args...) 1191 | } 1192 | 1193 | // ExitDepth acts as Exit but uses depth to determine which call frame to log. 1194 | // ExitDepth(0, "msg") is the same as Exit("msg"). 1195 | func ExitDepth(depth int, args ...interface{}) { 1196 | atomic.StoreUint32(&fatalNoStacks, 1) 1197 | logging.printDepth(fatalLog, depth, args...) 1198 | } 1199 | 1200 | // Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). 1201 | func Exitln(args ...interface{}) { 1202 | atomic.StoreUint32(&fatalNoStacks, 1) 1203 | logging.println(fatalLog, args...) 1204 | } 1205 | 1206 | // Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). 1207 | // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. 1208 | func Exitf(format string, args ...interface{}) { 1209 | atomic.StoreUint32(&fatalNoStacks, 1) 1210 | logging.printf(fatalLog, format, args...) 1211 | } 1212 | --------------------------------------------------------------------------------