From e865cc609bb1842f20c241be4996a164ee355b89 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 19 Nov 2012 14:48:53 +0800 Subject: [PATCH] =?UTF-8?q?review=20by=20=E5=9B=9B=E6=9C=88=E4=BB=BD?= =?UTF-8?q?=E5=B9=B3=E6=B0=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 11.3.md | 9 +- 12.1.md | 329 ++++++++++++++++++++++++++++---------------------------- 2 files changed, 169 insertions(+), 169 deletions(-) diff --git a/11.3.md b/11.3.md index 639437b7..45ee14e4 100644 --- a/11.3.md +++ b/11.3.md @@ -27,7 +27,6 @@ Go语言中自带有一个轻量级的测试框架`testing`和自带的`go test` 2. gotest_test.go:这是我们的单元测试文件,但是记住下面的这些原则: - 文件名必须是`_test.go`结尾的,这样在执行`go test`的时候才会执行到相应的代码 - - 包名必须和被测试文件的包名一致,例如上面的包名是gotest,那么test文件的包名也必须是gotest - 你必须import `testing`这个包 - 所有的测试用例函数必须是`Test`开头 - 测试用例会按照源代码中写的顺序依次执行 @@ -94,14 +93,14 @@ Go语言中自带有一个轻量级的测试框架`testing`和自带的`go test` ok gotest 0.013s ## 如何编写压力测试 -压力测试是用来检测我们写的函数是否有足够好的性能,和写单元功能测试类似,需要注意以下几点: +压力测试用来检测函数(方法)的性能,和编写单元功能测试的方法类似,此处不再赘述,但需要注意以下几点: -- 压力测试函数必须如下格式,其中`XXX`可以是任意字母数字的组合,但是首字母不能是小写字母 +- 压力测试用例必须遵循如下格式,其中XXX可以是任意字母数字的组合,但是首字母不能是小写字母 func BenchmarkXXX(b *testing.B) { ... } - `go test`不会默认执行压力测试的函数,如果要执行压力测试需要带上参数`-test.bench`,语法:`-test.bench="test_name_regex"`,例如`go test -test.bench=".*"`表示测试全部的压力测试函数 -- 压力测试函数中记得在循环中使用`testing.B.N`从而使得压力测试可以正常的运行 +- 在压力测试用例中,请记得在循环体内使用`testing.B.N`,以使测试可以正常的运行 - 文件名也必须以`_test.go`结尾 下面我们新建一个压力测试文件webbench_test.go,代码如下所示: @@ -141,7 +140,7 @@ Go语言中自带有一个轻量级的测试框架`testing`和自带的`go test` 上面的结果显示我们没有执行任何`TestXXX`的单元测试函数,显示的结果只执行了压力测试函数,第一条显示了`Benchmark_Division`执行了500000000次,每次的执行平均时间是7.76纳秒,第二条显示了`Benchmark_TimeConsumingFunction`执行了500000000,每次的平均执行时间是7.80纳秒。最后一条显示总过的执行时间。 ## 小结 -通过上面对单元测试和压力测试的编写,我们知道Go语言里面的`testing`包虽然轻量,但是编写测试用例和压力测试非常简单,而且使用内置的命令`go test`就可以非常方便的执行这些单元测试和压力测试,这样我们每次修改完函数执行一下单元测试和压力测试,可以简单的做回归测试。 +通过上面对单元测试和压力测试的学习,我们可以看到`testing`包很轻量,编写单元测试和压力测试用例非常简单,配合内置的`go test`命令就可以非常方便的进行测试,这样在我们每次修改完代码,执行一下go test就可以简单的完成回归测试了。 ## links diff --git a/12.1.md b/12.1.md index 79992397..ffeea4b9 100644 --- a/12.1.md +++ b/12.1.md @@ -1,165 +1,166 @@ -# 12.1 应用日志 -我们期望开发的Web应用程序能够把整个程序运行过程中出现的各种事件一一记录下来,Go语言中提供了一个简易的log包,我们使用该包可以方便的实现日志记录的功能,这些日志都是基本的给予fmt包的打印再结合panic之类的一些函数来处理我们一般的打印抛出错误之类的。如果我们想把我们的应用日志保存到文件,然后又能够结合日志实现很多复杂的功能,编写过Java或者C++的读者应该都使用过log4j和log4cpp之类的日志工具,Go目前标准包就如前面介绍的只是包含了一个简单的包,但是目前第三方开发的一个日志系统实现了很强大的日志功能,`https://github.com/cihub/seelog`,接下来我们介绍如何该日志系统来实现我们应用的日志功能。 - -## seelog介绍 -seelog是用Go语言实现的一个日志系统,他提供了一些简单的函数来实现复杂的日志分配、过滤和格式化。他主要有如下特性: - -- XML配置文件,可以不用重新编译程序而动态的加载配置信息 -- 能够动态改变配置而不需要重启应用 -- 能够同时把日志输出到多种流中 -- 支持不同的日志输出 - - - 命令行输出 - - 文件输出 - - 缓存输出 - - 支持log rotate - - SMTP邮件 - -上面介绍的只是部分特性,其他seelog是一个特别强大的日志处理系统,详细的可以参考官方网站的wiki。接下来介绍一下如何在项目中使用: - -首先安装seelog - - go get -u github.com/cihub/seelog - -然后我们来看一个简单的例子: - - package main - - import log "github.com/cihub/seelog" - - func main() { - defer log.Flush() - log.Info("Hello from Seelog!") - } - -我们编译之后可以正常的输出日志了,说明seelog日志系统已经成功安装并且可以正常运行了。 - -## 基于seelog的自定义日志处理 -seelog支持让我们给予seelog自定义日志处理,下面这个是我目前基于seelog日志系统做的自定义的日志处理包: - - package logs - - import ( - "errors" - "fmt" - seelog "github.com/cihub/seelog" - "io" - ) - - var Logger seelog.LoggerInterface - - func loadAppConfig() { - appConfig := ` - - - - - - - - - - - - - - - - - ` - logger, err := seelog.LoggerFromConfigAsBytes([]byte(appConfig)) - if err != nil { - fmt.Println(err) - return - } - UseLogger(logger) - } - - func init() { - DisableLog() - loadAppConfig() - } - - // DisableLog disables all library log output - func DisableLog() { - Logger = seelog.Disabled - } - - // UseLogger uses a specified seelog.LoggerInterface to output library log. - // Use this func if you are using Seelog logging system in your app. - func UseLogger(newLogger seelog.LoggerInterface) { - Logger = newLogger - } - -上面主要实现了三个函数, - -- `DisableLog` - - 初始化全局变量Logger为目前seelog的禁用状态,主要防止Logger初始化多次 -- `loadAppConfig` - - 根据配置文件初始化seelog的配置信息,这里我们把配置文件通过字符串读取设置好了,当然也可以通过读取XML文件。里面的配置说明如下: - - - seelog - - minlevel可有可无,有的话设置最小级别的日志级别需要记录,还有maxlevel也是类似的原理 - - outputs - - 输出信息到那些地方,这里分成了两个数据,一个记录到log rotate文件里面。第二设置了一个filter,如果这个错误级别是critical,那么发送邮件。 - - - formats - - 定义了各种日志的格式 - -- `UseLogger` - - 设置当前的日志器为相应的日志处理 - -上面我们定义了一个自定义的日志处理包,下面就是使用示例: - - package main - - import ( - "net/http" - "project/logs" - "project/configs" - "project/routes" - ) - - func main() { - addr, _ := configs.MainConfig.String("server", "addr") - logs.Logger.Info("Start server at:%v", addr) - err := http.ListenAndServe(addr, routes.NewMux()) - logs.Logger.Critical("Server err:%v", err) - } - -## 发生错误发送邮件 -上面的例子解释了如何设置发送邮件,我们通过如下的smtp配置用来发送邮件: - - - - - -邮件的格式通过criticalemail配置,然后通过其他的配置发送邮件服务器的配置,通过recipient配置接收邮件的用户,如果有多个用户可以再添加一行。 - -要测试这个代码是否正常工作,可以在代码中增加类似下面的一个假消息。不过记住过后要把它删除,否则上线之后就会收到很多垃圾邮件。 - - logs.Logger.Critical("test Critical message") - -现在,只要我们的应用在线上记录一个Critical的信息,你的邮箱就会收到一个Email,这样一旦线上的系统出现问题,你就能立马通过邮件获知,就能及时的进行处理。 -## 使用应用日志 -对于应用日志,每个人的应用场景可能会各不相同,有些人利用应用日志来做数据分析,有些人利用应用日志来做性能分析,有些人来做用户行为分析,还有些就是纯粹的记录方便应用出现问题的时候可以查找问题。 - -举一个例子,我们需要跟踪用户尝试登陆系统的操作。这里会把成功与不成功的尝试都记录下来。记录成功的使用"Info"日志级别,而不成功的使用"warn"级别。如果想查找所有不成功的登陆,我们可以利用linux的grep之类的命令工具,如下: - - # cat /data/logs/roll.log | grep "failed login" - 2012-12-11 11:12:00 WARN : failed login attempt from 11.22.33.44 username password - -通过这种方式我们就可以很方便的查找相应的信息,这样有利于我们针对应用日志做一些统计和分析。另外我们还需要考虑日志的大小,对于一个高流量的Web应用来说,日志的增长是相当可怕的,所以我们在seelog的配置文件里面设置了logrotate,这样就能保证日志文件不会因为不断变大而导致我们的磁盘空间不够引起问题。 -## 小结 -通过上面对seelog系统的介绍,以及如何开发基于seelog的日志处理包,我们现在可以很轻松的就搭建起来一个功能强大的日志处理系统,日志处理系统对于我们的数据分析提供可靠的数据源,通过分析可以进一步优化我们的系统,而且一旦应用出现问题也方便我们查找问题,而同时seelog也提供了调试输出的功能,这样对于开发阶段来说就可以很方便的来调试各种数据,上线的时候通过设置minlevel可以很方便的屏蔽这些调试级别的信息。 - -## links - * [目录]() - * 上一章: [部署与维护](<12.md>) +# 12.1 应用日志 +我们期望开发的Web应用程序能够把整个程序运行过程中出现的各种事件一一记录下来,Go语言中提供了一个简易的log包,我们使用该包可以方便的实现日志记录的功能,这些日志都是基本的给予fmt包的打印再结合panic之类的一些函数来处理我们一般的打印抛出错误之类的。如果我们想把我们的应用日志保存到文件,然后又能够结合日志实现很多复杂的功能,编写过Java或者C++的读者应该都使用过log4j和log4cpp之类的日志工具,Go目前标准包就如前面介绍的只是包含了一个简单的包,但是目前第三方开发的一个日志系统实现了很强大的日志功能,`https://github.com/cihub/seelog`,接下来我们介绍如何该日志系统来实现我们应用的日志功能。 + +## seelog介绍 +seelog是用Go语言实现的一个日志系统,它提供了一些简单的函数来实现复杂的日志分配、过滤和格式化。主要有如下特性: + +- XML的动态配置,可以不用重新编译程序而动态的加载配置信息 +- 支持热更新,能够动态改变配置而不需要重启应用 +- 支持多输出流,能够同时把日志输出到多种流中、例如文件流、网络流等 +- 支持不同的日志输出 + + - 命令行输出 + - 文件输出 + - 缓存输出 + - 支持log rotate + - SMTP邮件 + +上面只列举了部分特性,seelog是一个特别强大的日志处理系统,详细的内容请参看官方wiki。接下来我将简要介绍一下如何在项目中使用它: + +首先安装seelog + + go get -u github.com/cihub/seelog + +然后我们来看一个简单的例子: + + package main + + import log "github.com/cihub/seelog" + + func main() { + defer log.Flush() + log.Info("Hello from Seelog!") + } + +编译后运行如果出现了`Hello from seelog`,说明seelog日志系统已经成功安装并且可以正常运行了。 + +## 基于seelog的自定义日志处理 +seelog支持自定义日志处理,下面是我基于它自定义的日志处理包的部分内容: + + package logs + + import ( + "errors" + "fmt" + seelog "github.com/cihub/seelog" + "io" + ) + + var Logger seelog.LoggerInterface + + func loadAppConfig() { + appConfig := ` + + + + + + + + + + + + + + + + + ` + logger, err := seelog.LoggerFromConfigAsBytes([]byte(appConfig)) + if err != nil { + fmt.Println(err) + return + } + UseLogger(logger) + } + + func init() { + DisableLog() + loadAppConfig() + } + + // DisableLog disables all library log output + func DisableLog() { + Logger = seelog.Disabled + } + + // UseLogger uses a specified seelog.LoggerInterface to output library log. + // Use this func if you are using Seelog logging system in your app. + func UseLogger(newLogger seelog.LoggerInterface) { + Logger = newLogger + } + +上面主要实现了三个函数, + +- `DisableLog` + + 初始化全局变量Logger为seelog的禁用状态,主要为了防止Logger被多次初始化 +- `loadAppConfig` + + 根据配置文件初始化seelog的配置信息,这里我们把配置文件通过字符串读取设置好了,当然也可以通过读取XML文件。里面的配置说明如下: + + - seelog + + minlevel参数可选,如果被配置,高于或等于此级别的日志会被记录,同理maxlevel。 + - outputs + + 输出信息的目的地,这里分成了两份数据,一份记录到log rotate文件里面。另一份设置了filter,如果这个错误级别是critical,那么将发送报警邮件。 + + - formats + + 定义了各种日志的格式 + +- `UseLogger` + + 设置当前的日志器为相应的日志处理 + +上面我们定义了一个自定义的日志处理包,下面就是使用示例: + + package main + + import ( + "net/http" + "project/logs" + "project/configs" + "project/routes" + ) + + func main() { + addr, _ := configs.MainConfig.String("server", "addr") + logs.Logger.Info("Start server at:%v", addr) + err := http.ListenAndServe(addr, routes.NewMux()) + logs.Logger.Critical("Server err:%v", err) + } + +## 发生错误发送邮件 +上面的例子解释了如何设置发送邮件,我们通过如下的smtp配置用来发送邮件: + + + + + +邮件的格式通过criticalemail配置,然后通过其他的配置发送邮件服务器的配置,通过recipient配置接收邮件的用户,如果有多个用户可以再添加一行。 + +要测试这个代码是否正常工作,可以在代码中增加类似下面的一个假消息。不过记住过后要把它删除,否则上线之后就会收到很多垃圾邮件。 + + logs.Logger.Critical("test Critical message") + +现在,只要我们的应用在线上记录一个Critical的信息,你的邮箱就会收到一个Email,这样一旦线上的系统出现问题,你就能立马通过邮件获知,就能及时的进行处理。 +## 使用应用日志 +对于应用日志,每个人的应用场景可能会各不相同,有些人利用应用日志来做数据分析,有些人利用应用日志来做性能分析,有些人来做用户行为分析,还有些就是纯粹的记录,以方便应用出现问题的时候辅助查找问题。 + +举一个例子,我们需要跟踪用户尝试登陆系统的操作。这里会把成功与不成功的尝试都记录下来。记录成功的使用"Info"日志级别,而不成功的使用"warn"级别。如果想查找所有不成功的登陆,我们可以利用linux的grep之类的命令工具,如下: + + # cat /data/logs/roll.log | grep "failed login" + 2012-12-11 11:12:00 WARN : failed login attempt from 11.22.33.44 username password + +通过这种方式我们就可以很方便的查找相应的信息,这样有利于我们针对应用日志做一些统计和分析。另外我们还需要考虑日志的大小,对于一个高流量的Web应用来说,日志的增长是相当可怕的,所以我们在seelog的配置文件里面设置了logrotate,这样就能保证日志文件不会因为不断变大而导致我们的磁盘空间不够引起问题。 + +## 小结 +通过上面对seelog系统及如何基于它进行自定义日志系统的学习,现在我们可以很轻松的随需构建一个合适的功能强大的日志处理系统了。日志处理系统为数据分析提供了可靠的数据源,比如通过对日志的分析,我们可以进一步优化系统,或者应用出现问题时方便查找定位问题,另外seelog也提供了日志分级功能,通过对minlevel的配置,我们可以很方便的测试或发布版本的输出消息。 + +## links + * [目录]() + * 上一章: [部署与维护](<12.md>) * 下一节: [网站错误处理](<12.2.md>) \ No newline at end of file