btcd logモジュールソースコード分析
5699 ワード
btcdで使用されるlogモジュールは、自分のプロジェクトウェアハウスのbtclogパッケージを参照します.
パッケージ全体で、実装するインタフェースと簡単なパッケージを定義します.
logレベルの判断は、実装インタフェースに含まれていない方法で、対応するレベルと比較して、その日を出力するかどうかを判断します.例えば、次のようにします.
各実装インタフェースの方法では、書き込み動作は、
btclogパッケージには、グローバル変数Disabledがあり、init関数で初期化されていますが、このlogインスタンスはlogに書き込まれるのではなく、すべてのlogを破棄します.
使用
btcdプロジェクトmainパッケージでは、グローバルlog変数
Logger()メソッドは、tagフィールドを変換して、Backedを共有するだけです
mainパケット初期化関数では、各パケットのグローバル変数を割り当てます.mainパッケージのグローバル変数を各モジュールにコピーすることに相当します.各モジュールは,このパケットに対するlogコピーを用いてlogの記録を行う.
各モジュールでは、golang init関数の初期化順序の問題に関連するログはデフォルトで出力されません.簡単に説明すると、プログラム起動時にmainパケットからmainパケットimportの各モジュールが導入されるため、パッケージが先に初期化され、このとき各モジュールにおけるinit関数が実行する、DisableLog()メソッドは本モジュールにおけるグローバル変数logを初期化し、btclogとする.Disabled、つまり前述したすべてのログを破棄します.現在のモジュールのlogにはログ情報は記録されません.モジュールのinit実行が完了したら、指定したモジュールのグローバル変数を指定したtagを持つlog、すなわちmainの各モジュールlogを表すコピーに再割り当てするmainパケットのinit関数を実行します.これにより,各モジュールのlog定義が完了する.
ここで疑問に思うのは、なぜモジュールのlogが両方を初期化するのかということです.これは一定の柔軟性を実現するためです.たとえば、プログラム設計者がminingモジュールのログ記録を停止した場合、mainパケットからグローバル変数
mempoolモジュールを例に挙げると、
その他
btclogパケットにはバッファプールも含まれており、一部のモジュールではlogに対してカスタマイズされた処理が行われており、btcdのlog関連ソースコードを見ることができます.
パッケージ全体で、実装するインタフェースと簡単なパッケージを定義します.
type Logger interface {
Tracef(format string, params ...interface{})
Debugf(format string, params ...interface{})
Infof(format string, params ...interface{})
Warnf(format string, params ...interface{})
Errorf(format string, params ...interface{})
Criticalf(format string, params ...interface{})
Trace(v ...interface{})
Debug(v ...interface{})
Info(v ...interface{})
Warn(v ...interface{})
Error(v ...interface{})
Critical(v ...interface{})
Level() Level
SetLevel(level Level
}
//
type Backend struct {
w io.Writer
mu sync.Mutex // ensures atomic writes
flag uint32
}
// log ,tag subsystem
// slog
type struct {
lvl Level // atomic
tag string // tag log
b *Backend
}
logレベルの判断は、実装インタフェースに含まれていない方法で、対応するレベルと比較して、その日を出力するかどうかを判断します.例えば、次のようにします.
func (l *slog) Warn(args ...interface{}) {
lvl := l.Level()
if lvl <= LevelWarn {
l.b.print("WRN", l.tag, args...)
}
func (l *slog) Warnf(format string, args ...interface{}) {
lvl := l.Level()
if lvl <= LevelWarn {
l.b.printf("WRN", l.tag, format, args...)
}
}
各実装インタフェースの方法では、書き込み動作は、
print
およびprintf
によって実現される.func (b *Backend) print(lvl, tag string, args ...interface{}) {
t := time.Now() // get as early as possible
bytebuf := buffer()
var file string
var line int
if b.flag&(Lshortfile|Llongfile) != 0 {
file, line = callsite(b.flag)
}
formatHeader(bytebuf, t, lvl, tag, file, line)
buf := bytes.NewBuffer(*bytebuf)
fmt.Fprintln(buf, args...)
*bytebuf = buf.Bytes()
b.mu.Lock()
b.w.Write(*bytebuf)
b.mu.Unlock()
recycleBuffer(bytebuf)
}
//
func (b *Backend) printf(lvl, tag string, format string, args ...interface{}) {
t := time.Now() // get as early as possible
bytebuf := buffer()
var file string
var line int
if b.flag&(Lshortfile|Llongfile) != 0 {
file, line = callsite(b.flag)
}
formatHeader(bytebuf, t, lvl, tag, file, line)
buf := bytes.NewBuffer(*bytebuf)
fmt.Fprintf(buf, format, args...)
*bytebuf = append(buf.Bytes(), '
')
b.mu.Lock()
b.w.Write(*bytebuf)
b.mu.Unlock()
recycleBuffer(bytebuf)
}
btclogパッケージには、グローバル変数Disabledがあり、init関数で初期化されていますが、このlogインスタンスはlogに書き込まれるのではなく、すべてのlogを破棄します.
// Disabled is a Logger that will never output anything.
var Disabled Logger
func init() {
Disabled = &slog{lvl: LevelOff, b: NewBackend(ioutil.Discard)}
}
使用
btcdプロジェクトmainパッケージでは、グローバルlog変数
backendLog
を初期化します.次に、各サブモジュールに対して複数のグローバル変数を定義し、各モジュールlogレコードを担当する.これらの変数は、slog構造体のBackendフィールドがポインタタイプであるため、backedLogを共有します.
backendLog = btclog.NewBackend(logWriter{})
logRotator *rotator.Rotator
adxrLog = backendLog.Logger("ADXR")
amgrLog = backendLog.Logger("AMGR")
cmgrLog = backendLog.Logger("CMGR")
bcdbLog = backendLog.Logger("BCDB")
btcdLog = backendLog.Logger("BTCD")
chanLog = backendLog.Logger("CHAN")
discLog = backendLog.Logger("DISC")
indxLog = backendLog.Logger("INDX")
minrLog = backendLog.Logger("MINR")
peerLog = backendLog.Logger("PEER")
rpcsLog = backendLog.Logger("RPCS")
scrpLog = backendLog.Logger("SCRP")
srvrLog = backendLog.Logger("SRVR")
syncLog = backendLog.Logger("SYNC")
txmpLog = backendLog.Logger("TXMP")
Logger()メソッドは、tagフィールドを変換して、Backedを共有するだけです
func (b *Backend) Logger(subsystemTag string) Logger {
return &slog{LevelInfo, subsystemTag, b}
}
mainパケット初期化関数では、各パケットのグローバル変数を割り当てます.mainパッケージのグローバル変数を各モジュールにコピーすることに相当します.各モジュールは,このパケットに対するlogコピーを用いてlogの記録を行う.
func init() {
addrmgr.UseLogger(amgrLog) //
connmgr.UseLogger(cmgrLog)
database.UseLogger(bcdbLog)
blockchain.UseLogger(chanLog)
indexers.UseLogger(indxLog)
mining.UseLogger(minrLog)
cpuminer.UseLogger(minrLog)
peer.UseLogger(peerLog)
txscript.UseLogger(scrpLog)
netsync.UseLogger(syncLog)
mempool.UseLogger(txmpLog)
}
各モジュールでは、golang init関数の初期化順序の問題に関連するログはデフォルトで出力されません.簡単に説明すると、プログラム起動時にmainパケットからmainパケットimportの各モジュールが導入されるため、パッケージが先に初期化され、このとき各モジュールにおけるinit関数が実行する、DisableLog()メソッドは本モジュールにおけるグローバル変数logを初期化し、btclogとする.Disabled、つまり前述したすべてのログを破棄します.現在のモジュールのlogにはログ情報は記録されません.モジュールのinit実行が完了したら、指定したモジュールのグローバル変数を指定したtagを持つlog、すなわちmainの各モジュールlogを表すコピーに再割り当てするmainパケットのinit関数を実行します.これにより,各モジュールのlog定義が完了する.
ここで疑問に思うのは、なぜモジュールのlogが両方を初期化するのかということです.これは一定の柔軟性を実現するためです.たとえば、プログラム設計者がminingモジュールのログ記録を停止した場合、mainパケットからグローバル変数
minrLog = backendLog.Logger("MINR")
と関数呼び出しmining.UseLogger(minrLog)
を除去するだけで、miningがDisabledとしてデフォルト設定され、ログ記録は行われなくなる.mempoolモジュールを例に挙げると、
var log btclog.Logger
func init() {
DisableLog() //
}
// DisableLog disables all library log output. Logging output is disabled
// by default until either UseLogger or SetLogWriter are called.
func DisableLog() {
log = btclog.Disabled //
}
// UseLogger uses a specified Logger to output package logging info.
// This should be used in preference to SetLogWriter if the caller is also
// using btclog.
func UseLogger(logger btclog.Logger) {
log = logger // log
}
その他
btclogパケットにはバッファプールも含まれており、一部のモジュールではlogに対してカスタマイズされた処理が行われており、btcdのlog関連ソースコードを見ることができます.