目前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