Files
build-web-application-with-…/zh/12.1.md
Riverside fdfb50d127 Update 12.1.md
go 格式修复
2019-02-11 10:48:30 +08:00

253 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 12.1 应用日志
我们期望开发的Web应用程序能够把整个程序运行过程中出现的各种事件一一记录下来Go语言中提供了一个简易的log包我们使用该包可以方便的实现日志记录的功能这些日志都是基于fmt包的打印再结合panic之类的函数来进行一般的打印、抛出错误处理。Go目前标准包只是包含了简单的功能如果我们想把我们的应用日志保存到文件然后又能够结合日志实现很多复杂的功能编写过Java或者C++的读者应该都使用过log4j和log4cpp之类的日志工具可以使用第三方开发的日志系统:[logrus](https://github.com/sirupsen/logrus)和[seelog](https://github.com/cihub/seelog),它们实现了很强大的日志功能,可以结合自己项目选择。接下来我们介绍如何通过该日志系统来实现我们应用的日志功能。
## logrus介绍
logrus是用Go语言实现的一个日志系统与标准库log完全兼容并且核心API很稳定,是Go语言目前最活跃的日志库
首先安装logrus
```Go
go get -u github.com/sirupsen/logrus
```
简单例子:
```Go
package main
import (
log "github.com/Sirupsen/logrus"
)
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
}).Info("A walrus appears")
}
```
### 基于logrus的自定义日志处理
```Go
package main
import (
"os"
log "github.com/Sirupsen/logrus"
)
func init() {
// 日志格式化为JSON而不是默认的ASCII
log.SetFormatter(&log.JSONFormatter{})
// 输出stdout而不是默认的stderr也可以是一个文件
log.SetOutput(os.Stdout)
// 只记录严重或以上警告
log.SetLevel(log.WarnLevel)
}
func main() {
log.WithFields(log.Fields{
"animal": "walrus",
"size": 10,
}).Info("A group of walrus emerges from the ocean")
log.WithFields(log.Fields{
"omg": true,
"number": 122,
}).Warn("The group's number increased tremendously!")
log.WithFields(log.Fields{
"omg": true,
"number": 100,
}).Fatal("The ice breaks!")
// 通过日志语句重用字段
// logrus.Entry返回自WithFields()
contextLogger := log.WithFields(log.Fields{
"common": "this is a common field",
"other": "I also should be logged always",
})
contextLogger.Info("I'll be logged with common and other field")
contextLogger.Info("Me too")
}
```
## seelog介绍
seelog是用Go语言实现的一个日志系统它提供了一些简单的函数来实现复杂的日志分配、过滤和格式化。主要有如下特性
- XML的动态配置可以不用重新编译程序而动态的加载配置信息
- 支持热更新,能够动态改变配置而不需要重启应用
- 支持多输出流,能够同时把日志输出到多种流中、例如文件流、网络流等
- 支持不同的日志输出
- 命令行输出
- 文件输出
- 缓存输出
- 支持log rotate
- SMTP邮件
上面只列举了部分特性seelog是一个特别强大的日志处理系统详细的内容请参看官方wiki。接下来我将简要介绍一下如何在项目中使用它
首先安装seelog
```Go
go get -u github.com/cihub/seelog
```
然后我们来看一个简单的例子:
```Go
package main
import log "github.com/cihub/seelog"
func main() {
defer log.Flush()
log.Info("Hello from Seelog!")
}
```
编译后运行如果出现了`Hello from seelog`说明seelog日志系统已经成功安装并且可以正常运行了。
### 基于seelog的自定义日志处理
seelog支持自定义日志处理下面是我基于它自定义的日志处理包的部分内容
```Go
package logs
import (
// "errors"
"fmt"
// "io"
seelog "github.com/cihub/seelog"
)
var Logger seelog.LoggerInterface
func loadAppConfig() {
appConfig := `
<seelog minlevel="warn">
<outputs formatid="common">
<rollingfile type="size" filename="/data/logs/roll.log" maxsize="100000" maxrolls="5"/>
<filter levels="critical">
<file path="/data/logs/critical.log" formatid="critical"/>
<smtp formatid="criticalemail" senderaddress="astaxie@gmail.com" sendername="ShortUrl API" hostname="smtp.gmail.com" hostport="587" username="mailusername" password="mailpassword">
<recipient address="xiemengjun@gmail.com"/>
</smtp>
</filter>
</outputs>
<formats>
<format id="common" format="%Date/%Time [%LEV] %Msg%n" />
<format id="critical" format="%File %FullPath %Func %Msg%n" />
<format id="criticalemail" format="Critical error on our server!\n %Time %Date %RelFile %Func %Msg \nSent by Seelog"/>
</formats>
</seelog>
`
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`
设置当前的日志器为相应的日志处理
上面我们定义了一个自定义的日志处理包,下面就是使用示例:
```Go
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配置用来发送邮件
```html
<smtp formatid="criticalemail" senderaddress="astaxie@gmail.com" sendername="ShortUrl API" hostname="smtp.gmail.com" hostport="587" username="mailusername" password="mailpassword">
<recipient address="xiemengjun@gmail.com"/>
</smtp>
```
邮件的格式通过criticalemail配置然后通过其他的配置发送邮件服务器的配置通过recipient配置接收邮件的用户如果有多个用户可以再添加一行。
要测试这个代码是否正常工作,可以在代码中增加类似下面的一个假消息。不过记住过后要把它删除,否则上线之后就会收到很多垃圾邮件。
```Go
logs.Logger.Critical("test Critical message")
```
现在只要我们的应用在线上记录一个Critical的信息你的邮箱就会收到一个Email这样一旦线上的系统出现问题你就能立马通过邮件获知就能及时的进行处理。
## 使用应用日志
对于应用日志,每个人的应用场景可能会各不相同,有些人利用应用日志来做数据分析,有些人利用应用日志来做性能分析,有些人来做用户行为分析,还有些就是纯粹的记录,以方便应用出现问题的时候辅助查找问题。
举一个例子,我们需要跟踪用户尝试登陆系统的操作。这里会把成功与不成功的尝试都记录下来。记录成功的使用"Info"日志级别,而不成功的使用"warn"级别。如果想查找所有不成功的登陆我们可以利用linux的grep之类的命令工具如下
```Go
# 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
* [目录](<preface.md>)
* 上一章: [部署与维护](<12.0.md>)
* 下一节: [网站错误处理](<12.2.md>)