Fabric 1.0原始碼分析(14) flogging(Fabric日誌系統)

尹成發表於2018-05-20
# Fabric 1.0原始碼筆記 之 flogging(Fabric日誌系統)

## 1、flogging概述

flogging,即fabric logging,對第三方日誌包go-logging做了封裝,供全域性使用。go-logging地址:https://github.com/op/go-logging。
flogging程式碼集中在common/flogging目錄下,包括logging.go和grpclogger.go。

* logging.go,定義了預設的日誌格式、日誌級別和日誌輸出,以及modules和peerStartModules做模組和日誌級別的對映。並定義了若干對go-logging封裝的函式。
* grpclogger.go,基於封裝go-logging定義了結構體grpclogger及其方法,並用於設定grpclog。grpclog預設使用go標準庫日誌包,此舉可使得grpclog也使用go-logging和flogging功能。

## 2、flogging的常量和全域性變數

涉及常量:pkgLogID、defaultFormat和defaultLevel,分別表示僅在flogging包內程式碼使用的go-logging名稱、預設的日誌格式和預設的日誌級別。

涉及全域性變數如下:

```go
var (
    logger *logging.Logger //僅在flogging包內程式碼使用的logging.Logger物件
    defaultOutput *os.File //預設的日誌輸出
    modules map[string]string //儲存所有模組及其各自的日誌級別的對映
    peerStartModules map[string]string //儲存內容與modules相同
    lock sync.RWMutex //RWMutex讀寫鎖
    once sync.Once //對於從全域性的角度只需要執行一次的程式碼,比如全域性初化操始作,go語言提供了一個Once型別來保證全域性的唯一性操作
)
//程式碼在common/flogging/logging.go
```

## 3、flogging對go-logging的封裝

### 3.1、flogging包初始化

flogging包初始化,即init()函式,程式碼如下:

```go
func init() {
    logger = logging.MustGetLogger(pkgLogID) //建立僅在flogging包內程式碼使用的logging.Logger物件
    Reset() //全域性變數初始化為預設值
    initgrpclogger() //初始化gRPC Logger,即建立logging.Logger物件,並用這個物件設定grpclog
}
//程式碼在common/flogging/logging.go
```

其中func Reset()程式碼如下。
其作用為:初始化modules和lock,建立一個日誌輸出物件並設定為預設的日誌格式和預設的日誌級別。
設定各模組的日誌級別,並更新modules。

```go
func Reset() {
    modules = make(map[string]string) //初始化modules
    lock = sync.RWMutex{} //初始化lock
    defaultOutput = os.Stderr //預設的日誌輸出置為os.Stderr
    //SetFormat()設定並獲取go-logging日誌格式,InitBackend()建立一個日誌輸出物件並設定輸出格式和日誌級別
    InitBackend(SetFormat(defaultFormat), defaultOutput)
    InitFromSpec("") //設定各模組日誌級別,並更新modules
}
//程式碼在common/flogging/logging.go
```

func InitBackend(formatter logging.Formatter, output io.Writer)程式碼如下。
建立一個日誌輸出物件並設定輸出格式和日誌級別。

```go
func InitBackend(formatter logging.Formatter, output io.Writer) {
    backend := logging.NewLogBackend(output, "", 0) //建立一個日誌輸出物件
    backendFormatter := logging.NewBackendFormatter(backend, formatter) //設定日誌輸出物件的輸出格式
    logging.SetBackend(backendFormatter).SetLevel(defaultLevel, "") //設定日誌輸出物件的日誌級別
}
//程式碼在common/flogging/logging.go
```

func InitFromSpec(spec string) string程式碼如下。
其中spec格式為:[<module>[,<module>...]=]<level>[:[<module>[,<module>...]=]<level>...]。
此處傳入spec為"",將""模組日誌級別設定為defaultLevel,並會將modules初始化為defaultLevel。

