目前fabric的日志系统是将所有的日志输出定向到stderr,这在生产环境中显然是不可以接受的,日志持久化成了一个亟待解决的问题。
本篇文章将从源码入手,改造fabric的日志系统,实现日志的持久化、日志自动切割等功能。
源码修改
环境准备
- 获取源码
go get github.com/hyperledger/fabric
- 切换到 1.4.4版本(这里我本地已经搭建了1.4.4版本的fabric链,为了方便测试,选择1.4.4版本的源码进行修改)
git checkout v1.4.4
修改源码
要支持日志自动切割、自动清理,需要用到一个日志切割框架https://github.com/lestrrat-go/file-rotatelogs
fabric的日志模块在common/flogging下
经过摸索,问题定位到common/flogging/logging.go文件的Apply函数,
func (s *Logging) Apply(c Config) error {
err := s.SetFormat(c.Format)
if err != nil {
return err
}
if c.LogSpec == "" {
c.LogSpec = os.Getenv("FABRIC_LOGGING_SPEC")
}
if c.LogSpec == "" {
c.LogSpec = defaultLevel.String()
}
err = s.LoggerLevels.ActivateSpec(c.LogSpec)
if err != nil {
return err
}
// 这行代码一定执行
if c.Writer == nil {
c.Writer = os.Stderr
}
s.SetWriter(c.Writer)
var formatter logging.Formatter
switch s.Encoding() {
case JSON, LOGFMT:
formatter = SetFormat(defaultFormat)
default:
formatter = SetFormat(c.Format)
}
InitBackend(formatter, c.Writer)
return nil
}
这个函数接收Config对象,而在common/flogging包的初始化方法中,Config仅仅只是毫无内容对象,所以c.Writer最终指向了os.Stderr。
func init() {
logging, err := New(Config{})
if err != nil {
panic(err)
}
Global = logging
logger = Global.Logger("flogging")
grpcLogger := Global.ZapLogger("grpc")
grpclog.SetLogger(NewGRPCLogger(grpcLogger))
}
4.可以修改c.Writer使它指向文件输出,这里使用环境变量控制日志的持久化以及持久化的路径,完整代码如下
func (s *Logging) Apply(c Config) error {
err := s.SetFormat(c.Format)
if err != nil {
return err
}
if c.LogSpec == "" {
c.LogSpec = os.Getenv("FABRIC_LOGGING_SPEC")
}
if c.LogSpec == "" {
c.LogSpec = defaultLevel.String()
}
err = s.LoggerLevels.ActivateSpec(c.LogSpec)
if err != nil {
return err
}
if c.Writer == nil {
c.Writer = os.Stderr
}
// 如果开启了持久化,则持久化到文件,并且使用rotatelogs来管理日志
if enable,err:=strconv.ParseBool(os.Getenv("FABRIC_LOG_PERSISTENCE"));err==nil&&enable{
filePath:=os.Getenv("FABRIC_LOG_PERSISTENCE_PATH")
if filePath == ""{
// 持久化路径未配置的情况下,使用默认路径
filePath = "/var/log/hyperledger/fabric/logs"
}
fileName := path.Join(filePath,"log")
logWriter, err := rotatelogs.New(fileName + ".%Y%m%d.log",
rotatelogs.WithLinkName(fileName),// 软链接
rotatelogs.WithMaxAge(7*24*time.Hour), //日志保留7天
rotatelogs.WithRotationTime(24*time.Hour))// 每天切割一次
if err!=nil{
panic("build logWriter failed")
}
c.Writer = logWriter
}
s.SetWriter(c.Writer)
var formatter logging.Formatter
switch s.Encoding() {
case JSON, LOGFMT:
formatter = SetFormat(defaultFormat)
default:
formatter = SetFormat(c.Format)
}
InitBackend(formatter, c.Writer)
return nil
}
构建镜像
官方已经写好了所有镜像制作过程,在根目录下执行即可(建议在linux下执行)
make all
执行完后即可以看到制作好的镜像:
可以想办法将镜像保存下来,通过docker save 或者将它推到镜像仓库。
运行测试
这里只需注意设置好环境变量即可
FABRIC_LOG_PERSISTENCE=true
FABRIC_LOG_PERSISTENCE_PATH=/var/run/hyperlerdger/fabric/peer/logs