├── go.mod ├── go.sum ├── logger_other.go ├── .github └── workflows │ └── go.yml ├── logger_syslog.go ├── CONTRIBUTING ├── logger_test.go ├── logger_windows.go ├── README.md ├── LICENSE └── logger.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/google/logger 2 | 3 | go 1.18 4 | 5 | require golang.org/x/sys v0.1.0 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= 2 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 3 | -------------------------------------------------------------------------------- /logger_other.go: -------------------------------------------------------------------------------- 1 | //go:build !unix && !windows 2 | 3 | package logger 4 | 5 | import ( 6 | "errors" 7 | "io" 8 | ) 9 | 10 | func setup(src string) (io.Writer, io.Writer, io.Writer, error) { 11 | return nil, nil, nil, errors.New("system logging not implemented") 12 | } 13 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | schedule: 9 | - cron: '0 0 * * 0' # weekly 10 | 11 | jobs: 12 | 13 | build: 14 | strategy: 15 | matrix: 16 | os: [ubuntu-latest, windows-latest] 17 | runs-on: ${{ matrix.os }} 18 | steps: 19 | - uses: actions/checkout@v2 20 | 21 | - name: Set up Go 22 | uses: actions/setup-go@v3 23 | with: 24 | go-version: '>=1.15' 25 | 26 | - name: Build 27 | run: go build -v ./... 28 | 29 | - name: Test 30 | run: go test -v ./... 31 | -------------------------------------------------------------------------------- /logger_syslog.go: -------------------------------------------------------------------------------- 1 | //go:build unix 2 | 3 | /* 4 | Copyright 2016 Google Inc. All Rights Reserved. 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 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | package logger 17 | 18 | import ( 19 | "io" 20 | "log/syslog" 21 | ) 22 | 23 | func setup(src string) (io.Writer, io.Writer, io.Writer, error) { 24 | const facility = syslog.LOG_USER 25 | il, err := syslog.New(facility|syslog.LOG_NOTICE, src) 26 | if err != nil { 27 | return nil, nil, nil, err 28 | } 29 | wl, err := syslog.New(facility|syslog.LOG_WARNING, src) 30 | if err != nil { 31 | return nil, nil, nil, err 32 | } 33 | el, err := syslog.New(facility|syslog.LOG_ERR, src) 34 | if err != nil { 35 | return nil, nil, nil, err 36 | } 37 | return il, wl, el, nil 38 | } 39 | -------------------------------------------------------------------------------- /CONTRIBUTING: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at the end). 2 | 3 | ### Before you contribute 4 | Before we can use your code, you must sign the 5 | [Google Individual Contributor License Agreement] 6 | (https://cla.developers.google.com/about/google-individual) 7 | (CLA), which you can do online. The CLA is necessary mainly because you own the 8 | copyright to your changes, even after your contribution becomes part of our 9 | codebase, so we need your permission to use and distribute your code. We also 10 | need to be sure of various other things—for instance that you'll tell us if you 11 | know that your code infringes on other people's patents. You don't have to sign 12 | the CLA until after you've submitted your code for review and a member has 13 | approved it, but you must do it before we can put your code into our codebase. 14 | Before you start working on a larger contribution, you should get in touch with 15 | us first through the issue tracker with your idea so that we can help out and 16 | possibly guide you. Coordinating up front makes it much easier to avoid 17 | frustration later on. 18 | 19 | ### Code reviews 20 | All submissions, including submissions by project members, require review. We 21 | use Github pull requests for this purpose. 22 | 23 | ### The small print 24 | Contributions made by corporations are covered by a different agreement than 25 | the one above, the 26 | [Software Grant and Corporate Contributor License Agreement] 27 | (https://cla.developers.google.com/about/google-corporate). 28 | -------------------------------------------------------------------------------- /logger_test.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "os" 7 | "reflect" 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | func TestLoggingBeforeInit(t *testing.T) { 13 | old := os.Stderr 14 | r, w, err := os.Pipe() 15 | if err != nil { 16 | t.Fatal(err) 17 | } 18 | 19 | os.Stderr = w 20 | // Reset 21 | initialize() 22 | 23 | info := "info log" 24 | warning := "warning log" 25 | errL := "error log" 26 | fatal := "fatal log" 27 | 28 | Info(info) 29 | Warning(warning) 30 | Error(errL) 31 | // We don't want os.Exit in a test 32 | defaultLogger.output(sFatal, 0, fatal) 33 | 34 | w.Close() 35 | os.Stderr = old 36 | 37 | var b bytes.Buffer 38 | scanner := bufio.NewScanner(r) 39 | for scanner.Scan() { 40 | b.Write(scanner.Bytes()) 41 | } 42 | 43 | out := b.String() 44 | 45 | for _, txt := range []string{info, warning, errL, fatal} { 46 | if !strings.Contains(out, txt) { 47 | t.Errorf("log output %q does not contain expected text: %q", out, txt) 48 | } 49 | } 50 | } 51 | 52 | func TestInit(t *testing.T) { 53 | var buf1 bytes.Buffer 54 | l1 := Init("test1", false, false, &buf1) 55 | if !reflect.DeepEqual(l1, defaultLogger) { 56 | t.Fatal("defaultLogger does not match logger returned by Init") 57 | } 58 | 59 | // Subsequent runs of Init shouldn't change defaultLogger. 60 | var buf2 bytes.Buffer 61 | l2 := Init("test2", false, false, &buf2) 62 | if !reflect.DeepEqual(l1, defaultLogger) { 63 | t.Error("defaultLogger should not have changed") 64 | } 65 | 66 | // Check log output. 67 | l1.Info("logger #1") 68 | l2.Info("logger #2") 69 | defaultLogger.Info("default logger") 70 | 71 | tests := []struct { 72 | out string 73 | want int 74 | }{ 75 | {buf1.String(), 2}, 76 | {buf2.String(), 1}, 77 | } 78 | 79 | for i, tt := range tests { 80 | got := len(strings.Split(strings.TrimSpace(tt.out), "\n")) 81 | if got != tt.want { 82 | t.Errorf("logger %d wrong number of lines, want %d, got %d", i+1, tt.want, got) 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /logger_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | /* 3 | Copyright 2016 Google Inc. All Rights Reserved. 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | package logger 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | "strings" 21 | 22 | "golang.org/x/sys/windows" 23 | "golang.org/x/sys/windows/svc/eventlog" 24 | ) 25 | 26 | type writer struct { 27 | pri severity 28 | src string 29 | el *eventlog.Log 30 | } 31 | 32 | // Write sends a log message to the Event Log. 33 | func (w *writer) Write(b []byte) (int, error) { 34 | switch w.pri { 35 | case sInfo: 36 | return len(b), w.el.Info(1, string(b)) 37 | case sWarning: 38 | return len(b), w.el.Warning(3, string(b)) 39 | case sError: 40 | return len(b), w.el.Error(2, string(b)) 41 | } 42 | return 0, fmt.Errorf("unrecognized severity: %v", w.pri) 43 | } 44 | 45 | func (w *writer) Close() error { 46 | return w.el.Close() 47 | } 48 | 49 | func newW(pri severity, src string) (io.WriteCloser, error) { 50 | // Continue if we receive "registry key already exists" or if we get 51 | // ERROR_ACCESS_DENIED so that we can log without administrative permissions 52 | // for pre-existing eventlog sources. 53 | if err := eventlog.InstallAsEventCreate(src, eventlog.Info|eventlog.Warning|eventlog.Error); err != nil { 54 | if !strings.Contains(err.Error(), "registry key already exists") && err != windows.ERROR_ACCESS_DENIED { 55 | return nil, err 56 | } 57 | } 58 | el, err := eventlog.Open(src) 59 | if err != nil { 60 | return nil, err 61 | } 62 | return &writer{ 63 | pri: pri, 64 | src: src, 65 | el: el, 66 | }, nil 67 | } 68 | 69 | func setup(src string) (io.WriteCloser, io.WriteCloser, io.WriteCloser, error) { 70 | infoL, err := newW(sInfo, src) 71 | if err != nil { 72 | return nil, nil, nil, err 73 | } 74 | warningL, err := newW(sWarning, src) 75 | if err != nil { 76 | return nil, nil, nil, err 77 | } 78 | errL, err := newW(sError, src) 79 | if err != nil { 80 | return nil, nil, nil, err 81 | } 82 | return infoL, warningL, errL, nil 83 | } 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # logger # 2 | Logger is a simple cross platform Go logging library for Windows, Linux, FreeBSD, and 3 | macOS, it can log to the Windows event log, Linux/macOS syslog, and an io.Writer. 4 | 5 | This is not an official Google product. 6 | 7 | ## Usage ## 8 | 9 | Set up the default logger to log the system log (event log or syslog) and a 10 | file, include a flag to turn up verbosity: 11 | 12 | ```go 13 | import ( 14 | "flag" 15 | "os" 16 | 17 | "github.com/google/logger" 18 | ) 19 | 20 | const logPath = "/some/location/example.log" 21 | 22 | var verbose = flag.Bool("verbose", false, "print info level logs to stdout") 23 | 24 | func main() { 25 | flag.Parse() 26 | 27 | lf, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0660) 28 | if err != nil { 29 | logger.Fatalf("Failed to open log file: %v", err) 30 | } 31 | defer lf.Close() 32 | 33 | defer logger.Init("LoggerExample", *verbose, true, lf).Close() 34 | 35 | logger.Info("I'm about to do something!") 36 | if err := doSomething(); err != nil { 37 | logger.Errorf("Error running doSomething: %v", err) 38 | } 39 | } 40 | ``` 41 | 42 | The Init function returns a logger so you can setup multiple instances if you 43 | wish, only the first call to Init will set the default logger: 44 | 45 | ```go 46 | lf, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0660) 47 | if err != nil { 48 | logger.Fatalf("Failed to open log file: %v", err) 49 | } 50 | defer lf.Close() 51 | 52 | // Log to system log and a log file, Info logs don't write to stdout. 53 | loggerOne := logger.Init("LoggerExample", false, true, lf) 54 | defer loggerOne.Close() 55 | // Don't to system log or a log file, Info logs write to stdout.. 56 | loggerTwo := logger.Init("LoggerExample", true, false, ioutil.Discard) 57 | defer loggerTwo.Close() 58 | 59 | loggerOne.Info("This will log to the log file and the system log") 60 | loggerTwo.Info("This will only log to stdout") 61 | logger.Info("This is the same as using loggerOne") 62 | 63 | ``` 64 | 65 | ## Custom Format ## 66 | 67 | | Code | Example | 68 | |--------------------------------------|----------------------------------------------------------| 69 | | `logger.SetFlags(log.Ldate)` | ERROR: 2018/11/11 Error running Foobar: message | 70 | | `logger.SetFlags(log.Ltime)` | ERROR: 09:42:45 Error running Foobar: message | 71 | | `logger.SetFlags(log.Lmicroseconds)` | ERROR: 09:42:50.776015 Error running Foobar: message | 72 | | `logger.SetFlags(log.Llongfile)` | ERROR: /src/main.go:31: Error running Foobar: message | 73 | | `logger.SetFlags(log.Lshortfile)` | ERROR: main.go:31: Error running Foobar: message | 74 | | `logger.SetFlags(log.LUTC)` | ERROR: Error running Foobar: message | 75 | | `logger.SetFlags(log.LstdFlags)` | ERROR: 2018/11/11 09:43:12 Error running Foobar: message | 76 | 77 | ```go 78 | func main() { 79 | lf, err := os.OpenFile(logPath, …, 0660) 80 | defer logger.Init("foo", *verbose, true, lf).Close() 81 | logger.SetFlags(log.LstdFlags) 82 | } 83 | ``` 84 | 85 | More info: https://golang.org/pkg/log/#pkg-constants 86 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /logger.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 Google Inc. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | // Package logger offers simple cross platform logging for Windows and Linux. 15 | // Available logging endpoints are event log (Windows), syslog (Linux), and 16 | // an io.Writer. 17 | package logger 18 | 19 | import ( 20 | "fmt" 21 | "io" 22 | "log" 23 | "os" 24 | "sync" 25 | ) 26 | 27 | type severity int 28 | 29 | // Severity levels. 30 | const ( 31 | sInfo severity = iota 32 | sWarning 33 | sError 34 | sFatal 35 | ) 36 | 37 | // Severity tags. 38 | const ( 39 | tagInfo = "INFO : " 40 | tagWarning = "WARN : " 41 | tagError = "ERROR: " 42 | tagFatal = "FATAL: " 43 | ) 44 | 45 | const ( 46 | flags = log.Ldate | log.Lmicroseconds | log.Lshortfile 47 | initText = "ERROR: Logging before logger.Init.\n" 48 | ) 49 | 50 | var ( 51 | logLock sync.Mutex 52 | defaultLogger *Logger 53 | ) 54 | 55 | // initialize resets defaultLogger. Which allows tests to reset environment. 56 | func initialize() { 57 | defaultLogger = &Logger{ 58 | infoLog: log.New(os.Stderr, initText+tagInfo, flags), 59 | warningLog: log.New(os.Stderr, initText+tagWarning, flags), 60 | errorLog: log.New(os.Stderr, initText+tagError, flags), 61 | fatalLog: log.New(os.Stderr, initText+tagFatal, flags), 62 | } 63 | } 64 | 65 | func init() { 66 | initialize() 67 | } 68 | 69 | // Init sets up logging and should be called before log functions, usually in 70 | // the caller's main(). Default log functions can be called before Init(), but log 71 | // output will only go to stderr (along with a warning). 72 | // The first call to Init populates the default logger and returns the 73 | // generated logger, subsequent calls to Init will only return the generated 74 | // logger. 75 | // If the logFile passed in also satisfies io.Closer, logFile.Close will be called 76 | // when closing the logger. 77 | func Init(name string, verbose, systemLog bool, logFile io.Writer) *Logger { 78 | var il, wl, el io.Writer 79 | var syslogErr error 80 | if systemLog { 81 | il, wl, el, syslogErr = setup(name) 82 | } 83 | 84 | iLogs := []io.Writer{logFile} 85 | wLogs := []io.Writer{logFile} 86 | eLogs := []io.Writer{logFile} 87 | if il != nil { 88 | iLogs = append(iLogs, il) 89 | } 90 | if wl != nil { 91 | wLogs = append(wLogs, wl) 92 | } 93 | if el != nil { 94 | eLogs = append(eLogs, el) 95 | } 96 | // Windows services don't have stdout/stderr. Writes will fail, so try them last. 97 | eLogs = append(eLogs, os.Stderr) 98 | if verbose { 99 | iLogs = append(iLogs, os.Stdout) 100 | wLogs = append(wLogs, os.Stdout) 101 | } 102 | 103 | l := Logger{ 104 | infoLog: log.New(io.MultiWriter(iLogs...), tagInfo, flags), 105 | warningLog: log.New(io.MultiWriter(wLogs...), tagWarning, flags), 106 | errorLog: log.New(io.MultiWriter(eLogs...), tagError, flags), 107 | fatalLog: log.New(io.MultiWriter(eLogs...), tagFatal, flags), 108 | } 109 | for _, w := range []io.Writer{logFile, il, wl, el} { 110 | if c, ok := w.(io.Closer); ok && c != nil { 111 | l.closers = append(l.closers, c) 112 | } 113 | } 114 | l.initialized = true 115 | 116 | if syslogErr != nil { 117 | l.Error(syslogErr) 118 | } 119 | 120 | logLock.Lock() 121 | defer logLock.Unlock() 122 | if !defaultLogger.initialized { 123 | defaultLogger = &l 124 | } 125 | 126 | return &l 127 | } 128 | 129 | // Close closes the default logger. 130 | func Close() { 131 | defaultLogger.Close() 132 | } 133 | 134 | // Level describes the level of verbosity for info messages when using 135 | // V style logging. See documentation for the V function for more information. 136 | type Level int 137 | 138 | // Verbose is type that implements Infof, etc. 139 | type Verbose struct { 140 | enabled bool 141 | logger *Logger 142 | } 143 | 144 | // A Logger represents an active logging object. Multiple loggers can be used 145 | // simultaneously even if they are using the same writers. 146 | type Logger struct { 147 | infoLog *log.Logger 148 | warningLog *log.Logger 149 | errorLog *log.Logger 150 | fatalLog *log.Logger 151 | closers []io.Closer 152 | initialized bool 153 | level Level 154 | } 155 | 156 | func (l *Logger) output(s severity, depth int, txt string) { 157 | logLock.Lock() 158 | defer logLock.Unlock() 159 | switch s { 160 | case sInfo: 161 | l.infoLog.Output(3+depth, txt) 162 | case sWarning: 163 | l.warningLog.Output(3+depth, txt) 164 | case sError: 165 | l.errorLog.Output(3+depth, txt) 166 | case sFatal: 167 | l.fatalLog.Output(3+depth, txt) 168 | default: 169 | panic(fmt.Sprintln("unrecognized severity:", s)) 170 | } 171 | } 172 | 173 | // Close closes all the underlying log writers, which will flush any cached logs. 174 | // Any errors from closing the underlying log writers will be printed to stderr. 175 | // Once Close is called, all future calls to the logger will panic. 176 | func (l *Logger) Close() { 177 | logLock.Lock() 178 | defer logLock.Unlock() 179 | 180 | if !l.initialized { 181 | return 182 | } 183 | 184 | for _, c := range l.closers { 185 | if err := c.Close(); err != nil { 186 | fmt.Fprintf(os.Stderr, "Failed to close log %v: %v\n", c, err) 187 | } 188 | } 189 | } 190 | 191 | // Info logs with the Info severity. 192 | // Arguments are handled in the manner of fmt.Print. 193 | func (l *Logger) Info(v ...interface{}) { 194 | l.output(sInfo, 0, fmt.Sprint(v...)) 195 | } 196 | 197 | // InfoDepth acts as Info but uses depth to determine which call frame to log. 198 | // InfoDepth(0, "msg") is the same as Info("msg"). 199 | func (l *Logger) InfoDepth(depth int, v ...interface{}) { 200 | l.output(sInfo, depth, fmt.Sprint(v...)) 201 | } 202 | 203 | // Infoln logs with the Info severity. 204 | // Arguments are handled in the manner of fmt.Println. 205 | func (l *Logger) Infoln(v ...interface{}) { 206 | l.output(sInfo, 0, fmt.Sprintln(v...)) 207 | } 208 | 209 | // Infof logs with the Info severity. 210 | // Arguments are handled in the manner of fmt.Printf. 211 | func (l *Logger) Infof(format string, v ...interface{}) { 212 | l.output(sInfo, 0, fmt.Sprintf(format, v...)) 213 | } 214 | 215 | // Warning logs with the Warning severity. 216 | // Arguments are handled in the manner of fmt.Print. 217 | func (l *Logger) Warning(v ...interface{}) { 218 | l.output(sWarning, 0, fmt.Sprint(v...)) 219 | } 220 | 221 | // WarningDepth acts as Warning but uses depth to determine which call frame to log. 222 | // WarningDepth(0, "msg") is the same as Warning("msg"). 223 | func (l *Logger) WarningDepth(depth int, v ...interface{}) { 224 | l.output(sWarning, depth, fmt.Sprint(v...)) 225 | } 226 | 227 | // Warningln logs with the Warning severity. 228 | // Arguments are handled in the manner of fmt.Println. 229 | func (l *Logger) Warningln(v ...interface{}) { 230 | l.output(sWarning, 0, fmt.Sprintln(v...)) 231 | } 232 | 233 | // Warningf logs with the Warning severity. 234 | // Arguments are handled in the manner of fmt.Printf. 235 | func (l *Logger) Warningf(format string, v ...interface{}) { 236 | l.output(sWarning, 0, fmt.Sprintf(format, v...)) 237 | } 238 | 239 | // Error logs with the ERROR severity. 240 | // Arguments are handled in the manner of fmt.Print. 241 | func (l *Logger) Error(v ...interface{}) { 242 | l.output(sError, 0, fmt.Sprint(v...)) 243 | } 244 | 245 | // ErrorDepth acts as Error but uses depth to determine which call frame to log. 246 | // ErrorDepth(0, "msg") is the same as Error("msg"). 247 | func (l *Logger) ErrorDepth(depth int, v ...interface{}) { 248 | l.output(sError, depth, fmt.Sprint(v...)) 249 | } 250 | 251 | // Errorln logs with the ERROR severity. 252 | // Arguments are handled in the manner of fmt.Println. 253 | func (l *Logger) Errorln(v ...interface{}) { 254 | l.output(sError, 0, fmt.Sprintln(v...)) 255 | } 256 | 257 | // Errorf logs with the Error severity. 258 | // Arguments are handled in the manner of fmt.Printf. 259 | func (l *Logger) Errorf(format string, v ...interface{}) { 260 | l.output(sError, 0, fmt.Sprintf(format, v...)) 261 | } 262 | 263 | // Fatal logs with the Fatal severity, and ends with os.Exit(1). 264 | // Arguments are handled in the manner of fmt.Print. 265 | func (l *Logger) Fatal(v ...interface{}) { 266 | l.output(sFatal, 0, fmt.Sprint(v...)) 267 | l.Close() 268 | os.Exit(1) 269 | } 270 | 271 | // FatalDepth acts as Fatal but uses depth to determine which call frame to log. 272 | // FatalDepth(0, "msg") is the same as Fatal("msg"). 273 | func (l *Logger) FatalDepth(depth int, v ...interface{}) { 274 | l.output(sFatal, depth, fmt.Sprint(v...)) 275 | l.Close() 276 | os.Exit(1) 277 | } 278 | 279 | // Fatalln logs with the Fatal severity, and ends with os.Exit(1). 280 | // Arguments are handled in the manner of fmt.Println. 281 | func (l *Logger) Fatalln(v ...interface{}) { 282 | l.output(sFatal, 0, fmt.Sprintln(v...)) 283 | l.Close() 284 | os.Exit(1) 285 | } 286 | 287 | // Fatalf logs with the Fatal severity, and ends with os.Exit(1). 288 | // Arguments are handled in the manner of fmt.Printf. 289 | func (l *Logger) Fatalf(format string, v ...interface{}) { 290 | l.output(sFatal, 0, fmt.Sprintf(format, v...)) 291 | l.Close() 292 | os.Exit(1) 293 | } 294 | 295 | // SetLevel sets the logger verbosity level for verbose info logging. 296 | func (l *Logger) SetLevel(lvl Level) { 297 | l.level = lvl 298 | l.output(sInfo, 0, fmt.Sprintf("Info verbosity set to %d", lvl)) 299 | } 300 | 301 | // V generates a log record depends on the setting of the Level; or none default. 302 | // It uses the specified logger. 303 | func (l *Logger) V(lvl Level) Verbose { 304 | return Verbose{ 305 | enabled: l.level >= lvl, 306 | logger: l, 307 | } 308 | } 309 | 310 | // Info is equivalent to the global Info function, guarded by the value of v. 311 | func (v Verbose) Info(args ...interface{}) { 312 | if v.enabled { 313 | v.logger.output(sInfo, 0, fmt.Sprint(args...)) 314 | } 315 | } 316 | 317 | // Infoln is equivalent to the global Infoln function, guarded by the value of v. 318 | // See the documentation of V for usage. 319 | func (v Verbose) Infoln(args ...interface{}) { 320 | if v.enabled { 321 | v.logger.output(sInfo, 0, fmt.Sprintln(args...)) 322 | } 323 | } 324 | 325 | // Infof is equivalent to the global Infof function, guarded by the value of v. 326 | // See the documentation of V for usage. 327 | func (v Verbose) Infof(format string, args ...interface{}) { 328 | if v.enabled { 329 | v.logger.output(sInfo, 0, fmt.Sprintf(format, args...)) 330 | } 331 | } 332 | 333 | // SetFlags sets the output flags for the logger. 334 | func SetFlags(flag int) { 335 | defaultLogger.infoLog.SetFlags(flag) 336 | defaultLogger.warningLog.SetFlags(flag) 337 | defaultLogger.errorLog.SetFlags(flag) 338 | defaultLogger.fatalLog.SetFlags(flag) 339 | } 340 | 341 | // SetLevel sets the verbosity level for verbose info logging in the 342 | // default logger. 343 | func SetLevel(lvl Level) { 344 | defaultLogger.SetLevel(lvl) 345 | } 346 | 347 | // V generates a log record depends on the setting of the Level or none 348 | // by default using the default logger. 349 | func V(lvl Level) Verbose { 350 | return defaultLogger.V(lvl) 351 | } 352 | 353 | // Info uses the default logger and logs with the Info severity. 354 | // Arguments are handled in the manner of fmt.Print. 355 | func Info(v ...interface{}) { 356 | defaultLogger.output(sInfo, 0, fmt.Sprint(v...)) 357 | } 358 | 359 | // InfoDepth acts as Info but uses depth to determine which call frame to log. 360 | // InfoDepth(0, "msg") is the same as Info("msg"). 361 | func InfoDepth(depth int, v ...interface{}) { 362 | defaultLogger.output(sInfo, depth, fmt.Sprint(v...)) 363 | } 364 | 365 | // Infoln uses the default logger and logs with the Info severity. 366 | // Arguments are handled in the manner of fmt.Println. 367 | func Infoln(v ...interface{}) { 368 | defaultLogger.output(sInfo, 0, fmt.Sprintln(v...)) 369 | } 370 | 371 | // Infof uses the default logger and logs with the Info severity. 372 | // Arguments are handled in the manner of fmt.Printf. 373 | func Infof(format string, v ...interface{}) { 374 | defaultLogger.output(sInfo, 0, fmt.Sprintf(format, v...)) 375 | } 376 | 377 | // Warning uses the default logger and logs with the Warning severity. 378 | // Arguments are handled in the manner of fmt.Print. 379 | func Warning(v ...interface{}) { 380 | defaultLogger.output(sWarning, 0, fmt.Sprint(v...)) 381 | } 382 | 383 | // WarningDepth acts as Warning but uses depth to determine which call frame to log. 384 | // WarningDepth(0, "msg") is the same as Warning("msg"). 385 | func WarningDepth(depth int, v ...interface{}) { 386 | defaultLogger.output(sWarning, depth, fmt.Sprint(v...)) 387 | } 388 | 389 | // Warningln uses the default logger and logs with the Warning severity. 390 | // Arguments are handled in the manner of fmt.Println. 391 | func Warningln(v ...interface{}) { 392 | defaultLogger.output(sWarning, 0, fmt.Sprintln(v...)) 393 | } 394 | 395 | // Warningf uses the default logger and logs with the Warning severity. 396 | // Arguments are handled in the manner of fmt.Printf. 397 | func Warningf(format string, v ...interface{}) { 398 | defaultLogger.output(sWarning, 0, fmt.Sprintf(format, v...)) 399 | } 400 | 401 | // Error uses the default logger and logs with the Error severity. 402 | // Arguments are handled in the manner of fmt.Print. 403 | func Error(v ...interface{}) { 404 | defaultLogger.output(sError, 0, fmt.Sprint(v...)) 405 | } 406 | 407 | // ErrorDepth acts as Error but uses depth to determine which call frame to log. 408 | // ErrorDepth(0, "msg") is the same as Error("msg"). 409 | func ErrorDepth(depth int, v ...interface{}) { 410 | defaultLogger.output(sError, depth, fmt.Sprint(v...)) 411 | } 412 | 413 | // Errorln uses the default logger and logs with the Error severity. 414 | // Arguments are handled in the manner of fmt.Println. 415 | func Errorln(v ...interface{}) { 416 | defaultLogger.output(sError, 0, fmt.Sprintln(v...)) 417 | } 418 | 419 | // Errorf uses the default logger and logs with the Error severity. 420 | // Arguments are handled in the manner of fmt.Printf. 421 | func Errorf(format string, v ...interface{}) { 422 | defaultLogger.output(sError, 0, fmt.Sprintf(format, v...)) 423 | } 424 | 425 | // Fatal uses the default logger, logs with the Fatal severity, 426 | // and ends with os.Exit(1). 427 | // Arguments are handled in the manner of fmt.Print. 428 | func Fatal(v ...interface{}) { 429 | defaultLogger.output(sFatal, 0, fmt.Sprint(v...)) 430 | defaultLogger.Close() 431 | os.Exit(1) 432 | } 433 | 434 | // FatalDepth acts as Fatal but uses depth to determine which call frame to log. 435 | // FatalDepth(0, "msg") is the same as Fatal("msg"). 436 | func FatalDepth(depth int, v ...interface{}) { 437 | defaultLogger.output(sFatal, depth, fmt.Sprint(v...)) 438 | defaultLogger.Close() 439 | os.Exit(1) 440 | } 441 | 442 | // Fatalln uses the default logger, logs with the Fatal severity, 443 | // and ends with os.Exit(1). 444 | // Arguments are handled in the manner of fmt.Println. 445 | func Fatalln(v ...interface{}) { 446 | defaultLogger.output(sFatal, 0, fmt.Sprintln(v...)) 447 | defaultLogger.Close() 448 | os.Exit(1) 449 | } 450 | 451 | // Fatalf uses the default logger, logs with the Fatal severity, 452 | // and ends with os.Exit(1). 453 | // Arguments are handled in the manner of fmt.Printf. 454 | func Fatalf(format string, v ...interface{}) { 455 | defaultLogger.output(sFatal, 0, fmt.Sprintf(format, v...)) 456 | defaultLogger.Close() 457 | os.Exit(1) 458 | } 459 | --------------------------------------------------------------------------------