diff --git a/FUTURE.md b/FUTURE.md index 67acd60..a4e9e91 100644 --- a/FUTURE.md +++ b/FUTURE.md @@ -1,8 +1,14 @@ ## ✒ 未来版本的新特性 (Features in future version) -### v0.0.9 +### v0.0.11 +* 时间格式化操作中 time.Time.AppendFormat 消耗性能很多,思考优化方案 + +### v0.0.10 * 支持日志输出为 Json 形式,通过增加 JSON 日志处理器实现 + +### v0.0.9 * 支持日志输出函数,日志信息可以是一个返回 string 的函数 +* 公开 PrefixOf 方法,方便用户自定义处理器的时候获取日志级别字符串 ### v0.0.8 * 进行第一次性能优化,性能相比之前版本提升 30% diff --git a/HISTORY.md b/HISTORY.md index 879ac20..2a014ab 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,10 @@ ## ✒ 历史版本的特性介绍 (Features in old version) +### v0.0.9 +> 此版本发布于 2020-03-09 +* 支持日志输出函数,日志信息可以是一个返回 string 的函数 +* 公开 PrefixOf 方法,方便用户自定义处理器的时候获取日志级别字符串 + ### v0.0.8 > 此版本发布于 2020-03-08 * 进行第一次性能优化,性能相比之前版本提升 30% diff --git a/README.en.md b/README.en.md index c16b439..c3f85da 100644 --- a/README.en.md +++ b/README.en.md @@ -10,6 +10,7 @@ * Modularization design, easy to extend your logger with wrapper and handler * Level-based logging, and there are four levels to use +* Log Function supports, it is a better way to output a very long log. * Enable or disable Logger, you can disable or switch to a higher level in your production environment * Log file supports, and you can customer the name of your log file. * Duration rolling supports, which means it will roll to a new log file by duration automatically, such as one day one log file. @@ -38,7 +39,7 @@ module your_project_name go 1.14 require ( - github.com/FishGoddess/logit v0.0.8 + github.com/FishGoddess/logit v0.0.9 ) ``` @@ -54,6 +55,10 @@ logit has no more external dependencies. package main import ( + "math/rand" + "strconv" + "time" + "github.com/FishGoddess/logit" ) @@ -71,6 +76,14 @@ func main() { // If you want to output log with file info, try this: logit.EnableFileInfo() logit.Info("Show file info!") + + // If you have a long log and it is made of many variables, try this: + // The msg is the return value of msgGenerator. + logit.DebugFunction(func() string { + // Use time as the source of random number generator. + r := rand.New(rand.NewSource(time.Now().Unix())) + return "debug rand int: " + strconv.Itoa(r.Intn(100)) + }) } ``` @@ -89,17 +102,17 @@ _Check more examples in [_examples](./_examples)._ ### 🔥 Benchmarks ```bash -$ go test -v ./_examples/benchmarks_test.go -bench=. -benchtime=20s +$ go test -v ./_examples/benchmarks_test.go -bench=. -benchtime=1s ``` > Benchmark file:[_examples/benchmarks_test.go](./_examples/benchmarks_test.go) | test case | times ran (large is better) | ns/op (small is better) | features | extension | | -----------|--------|-------------|-------------|-------------| -| **logit** | 12448242 | 2161 ns/op | powerful | high | -| logrus |   2990408 | 7991 ns/op | normal | normal | -| Golog | 15536137 | 1556 ns/op | normal | normal | -| Golang log | 25268450 |   945 ns/op | not good | none | +| **logit** |   572947 | 1939 ns/op | powerful | high | +| logrus |   158262 | 7751 ns/op | normal | normal | +| Golog |   751064 | 1614 ns/op | normal | normal | +| Golang log | 1000000 | 1019 ns/op | not good | none | > Environment:I7-6700HQ CPU @ 2.6 GHZ, 16 GB RAM @@ -109,10 +122,13 @@ $ go test -v ./_examples/benchmarks_test.go -bench=. -benchtime=20s **However, we think file info is useful in check errors,** **so we keep this feature, and provide a switch to turn off it for high-performance.** -**2. For now logit uses some functions of fmt, and these functions is expensive** +**2. v0.0.7 and lower versions use some functions of fmt, and these functions is expensive** **because of reflect (for judging the parameter v interface{}). Actually, these judgements** **are redundant in a logger. The more effective output will be used in v0.0.8 and higher versions.** +**3. After checking the benchmarks of v0.0.8 version, we found that time format takes a lots of time** +**because of time.Time.AppendFormat. We are finding a more effective way to fix it, maybe fixed in higher version!** + ### 👥 Contributing If you find that something is not working as expected please open an _**issue**_. diff --git a/README.md b/README.md index 44b83ba..bba335a 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,9 @@ * 独特的日志输出模块设计,使用 wrapper 和 handler 装载特定的模块,实现扩展功能 * 支持日志级别控制,一共有四个日志级别,分别是 debug,info,warn 和 error。 +* 支持日志记录函数,使用回调的形式获取日志内容,对长日志内容的组织逻辑会更清晰 * 支持开启或者关闭日志功能,线上环境可以关闭或调高日志级别 -* 支持记录日志到文件中,自定义日志文件名 +* 支持记录日志到文件中,并且可以自定义日志文件名 * 支持按照时间间隔进行自动划分日志文件,比如每一天划分一个日志文件 * 支持按照文件大小进行自动划分日志文件,比如每 64 MB 划分一个日志文件 * 增加日志处理器模块,支持用户自定义日志处理逻辑,具有很高的扩展能力 @@ -38,7 +39,7 @@ module your_project_name go 1.14 require ( - github.com/FishGoddess/logit v0.0.8 + github.com/FishGoddess/logit v0.0.9 ) ``` @@ -54,6 +55,10 @@ logit 没有任何其他额外的依赖,纯使用 [Golang 标准库](https://g package main import ( + "math/rand" + "strconv" + "time" + "github.com/FishGoddess/logit" ) @@ -71,6 +76,14 @@ func main() { // If you want to output log with file info, try this: logit.EnableFileInfo() logit.Info("Show file info!") + + // If you have a long log and it is made of many variables, try this: + // The msg is the return value of msgGenerator. + logit.DebugFunction(func() string { + // Use time as the source of random number generator. + r := rand.New(rand.NewSource(time.Now().Unix())) + return "debug rand int: " + strconv.Itoa(r.Intn(100)) + }) } ``` @@ -89,17 +102,17 @@ _更多使用案例请查看 [_examples](./_examples) 目录。_ ### 🔥 性能测试 ```bash -$ go test -v ./_examples/benchmarks_test.go -bench=. -benchtime=20s +$ go test -v ./_examples/benchmarks_test.go -bench=. -benchtime=1s ``` > 测试文件:[_examples/benchmarks_test.go](./_examples/benchmarks_test.go) | 测试 | 单位时间内运行次数 (越大越好) | 每个操作消耗时间 (越小越好) | 功能性 | 扩展性 | | -----------|--------|-------------|-------------|-------------| -| **logit** | 12448242 | 2161 ns/op | 强大 | 高 | -| logrus |   2990408 | 7991 ns/op | 正常 | 正常 | -| Golog | 15536137 | 1556 ns/op | 正常 | 正常 | -| Golang log | 25268450 |   945 ns/op | 一般 | 无 | +| **logit** |   572947 | 1939 ns/op | 强大 | 高 | +| logrus |   158262 | 7751 ns/op | 正常 | 正常 | +| Golog |   751064 | 1614 ns/op | 正常 | 正常 | +| Golang log | 1000000 | 1019 ns/op | 一般 | 无 | > 测试环境:I7-6700HQ CPU @ 2.6 GHZ,16 GB RAM @@ -109,10 +122,13 @@ $ go test -v ./_examples/benchmarks_test.go -bench=. -benchtime=20s **但是这个功能感觉还是比较实用的,尤其是在查找错误的时候,所以我们还是加了这个功能!** **如果你更在乎性能,那我们也提供了一个选项可以关闭文件信息的查询!** -**2. 目前的日志输出使用了 fmt 包的一些方法,经过性能检测发现这些方法存在大量使用反射的** +**2. v0.0.7 及以前版本的日志输出使用了 fmt 包的一些方法,经过性能检测发现这些方法存在大量使用反射的** **行为,主要体现在对参数 v interface{} 进行类型检测的逻辑上,而日志输出都是字符串,这一个** **判断是可以省略的,可以减少很多运行时操作时间!v0.0.8 版本开始使用了更有效率的输出方式!** +**3. 经过对 v0.0.8 版本的性能检测,发现时间格式化操作消耗了接近一般的处理时间,** +**主要体现在 time.Time.AppendFormat 的调用上。目前正在思考优化方案,或许会在之后的版本中解决!** + ### 👥 贡献者 如果您觉得 logit 缺少您需要的功能,请不要犹豫,马上参与进来,发起一个 _**issue**_。 diff --git a/_examples/logger.go b/_examples/logger.go index 0b806ec..c961006 100644 --- a/_examples/logger.go +++ b/_examples/logger.go @@ -19,7 +19,10 @@ package main import ( + "math/rand" "os" + "strconv" + "time" "github.com/FishGoddess/logit" ) @@ -49,4 +52,12 @@ func main() { logger.EnableFileInfo() logger.Info("What file is it? Which line?") logger.DisableFileInfo() + + // If you have a long log and it is made of many variables, try this: + // The msg is the return value of msgGenerator. + logger.DebugFunction(func() string { + // Use time as the source of random number generator. + r := rand.New(rand.NewSource(time.Now().Unix())) + return "debug rand int: " + strconv.Itoa(r.Intn(100)) + }) } diff --git a/doc.go b/doc.go index 814cb2c..a19e03f 100644 --- a/doc.go +++ b/doc.go @@ -61,6 +61,14 @@ Package logit provides an easy way to use foundation for your logging operations logger.Info("What file is it? Which line?") logger.DisableFileInfo() + // If you have a long log and it is made of many variables, try this: + // The msg is the return value of msgGenerator. + logger.DebugFunction(func() string { + // Use time as the source of random number generator. + r := rand.New(rand.NewSource(time.Now().Unix())) + return "debug rand int: " + strconv.Itoa(r.Intn(100)) + }) + 3. enable or disable: // Every new Logger is running. @@ -159,4 +167,4 @@ Package logit provides an easy way to use foundation for your logging operations package logit // import "github.com/FishGoddess/logit" // Version is the version string representation of the "logit" package. -const Version = "0.0.8" +const Version = "0.0.9" diff --git a/logger_extension.go b/logger_extension.go index b74073c..ef04e54 100644 --- a/logger_extension.go +++ b/logger_extension.go @@ -93,3 +93,31 @@ func NewSizeRollingLogger(directory string, limitedSize int64, level LoggerLevel func NewDefaultSizeRollingLogger(directory string, level LoggerLevel) *Logger { return NewSizeRollingLogger(directory, 64*wrapper.MB, level) } + +// DebugFunction will output msg as a debug message. +// The msg is the return value of msgGenerator. +// This is a better way to output a long log made of many variables. +func (l *Logger) DebugFunction(msgGenerator func() string) { + l.Debug(msgGenerator()) +} + +// InfoFunction will output msg as an info message. +// The msg is the return value of msgGenerator. +// This is a better way to output a long log made of many variables. +func (l *Logger) InfoFunction(msgGenerator func() string) { + l.Info(msgGenerator()) +} + +// WarnFunction will output msg as a warn message. +// The msg is the return value of messageGenerator. +// This is a better way to output a long log made of many variables. +func (l *Logger) WarnFunction(msgGenerator func() string) { + l.Warn(msgGenerator()) +} + +// ErrorFunction will output msg as an error message. +// The msg is the return value of messageGenerator. +// This is a better way to output a long log made of many variables. +func (l *Logger) ErrorFunction(messageGenerator func() string) { + l.Error(messageGenerator()) +} diff --git a/logger_extension_test.go b/logger_extension_test.go index bf3337b..52a0d2b 100644 --- a/logger_extension_test.go +++ b/logger_extension_test.go @@ -19,6 +19,7 @@ package logit import ( + "math/rand" "strconv" "testing" "time" @@ -93,3 +94,20 @@ func TestNewDefaultSizeRollingLogger(t *testing.T) { logger.Error("error...") } } + +// 测试输出日志是从函数中生成的几个方法 +func TestLoggerLogFunction(t *testing.T) { + logger := NewStdoutLogger(DebugLevel) + logger.DebugFunction(func() string { + return "debug rand int: " + strconv.Itoa(rand.Intn(100)) + }) + logger.InfoFunction(func() string { + return "info rand int: " + strconv.Itoa(rand.Intn(100)) + }) + logger.WarnFunction(func() string { + return "warn rand int: " + strconv.Itoa(rand.Intn(100)) + }) + logger.ErrorFunction(func() string { + return "error rand int: " + strconv.Itoa(rand.Intn(100)) + }) +} diff --git a/logger_handler.go b/logger_handler.go index a2da2a6..1195cd3 100644 --- a/logger_handler.go +++ b/logger_handler.go @@ -39,6 +39,6 @@ func (lh LoggerHandler) handle(logger *Logger, level LoggerLevel, now time.Time, // The log handled by this handler will be like "[Info] [2020-03-06 16:10:44] msg". // If you want to customize, just code your own handler, then replace it! func DefaultLoggerHandler(logger *Logger, level LoggerLevel, now time.Time, msg string) bool { - logger.Writer().Write([]byte("[" + prefixOf(level) + "] [" + now.Format(logger.formatOfTime) + "] " + msg + "\n")) + logger.Writer().Write([]byte("[" + PrefixOf(level) + "] [" + now.Format(logger.formatOfTime) + "] " + msg + "\n")) return true } diff --git a/logger_level.go b/logger_level.go index 05b6dc2..ee5d602 100644 --- a/logger_level.go +++ b/logger_level.go @@ -37,7 +37,13 @@ var prefixOfLevels = map[LoggerLevel]string{ ErrorLevel: "Error", } +// The String method is used to print values passed as an operand +// to any format that accepts a string or to an printer without format such as Print. +func (ll LoggerLevel) String() string { + return prefixOfLevels[ll] +} + // prefixOf gets the prefix of this level. -func prefixOf(level LoggerLevel) string { - return prefixOfLevels[level] +func PrefixOf(level LoggerLevel) string { + return level.String() } diff --git a/logit.go b/logit.go index 95d4920..a767db8 100644 --- a/logit.go +++ b/logit.go @@ -94,3 +94,31 @@ func Warn(msg string) { func Error(msg string) { defaultLogger.log(callDepthOfDefaultLogger, ErrorLevel, msg) } + +// DebugFunction will output msg as a debug message. +// The msg is the return value of msgGenerator. +// This is a better way to output a long log made of many variables. +func DebugFunction(msgGenerator func() string) { + defaultLogger.DebugFunction(msgGenerator) +} + +// InfoFunction will output msg as an info message. +// The msg is the return value of msgGenerator. +// This is a better way to output a long log made of many variables. +func InfoFunction(msgGenerator func() string) { + defaultLogger.InfoFunction(msgGenerator) +} + +// WarnFunction will output msg as a warn message. +// The msg is the return value of messageGenerator. +// This is a better way to output a long log made of many variables. +func WarnFunction(msgGenerator func() string) { + defaultLogger.WarnFunction(msgGenerator) +} + +// ErrorFunction will output msg as an error message. +// The msg is the return value of messageGenerator. +// This is a better way to output a long log made of many variables. +func ErrorFunction(messageGenerator func() string) { + defaultLogger.ErrorFunction(messageGenerator) +}