Go微服务 - 第十部分 - 集中化日志
第十部分: Go微服务 - 集中化日志本文介绍我们的Go微服务基于Logrus、Docker Gelf日志驱动以及Loggly服务(Logging as a Service)的日志策略。
简介日志。你根本不知道你会失去多少,直到你这样做。为你的团队制定关于记录什么,什么时候记录以及如何记录,可能是产生可维护应用程序的关键因素之一。然后,微服务就发生了。 虽然对于单体应用来说处理一些日志文件通常都是可管理的(虽然存在例外...),但考虑到对于基于微服务的应用程序来说,同样可能使用数百个甚至数千个服务容器来产生日志。如果没有一个搜集和汇总日志的解决方案,基本上考虑不了变得更大的时候的问题了。 谢天谢地,很多聪明人已经想到这一点 - 叫做ELK的著名栈可能就是开源社区中最著名的一个。它是ElasticSearch,LogStash和Kibana构成的Elastic Stack(ELK),推荐可以在驻机和云主机上使用。然而ELK的文章遍地都是,所以本文我们基于四个部分来探索集中日志记录解决方案LaaS:
解决方案概览
源代码https://github.com/walkerqiao... 通常我们的Go微服务直到现在都是使用的fmt或log包打的日志,一般都输出到stdout或stderr。我们希望能更好的控制日志级别和格式。在Java世界,我们很多(大部分)都使用log4j、logback、slf4j之类的框架来处理日志。本文我们选择使用Logrus作为日志API,它大体上提供了我刚提到的关于日志级别、格式化还是钩子API同样类型的功能。 使用logrus使用logrus很好的特性就是它实现了目前我们用于日志的fmt,log相同的接口。这就意味着我们或多或少的可以使用logrus作为无需太多改变的替换。首先确保你的GOPATH设置正确,然后使用下面的命令获取logrus: go get github.com/sirupsen/logrus 更新代码我们使用老派方式来操作。对于common,accountservice,vipservice分别使用IDE或文本编辑器做全局搜索替换。 fmt.和log.替换为logrus.。那么现在就会出现很多logrus.Println和logrus.Pringf调用。 即便通过这样的方式不错,但是我还是建议使用logrus更一般化的严格支持,例如INFO,WARN,DEBUG之类的。例如: fmt log logrus Println Println Infoln printf Printf Infof Error Errorln 有一个例外,fmt.Error用于产生错误实例。不要替换fmt.Error。 使用goimports更新imports鉴于我们已经使用logrus替换了大量的log.Println和fmt.Println(和其他日志函数),我们就有大量无用的import,这样会产生编译错误。与其一个文件一个文件的修改,不如我们使用一个小工具来帮我们做到这些。 这个工具就是goimports,可以通过下面的方式安装: go get golang.org/x/tools/cmd/goimports 安装完后,这个命令工具在$GOPATH/bin目录。 接下来可以进入accountservice,vipservice,执行下面的命令: cd $GOPATH/src/github.com/callistaenterprise/goblog/accountservice $GOPATH/bin/goimports -w **/*.go 执行goimports会自动为所有的文件添加未import的语句,同时会去掉无用的import语句。 然后可以对我们所有的微服务代码进行这样的操作,包括common目录。 然后运行go build确保每个服务都能正常编译。 配置logrus如果我们不配置logrus,它将直接以纯文本的形式输出日志内容。例如: logrus.Infof("Starting our service...") // 输出内容 INFO[0000] Starting our service... 这里0000是服务启动的时间。不是我们所想要的,我想要一个datetime类型的。 因此我们需要提供一个格式。 func init() { logrus.SetFormatter(&logrus.TextFormatter{ TimestampFormat: "2006-01-02T15:04:05.000",FullTimestamp: true,}) } init函数最适合干这种事情了。设置之后,我们的日志输出就如下所示: INFO[2017-07-17T13:22:49.164] Starting our service... 要比刚才好些。 然而,在我们微服务用例中,我们希望日志日志语句更容易解析,这样我们可以将它们发送到我们的选择的LaaS上,让日志索引、排序、聚合等等。因此当我们不在单例模式(-profile=dev)下运行微服务时,我们将希望使用JSON格式。 我们再次修改init函数, 这样它将使用json格式替代除非有-profile=dev标志传入。 func init() { profile := flag.String("profile","test","Environment profile") if *profile == "dev" { logrus.SetFormatter(&logrus.TextFormatter{ TimestampFormat: "2006-01-02T15:04:05.000",}) } else { logrus.SetFormatter(&logrus.JSONFormatter{}) } } 输出内容如下: {"level":"info","msg":"Starting our service...","time":"2017-07-17T16:03:35+02:00"} 就是这样,你可以阅读logrus的文档获得更全面的例子。 应该清楚的是,标准的logrus日志不支持来你在其他平台使用的细粒度的控制,例如通过配置修改某些给定包以调试模式进行日志。然而,可以创建范围话的日志实例,使得更细粒度的配置成为可能,例如: var LOGGER = logrus.Logger{} // <-- Create logger instance func init() { // Some other init code... // Example 1 - using global logrus API logrus.Infof("Successfully initialized") // Example 2 - using logger instance LOGGER.Infof("Successfully initialized") } 这里只是示例代码,仓库中是不存在的。 通过使用LOGGER实例,就可以配置更细粒度的应用级别的日志。然而,我已经选择使用全局日志,使用logrus.X作为本文中代码使用的日志记录。 2. Docker Gelf驱动器Gelf是什么? 它是Greylog Extended Log Format的首字母缩写,是logstash的标准格式。 { "version":"1.1","host":"swarm-manager-0","short_message":"Starting HTTP service at 6868","timestamp":1.487625824614e+09,"level":6,"_command":"./vipservice-linux-amd64 -profile=test","_container_id":"894edfe2faed131d417eebf77306a0386b43027e0bdf75269e7f9dcca0ac5608","_container_name":"vipservice.1.jgaludcy21iriskcu1fx9nx2p","_created":"2017-02-20T21:23:38.877748337Z","_image_id":"sha256:1df84e91e0931ec14c6fb4e559b5aca5afff7abd63f0dc8445a4e1dc9e31cfe1","_image_name":"someprefix/vipservice:latest","_tag":"894edfe2faed" } 让我们看看如何修改copyall.sh脚本中的docker service create让它支持Gelf驱动的: docker service create --log-driver=gelf --log-opt gelf-address=udp://192.168.99.100:12202 --log-opt gelf-compression-type=none --name=accountservice --replicas=1 --network=my_network -p=6767:6767 someprefix/accountservice
3. 日志集合和使用Gelftail进行日志聚合4. Loggly总结本文我们看了集中化日志方面的东西 - 为什么它很重要,如何对Go微服务进行格式化日志,如何使用容器编排里边的日志驱动器在日志状态上传到LaaS提供商之前对日志进行预处理。 下一节,是时候使用Netflix Hystrix为我们微服务添加断路器和弹性(resilience)。 中英文对照
参考链接
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |