├── .gitignore ├── LICENSE ├── README.md ├── README_CN.md ├── _example ├── api.go ├── console.go ├── file.go └── images │ └── console.png ├── api.go ├── benchmark_test.go ├── config.go ├── console.go ├── console_test.go ├── file.go ├── file_test.go ├── go.mod ├── go.sum ├── logger.go ├── logger_easyjson.go ├── logger_test.go └── utils ├── file.go └── misc.go /.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | *.exe 3 | *.exe~ 4 | *.log 5 | *.test 6 | *.profile 7 | *.log -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 phachon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-logger 2 | A simple but powerful golang log Toolkit 3 | 4 | [![Sourcegraph](https://sourcegraph.com/github.com/phachon/go-logger/-/badge.svg)](https://sourcegraph.com/github.com/phachon/go-logger?badge) 5 | [![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/phachon/go-logger) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/phachon/go-logger/master/LICENSE) 6 | 7 | [中文文档](/README_CN.md) 8 | 9 | # Feature 10 | - Support at the same time to console, file, URL 11 | - console output fonts can be colored with 12 | - File output supports three types of segmentation based on the size of the file, the number of file lines, and the date. 13 | - file output support is saved to different files at the log level. 14 | - Two ways of writing to support asynchronous and synchronous 15 | - Support json format output 16 | - The code is designed to be extensible, and you can design your own adapter as needed 17 | 18 | # Install 19 | 20 | ``` 21 | go get github.com/phachon/go-logger 22 | go get ./... 23 | ``` 24 | 25 | # Requirement 26 | go 1.8 27 | 28 | # Support outputs 29 | - console // write console 30 | - file // write file 31 | - api // http request url 32 | - ... 33 | 34 | 35 | # Quick Used 36 | 37 | - sync 38 | 39 | ``` 40 | import ( 41 | "github.com/phachon/go-logger" 42 | ) 43 | func main() { 44 | logger := go_logger.NewLogger() 45 | 46 | logger.Info("this is a info log!") 47 | logger.Errorf("this is a error %s log!", "format") 48 | } 49 | ``` 50 | 51 | - async 52 | 53 | ``` 54 | import ( 55 | "github.com/phachon/go-logger" 56 | ) 57 | func main() { 58 | logger := go_logger.NewLogger() 59 | logger.SetAsync() 60 | 61 | logger.Info("this is a info log!") 62 | logger.Errorf("this is a error %s log!", "format") 63 | 64 | // Flush must be called before the end of the process 65 | logger.Flush() 66 | } 67 | ``` 68 | 69 | - Multiple output 70 | 71 | ``` 72 | import ( 73 | "github.com/phachon/go-logger" 74 | ) 75 | func main() { 76 | logger := go_logger.NewLogger() 77 | 78 | logger.Detach("console") 79 | 80 | // console adapter config 81 | consoleConfig := &go_logger.ConsoleConfig{ 82 | Color: true, // Does the text display the color 83 | JsonFormat: true, // Whether or not formatted into a JSON string 84 | Format: "", // JsonFormat is false, logger message output to console format string 85 | } 86 | // add output to the console 87 | logger.Attach("console", go_logger.LOGGER_LEVEL_DEBUG, consoleConfig) 88 | 89 | // file adapter config 90 | fileConfig := &go_logger.FileConfig { 91 | Filename : "./test.log", // The file name of the logger output, does not exist automatically 92 | // If you want to separate separate logs into files, configure LevelFileName parameters. 93 | LevelFileName : map[int]string { 94 | logger.LoggerLevel("error"): "./error.log", // The error level log is written to the error.log file. 95 | logger.LoggerLevel("info"): "./info.log", // The info level log is written to the info.log file. 96 | logger.LoggerLevel("debug"): "./debug.log", // The debug level log is written to the debug.log file. 97 | }, 98 | MaxSize : 1024 * 1024, // File maximum (KB), default 0 is not limited 99 | MaxLine : 100000, // The maximum number of lines in the file, the default 0 is not limited 100 | DateSlice : "d", // Cut the document by date, support "Y" (year), "m" (month), "d" (day), "H" (hour), default "no". 101 | JsonFormat: true, // Whether the file data is written to JSON formatting 102 | Format: "", // JsonFormat is false, logger message written to file format string 103 | } 104 | // add output to the file 105 | logger.Attach("file", go_logger.LOGGER_LEVEL_DEBUG, fileConfig) 106 | 107 | 108 | logger.Info("this is a info log!") 109 | logger.Errorf("this is a error %s log!", "format") 110 | } 111 | ``` 112 | 113 | ## Console text with color effect 114 | ![image](https://github.com/phachon/go-logger/blob/master/_example/images/console.png) 115 | 116 | ## Customize Format output 117 | 118 | ### Logger Message 119 | 120 | | Field | Alias |Type | Comment | Example | 121 | |-------|-------|------|---------|----------| 122 | | Timestamp | timestamp | int64 | unix timestamp| 1521791201 | 123 | | TimestampFormat | timestamp_format| string | timestamp format | 2018-3-23 15:46:41| 124 | | Millisecond | millisecond | int64 | millisecond | 1524472688352 | 125 | | MillisecondFormat | millisecond_format| string | millisecond_format | 2018-3-23 15:46:41.970 | 126 | | Level | level| int | logger level | 1 | 127 | | LevelString | level_string | string | logger level string | Error | 128 | | Body | body | string | logger message body | this is a info log | 129 | | File | file | string | Call the file of the logger | main.go | 130 | | Line | line | int | The number of specific lines to call logger |64| 131 | | Function | function| string | The function name to call logger | main.main | 132 | 133 | >> If you want to customize the format of the log output ? 134 | 135 | **config format**: 136 | ``` 137 | consoleConfig := &go_logger.ConsoleConfig{ 138 | Format: "%millisecond_format% [%level_string%] %body%", 139 | } 140 | fileConfig := &go_logger.FileConfig{ 141 | Format: "%millisecond_format% [%level_string%] %body%", 142 | } 143 | ``` 144 | **output**: 145 | ``` 146 | 2018-03-23 14:55:07.003 [Critical] this is a critical log! 147 | ``` 148 | 149 | >> You can customize the format, Only needs to be satisfied Format: "%Logger Message Alias%" 150 | 151 | ## More adapter examples 152 | - [console](./_example/console.go) 153 | - [file](./_example/file.go) 154 | - [api](./_example/api.go) 155 | 156 | 157 | ## Benchmark 158 | 159 | system: Linux Mint 18.2 Sonya 160 | cpu(s): 4 161 | model name: Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz 162 | memery: 4G 163 | 164 | ``` 165 | BenchmarkLoggerConsoleText 500000 11375 ns/op 672 B/op 15 allocs/op 166 | BenchmarkLoggerConsoleText-2 500000 11345 ns/op 672 B/op 15 allocs/op 167 | BenchmarkLoggerConsoleText-4 500000 9897 ns/op 672 B/op 15 allocs/op 168 | ``` 169 | 170 | ``` 171 | BenchmarkLoggerConsoleAsyncText 500000 9323 ns/op 672 B/op 15 allocs/op 172 | BenchmarkLoggerConsoleAsyncText-2 500000 9087 ns/op 672 B/op 15 allocs/op 173 | BenchmarkLoggerConsoleAsyncText-4 500000 10685 ns/op 672 B/op 15 allocs/op 174 | ``` 175 | 176 | ``` 177 | BenchmarkLoggerConsoleJson 200000 30918 ns/op 2048 B/op 10 allocs/op 178 | BenchmarkLoggerConsoleJson-2 200000 33153 ns/op 2048 B/op 10 allocs/op 179 | BenchmarkLoggerConsoleJson-4 200000 30918 ns/op 2048 B/op 10 allocs/op 180 | ``` 181 | 182 | ``` 183 | BenchmarkLoggerFileText 300000 14083 ns/op 912 B/op 21 allocs/op 184 | BenchmarkLoggerFileText-2 200000 21159 ns/op 912 B/op 21 allocs/op 185 | BenchmarkLoggerFileText-4 200000 23776 ns/op 912 B/op 21 allocs/op 186 | ``` 187 | 188 | ``` 189 | BenchmarkLoggerFileAsyncText 300000 13956 ns/op 912 B/op 21 allocs/op 190 | BenchmarkLoggerFileAsyncText-2 300000 16124 ns/op 912 B/op 21 allocs/op 191 | BenchmarkLoggerFileAsyncText-4 300000 18641 ns/op 912 B/op 21 allocs/op 192 | ``` 193 | 194 | ``` 195 | BenchmarkLoggerFileJson 200000 15472 ns/op 1968 B/op 15 allocs/op 196 | BenchmarkLoggerFileJson-2 200000 22523 ns/op 1968 B/op 15 allocs/op 197 | BenchmarkLoggerFileJson-4 200000 25596 ns/op 1968 B/op 15 allocs/op 198 | ``` 199 | 200 | ## Reference 201 | beego/logs : github.com/astaxie/beego/logs 202 | 203 | ## Feedback 204 | 205 | Welcome to submit comments and code, contact information phachon@163.com 206 | 207 | ## License 208 | 209 | MIT 210 | 211 | Thanks 212 | --------- 213 | Create By phachon@163.com 214 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # go-logger 2 | 一个简单而强大的 golang 日志工具包 3 | 4 | [![Sourcegraph](https://sourcegraph.com/github.com/phachon/go-logger/-/badge.svg)](https://sourcegraph.com/github.com/phachon/go-logger?badge) 5 | [![godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/phachon/go-logger) [![license](http://img.shields.io/badge/license-MIT-red.svg?style=flat)](https://raw.githubusercontent.com/phachon/go-logger/master/LICENSE) 6 | 7 | [English document](/README.md) 8 | 9 | # 功能 10 | - 支持同时输出到 console, file, url 11 | - 命令行输出字体可带颜色 12 | - 文件输出支持根据 文件大小,文件行数,日期三种方式切分 13 | - 文件输出支持根据日志级别分别保存到不同的文件 14 | - 支持异步和同步两种方式写入 15 | - 支持 json 格式化输出 16 | - 代码设计易扩展,可根据需要设计自己的 adapter 17 | 18 | # 安装使用 19 | 20 | ``` 21 | go get github.com/phachon/go-logger 22 | go get ./... 23 | ``` 24 | # 环境需要 25 | go 1.8 26 | 27 | # 支持输出 28 | - console // 输出到命令行 29 | - file // 文件 30 | - api // http url 接口 31 | - ... 32 | 33 | # 快速使用 34 | 35 | - 同步方式 36 | 37 | ``` 38 | import ( 39 | "github.com/phachon/go-logger" 40 | ) 41 | func main() { 42 | logger := go_logger.NewLogger() 43 | 44 | logger.Info("this is a info log!") 45 | logger.Errorf("this is a error %s log!", "format") 46 | } 47 | ``` 48 | 49 | - 异步方式 50 | 51 | ``` 52 | import ( 53 | "github.com/phachon/go-logger" 54 | ) 55 | func main() { 56 | logger := go_logger.NewLogger() 57 | logger.SetAsync() 58 | 59 | logger.Info("this is a info log!") 60 | logger.Errorf("this is a error %s log!", "format") 61 | 62 | // 程序结束前必须调用 Flush 63 | logger.Flush() 64 | } 65 | ``` 66 | 67 | - 多个输出 68 | 69 | ``` 70 | import ( 71 | "github.com/phachon/go-logger" 72 | ) 73 | func main() { 74 | logger := go_logger.NewLogger() 75 | 76 | logger.Detach("console") 77 | 78 | // 命令行输出配置 79 | consoleConfig := &go_logger.ConsoleConfig{ 80 | Color: true, // 命令行输出字符串是否显示颜色 81 | JsonFormat: true, // 命令行输出字符串是否格式化 82 | Format: "", // 如果输出的不是 json 字符串,JsonFormat: false, 自定义输出的格式 83 | } 84 | // 添加 console 为 logger 的一个输出 85 | logger.Attach("console", go_logger.LOGGER_LEVEL_DEBUG, consoleConfig) 86 | 87 | // 文件输出配置 88 | fileConfig := &go_logger.FileConfig { 89 | Filename : "./test.log", // 日志输出文件名,不自动存在 90 | // 如果要将单独的日志分离为文件,请配置LealFrimeNem参数。 91 | LevelFileName : map[int]string { 92 | logger.LoggerLevel("error"): "./error.log", // Error 级别日志被写入 error .log 文件 93 | logger.LoggerLevel("info"): "./info.log", // Info 级别日志被写入到 info.log 文件中 94 | logger.LoggerLevel("debug"): "./debug.log", // Debug 级别日志被写入到 debug.log 文件中 95 | }, 96 | MaxSize : 1024 * 1024, // 文件最大值(KB),默认值0不限 97 | MaxLine : 100000, // 文件最大行数,默认 0 不限制 98 | DateSlice : "d", // 文件根据日期切分, 支持 "Y" (年), "m" (月), "d" (日), "H" (时), 默认 "no", 不切分 99 | JsonFormat: true, // 写入文件的数据是否 json 格式化 100 | Format: "", // 如果写入文件的数据不 json 格式化,自定义日志格式 101 | } 102 | // 添加 file 为 logger 的一个输出 103 | logger.Attach("file", go_logger.LOGGER_LEVEL_DEBUG, fileConfig) 104 | 105 | 106 | logger.Info("this is a info log!") 107 | logger.Errorf("this is a error %s log!", "format") 108 | } 109 | ``` 110 | 111 | ## 命令行下的文本带颜色效果 112 | ![image](https://github.com/phachon/go-logger/blob/master/_example/images/console.png) 113 | 114 | ## 自定义格式化输出 115 | 116 | Logger Message 117 | 118 | | 字段 | 别名 |类型 | 说明 | 例子 | 119 | |-------|-------|------|---------|----------| 120 | | Timestamp | timestamp | int64 | Unix时间戳 | 1521791201 | 121 | | TimestampFormat | timestamp_format| string | 时间戳格式化字符串 | 2018-3-23 15:46:41| 122 | | Millisecond | millisecond | int64 | 毫秒时间戳 | 1524472688352 | 123 | | MillisecondFormat | millisecond_format| string | 毫秒时间戳格式化字符串 | 2018-3-23 15:46:41.970 | 124 | | Level | level| int | 日志级别 | 1 | 125 | | LevelString | level_string | string | 日志级别字符串 | Error | 126 | | Body | body | string | 日志内容 | this is a info log | 127 | | File | file | string | 调用本次日志输出的文件名 | main.go | 128 | | Line | line | int | 调用本次日志输出的方法 |64| 129 | | Function | function| string | 调用本次日志输出的方法名 | main.main | 130 | 131 | >> 你想要自定义日志输出格式 ? 132 | 133 | **配置 Format 参数**: 134 | ``` 135 | consoleConfig := &go_logger.ConsoleConfig{ 136 | Format: "%millisecond_format% [%level_string%] %body%", 137 | } 138 | fileConfig := &go_logger.FileConfig{ 139 | Format: "%millisecond_format% [%level_string%] %body%", 140 | } 141 | ``` 142 | **输出结果**: 143 | ``` 144 | 2018-03-23 14:55:07.003 [Critical] this is a critical log! 145 | ``` 146 | 147 | >> 你只需要配置参数 Format: "% Logger Message 别名%" 来自定义输出字符串格式 148 | 149 | ## 更多的 adapter 例子 150 | - [console](./_example/console.go) 151 | - [file](./_example/file.go) 152 | - [api](./_example/api.go) 153 | 154 | 155 | ## 性能测试结果 156 | 157 | system: Linux Mint 18.2 Sonya 158 | cpu(s): 4 159 | model name: Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz 160 | memery: 4G 161 | 162 | ``` 163 | BenchmarkLoggerConsoleText 500000 11375 ns/op 672 B/op 15 allocs/op 164 | BenchmarkLoggerConsoleText-2 500000 11345 ns/op 672 B/op 15 allocs/op 165 | BenchmarkLoggerConsoleText-4 500000 9897 ns/op 672 B/op 15 allocs/op 166 | ``` 167 | 168 | ``` 169 | BenchmarkLoggerConsoleAsyncText 500000 9323 ns/op 672 B/op 15 allocs/op 170 | BenchmarkLoggerConsoleAsyncText-2 500000 9087 ns/op 672 B/op 15 allocs/op 171 | BenchmarkLoggerConsoleAsyncText-4 500000 10685 ns/op 672 B/op 15 allocs/op 172 | ``` 173 | 174 | ``` 175 | BenchmarkLoggerConsoleJson 200000 30918 ns/op 2048 B/op 10 allocs/op 176 | BenchmarkLoggerConsoleJson-2 200000 33153 ns/op 2048 B/op 10 allocs/op 177 | BenchmarkLoggerConsoleJson-4 200000 30918 ns/op 2048 B/op 10 allocs/op 178 | ``` 179 | 180 | ``` 181 | BenchmarkLoggerFileText 300000 14083 ns/op 912 B/op 21 allocs/op 182 | BenchmarkLoggerFileText-2 200000 21159 ns/op 912 B/op 21 allocs/op 183 | BenchmarkLoggerFileText-4 200000 23776 ns/op 912 B/op 21 allocs/op 184 | ``` 185 | 186 | ``` 187 | BenchmarkLoggerFileAsyncText 300000 13956 ns/op 912 B/op 21 allocs/op 188 | BenchmarkLoggerFileAsyncText-2 300000 16124 ns/op 912 B/op 21 allocs/op 189 | BenchmarkLoggerFileAsyncText-4 300000 18641 ns/op 912 B/op 21 allocs/op 190 | ``` 191 | 192 | ``` 193 | BenchmarkLoggerFileJson 200000 15472 ns/op 1968 B/op 15 allocs/op 194 | BenchmarkLoggerFileJson-2 200000 22523 ns/op 1968 B/op 15 allocs/op 195 | BenchmarkLoggerFileJson-4 200000 25596 ns/op 1968 B/op 15 allocs/op 196 | ``` 197 | 198 | ## 参考 199 | beego/logs : github.com/astaxie/beego/logs 200 | 201 | ## 反馈 202 | 203 | 欢迎提交意见和代码,联系信息 phachon@163.com 204 | 205 | ## License 206 | 207 | MIT 208 | 209 | 谢谢 210 | --- 211 | Create By phachon@163.com 212 | -------------------------------------------------------------------------------- /_example/api.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/phachon/go-logger" 5 | ) 6 | 7 | func main() { 8 | 9 | logger := go_logger.NewLogger() 10 | 11 | apiConfig := &go_logger.ApiConfig{ 12 | Url: "http://127.0.0.1:8081/index.php", 13 | Method: "GET", 14 | Headers: map[string]string{}, 15 | IsVerify: false, 16 | VerifyCode: 0, 17 | } 18 | logger.Attach("api", go_logger.LOGGER_LEVEL_DEBUG, apiConfig) 19 | logger.SetAsync() 20 | 21 | logger.Emergency("this is a emergency log!") 22 | logger.Alert("this is a alert log!") 23 | 24 | logger.Flush() 25 | } 26 | -------------------------------------------------------------------------------- /_example/console.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/phachon/go-logger" 5 | ) 6 | 7 | func main() { 8 | 9 | logger := go_logger.NewLogger() 10 | //default attach console, detach console 11 | logger.Detach("console") 12 | 13 | consoleConfig := &go_logger.ConsoleConfig{ 14 | Color: true, 15 | JsonFormat: false, 16 | Format: "%millisecond_format% [%level_string%] [%file%:%line%] %body%", 17 | } 18 | 19 | logger.Attach("console", go_logger.LOGGER_LEVEL_DEBUG, consoleConfig) 20 | 21 | logger.SetAsync() 22 | 23 | logger.Emergency("this is a emergency log!") 24 | logger.Alert("this is a alert log!") 25 | logger.Critical("this is a critical log!") 26 | logger.Error("this is a error log!") 27 | logger.Warning("this is a warning log!") 28 | logger.Notice("this is a notice log!") 29 | logger.Info("this is a info log!") 30 | logger.Debug("this is a debug log!") 31 | 32 | logger.Emergency("this is a emergency log!") 33 | logger.Notice("this is a notice log!") 34 | logger.Info("this is a info log!") 35 | logger.Debug("this is a debug log!") 36 | 37 | logger.Emergencyf("this is a emergency %d log!", 10) 38 | logger.Alertf("this is a alert %s log!", "format") 39 | logger.Criticalf("this is a critical %s log!", "format") 40 | logger.Errorf("this is a error %s log!", "format") 41 | logger.Warningf("this is a warning %s log!", "format") 42 | logger.Noticef("this is a notice %s log!", "format") 43 | logger.Infof("this is a info %s log!", "format") 44 | logger.Debugf("this is a debug %s log!", "format") 45 | 46 | logger.Flush() 47 | } 48 | -------------------------------------------------------------------------------- /_example/file.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/phachon/go-logger" 5 | ) 6 | 7 | func main() { 8 | 9 | logger := go_logger.NewLogger() 10 | 11 | fileConfig := &go_logger.FileConfig{ 12 | Filename: "./test.log", 13 | LevelFileName: map[int]string{ 14 | logger.LoggerLevel("error"): "./error.log", 15 | logger.LoggerLevel("info"): "./info.log", 16 | logger.LoggerLevel("debug"): "./debug.log", 17 | }, 18 | MaxSize: 1024 * 1024, 19 | MaxLine: 10000, 20 | DateSlice: "d", 21 | JsonFormat: false, 22 | Format: "%millisecond_format% [%level_string%] [%file%:%line%] %body%", 23 | } 24 | logger.Attach("file", go_logger.LOGGER_LEVEL_DEBUG, fileConfig) 25 | logger.SetAsync() 26 | 27 | i := 0 28 | for { 29 | logger.Emergency("this is a emergency log!") 30 | logger.Alert("this is a alert log!") 31 | logger.Critical("this is a critical log!") 32 | logger.Error("this is a error log!") 33 | logger.Warning("this is a warning log!") 34 | logger.Notice("this is a notice log!") 35 | logger.Info("this is a info log!") 36 | logger.Debug("this is a debug log!") 37 | 38 | logger.Emergency("this is a emergency log!") 39 | logger.Notice("this is a notice log!") 40 | logger.Info("this is a info log!") 41 | logger.Debug("this is a debug log!") 42 | 43 | logger.Emergency("this is a emergency log!") 44 | logger.Alert("this is a alert log!") 45 | logger.Critical("this is a critical log!") 46 | 47 | i += 1 48 | if i == 21000 { 49 | break 50 | } 51 | } 52 | 53 | logger.Flush() 54 | } 55 | -------------------------------------------------------------------------------- /_example/images/console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phachon/go-logger/86e4227f71eaece5a46ca0f6d59b78af81ad34f2/_example/images/console.png -------------------------------------------------------------------------------- /api.go: -------------------------------------------------------------------------------- 1 | package go_logger 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/phachon/go-logger/utils" 7 | "reflect" 8 | "strconv" 9 | ) 10 | 11 | const API_ADAPTER_NAME = "api" 12 | 13 | // adapter api 14 | type AdapterApi struct { 15 | config *ApiConfig 16 | } 17 | 18 | // api config 19 | type ApiConfig struct { 20 | 21 | // request url adddress 22 | Url string 23 | 24 | // request method 25 | // GET, POST 26 | Method string 27 | 28 | // request headers 29 | Headers map[string]string 30 | 31 | // is verify response code 32 | IsVerify bool 33 | 34 | // verify response http code 35 | VerifyCode int 36 | } 37 | 38 | func (ac *ApiConfig) Name() string { 39 | return API_ADAPTER_NAME 40 | } 41 | 42 | func NewAdapterApi() LoggerAbstract { 43 | return &AdapterApi{} 44 | } 45 | 46 | func (adapterApi *AdapterApi) Init(apiConfig Config) error { 47 | 48 | if apiConfig.Name() != API_ADAPTER_NAME { 49 | return errors.New("logger api adapter init error, config must ApiConfig") 50 | } 51 | 52 | vc := reflect.ValueOf(apiConfig) 53 | ac := vc.Interface().(*ApiConfig) 54 | adapterApi.config = ac 55 | 56 | if adapterApi.config.Url == "" { 57 | return errors.New("config Url cannot be empty!") 58 | } 59 | if adapterApi.config.Method != "GET" && adapterApi.config.Method != "POST" { 60 | return errors.New("config Method must one of the 'GET', 'POST'!") 61 | } 62 | if adapterApi.config.IsVerify && (adapterApi.config.VerifyCode == 0) { 63 | return errors.New("config if IsVerify is true, VerifyCode cannot be 0!") 64 | } 65 | return nil 66 | } 67 | 68 | func (adapterApi *AdapterApi) Write(loggerMsg *loggerMessage) error { 69 | 70 | url := adapterApi.config.Url 71 | method := adapterApi.config.Method 72 | isVerify := adapterApi.config.IsVerify 73 | verifyCode := adapterApi.config.VerifyCode 74 | headers := adapterApi.config.Headers 75 | 76 | loggerMap := map[string]string{ 77 | "timestamp": strconv.FormatInt(loggerMsg.Timestamp, 10), 78 | "timestamp_format": loggerMsg.TimestampFormat, 79 | "millisecond": strconv.FormatInt(loggerMsg.Millisecond, 10), 80 | "millisecond_format": loggerMsg.MillisecondFormat, 81 | "level": strconv.Itoa(loggerMsg.Level), 82 | "level_string": loggerMsg.LevelString, 83 | "body": loggerMsg.Body, 84 | "file": loggerMsg.File, 85 | "line": strconv.Itoa(loggerMsg.Line), 86 | "function": loggerMsg.Function, 87 | } 88 | 89 | var err error 90 | var code int 91 | if method == "GET" { 92 | _, code, err = utils.NewMisc().HttpGet(url, loggerMap, headers, 0) 93 | } else { 94 | _, code, err = utils.NewMisc().HttpPost(url, loggerMap, headers, 0) 95 | } 96 | if err != nil { 97 | return err 98 | } 99 | if isVerify && (code != verifyCode) { 100 | return fmt.Errorf("%s", "request "+url+" faild, code="+strconv.Itoa(code)) 101 | } 102 | 103 | return nil 104 | } 105 | 106 | func (adapterApi *AdapterApi) Flush() { 107 | 108 | } 109 | 110 | func (adapterApi *AdapterApi) Name() string { 111 | return API_ADAPTER_NAME 112 | } 113 | 114 | func init() { 115 | Register(API_ADAPTER_NAME, NewAdapterApi) 116 | } 117 | -------------------------------------------------------------------------------- /benchmark_test.go: -------------------------------------------------------------------------------- 1 | package go_logger 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | // go test -run=benchmark -cpu=1,2,4 -benchmem -benchtime=3s -bench="ConsoleText" 8 | func BenchmarkLoggerConsoleText(b *testing.B) { 9 | logger := NewLogger() 10 | b.ResetTimer() 11 | b.RunParallel(func(pb *testing.PB) { 12 | for pb.Next() { 13 | logger.Info("benchmark logger message") 14 | } 15 | }) 16 | } 17 | 18 | // go test -run=benchmark -cpu=1,2,4 -benchmem -benchtime=3s -bench="ConsoleAsyncText" 19 | func BenchmarkLoggerConsoleAsyncText(b *testing.B) { 20 | logger := NewLogger() 21 | logger.SetAsync() 22 | 23 | b.ResetTimer() 24 | b.RunParallel(func(pb *testing.PB) { 25 | for pb.Next() { 26 | logger.Info("benchmark logger message") 27 | } 28 | }) 29 | logger.Flush() 30 | } 31 | 32 | // go test -run=benchmark -cpu=1,2,4 -benchmem -benchtime=3s -bench="ConsoleJson" 33 | func BenchmarkLoggerConsoleJson(b *testing.B) { 34 | logger := NewLogger() 35 | logger.Detach("console") 36 | logger.Attach("console", LOGGER_LEVEL_DEBUG, &ConsoleConfig{ 37 | JsonFormat: true, 38 | }) 39 | b.ResetTimer() 40 | b.RunParallel(func(pb *testing.PB) { 41 | for pb.Next() { 42 | logger.Info("benchmark logger message") 43 | } 44 | }) 45 | } 46 | 47 | // go test -run=benchmark -cpu=1,2,4 -benchmem -benchtime=3s -bench="FileText" 48 | func BenchmarkLoggerFileText(b *testing.B) { 49 | logger := NewLogger() 50 | logger.Detach("console") 51 | logger.Attach("file", LOGGER_LEVEL_DEBUG, &FileConfig{ 52 | Filename: "./test.log", 53 | DateSlice: "d", 54 | }) 55 | b.ResetTimer() 56 | b.RunParallel(func(pb *testing.PB) { 57 | for pb.Next() { 58 | logger.Info("benchmark logger message") 59 | } 60 | }) 61 | } 62 | 63 | // go test -run=benchmark -cpu=1,2,4 -benchmem -benchtime=3s -bench="AsyncText" 64 | func BenchmarkLoggerFileAsyncText(b *testing.B) { 65 | logger := NewLogger() 66 | logger.Detach("console") 67 | logger.Attach("file", LOGGER_LEVEL_DEBUG, &FileConfig{ 68 | Filename: "./test.log", 69 | DateSlice: "d", 70 | }) 71 | logger.SetAsync() 72 | 73 | b.ResetTimer() 74 | b.RunParallel(func(pb *testing.PB) { 75 | for pb.Next() { 76 | logger.Info("benchmark logger message") 77 | } 78 | }) 79 | logger.Flush() 80 | } 81 | 82 | // go test -run=benchmark -cpu=1,2,4 -benchmem -benchtime=3s -bench="FileJson" 83 | func BenchmarkLoggerFileJson(b *testing.B) { 84 | logger := NewLogger() 85 | logger.Detach("console") 86 | logger.Attach("file", LOGGER_LEVEL_DEBUG, &FileConfig{ 87 | Filename: "./test.log", 88 | DateSlice: "d", 89 | JsonFormat: true, 90 | }) 91 | b.ResetTimer() 92 | b.RunParallel(func(pb *testing.PB) { 93 | for pb.Next() { 94 | logger.Info("benchmark logger message") 95 | } 96 | }) 97 | } 98 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | package go_logger 2 | 3 | // logger config interface 4 | type Config interface { 5 | Name() string 6 | } 7 | -------------------------------------------------------------------------------- /console.go: -------------------------------------------------------------------------------- 1 | package go_logger 2 | 3 | import ( 4 | "errors" 5 | "github.com/fatih/color" 6 | "io" 7 | "os" 8 | "reflect" 9 | "sync" 10 | ) 11 | 12 | const CONSOLE_ADAPTER_NAME = "console" 13 | 14 | var levelColors = map[int]color.Attribute{ 15 | LOGGER_LEVEL_EMERGENCY: color.FgWhite, //white 16 | LOGGER_LEVEL_ALERT: color.FgCyan, //cyan 17 | LOGGER_LEVEL_CRITICAL: color.FgMagenta, //magenta 18 | LOGGER_LEVEL_ERROR: color.FgRed, //red 19 | LOGGER_LEVEL_WARNING: color.FgYellow, //yellow 20 | LOGGER_LEVEL_NOTICE: color.FgGreen, //green 21 | LOGGER_LEVEL_INFO: color.FgBlue, //blue 22 | LOGGER_LEVEL_DEBUG: color.BgBlue, //background blue 23 | } 24 | 25 | // adapter console 26 | type AdapterConsole struct { 27 | write *ConsoleWriter 28 | config *ConsoleConfig 29 | } 30 | 31 | // console writer 32 | type ConsoleWriter struct { 33 | lock sync.Mutex 34 | writer io.Writer 35 | } 36 | 37 | // console config 38 | type ConsoleConfig struct { 39 | // console text is show color 40 | Color bool 41 | 42 | // is json format 43 | JsonFormat bool 44 | 45 | // jsonFormat is false, please input format string 46 | // if format is empty, default format "%millisecond_format% [%level_string%] %body%" 47 | // 48 | // Timestamp "%timestamp%" 49 | // TimestampFormat "%timestamp_format%" 50 | // Millisecond "%millisecond%" 51 | // MillisecondFormat "%millisecond_format%" 52 | // Level int "%level%" 53 | // LevelString "%level_string%" 54 | // Body string "%body%" 55 | // File string "%file%" 56 | // Line int "%line%" 57 | // Function "%function%" 58 | // 59 | // example: format = "%millisecond_format% [%level_string%] %body%" 60 | Format string 61 | } 62 | 63 | func (cc *ConsoleConfig) Name() string { 64 | return CONSOLE_ADAPTER_NAME 65 | } 66 | 67 | func NewAdapterConsole() LoggerAbstract { 68 | consoleWrite := &ConsoleWriter{ 69 | writer: os.Stdout, 70 | } 71 | config := &ConsoleConfig{} 72 | return &AdapterConsole{ 73 | write: consoleWrite, 74 | config: config, 75 | } 76 | } 77 | 78 | func (adapterConsole *AdapterConsole) Init(consoleConfig Config) error { 79 | if consoleConfig.Name() != CONSOLE_ADAPTER_NAME { 80 | return errors.New("logger console adapter init error, config must ConsoleConfig") 81 | } 82 | 83 | vc := reflect.ValueOf(consoleConfig) 84 | cc := vc.Interface().(*ConsoleConfig) 85 | adapterConsole.config = cc 86 | 87 | if cc.JsonFormat == false && cc.Format == "" { 88 | cc.Format = defaultLoggerMessageFormat 89 | } 90 | 91 | return nil 92 | } 93 | 94 | func (adapterConsole *AdapterConsole) Write(loggerMsg *loggerMessage) error { 95 | 96 | msg := "" 97 | if adapterConsole.config.JsonFormat == true { 98 | //jsonByte, _ := json.Marshal(loggerMsg) 99 | jsonByte, _ := loggerMsg.MarshalJSON() 100 | msg = string(jsonByte) 101 | } else { 102 | msg = loggerMessageFormat(adapterConsole.config.Format, loggerMsg) 103 | } 104 | consoleWriter := adapterConsole.write 105 | 106 | if adapterConsole.config.Color { 107 | colorAttr := adapterConsole.getColorByLevel(loggerMsg.Level, msg) 108 | consoleWriter.lock.Lock() 109 | color.New(colorAttr).Println(msg) 110 | consoleWriter.lock.Unlock() 111 | return nil 112 | } 113 | 114 | consoleWriter.lock.Lock() 115 | consoleWriter.writer.Write([]byte(msg + "\n")) 116 | consoleWriter.lock.Unlock() 117 | 118 | return nil 119 | } 120 | 121 | func (adapterConsole *AdapterConsole) Name() string { 122 | return CONSOLE_ADAPTER_NAME 123 | } 124 | 125 | func (adapterConsole *AdapterConsole) Flush() { 126 | 127 | } 128 | 129 | func (adapterConsole *AdapterConsole) getColorByLevel(level int, content string) color.Attribute { 130 | lc, ok := levelColors[level] 131 | if !ok { 132 | lc = color.FgWhite 133 | } 134 | return lc 135 | } 136 | 137 | func init() { 138 | Register(CONSOLE_ADAPTER_NAME, NewAdapterConsole) 139 | } 140 | -------------------------------------------------------------------------------- /console_test.go: -------------------------------------------------------------------------------- 1 | package go_logger 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestNewAdapterConsole(t *testing.T) { 9 | NewAdapterConsole() 10 | } 11 | 12 | func TestAdapterConsole_Init(t *testing.T) { 13 | 14 | consoleAdapter := NewAdapterConsole() 15 | 16 | consoleConfig := &ConsoleConfig{} 17 | consoleAdapter.Init(consoleConfig) 18 | } 19 | 20 | func TestAdapterConsole_Name(t *testing.T) { 21 | 22 | consoleAdapter := NewAdapterConsole() 23 | 24 | if consoleAdapter.Name() != CONSOLE_ADAPTER_NAME { 25 | t.Error("consoleAdapter name error") 26 | } 27 | } 28 | 29 | func TestAdapterConsole_WriteColor(t *testing.T) { 30 | 31 | consoleAdapter := NewAdapterConsole() 32 | 33 | consoleConfig := &ConsoleConfig{ 34 | Color: true, 35 | } 36 | consoleAdapter.Init(consoleConfig) 37 | 38 | loggerMsg := &loggerMessage{ 39 | Timestamp: time.Now().Unix(), 40 | TimestampFormat: time.Now().Format("2006-01-02 15:04:05"), 41 | Millisecond: time.Now().UnixNano() / 1e6, 42 | MillisecondFormat: time.Now().Format("2006-01-02 15:04:05.999"), 43 | Level: LOGGER_LEVEL_DEBUG, 44 | LevelString: "debug", 45 | Body: "logger console adapter test color", 46 | File: "console_test.go", 47 | Line: 50, 48 | Function: "TestAdapterConsole_WriteIsColor", 49 | } 50 | err := consoleAdapter.Write(loggerMsg) 51 | if err != nil { 52 | t.Error(err.Error()) 53 | } 54 | } 55 | 56 | func TestAdapterConsole_WriteJsonFormat(t *testing.T) { 57 | 58 | consoleAdapter := NewAdapterConsole() 59 | 60 | consoleConfig := &ConsoleConfig{ 61 | JsonFormat: true, 62 | } 63 | consoleAdapter.Init(consoleConfig) 64 | 65 | loggerMsg := &loggerMessage{ 66 | Timestamp: time.Now().Unix(), 67 | TimestampFormat: time.Now().Format("2006-01-02 15:04:05"), 68 | Millisecond: time.Now().UnixNano() / 1e6, 69 | MillisecondFormat: time.Now().Format("2006-01-02 15:04:05.999"), 70 | Level: LOGGER_LEVEL_DEBUG, 71 | LevelString: "debug", 72 | Body: "logger console adapter test jsonFormat", 73 | File: "console_test.go", 74 | Line: 77, 75 | Function: "TestAdapterConsole_WriteJsonFormat", 76 | } 77 | err := consoleAdapter.Write(loggerMsg) 78 | if err != nil { 79 | t.Error(err.Error()) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /file.go: -------------------------------------------------------------------------------- 1 | package go_logger 2 | 3 | import ( 4 | "errors" 5 | "github.com/phachon/go-logger/utils" 6 | "os" 7 | "path" 8 | "reflect" 9 | "strings" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | const FILE_ADAPTER_NAME = "file" 15 | 16 | const ( 17 | FILE_SLICE_DATE_NULL = "" 18 | FILE_SLICE_DATE_YEAR = "y" 19 | FILE_SLICE_DATE_MONTH = "m" 20 | FILE_SLICE_DATE_DAY = "d" 21 | FILE_SLICE_DATE_HOUR = "h" 22 | ) 23 | 24 | const ( 25 | FILE_ACCESS_LEVEL = 1000 26 | ) 27 | 28 | // adapter file 29 | type AdapterFile struct { 30 | write map[int]*FileWriter 31 | config *FileConfig 32 | } 33 | 34 | // file writer 35 | type FileWriter struct { 36 | lock sync.RWMutex 37 | writer *os.File 38 | startLine int64 39 | startTime int64 40 | filename string 41 | } 42 | 43 | func NewFileWrite(fn string) *FileWriter { 44 | return &FileWriter{ 45 | filename: fn, 46 | } 47 | } 48 | 49 | // file config 50 | type FileConfig struct { 51 | 52 | // log filename 53 | Filename string 54 | 55 | // level log filename 56 | LevelFileName map[int]string 57 | 58 | // max file size 59 | MaxSize int64 60 | 61 | // max file line 62 | MaxLine int64 63 | 64 | // file slice by date 65 | // "y" Log files are cut through year 66 | // "m" Log files are cut through mouth 67 | // "d" Log files are cut through day 68 | // "h" Log files are cut through hour 69 | DateSlice string 70 | 71 | // is json format 72 | JsonFormat bool 73 | 74 | // jsonFormat is false, please input format string 75 | // if format is empty, default format "%millisecond_format% [%level_string%] %body%" 76 | // 77 | // Timestamp "%timestamp%" 78 | // TimestampFormat "%timestamp_format%" 79 | // Millisecond "%millisecond%" 80 | // MillisecondFormat "%millisecond_format%" 81 | // Level int "%level%" 82 | // LevelString "%level_string%" 83 | // Body string "%body%" 84 | // File string "%file%" 85 | // Line int "%line%" 86 | // Function "%function%" 87 | // 88 | // example: format = "%millisecond_format% [%level_string%] %body%" 89 | Format string 90 | } 91 | 92 | func (fc *FileConfig) Name() string { 93 | return FILE_ADAPTER_NAME 94 | } 95 | 96 | var fileSliceDateMapping = map[string]int{ 97 | FILE_SLICE_DATE_YEAR: 0, 98 | FILE_SLICE_DATE_MONTH: 1, 99 | FILE_SLICE_DATE_DAY: 2, 100 | FILE_SLICE_DATE_HOUR: 3, 101 | } 102 | 103 | func NewAdapterFile() LoggerAbstract { 104 | return &AdapterFile{ 105 | write: map[int]*FileWriter{}, 106 | config: &FileConfig{}, 107 | } 108 | } 109 | 110 | // init 111 | func (adapterFile *AdapterFile) Init(fileConfig Config) error { 112 | if fileConfig.Name() != FILE_ADAPTER_NAME { 113 | return errors.New("logger file adapter init error, config must FileConfig") 114 | } 115 | 116 | vc := reflect.ValueOf(fileConfig) 117 | fc := vc.Interface().(*FileConfig) 118 | adapterFile.config = fc 119 | 120 | if fc.JsonFormat == false && fc.Format == "" { 121 | fc.Format = defaultLoggerMessageFormat 122 | } 123 | 124 | if len(adapterFile.config.LevelFileName) == 0 { 125 | if adapterFile.config.Filename == "" { 126 | return errors.New("config Filename can't be empty!") 127 | } 128 | } 129 | _, ok := fileSliceDateMapping[adapterFile.config.DateSlice] 130 | if !ok { 131 | return errors.New("config DateSlice must be one of the 'y', 'd', 'm','h'!") 132 | } 133 | 134 | // init FileWriter 135 | if len(adapterFile.config.LevelFileName) > 0 { 136 | fileWriters := map[int]*FileWriter{} 137 | for level, filename := range adapterFile.config.LevelFileName { 138 | _, ok := levelStringMapping[level] 139 | if !ok { 140 | return errors.New("config LevelFileName key level is illegal!") 141 | } 142 | fw := NewFileWrite(filename) 143 | fw.initFile() 144 | fileWriters[level] = fw 145 | } 146 | adapterFile.write = fileWriters 147 | } 148 | 149 | if adapterFile.config.Filename != "" { 150 | fw := NewFileWrite(adapterFile.config.Filename) 151 | fw.initFile() 152 | adapterFile.write[FILE_ACCESS_LEVEL] = fw 153 | } 154 | 155 | return nil 156 | } 157 | 158 | // Write 159 | func (adapterFile *AdapterFile) Write(loggerMsg *loggerMessage) error { 160 | 161 | var accessChan = make(chan error, 1) 162 | var levelChan = make(chan error, 1) 163 | 164 | // access file write 165 | if adapterFile.config.Filename != "" { 166 | go func() { 167 | accessFileWrite, ok := adapterFile.write[FILE_ACCESS_LEVEL] 168 | if !ok { 169 | accessChan <- nil 170 | return 171 | } 172 | err := accessFileWrite.writeByConfig(adapterFile.config, loggerMsg) 173 | if err != nil { 174 | accessChan <- err 175 | return 176 | } 177 | accessChan <- nil 178 | }() 179 | } 180 | 181 | // level file write 182 | if len(adapterFile.config.LevelFileName) != 0 { 183 | go func() { 184 | fileWrite, ok := adapterFile.write[loggerMsg.Level] 185 | if !ok { 186 | levelChan <- nil 187 | return 188 | } 189 | err := fileWrite.writeByConfig(adapterFile.config, loggerMsg) 190 | if err != nil { 191 | levelChan <- err 192 | return 193 | } 194 | levelChan <- nil 195 | }() 196 | } 197 | 198 | var accessErr error 199 | var levelErr error 200 | if adapterFile.config.Filename != "" { 201 | accessErr = <-accessChan 202 | } 203 | if len(adapterFile.config.LevelFileName) != 0 { 204 | levelErr = <-levelChan 205 | } 206 | if accessErr != nil { 207 | return accessErr.(error) 208 | } 209 | if levelErr != nil { 210 | return levelErr.(error) 211 | } 212 | return nil 213 | } 214 | 215 | // Flush 216 | func (adapterFile *AdapterFile) Flush() { 217 | for _, fileWrite := range adapterFile.write { 218 | fileWrite.writer.Close() 219 | } 220 | } 221 | 222 | // Name 223 | func (adapterFile *AdapterFile) Name() string { 224 | return FILE_ADAPTER_NAME 225 | } 226 | 227 | // init file 228 | func (fw *FileWriter) initFile() error { 229 | 230 | //check file exits, otherwise create a file 231 | ok, _ := utils.UtilFile.PathExists(fw.filename) 232 | if ok == false { 233 | err := utils.UtilFile.CreateFile(fw.filename) 234 | if err != nil { 235 | return err 236 | } 237 | } 238 | 239 | // get start time 240 | fw.startTime = time.Now().Unix() 241 | 242 | // get file start lines 243 | nowLines, err := utils.UtilFile.GetFileLines(fw.filename) 244 | if err != nil { 245 | return err 246 | } 247 | fw.startLine = nowLines 248 | 249 | //get a file pointer 250 | file, err := fw.getFileObject(fw.filename) 251 | if err != nil { 252 | return err 253 | } 254 | fw.writer = file 255 | return nil 256 | } 257 | 258 | // write by config 259 | func (fw *FileWriter) writeByConfig(config *FileConfig, loggerMsg *loggerMessage) error { 260 | 261 | fw.lock.Lock() 262 | defer fw.lock.Unlock() 263 | 264 | if config.DateSlice != "" { 265 | // file slice by date 266 | err := fw.sliceByDate(config.DateSlice) 267 | if err != nil { 268 | return err 269 | } 270 | } 271 | if config.MaxLine != 0 { 272 | // file slice by line 273 | err := fw.sliceByFileLines(config.MaxLine) 274 | if err != nil { 275 | return err 276 | } 277 | } 278 | if config.MaxSize != 0 { 279 | // file slice by size 280 | err := fw.sliceByFileSize(config.MaxSize) 281 | if err != nil { 282 | return err 283 | } 284 | } 285 | 286 | msg := "" 287 | if config.JsonFormat == true { 288 | //jsonByte, _ := json.Marshal(loggerMsg) 289 | jsonByte, _ := loggerMsg.MarshalJSON() 290 | msg = string(jsonByte) + "\r\n" 291 | } else { 292 | msg = loggerMessageFormat(config.Format, loggerMsg) + "\r\n" 293 | } 294 | 295 | fw.writer.Write([]byte(msg)) 296 | if config.MaxLine != 0 { 297 | if config.JsonFormat == true { 298 | fw.startLine += 1 299 | } else { 300 | fw.startLine += int64(strings.Count(msg, "\n")) 301 | } 302 | } 303 | return nil 304 | } 305 | 306 | //slice file by date (y, m, d, h, i, s), rename file is file_time.log and recreate file 307 | func (fw *FileWriter) sliceByDate(dataSlice string) error { 308 | 309 | filename := fw.filename 310 | filenameSuffix := path.Ext(filename) 311 | startTime := time.Unix(fw.startTime, 0) 312 | nowTime := time.Now() 313 | 314 | oldFilename := "" 315 | isHaveSlice := false 316 | if (dataSlice == FILE_SLICE_DATE_YEAR) && 317 | (startTime.Year() != nowTime.Year()) { 318 | isHaveSlice = true 319 | oldFilename = strings.Replace(filename, filenameSuffix, "", 1) + "_" + startTime.Format("2006") + filenameSuffix 320 | } 321 | if (dataSlice == FILE_SLICE_DATE_MONTH) && 322 | (startTime.Format("200601") != nowTime.Format("200601")) { 323 | isHaveSlice = true 324 | oldFilename = strings.Replace(filename, filenameSuffix, "", 1) + "_" + startTime.Format("200601") + filenameSuffix 325 | } 326 | if (dataSlice == FILE_SLICE_DATE_DAY) && 327 | (startTime.Format("20060102") != nowTime.Format("20060102")) { 328 | isHaveSlice = true 329 | oldFilename = strings.Replace(filename, filenameSuffix, "", 1) + "_" + startTime.Format("20060102") + filenameSuffix 330 | } 331 | if (dataSlice == FILE_SLICE_DATE_HOUR) && 332 | (startTime.Format("2006010215") != startTime.Format("2006010215")) { 333 | isHaveSlice = true 334 | oldFilename = strings.Replace(filename, filenameSuffix, "", 1) + "_" + startTime.Format("2006010215") + filenameSuffix 335 | } 336 | 337 | if isHaveSlice == true { 338 | //close file handle 339 | fw.writer.Close() 340 | err := os.Rename(fw.filename, oldFilename) 341 | if err != nil { 342 | return err 343 | } 344 | err = fw.initFile() 345 | if err != nil { 346 | return err 347 | } 348 | } 349 | 350 | return nil 351 | } 352 | 353 | //slice file by line, if maxLine < fileLine, rename file is file_line_maxLine_time.log and recreate file 354 | func (fw *FileWriter) sliceByFileLines(maxLine int64) error { 355 | 356 | filename := fw.filename 357 | filenameSuffix := path.Ext(filename) 358 | startLine := fw.startLine 359 | 360 | if startLine >= maxLine { 361 | //close file handle 362 | fw.writer.Close() 363 | timeFlag := time.Now().Format("2006-01-02-15.04.05.9999") 364 | oldFilename := strings.Replace(filename, filenameSuffix, "", 1) + "." + timeFlag + filenameSuffix 365 | err := os.Rename(filename, oldFilename) 366 | if err != nil { 367 | return err 368 | } 369 | err = fw.initFile() 370 | if err != nil { 371 | return err 372 | } 373 | } 374 | 375 | return nil 376 | } 377 | 378 | //slice file by size, if maxSize < fileSize, rename file is file_size_maxSize_time.log and recreate file 379 | func (fw *FileWriter) sliceByFileSize(maxSize int64) error { 380 | 381 | filename := fw.filename 382 | filenameSuffix := path.Ext(filename) 383 | nowSize, _ := fw.getFileSize(filename) 384 | 385 | if nowSize >= maxSize { 386 | //close file handle 387 | fw.writer.Close() 388 | timeFlag := time.Now().Format("2006-01-02-15.04.05.9999") 389 | oldFilename := strings.Replace(filename, filenameSuffix, "", 1) + "." + timeFlag + filenameSuffix 390 | err := os.Rename(filename, oldFilename) 391 | if err != nil { 392 | return err 393 | } 394 | err = fw.initFile() 395 | if err != nil { 396 | return err 397 | } 398 | } 399 | 400 | return nil 401 | } 402 | 403 | //get file object 404 | //params : filename 405 | //return : *os.file, error 406 | func (fw *FileWriter) getFileObject(filename string) (file *os.File, err error) { 407 | file, err = os.OpenFile(filename, os.O_RDWR|os.O_APPEND, 0766) 408 | return file, err 409 | } 410 | 411 | //get file size 412 | //params : filename 413 | //return : fileSize(byte int64), error 414 | func (fw *FileWriter) getFileSize(filename string) (fileSize int64, err error) { 415 | fileInfo, err := os.Stat(filename) 416 | if err != nil { 417 | return fileSize, err 418 | } 419 | 420 | return fileInfo.Size() / 1024, nil 421 | } 422 | 423 | func init() { 424 | Register(FILE_ADAPTER_NAME, NewAdapterFile) 425 | } 426 | -------------------------------------------------------------------------------- /file_test.go: -------------------------------------------------------------------------------- 1 | package go_logger 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestNewAdapterFile(t *testing.T) { 9 | NewAdapterFile() 10 | } 11 | 12 | func TestAdapterFile_Name(t *testing.T) { 13 | fileAdapter := NewAdapterFile() 14 | 15 | if fileAdapter.Name() != FILE_ADAPTER_NAME { 16 | t.Error("file adapter name error") 17 | } 18 | } 19 | 20 | func TestAdapterFile_Write(t *testing.T) { 21 | 22 | fileAdapter := NewAdapterFile() 23 | 24 | fileConfig := &FileConfig{ 25 | Filename: "./test.log", 26 | LevelFileName: map[int]string{}, 27 | MaxLine: 2000, 28 | MaxSize: 10000 * 4, 29 | JsonFormat: true, 30 | DateSlice: "d", 31 | } 32 | err := fileAdapter.Init(fileConfig) 33 | if err != nil { 34 | t.Fatal(err.Error()) 35 | } 36 | 37 | loggerMsg := &loggerMessage{ 38 | Timestamp: time.Now().Unix(), 39 | TimestampFormat: time.Now().Format("2006-01-02 15:04:05"), 40 | Millisecond: time.Now().UnixNano() / 1e6, 41 | MillisecondFormat: time.Now().Format("2006-01-02 15:04:05.999"), 42 | Level: LOGGER_LEVEL_DEBUG, 43 | LevelString: "debug", 44 | Body: "logger test file adapter write", 45 | File: "file_test.go", 46 | Line: 50, 47 | Function: "TestAdapterFile_Write", 48 | } 49 | err = fileAdapter.Write(loggerMsg) 50 | if err != nil { 51 | t.Error(err.Error()) 52 | } 53 | } 54 | 55 | func TestAdapterFile_WriteLevelFile(t *testing.T) { 56 | 57 | fileAdapter := NewAdapterFile() 58 | 59 | fileConfig := &FileConfig{ 60 | Filename: "./test.log", 61 | LevelFileName: map[int]string{ 62 | LOGGER_LEVEL_DEBUG: "./debug.log", 63 | LOGGER_LEVEL_INFO: "./info.log", 64 | LOGGER_LEVEL_ERROR: "./error.log", 65 | }, 66 | MaxLine: 2000, 67 | MaxSize: 10000 * 4, 68 | JsonFormat: true, 69 | DateSlice: "d", 70 | } 71 | err := fileAdapter.Init(fileConfig) 72 | if err != nil { 73 | t.Fatal(err.Error()) 74 | } 75 | 76 | loggerMsg := &loggerMessage{ 77 | Timestamp: time.Now().Unix(), 78 | TimestampFormat: time.Now().Format("2006-01-02 15:04:05"), 79 | Millisecond: time.Now().UnixNano() / 1e6, 80 | MillisecondFormat: time.Now().Format("2006-01-02 15:04:05.999"), 81 | Level: LOGGER_LEVEL_DEBUG, 82 | LevelString: "debug", 83 | Body: "logger test file adapter write", 84 | File: "file_test.go", 85 | Line: 50, 86 | Function: "TestAdapterFile_Write", 87 | } 88 | fileAdapter.Write(loggerMsg) 89 | loggerMsg.Level = LOGGER_LEVEL_INFO 90 | fileAdapter.Write(loggerMsg) 91 | loggerMsg.Level = LOGGER_LEVEL_ERROR 92 | fileAdapter.Write(loggerMsg) 93 | } 94 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/phachon/go-logger 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/fatih/color v1.7.0 7 | github.com/mailru/easyjson v0.7.0 8 | github.com/mattn/go-colorable v0.1.4 // indirect 9 | github.com/mattn/go-isatty v0.0.11 // indirect 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= 2 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 3 | github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= 4 | github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= 5 | github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= 6 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 7 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 8 | github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= 9 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 10 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 11 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= 12 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 13 | -------------------------------------------------------------------------------- /logger.go: -------------------------------------------------------------------------------- 1 | package go_logger 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path" 7 | "runtime" 8 | "strconv" 9 | "strings" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | var Version = "v1.2" 15 | 16 | const ( 17 | LOGGER_LEVEL_EMERGENCY = iota 18 | LOGGER_LEVEL_ALERT 19 | LOGGER_LEVEL_CRITICAL 20 | LOGGER_LEVEL_ERROR 21 | LOGGER_LEVEL_WARNING 22 | LOGGER_LEVEL_NOTICE 23 | LOGGER_LEVEL_INFO 24 | LOGGER_LEVEL_DEBUG 25 | ) 26 | 27 | type adapterLoggerFunc func() LoggerAbstract 28 | 29 | type LoggerAbstract interface { 30 | Name() string 31 | Init(config Config) error 32 | Write(loggerMsg *loggerMessage) error 33 | Flush() 34 | } 35 | 36 | var adapters = make(map[string]adapterLoggerFunc) 37 | 38 | var levelStringMapping = map[int]string{ 39 | LOGGER_LEVEL_EMERGENCY: "Emergency", 40 | LOGGER_LEVEL_ALERT: "Alert", 41 | LOGGER_LEVEL_CRITICAL: "Critical", 42 | LOGGER_LEVEL_ERROR: "Error", 43 | LOGGER_LEVEL_WARNING: "Warning", 44 | LOGGER_LEVEL_NOTICE: "Notice", 45 | LOGGER_LEVEL_INFO: "Info", 46 | LOGGER_LEVEL_DEBUG: "Debug", 47 | } 48 | 49 | var defaultLoggerMessageFormat = "%millisecond_format% [%level_string%] %body%" 50 | 51 | //Register logger adapter 52 | func Register(adapterName string, newLog adapterLoggerFunc) { 53 | if adapters[adapterName] != nil { 54 | panic("logger: logger adapter " + adapterName + " already registered!") 55 | } 56 | if newLog == nil { 57 | panic("logger: logger adapter " + adapterName + " is nil!") 58 | } 59 | 60 | adapters[adapterName] = newLog 61 | } 62 | 63 | type Logger struct { 64 | lock sync.Mutex //sync lock 65 | outputs []*outputLogger // outputs loggers 66 | msgChan chan *loggerMessage // message channel 67 | synchronous bool // is sync 68 | wait sync.WaitGroup // process wait 69 | signalChan chan string 70 | } 71 | 72 | type outputLogger struct { 73 | Name string 74 | Level int 75 | LoggerAbstract 76 | } 77 | 78 | type loggerMessage struct { 79 | Timestamp int64 `json:"timestamp"` 80 | TimestampFormat string `json:"timestamp_format"` 81 | Millisecond int64 `json:"millisecond"` 82 | MillisecondFormat string `json:"millisecond_format"` 83 | Level int `json:"level"` 84 | LevelString string `json:"level_string"` 85 | Body string `json:"body"` 86 | File string `json:"file"` 87 | Line int `json:"line"` 88 | Function string `json:"function"` 89 | } 90 | 91 | //new logger 92 | //return logger 93 | func NewLogger() *Logger { 94 | logger := &Logger{ 95 | outputs: []*outputLogger{}, 96 | msgChan: make(chan *loggerMessage, 10), 97 | synchronous: true, 98 | wait: sync.WaitGroup{}, 99 | signalChan: make(chan string, 1), 100 | } 101 | //default adapter console 102 | logger.attach("console", LOGGER_LEVEL_DEBUG, &ConsoleConfig{}) 103 | 104 | return logger 105 | } 106 | 107 | //start attach a logger adapter 108 | //param : adapterName console | file | database | ... 109 | //return : error 110 | func (logger *Logger) Attach(adapterName string, level int, config Config) error { 111 | logger.lock.Lock() 112 | defer logger.lock.Unlock() 113 | 114 | return logger.attach(adapterName, level, config) 115 | } 116 | 117 | //attach a logger adapter after lock 118 | //param : adapterName console | file | database | ... 119 | //return : error 120 | func (logger *Logger) attach(adapterName string, level int, config Config) error { 121 | for _, output := range logger.outputs { 122 | if output.Name == adapterName { 123 | printError("logger: adapter " + adapterName + "already attached!") 124 | } 125 | } 126 | logFun, ok := adapters[adapterName] 127 | if !ok { 128 | printError("logger: adapter " + adapterName + "is nil!") 129 | } 130 | adapterLog := logFun() 131 | err := adapterLog.Init(config) 132 | if err != nil { 133 | printError("logger: adapter " + adapterName + " init failed, error: " + err.Error()) 134 | } 135 | 136 | output := &outputLogger{ 137 | Name: adapterName, 138 | Level: level, 139 | LoggerAbstract: adapterLog, 140 | } 141 | 142 | logger.outputs = append(logger.outputs, output) 143 | return nil 144 | } 145 | 146 | //start attach a logger adapter 147 | //param : adapterName console | file | database | ... 148 | //return : error 149 | func (logger *Logger) Detach(adapterName string) error { 150 | logger.lock.Lock() 151 | defer logger.lock.Unlock() 152 | 153 | return logger.detach(adapterName) 154 | } 155 | 156 | //detach a logger adapter after lock 157 | //param : adapterName console | file | database | ... 158 | //return : error 159 | func (logger *Logger) detach(adapterName string) error { 160 | outputs := []*outputLogger{} 161 | for _, output := range logger.outputs { 162 | if output.Name == adapterName { 163 | continue 164 | } 165 | outputs = append(outputs, output) 166 | } 167 | logger.outputs = outputs 168 | return nil 169 | } 170 | 171 | //set logger level 172 | //params : level int 173 | //func (logger *Logger) SetLevel(level int) { 174 | // logger.level = level 175 | //} 176 | 177 | //set logger synchronous false 178 | //params : sync bool 179 | func (logger *Logger) SetAsync(data ...int) { 180 | logger.lock.Lock() 181 | defer logger.lock.Unlock() 182 | logger.synchronous = false 183 | 184 | msgChanLen := 100 185 | if len(data) > 0 { 186 | msgChanLen = data[0] 187 | } 188 | 189 | logger.msgChan = make(chan *loggerMessage, msgChanLen) 190 | logger.signalChan = make(chan string, 1) 191 | 192 | if !logger.synchronous { 193 | go func() { 194 | defer func() { 195 | e := recover() 196 | if e != nil { 197 | fmt.Printf("%v", e) 198 | } 199 | }() 200 | logger.startAsyncWrite() 201 | }() 202 | } 203 | } 204 | 205 | //write log message 206 | //params : level int, msg string 207 | //return : error 208 | func (logger *Logger) Writer(level int, msg string) error { 209 | funcName := "null" 210 | pc, file, line, ok := runtime.Caller(2) 211 | if !ok { 212 | file = "null" 213 | line = 0 214 | } else { 215 | funcName = runtime.FuncForPC(pc).Name() 216 | } 217 | _, filename := path.Split(file) 218 | 219 | if levelStringMapping[level] == "" { 220 | printError("logger: level " + strconv.Itoa(level) + " is illegal!") 221 | } 222 | 223 | loggerMsg := &loggerMessage{ 224 | Timestamp: time.Now().Unix(), 225 | TimestampFormat: time.Now().Format("2006-01-02 15:04:05"), 226 | Millisecond: time.Now().UnixNano() / 1e6, 227 | MillisecondFormat: time.Now().Format("2006-01-02 15:04:05.999"), 228 | Level: level, 229 | LevelString: levelStringMapping[level], 230 | Body: msg, 231 | File: filename, 232 | Line: line, 233 | Function: funcName, 234 | } 235 | 236 | if !logger.synchronous { 237 | logger.wait.Add(1) 238 | logger.msgChan <- loggerMsg 239 | } else { 240 | logger.writeToOutputs(loggerMsg) 241 | } 242 | 243 | return nil 244 | } 245 | 246 | //sync write message to loggerOutputs 247 | //params : loggerMessage 248 | func (logger *Logger) writeToOutputs(loggerMsg *loggerMessage) { 249 | for _, loggerOutput := range logger.outputs { 250 | // write level 251 | if loggerOutput.Level >= loggerMsg.Level { 252 | err := loggerOutput.Write(loggerMsg) 253 | if err != nil { 254 | fmt.Fprintf(os.Stderr, "logger: unable write loggerMessage to adapter:%v, error: %v\n", loggerOutput.Name, err) 255 | } 256 | } 257 | } 258 | } 259 | 260 | //start async write by read logger.msgChan 261 | func (logger *Logger) startAsyncWrite() { 262 | for { 263 | select { 264 | case loggerMsg := <-logger.msgChan: 265 | logger.writeToOutputs(loggerMsg) 266 | logger.wait.Done() 267 | case signal := <-logger.signalChan: 268 | if signal == "flush" { 269 | logger.flush() 270 | } 271 | } 272 | } 273 | } 274 | 275 | //flush msgChan data 276 | func (logger *Logger) flush() { 277 | if !logger.synchronous { 278 | for { 279 | if len(logger.msgChan) > 0 { 280 | loggerMsg := <-logger.msgChan 281 | logger.writeToOutputs(loggerMsg) 282 | logger.wait.Done() 283 | continue 284 | } 285 | break 286 | } 287 | for _, loggerOutput := range logger.outputs { 288 | loggerOutput.Flush() 289 | } 290 | } 291 | } 292 | 293 | //if SetAsync() or logger.synchronous is false, must call Flush() to flush msgChan data 294 | func (logger *Logger) Flush() { 295 | if !logger.synchronous { 296 | logger.signalChan <- "flush" 297 | logger.wait.Wait() 298 | return 299 | } 300 | logger.flush() 301 | } 302 | 303 | func (logger *Logger) LoggerLevel(levelStr string) int { 304 | levelStr = strings.ToUpper(levelStr) 305 | switch levelStr { 306 | case "EMERGENCY": 307 | return LOGGER_LEVEL_EMERGENCY 308 | case "ALERT": 309 | return LOGGER_LEVEL_ALERT 310 | case "CRITICAL": 311 | return LOGGER_LEVEL_CRITICAL 312 | case "ERROR": 313 | return LOGGER_LEVEL_ERROR 314 | case "WARNING": 315 | return LOGGER_LEVEL_WARNING 316 | case "NOTICE": 317 | return LOGGER_LEVEL_NOTICE 318 | case "INFO": 319 | return LOGGER_LEVEL_INFO 320 | case "DEBUG": 321 | return LOGGER_LEVEL_DEBUG 322 | default: 323 | return LOGGER_LEVEL_DEBUG 324 | } 325 | } 326 | 327 | func loggerMessageFormat(format string, loggerMsg *loggerMessage) string { 328 | message := strings.Replace(format, "%timestamp%", strconv.FormatInt(loggerMsg.Timestamp, 10), 1) 329 | message = strings.Replace(message, "%timestamp_format%", loggerMsg.TimestampFormat, 1) 330 | message = strings.Replace(message, "%millisecond%", strconv.FormatInt(loggerMsg.Millisecond, 10), 1) 331 | message = strings.Replace(message, "%millisecond_format%", loggerMsg.MillisecondFormat, 1) 332 | message = strings.Replace(message, "%level%", strconv.Itoa(loggerMsg.Level), 1) 333 | message = strings.Replace(message, "%level_string%", loggerMsg.LevelString, 1) 334 | message = strings.Replace(message, "%file%", loggerMsg.File, 1) 335 | message = strings.Replace(message, "%line%", strconv.Itoa(loggerMsg.Line), 1) 336 | message = strings.Replace(message, "%function%", loggerMsg.Function, 1) 337 | message = strings.Replace(message, "%body%", loggerMsg.Body, 1) 338 | 339 | return message 340 | } 341 | 342 | //log emergency level 343 | func (logger *Logger) Emergency(msg string) { 344 | logger.Writer(LOGGER_LEVEL_EMERGENCY, msg) 345 | } 346 | 347 | //log emergency format 348 | func (logger *Logger) Emergencyf(format string, a ...interface{}) { 349 | msg := fmt.Sprintf(format, a...) 350 | logger.Writer(LOGGER_LEVEL_EMERGENCY, msg) 351 | } 352 | 353 | //log alert level 354 | func (logger *Logger) Alert(msg string) { 355 | logger.Writer(LOGGER_LEVEL_ALERT, msg) 356 | } 357 | 358 | //log alert format 359 | func (logger *Logger) Alertf(format string, a ...interface{}) { 360 | msg := fmt.Sprintf(format, a...) 361 | logger.Writer(LOGGER_LEVEL_ALERT, msg) 362 | } 363 | 364 | //log critical level 365 | func (logger *Logger) Critical(msg string) { 366 | logger.Writer(LOGGER_LEVEL_CRITICAL, msg) 367 | } 368 | 369 | //log critical format 370 | func (logger *Logger) Criticalf(format string, a ...interface{}) { 371 | msg := fmt.Sprintf(format, a...) 372 | logger.Writer(LOGGER_LEVEL_CRITICAL, msg) 373 | } 374 | 375 | //log error level 376 | func (logger *Logger) Error(msg string) { 377 | logger.Writer(LOGGER_LEVEL_ERROR, msg) 378 | } 379 | 380 | //log error format 381 | func (logger *Logger) Errorf(format string, a ...interface{}) { 382 | msg := fmt.Sprintf(format, a...) 383 | logger.Writer(LOGGER_LEVEL_ERROR, msg) 384 | } 385 | 386 | //log warning level 387 | func (logger *Logger) Warning(msg string) { 388 | logger.Writer(LOGGER_LEVEL_WARNING, msg) 389 | } 390 | 391 | //log warning format 392 | func (logger *Logger) Warningf(format string, a ...interface{}) { 393 | msg := fmt.Sprintf(format, a...) 394 | logger.Writer(LOGGER_LEVEL_WARNING, msg) 395 | } 396 | 397 | //log notice level 398 | func (logger *Logger) Notice(msg string) { 399 | logger.Writer(LOGGER_LEVEL_NOTICE, msg) 400 | } 401 | 402 | //log notice format 403 | func (logger *Logger) Noticef(format string, a ...interface{}) { 404 | msg := fmt.Sprintf(format, a...) 405 | logger.Writer(LOGGER_LEVEL_NOTICE, msg) 406 | } 407 | 408 | //log info level 409 | func (logger *Logger) Info(msg string) { 410 | logger.Writer(LOGGER_LEVEL_INFO, msg) 411 | } 412 | 413 | //log info format 414 | func (logger *Logger) Infof(format string, a ...interface{}) { 415 | msg := fmt.Sprintf(format, a...) 416 | logger.Writer(LOGGER_LEVEL_INFO, msg) 417 | } 418 | 419 | //log debug level 420 | func (logger *Logger) Debug(msg string) { 421 | logger.Writer(LOGGER_LEVEL_DEBUG, msg) 422 | } 423 | 424 | //log debug format 425 | func (logger *Logger) Debugf(format string, a ...interface{}) { 426 | msg := fmt.Sprintf(format, a...) 427 | logger.Writer(LOGGER_LEVEL_DEBUG, msg) 428 | } 429 | 430 | func printError(message string) { 431 | fmt.Println(message) 432 | os.Exit(0) 433 | } 434 | -------------------------------------------------------------------------------- /logger_easyjson.go: -------------------------------------------------------------------------------- 1 | // Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. 2 | 3 | package go_logger 4 | 5 | import ( 6 | json "encoding/json" 7 | easyjson "github.com/mailru/easyjson" 8 | jlexer "github.com/mailru/easyjson/jlexer" 9 | jwriter "github.com/mailru/easyjson/jwriter" 10 | ) 11 | 12 | // suppress unused package warning 13 | var ( 14 | _ *json.RawMessage 15 | _ *jlexer.Lexer 16 | _ *jwriter.Writer 17 | _ easyjson.Marshaler 18 | ) 19 | 20 | func easyjson22b64118DecodeGithubComPhachonGoLogger(in *jlexer.Lexer, out *loggerMessage) { 21 | isTopLevel := in.IsStart() 22 | if in.IsNull() { 23 | if isTopLevel { 24 | in.Consumed() 25 | } 26 | in.Skip() 27 | return 28 | } 29 | in.Delim('{') 30 | for !in.IsDelim('}') { 31 | key := in.UnsafeString() 32 | in.WantColon() 33 | if in.IsNull() { 34 | in.Skip() 35 | in.WantComma() 36 | continue 37 | } 38 | switch key { 39 | case "timestamp": 40 | out.Timestamp = int64(in.Int64()) 41 | case "timestamp_format": 42 | out.TimestampFormat = string(in.String()) 43 | case "millisecond": 44 | out.Millisecond = int64(in.Int64()) 45 | case "millisecond_format": 46 | out.MillisecondFormat = string(in.String()) 47 | case "level": 48 | out.Level = int(in.Int()) 49 | case "level_string": 50 | out.LevelString = string(in.String()) 51 | case "body": 52 | out.Body = string(in.String()) 53 | case "file": 54 | out.File = string(in.String()) 55 | case "line": 56 | out.Line = int(in.Int()) 57 | case "function": 58 | out.Function = string(in.String()) 59 | default: 60 | in.SkipRecursive() 61 | } 62 | in.WantComma() 63 | } 64 | in.Delim('}') 65 | if isTopLevel { 66 | in.Consumed() 67 | } 68 | } 69 | func easyjson22b64118EncodeGithubComPhachonGoLogger(out *jwriter.Writer, in loggerMessage) { 70 | out.RawByte('{') 71 | first := true 72 | _ = first 73 | { 74 | const prefix string = ",\"timestamp\":" 75 | if first { 76 | first = false 77 | out.RawString(prefix[1:]) 78 | } else { 79 | out.RawString(prefix) 80 | } 81 | out.Int64(int64(in.Timestamp)) 82 | } 83 | { 84 | const prefix string = ",\"timestamp_format\":" 85 | if first { 86 | first = false 87 | out.RawString(prefix[1:]) 88 | } else { 89 | out.RawString(prefix) 90 | } 91 | out.String(string(in.TimestampFormat)) 92 | } 93 | { 94 | const prefix string = ",\"millisecond\":" 95 | if first { 96 | first = false 97 | out.RawString(prefix[1:]) 98 | } else { 99 | out.RawString(prefix) 100 | } 101 | out.Int64(int64(in.Millisecond)) 102 | } 103 | { 104 | const prefix string = ",\"millisecond_format\":" 105 | if first { 106 | first = false 107 | out.RawString(prefix[1:]) 108 | } else { 109 | out.RawString(prefix) 110 | } 111 | out.String(string(in.MillisecondFormat)) 112 | } 113 | { 114 | const prefix string = ",\"level\":" 115 | if first { 116 | first = false 117 | out.RawString(prefix[1:]) 118 | } else { 119 | out.RawString(prefix) 120 | } 121 | out.Int(int(in.Level)) 122 | } 123 | { 124 | const prefix string = ",\"level_string\":" 125 | if first { 126 | first = false 127 | out.RawString(prefix[1:]) 128 | } else { 129 | out.RawString(prefix) 130 | } 131 | out.String(string(in.LevelString)) 132 | } 133 | { 134 | const prefix string = ",\"body\":" 135 | if first { 136 | first = false 137 | out.RawString(prefix[1:]) 138 | } else { 139 | out.RawString(prefix) 140 | } 141 | out.String(string(in.Body)) 142 | } 143 | { 144 | const prefix string = ",\"file\":" 145 | if first { 146 | first = false 147 | out.RawString(prefix[1:]) 148 | } else { 149 | out.RawString(prefix) 150 | } 151 | out.String(string(in.File)) 152 | } 153 | { 154 | const prefix string = ",\"line\":" 155 | if first { 156 | first = false 157 | out.RawString(prefix[1:]) 158 | } else { 159 | out.RawString(prefix) 160 | } 161 | out.Int(int(in.Line)) 162 | } 163 | { 164 | const prefix string = ",\"function\":" 165 | if first { 166 | first = false 167 | out.RawString(prefix[1:]) 168 | } else { 169 | out.RawString(prefix) 170 | } 171 | out.String(string(in.Function)) 172 | } 173 | out.RawByte('}') 174 | } 175 | 176 | // MarshalJSON supports json.Marshaler interface 177 | func (v loggerMessage) MarshalJSON() ([]byte, error) { 178 | w := jwriter.Writer{} 179 | easyjson22b64118EncodeGithubComPhachonGoLogger(&w, v) 180 | return w.Buffer.BuildBytes(), w.Error 181 | } 182 | 183 | // MarshalEasyJSON supports easyjson.Marshaler interface 184 | func (v loggerMessage) MarshalEasyJSON(w *jwriter.Writer) { 185 | easyjson22b64118EncodeGithubComPhachonGoLogger(w, v) 186 | } 187 | 188 | // UnmarshalJSON supports json.Unmarshaler interface 189 | func (v *loggerMessage) UnmarshalJSON(data []byte) error { 190 | r := jlexer.Lexer{Data: data} 191 | easyjson22b64118DecodeGithubComPhachonGoLogger(&r, v) 192 | return r.Error() 193 | } 194 | 195 | // UnmarshalEasyJSON supports easyjson.Unmarshaler interface 196 | func (v *loggerMessage) UnmarshalEasyJSON(l *jlexer.Lexer) { 197 | easyjson22b64118DecodeGithubComPhachonGoLogger(l, v) 198 | } 199 | -------------------------------------------------------------------------------- /logger_test.go: -------------------------------------------------------------------------------- 1 | package go_logger 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestNewLogger(t *testing.T) { 10 | NewLogger() 11 | } 12 | 13 | func TestLogger_Attach(t *testing.T) { 14 | 15 | logger := NewLogger() 16 | fileConfig := &FileConfig{ 17 | Filename: "./test.log", 18 | } 19 | logger.Attach("file", LOGGER_LEVEL_DEBUG, fileConfig) 20 | outputs := logger.outputs 21 | for _, outputLogger := range outputs { 22 | if outputLogger.Name != "file" { 23 | t.Error("file attach failed") 24 | } 25 | } 26 | } 27 | 28 | func TestLogger_Detach(t *testing.T) { 29 | 30 | logger := NewLogger() 31 | logger.Detach("console") 32 | 33 | outputs := logger.outputs 34 | 35 | if len(outputs) > 0 { 36 | t.Error("logger detach error") 37 | } 38 | } 39 | 40 | func TestLogger_LoggerLevel(t *testing.T) { 41 | 42 | logger := NewLogger() 43 | 44 | level := logger.LoggerLevel("emerGENCY") 45 | if level != LOGGER_LEVEL_EMERGENCY { 46 | t.Error("logger loggerLevel error") 47 | } 48 | level = logger.LoggerLevel("ALERT") 49 | if level != LOGGER_LEVEL_ALERT { 50 | t.Error("logger loggerLevel error") 51 | } 52 | level = logger.LoggerLevel("cRITICAL") 53 | if level != LOGGER_LEVEL_CRITICAL { 54 | t.Error("logger loggerLevel error") 55 | } 56 | level = logger.LoggerLevel("DEBUG") 57 | if level != LOGGER_LEVEL_DEBUG { 58 | t.Error("logger loggerLevel error") 59 | } 60 | level = logger.LoggerLevel("ooox") 61 | if level != LOGGER_LEVEL_DEBUG { 62 | t.Error("logger loggerLevel error") 63 | } 64 | } 65 | 66 | func TestLogger_loggerMessageFormat(t *testing.T) { 67 | 68 | loggerMsg := &loggerMessage{ 69 | Timestamp: time.Now().Unix(), 70 | TimestampFormat: time.Now().Format("2006-01-02 15:04:05"), 71 | Millisecond: time.Now().UnixNano() / 1e6, 72 | MillisecondFormat: time.Now().Format("2006-01-02 15:04:05.999"), 73 | Level: LOGGER_LEVEL_DEBUG, 74 | LevelString: "debug", 75 | Body: "logger console adapter test", 76 | File: "console_test.go", 77 | Line: 77, 78 | Function: "TestAdapterConsole_WriteJsonFormat", 79 | } 80 | 81 | format := "%millisecond_format% [%level_string%] [%file%:%line%] %body%" 82 | str := loggerMessageFormat(format, loggerMsg) 83 | 84 | fmt.Println(str) 85 | } 86 | -------------------------------------------------------------------------------- /utils/file.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | "os" 7 | ) 8 | 9 | var UtilFile = NewFile() 10 | 11 | func NewFile() *File { 12 | return &File{} 13 | } 14 | 15 | type File struct { 16 | } 17 | 18 | // create file 19 | func (f *File) CreateFile(filename string) error { 20 | newFile, err := os.Create(filename) 21 | defer newFile.Close() 22 | return err 23 | } 24 | 25 | // file or path is exists 26 | func (f *File) PathExists(path string) (bool, error) { 27 | _, err := os.Stat(path) 28 | if err == nil { 29 | return true, nil 30 | } 31 | if os.IsNotExist(err) { 32 | return false, nil 33 | } 34 | return false, err 35 | } 36 | 37 | //get file lines 38 | //params : filename 39 | //return : fileLine, error 40 | func (f *File) GetFileLines(filename string) (fileLine int64, err error) { 41 | file, err := os.OpenFile(filename, os.O_RDONLY, 0766) 42 | if err != nil { 43 | return fileLine, err 44 | } 45 | defer file.Close() 46 | 47 | fileLine = 1 48 | r := bufio.NewReader(file) 49 | for { 50 | _, err := r.ReadString('\n') 51 | if err != nil || err == io.EOF { 52 | break 53 | } 54 | fileLine += 1 55 | } 56 | return fileLine, nil 57 | } 58 | -------------------------------------------------------------------------------- /utils/misc.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "io/ioutil" 5 | "math/rand" 6 | "net/http" 7 | "net/url" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | type Misc struct { 13 | } 14 | 15 | func NewMisc() *Misc { 16 | return &Misc{} 17 | } 18 | 19 | //格式化 unix 时间戳 20 | func (misc *Misc) FormatUnixTime(unixTime int64) string { 21 | tm := time.Unix(unixTime, 0) 22 | return tm.Format("2006-01-02 15:04:05") 23 | } 24 | 25 | //map Intersect 26 | func (misc *Misc) MapIntersect(defaultMap map[string]interface{}, inputMap map[string]interface{}) map[string]interface{} { 27 | for key, _ := range defaultMap { 28 | inputValue, ok := inputMap[key] 29 | if !ok { 30 | continue 31 | } 32 | defaultMap[key] = inputValue 33 | } 34 | return defaultMap 35 | } 36 | 37 | //http get request 38 | func (misc *Misc) HttpGet(queryUrl string, queryValues map[string]string, headerValues map[string]string, timeout int) (body string, code int, err error) { 39 | if !strings.Contains(queryUrl, "?") { 40 | queryUrl += "?" 41 | } 42 | 43 | queryString := "" 44 | for queryKey, queryValue := range queryValues { 45 | queryString = queryString + "&" + queryKey + "=" + url.QueryEscape(queryValue) 46 | } 47 | queryString = strings.Replace(queryString, "&", "", 1) 48 | queryUrl += queryString 49 | 50 | req, err := http.NewRequest("GET", queryUrl, nil) 51 | if err != nil { 52 | return 53 | } 54 | if (headerValues != nil) && (len(headerValues) > 0) { 55 | for key, value := range headerValues { 56 | req.Header.Set(key, value) 57 | } 58 | } 59 | 60 | client := &http.Client{} 61 | resp, err := client.Do(req) 62 | code = resp.StatusCode 63 | defer resp.Body.Close() 64 | 65 | bodyByte, err := ioutil.ReadAll(resp.Body) 66 | if err != nil { 67 | return 68 | } 69 | 70 | return string(bodyByte), code, nil 71 | } 72 | 73 | //http post request 74 | func (misc *Misc) HttpPost(queryUrl string, queryValues map[string]string, headerValues map[string]string, timeout int) (body string, code int, err error) { 75 | if !strings.Contains(queryUrl, "?") { 76 | queryUrl += "?" 77 | } 78 | queryString := "" 79 | for queryKey, queryValue := range queryValues { 80 | queryString = queryString + "&" + queryKey + "=" + url.QueryEscape(queryValue) 81 | } 82 | queryString = strings.Replace(queryString, "&", "", 1) 83 | queryUrl += queryString 84 | 85 | req, err := http.NewRequest("POST", queryUrl, strings.NewReader(queryString)) 86 | if err != nil { 87 | return 88 | } 89 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 90 | if (headerValues != nil) && (len(headerValues) > 0) { 91 | for key, value := range headerValues { 92 | req.Header.Set(key, value) 93 | } 94 | } 95 | client := &http.Client{} 96 | resp, err := client.Do(req) 97 | if err != nil { 98 | return 99 | } 100 | code = resp.StatusCode 101 | defer resp.Body.Close() 102 | 103 | bodyByte, err := ioutil.ReadAll(resp.Body) 104 | if err != nil { 105 | return 106 | } 107 | 108 | return string(bodyByte), code, nil 109 | } 110 | 111 | // rand string 112 | func (m *Misc) RandString(strlen int) string { 113 | codes := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" 114 | codeLen := len(codes) 115 | data := make([]byte, strlen) 116 | rand.Seed(time.Now().UnixNano() + rand.Int63() + rand.Int63() + rand.Int63() + rand.Int63()) 117 | for i := 0; i < strlen; i++ { 118 | idx := rand.Intn(codeLen) 119 | data[i] = byte(codes[idx]) 120 | } 121 | return string(data) 122 | } 123 | --------------------------------------------------------------------------------