```go
levelAll := defaultLevel //defaultLevel為logging.INFO
var err error

if spec != "" { //如果spec不為空,則按既定格式讀取
    fields := strings.Split(spec, ":") //按:分割
    for _, field := range fields {
        split := strings.Split(field, "=") //按=分割
        switch len(split) {
        case 1: //只有level
            if levelAll, err = logging.LogLevel(field); err != nil { //levelAll賦值為logging.LogLevel列舉中定義的Level級別
                levelAll = defaultLevel // 如果沒有定義,則使用預設日誌級別
            }
        case 2: //針對module,module...=level,split[0]為模組集,split[1]為要設定的日誌級別
            levelSingle, err := logging.LogLevel(split[1]) //levelSingle賦值為logging.LogLevel列舉中定義的Level級別
            modules := strings.Split(split[0], ",") //按,分割獲取模組名
            for _, module := range modules {
                logging.SetLevel(levelSingle, module) //本條規則中所有模組日誌級別均設定為levelSingle
            }
        default:
            //...
        }
    }
}

logging.SetLevel(levelAll, "") // 將""模組日誌級別設定為levelAll,如果logging.GetLevel(module)沒找到時將使用""模組日誌級別
for k := range modules {
    MustGetLogger(k) //獲取模組日誌級別,並更新modules
}
MustGetLogger(pkgLogID) //pkgLogID及其日誌級別,更新至modules
return levelAll.String() //返回levelAll
//程式碼在common/flogging/logging.go
```

MustGetLogger會調取go-logging包中GetLevel(),附GetLevel()程式碼如下。
優先按module獲取日誌級別,如未找到則按""模組獲取日誌級別,如仍未找到則預設按DEBUG級別。

```go
func (l *moduleLeveled) GetLevel(module string) Level {
    level, exists := l.levels[module]
    if exists == false {
        level, exists = l.levels[""]
        if exists == false {
            level = DEBUG
        }
    }
    return level
}
//程式碼在github.com/op/go-logging/level.go
```

### 3.2、flogging包封裝的方法

flogging包封裝的方法,如下:

```go
func Reset() //全域性變數初始化為預設值
func SetFormat(formatSpec string) logging.Formatter //設定並獲取go-logging日誌格式
func InitBackend(formatter logging.Formatter, output io.Writer) //建立一個日誌輸出物件並設定輸出格式和日誌級別
func DefaultLevel() string //獲取defaultLevel
func GetModuleLevel(module string) string //呼叫logging.GetLevel(module)獲取模組日誌級別
func SetModuleLevel(moduleRegExp string, level string) (string, error) //包裝setModuleLevel
func setModuleLevel(moduleRegExp string, level string, isRegExp bool, revert bool) (string, error) //設定模組日誌級別並更新modules
func MustGetLogger(module string) *logging.Logger //建立logging.Logger例項,獲取模組日誌級別,並更新modules
func InitFromSpec(spec string) string //設定各模組日誌級別,並更新modules
func SetPeerStartupModulesMap() //modules內容複製給peerStartModules
func GetPeerStartupLevel(module string) string //從peerStartModules中獲取模組日誌級別
func RevertToPeerStartupLevels() error //按peerStartModules中內容,設定模組日誌級別並更新modules
//程式碼在common/flogging/logging.go
```

## 4、grpclogger實現

grpclogger結構體定義:

```go
type grpclogger struct {
    logger *logging.Logger
}
//程式碼在common/flogging/grpclogger.go
```

grpclogger初始化:

```go
func initgrpclogger() {
    glogger := MustGetLogger(GRPCModuleID) //建立logging.Logger物件,獲取模組日誌級別,並更新modules
    grpclog.SetLogger(&grpclogger{glogger}) //用建立的logging.Logger物件設定grpclog
}
//程式碼在common/flogging/grpclogger.go
```

其他方法均為對go-logging的包裝,程式碼如下:

```go
func (g *grpclogger) Fatal(args ...interface{}) {
    g.logger.Fatal(args...)
}

func (g *grpclogger) Fatalf(format string, args ...interface{}) {
    g.logger.Fatalf(format, args...)
}

func (g *grpclogger) Fatalln(args ...interface{}) {
    g.logger.Fatal(args...)
}

// NOTE: grpclog does not support leveled logs so for now use DEBUG
func (g *grpclogger) Print(args ...interface{}) {
    g.logger.Debug(args...)
}

func (g *grpclogger) Printf(format string, args ...interface{}) {
    g.logger.Debugf(format, args...)
}

func (g *grpclogger) Println(args ...interface{}) {
    g.logger.Debug(args...)
}
//程式碼在common/flogging/grpclogger.go
```





網址:http://www.qukuailianxueyuan.io/



欲領取造幣技術與全套虛擬機器資料

區塊鏈技術交流QQ群:756146052  備註:CSDN

尹成學院微信:備註:CSDN






網址:http://www.qukuailianxueyuan.io/



欲領取造幣技術與全套虛擬機器資料

區塊鏈技術交流QQ群:756146052  備註:CSDN

尹成學院微信:備註:CSDN

相關文章