日誌和配置的重要性
前面已經介紹過日誌在我們程式開發中起著很重要的作用,透過日誌我們可以記錄除錯我們的資訊,當初介紹過一個日誌系統 seelog,根據不同的 level 輸出不同的日誌,這個對於程式開發和程式部署來說至關重要。我們可以在程式開發中設定 level 低一點,部署的時候把 level 設定高,這樣我們開發中的除錯資訊可以遮蔽掉。
配置模組對於應用部署牽涉到伺服器不同的一些配置資訊非常有用,例如一些資料庫配置資訊、監聽埠、監聽地址等都是可以透過配置檔案來配置,這樣我們的應用程式就具有很強的靈活性,可以透過配置檔案的配置部署在不同的機器上,可以連線不同的資料庫之類別的。
beego 的日誌設計
beego 的日誌設計部署思路來自於 seelog,根據不同的 level 來記錄日誌,但是 beego 設計的日誌系統比較輕量級,採用了系統的 log.Logger 介面,預設輸出到 os.Stdout,使用者可以實現這個介面然後透過 beego.SetLogger 設定自訂的輸出,詳細的實現如下所示:
Copy // Log levels to control the logging output.
const (
LevelTrace = iota
LevelDebug
LevelInfo
LevelWarning
LevelError
LevelCritical
)
// logLevel controls the global log level used by the logger.
var level = LevelTrace
// LogLevel returns the global log level and can be used in
// own implementations of the logger interface.
func Level () int {
return level
}
// SetLogLevel sets the global log level used by the simple
// logger.
func SetLevel (l int ) {
level = l
}
上面這一段實現了日誌系統的日誌分級,預設的級別是 Trace,使用者透過 SetLevel 可以設定不同的分級。
Copy // logger references the used application logger.
var BeeLogger = log. New (os.Stdout, "" , log.Ldate | log.Ltime)
// SetLogger sets a new logger.
func SetLogger (l * log . Logger ) {
BeeLogger = l
}
// Trace logs a message at trace level.
func Trace (v ...interface {}) {
if level <= LevelTrace {
BeeLogger. Printf ( "[T] %v \n" , v)
}
}
// Debug logs a message at debug level.
func Debug (v ...interface {}) {
if level <= LevelDebug {
BeeLogger. Printf ( "[D] %v \n" , v)
}
}
// Info logs a message at info level.
func Info (v ...interface {}) {
if level <= LevelInfo {
BeeLogger. Printf ( "[I] %v \n" , v)
}
}
// Warning logs a message at warning level.
func Warn (v ...interface {}) {
if level <= LevelWarning {
BeeLogger. Printf ( "[W] %v \n" , v)
}
}
// Error logs a message at error level.
func Error (v ...interface {}) {
if level <= LevelError {
BeeLogger. Printf ( "[E] %v \n" , v)
}
}
// Critical logs a message at critical level.
func Critical (v ...interface {}) {
if level <= LevelCritical {
BeeLogger. Printf ( "[C] %v \n" , v)
}
}
上面這一段程式碼預設初始化了一個 BeeLogger 物件,預設輸出到 os.Stdout,使用者可以透過 beego.SetLogger 來設定實現了 logger 的介面輸出。這裡面實現了六個函式:
Trace(一般的記錄資訊,舉例如下:)
"Entered parse function validation block"
"Validation: entered second 'if'"
"Dictionary 'Dict' is empty. Using default value"
Debug(除錯資訊,舉例如下:)
"Response generated. Response size: 10000. Sending."
"New file received. Type:PNG Size:20000"
Info(列印資訊,舉例如下:)
"Hourly statistics: Requested pages: 12345 Errors: 123 ..."
"Service paused. Waiting for 'resume' call"
Warn(警告資訊,舉例如下:)
"Cache corrupted for file='test.file'. Reading from back-end"
"Database 192.168.0.7/DB not responding. Using backup 192.168.0.8/DB"
"No response from statistics server. Statistics not sent"
Error(錯誤資訊,舉例如下:)
"Internal error. Cannot process request #12345 Error:...."
"Cannot perform login: credentials DB not responding"
Critical(致命錯誤,舉例如下:)
"Critical panic received: .... Shutting down"
"Fatal error: ... App is shutting down to prevent data corruption or loss"
可以看到每個函式裡面都有對 level 的判斷,所以如果我們在部署的時候設定了 level=LevelWarning,那麼 Trace、Debug、Info 這三個函式都不會有任何的輸出,以此類推。
beego 的配置設計
配置資訊的解析,beego 實現了一個 key=value 的配置檔案讀取,類似 ini 配置檔案的格式,就是一個檔案解析的過程,然後把解析的資料儲存到 map 中,最後在呼叫的時候通過幾個 string、int 之類別的函式呼叫回傳相應的值,具體的實現請看下面:
首先定義了一些 ini 配置檔案的一些全域常數:
Copy var (
bComment = [] byte { '#' }
bEmpty = [] byte {}
bEqual = [] byte { '=' }
bDQuote = [] byte { '"' }
)
定義了配置檔案的格式:
Copy // A Config represents the configuration.
type Config struct {
filename string
comment map [ int ][] string // id: []{comment, key...}; id 1 is for main comment.
data map [ string ] string // key: value
offset map [ string ] int64 // key: offset; for editing.
sync . RWMutex
}
定義了解析檔案的函式,解析檔案的過程是開啟檔案,然後一行一行的讀取,解析註釋、空行和 key=value 資料:
Copy // ParseFile creates a new Config and parses the file configuration from the
// named file.
func LoadConfig (name string ) ( * Config , error ) {
file, err := os. Open (name)
if err != nil {
return nil , err
}
cfg := & Config {
file. Name (),
make ( map [ int ][] string ),
make ( map [ string ] string ),
make ( map [ string ] int64 ),
sync . RWMutex {},
}
cfg. Lock ()
defer cfg. Unlock ()
defer file. Close ()
var comment bytes . Buffer
buf := bufio. NewReader (file)
for nComment, off := 0 , int64 ( 1 ); ; {
line, _, err := buf. ReadLine ()
if err == io.EOF {
break
}
if bytes. Equal (line, bEmpty) {
continue
}
off += int64 ( len (line))
if bytes. HasPrefix (line, bComment) {
line = bytes. TrimLeft (line, "#" )
line = bytes. TrimLeftFunc (line, unicode.IsSpace)
comment. Write (line)
comment. WriteByte ( '\n' )
continue
}
if comment. Len () != 0 {
cfg.comment[nComment] = [] string {comment. String ()}
comment. Reset ()
nComment ++
}
val := bytes. SplitN (line, bEqual, 2 )
if bytes. HasPrefix (val[ 1 ], bDQuote) {
val[ 1 ] = bytes. Trim (val[ 1 ], `"` )
}
key := strings. TrimSpace ( string (val[ 0 ]))
cfg.comment[nComment - 1 ] = append (cfg.comment[nComment - 1 ], key)
cfg.data[key] = strings. TrimSpace ( string (val[ 1 ]))
cfg.offset[key] = off
}
return cfg, nil
}
下面實現了一些讀取配置檔案的函式,回傳的值確定為 bool、int、float64 或 string:
Copy // Bool returns the boolean value for a given key.
func (c * Config ) Bool (key string ) ( bool , error ) {
return strconv. ParseBool (c.data[key])
}
// Int returns the integer value for a given key.
func (c * Config ) Int (key string ) ( int , error ) {
return strconv. Atoi (c.data[key])
}
// Float returns the float value for a given key.
func (c * Config ) Float (key string ) ( float64 , error ) {
return strconv. ParseFloat (c.data[key], 64 )
}
// String returns the string value for a given key.
func (c * Config ) String (key string ) string {
return c.data[key]
}
應用指南
下面這個函式是我一個應用中的例子,用來取得遠端 url 地址的 json 資料,實現如下:
Copy func GetJson () {
resp, err := http. Get (beego.AppConfig. String ( "url" ))
if err != nil {
beego. Critical ( "http get info error" )
return
}
defer resp.Body. Close ()
body, err := ioutil. ReadAll (resp.Body)
err = json. Unmarshal (body, & AllInfo)
if err != nil {
beego. Critical ( "error:" , err)
}
}
函式中呼叫了框架的日誌函式beego.Critical
函式用來報錯,呼叫了beego.AppConfig.String("url")
用來取得配置檔案中的資訊,配置檔案的資訊如下(app.conf):
Copy appname = hs
url = "http://www.api.com/api.html"
links