0
点赞
收藏
分享

微信扫一扫

5、GO语言日志管理与平滑升级


5、GO语言日志管理与平滑升级_子进程

5、GO语言日志管理与平滑升级

  • ​​1、Go的日志管理工具​​
  • ​​2、logrus的使用​​
  • ​​2.1 简单使用​​
  • ​​2.2 自定义日志处理​​
  • ​​3、使用应用日志​​
  • ​​4、平滑升级​​

1、Go的日志管理工具

Go语言提供了一个简易的log包,可以方便的实现日志记录的功能,但是这些日志都是基于fmt包的打印再结合panic之类的函数来进行一般的打印、抛出错误处理。

Go目前标准包只是包含了简单的功能,如果我们想把我们的应用日志保存到文件,然后又能够结合日志实现很多复杂的功能(例如Java的log4j,Node的log4js),推荐​​logrus​​。

2、logrus的使用

2.1 简单使用

安装logrus

go get -u github.com/sirupsen/logrus

简单例子:

package main

import (
log "github.com/sirupsen/logrus"
)

func main() {
log.WithFields(log.Fields{
"animal": "walrus",
}).Info("Info信息*****")
}

2.2 自定义日志处理

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.InfoLevel)
}

func main() {

log.WithFields(log.Fields{
"params1": "walrus",
"params2": 10,
}).Info("info信息.....")

log.WithFields(log.Fields{
"params3": true,
"params4": 122,
}).Warn("warn信息.....")

log.WithFields(log.Fields{
"params5": true,
"params6": 100,
}).Fatal("fatal信息....")

}

3、使用应用日志

对于应用日志,每个人的应用场景可能会各不相同,有些人利用应用日志来做数据分析,有些人利用应用日志来做性能分析,有些人来做用户行为分析,还有些就是纯粹的记录,以方便应用出现问题的时候辅助查找问题。

举一个例子,我们需要跟踪用户尝试登陆系统的操作。这里会把成功与不成功的尝试都记录下来。记录成功的使用"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,这样就能保证日志文件不会因为不断变大而导致我们的磁盘空间不够引起问题。

4、平滑升级

服务器在升级时,正在处理的请求需要等待其完成,再退出。Go1.8之后支持该设计。

实现步骤原理:

  • 1 fork一个子进程,继承父进程的监听socket
  • 2 子进程启动后,接收新的连接,父进程处理原有请求并且不再接收新请求

当系统重启或者升级时,正在处理的请求以及新来的请求该如何处理?

正在处理的请求如何处理:

等待处理完成之后,再推出,Go1.8之后已经支持。比如每来一个请求,计数+1,处理完一个请求,计数-1,当计数为0时,则执行系统升级。

新进来的请求如何处理:

  • Fork一个子进程,继承父进程的监听socket(os.Cmd对象中的ExtraFiles参数进行传递,并继承文件句柄)
  • 子进程启动成功后,接收新的连接
  • 父进程停止接收新的连接,等已有的请求处理完毕,退出,优雅重启成功。

package main

import (
"flag"
"fmt"
"os"
"os/exec"
"time"
)

var (
child *bool
)

func startChild(file *os.File) {

args := []string{"-child"}
cmd := exec.Command(os.Args[0], args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr

cmd.ExtraFiles = []*os.File{file}
err := cmd.Start()
if err != nil {
fmt.Printf("start child failed, err:%v\n", err)
return
}

}

func init() {
//命令行嗲有child选项,则是子进程,没有则是父进程
child = flag.Bool("child", false, "继承于父进程")
flag.Parse()
}

func readFromParent() {
// fd=0 标准输出,=1标准输入,=2标准错误输出,=3 ExtraFiles[0] =4 EAxtraFiles[1]
f := os.NewFile(3, "")
count := 0
for {
str := fmt.Sprintf("hello, i'child process, write:%d line \n", count)
count++
_, err := f.WriteString(str)
if err != nil {
fmt.Printf("wrote string failed, err:%v\n", err)
time.Sleep(time.Second)
continue
}
time.Sleep(time.Second)
}
}

func main() {

if child != nil && *child == true {
fmt.Printf("继承于父进程的文件句柄\n")
readFromParent()
return
}

//父进程逻辑
file, err := os.OpenFile("./test_inherit.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0755)
if err != nil {
fmt.Printf("open file failed, err:%v\n", err)
return
}

_, err = file.WriteString("parent write one line \n")
if err != nil {
fmt.Printf("parent write failed, err:%v\n", err)
}

startChild(file)
fmt.Printf("parent exited")
}

5、GO语言日志管理与平滑升级_go_02


举报

相关推荐

0 条评论