## 使用包级全局变量 在前面的章节中,我们使用的log都是包级别的变量。有时,构建库以支持具有各种方法和顶级函数的结构是有用的,这样就可以直接使用它们而无需传递。 本节演示了如何使用sync.Once确保全局日志logger仅初始化一次。它也可以通过Set方法绕过。示例中只导出WithField和Debug,但对于你可以想象到的其他附加到日志对象的每个方法也适用。 ### 实践 1. 获取第三方库: ``` go get github.com/apex/log ``` 2. 建立global.go: ``` package global import ( "errors" "os" "sync" "github.com/sirupsen/logrus" ) var ( log *logrus.Logger initLog sync.Once ) // Init设置logger 如果多次初始化 会返回错误 func Init() error { err := errors.New("already initialized") initLog.Do(func() { err = nil log = logrus.New() log.Formatter = &logrus.JSONFormatter{} log.Out = os.Stdout log.Level = logrus.DebugLevel }) return err } // SetLog 设置log func SetLog(l *logrus.Logger) { log = l } // WithField 使用全局log返回logrus.Entry指针 func WithField(key string, value interface{}) *logrus.Entry { return log.WithField(key, value) } // Debug 使用全局log记录信息 func Debug(args ...interface{}) { log.Debug(args...) } ``` 3. 建立log.go: ``` package global // UseLog 演示了使用全局 log func UseLog() error { if err := Init(); err != nil { return err } // 如果在其他包 应该调用global.WithField 和 global.Debug WithField("key", "value").Debug("hello") Debug("test") return nil } ``` 4. 建立main.go: ``` package main import "github.com/agtorre/go-cookbook/chapter4/global" func main() { if err := global.UseLog(); err != nil { panic(err) } } ``` 5. 这会输出: ``` {"key":"value","level":"debug","msg":"hello","time":"2017-02- 12T19:22:50-08:00"} {"level":"debug","msg":"test","time":"2017-02-12T19:22:50- 08:00"} ``` ### 说明 这些全局包级对象的常见模式是保持全局未导出,并仅通过方法公开所需的功能。 通常,你还可以包含一个方法,以便为需要调用对象的包返回该对象的副本。 sync.Once类型是新引入的结构。这个结构与Do方法一起只在代码中执行一次。我们在初始化代码中使用它,如果多次调用Init,Init函数将抛出一个错误。 虽然此示例使用的是日志操作,但同样也可以想象为对数据库连接,数据流和许多常见用例的使用。 * * * * 学识浅薄,错误在所难免。欢迎在群中就本书提出修改意见,以飨后来者,长风拜谢。 Golang中国(211938256) beego实战(258969317) Go实践(386056972)