原文地址: 編寫一個簡單的文件日誌
在上一節中,咱們解決了API's能夠任意訪問的問題,那麼咱們如今還有一個問題。html
就是咱們的日誌,都是輸出到控制檯上的,這顯然對於一個項目來講是不合理的,所以咱們這一節簡單封裝log
庫,使其支持簡單的文件日誌!git
項目地址:https://github.com/EDDYCJY/go...github
logging
包咱們在pkg
下新建logging
目錄,新建file.go
和log.go
文件,寫入內容:golang
file
文件一、 file.go:segmentfault
package logging import ( "os" "time" "fmt" "log" ) var ( LogSavePath = "runtime/logs/" LogSaveName = "log" LogFileExt = "log" TimeFormat = "20060102" ) func getLogFilePath() string { return fmt.Sprintf("%s", LogSavePath) } func getLogFileFullPath() string { prefixPath := getLogFilePath() suffixPath := fmt.Sprintf("%s%s.%s", LogSaveName, time.Now().Format(TimeFormat), LogFileExt) return fmt.Sprintf("%s%s", prefixPath, suffixPath) } func openLogFile(filePath string) *os.File { _, err := os.Stat(filePath) switch { case os.IsNotExist(err): mkDir() case os.IsPermission(err): log.Fatalf("Permission :%v", err) } handle, err := os.OpenFile(filePath, os.O_APPEND | os.O_CREATE | os.O_WRONLY, 0644) if err != nil { log.Fatalf("Fail to OpenFile :%v", err) } return handle } func mkDir() { dir, _ := os.Getwd() err := os.MkdirAll(dir + "/" + getLogFilePath(), os.ModePerm) if err != nil { panic(err) } }
os.Stat
:返回文件信息結構描述文件。若是出現錯誤,會返回*PathError
type PathError struct { Op string Path string Err error }
os.IsNotExist
:可以接受ErrNotExist
、syscall
的一些錯誤,它會返回一個布爾值,可以得知文件不存在或目錄不存在os.IsPermission
:可以接受ErrPermission
、syscall
的一些錯誤,它會返回一個布爾值,可以得知權限是否知足os.OpenFile
:調用文件,支持傳入文件名稱、指定的模式調用文件、文件權限,返回的文件的方法能夠用於I/O。若是出現錯誤,則爲*PathError
。const ( // Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified. O_RDONLY int = syscall.O_RDONLY // 以只讀模式打開文件 O_WRONLY int = syscall.O_WRONLY // 以只寫模式打開文件 O_RDWR int = syscall.O_RDWR // 以讀寫模式打開文件 // The remaining values may be or'ed in to control behavior. O_APPEND int = syscall.O_APPEND // 在寫入時將數據追加到文件中 O_CREATE int = syscall.O_CREAT // 若是不存在,則建立一個新文件 O_EXCL int = syscall.O_EXCL // 使用O_CREATE時,文件必須不存在 O_SYNC int = syscall.O_SYNC // 同步IO O_TRUNC int = syscall.O_TRUNC // 若是能夠,打開時 )
os.Getwd
:返回與當前目錄對應的根路徑名os.MkdirAll
:建立對應的目錄以及所需的子目錄,若成功則返回nil
,不然返回error
os.ModePerm
:const
定義ModePerm FileMode = 0777
log
文件二、 log.goapi
package logging import ( "log" "os" "runtime" "path/filepath" "fmt" ) type Level int var ( F *os.File DefaultPrefix = "" DefaultCallerDepth = 2 logger *log.Logger logPrefix = "" levelFlags = []string{"DEBUG", "INFO", "WARN", "ERROR", "FATAL"} ) const ( DEBUG Level = iota INFO WARNING ERROR FATAL ) func init() { filePath := getLogFileFullPath() F = openLogFile(filePath) logger = log.New(F, DefaultPrefix, log.LstdFlags) } func Debug(v ...interface{}) { setPrefix(DEBUG) logger.Println(v) } func Info(v ...interface{}) { setPrefix(INFO) logger.Println(v) } func Warn(v ...interface{}) { setPrefix(WARNING) logger.Println(v) } func Error(v ...interface{}) { setPrefix(ERROR) logger.Println(v) } func Fatal(v ...interface{}) { setPrefix(FATAL) logger.Fatalln(v) } func setPrefix(level Level) { _, file, line, ok := runtime.Caller(DefaultCallerDepth) if ok { logPrefix = fmt.Sprintf("[%s][%s:%d]", levelFlags[level], filepath.Base(file), line) } else { logPrefix = fmt.Sprintf("[%s]", levelFlags[level]) } logger.SetPrefix(logPrefix) }
log.New
:建立一個新的日誌記錄器。out
定義要寫入日誌數據的IO
句柄。prefix
定義每一個生成的日誌行的開頭。flag
定義了日誌記錄屬性func New(out io.Writer, prefix string, flag int) *Logger { return &Logger{out: out, prefix: prefix, flag: flag} }
log.LstdFlags
:日誌記錄的格式屬性之一,其他的選項以下const ( Ldate = 1 << iota // the date in the local time zone: 2009/01/23 Ltime // the time in the local time zone: 01:23:23 Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime. Llongfile // full file name and line number: /a/b/c/d.go:23 Lshortfile // final file name element and line number: d.go:23. overrides Llongfile LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone LstdFlags = Ldate | Ltime // initial values for the standard logger )
當前目錄結構:緩存
gin-blog/ ├── conf │ └── app.ini ├── main.go ├── middleware │ └── jwt │ └── jwt.go ├── models │ ├── article.go │ ├── auth.go │ ├── models.go │ └── tag.go ├── pkg │ ├── e │ │ ├── code.go │ │ └── msg.go │ ├── logging │ │ ├── file.go │ │ └── log.go │ ├── setting │ │ └── setting.go │ └── util │ ├── jwt.go │ └── pagination.go ├── routers │ ├── api │ │ ├── auth.go │ │ └── v1 │ │ ├── article.go │ │ └── tag.go │ └── router.go ├── runtime
咱們自定義的logging
包,已經基本完成了,接下來讓它接入到咱們的項目之中吧!app
咱們打開先前包含log
包的代碼,ide
routers
目錄下的article.go
、tag.go
、auth.go
log
包的引用刪除,修改引用咱們本身的日誌包爲gin-blog/pkg/logging
log.Println(...)
改成log.Info(...)
例如auth.go
文件的修改內容:優化
package api import ( "net/http" "github.com/gin-gonic/gin" "github.com/astaxie/beego/validation" "gin-blog/pkg/e" "gin-blog/pkg/util" "gin-blog/models" "gin-blog/pkg/logging" ) ... func GetAuth(c *gin.Context) { ... code := e.INVALID_PARAMS if ok { ... } else { for _, err := range valid.Errors { logging.Info(err.Key, err.Message) } } c.JSON(http.StatusOK, gin.H{ "code" : code, "msg" : e.GetMsg(code), "data" : data, }) }
修改文件後,重啓服務,咱們來試試吧!
獲取到API的Token後,咱們故意傳錯誤URL參數給接口,如:http://127.0.0.1:8000/api/v1/articles?tag_id=0&state=9999999&token=eyJhbG..
而後咱們到$GOPATH/gin-blog/runtime/logs
查看日誌:
$ tail -f log20180216.log [INFO][article.go:79]2018/02/16 18:33:12 [state 狀態只容許0或1] [INFO][article.go:79]2018/02/16 18:33:42 [state 狀態只容許0或1] [INFO][article.go:79]2018/02/16 18:33:42 [tag_id 標籤ID必須大於0] [INFO][article.go:79]2018/02/16 18:38:39 [state 狀態只容許0或1] [INFO][article.go:79]2018/02/16 18:38:39 [tag_id 標籤ID必須大於0]
日誌結構一切正常,咱們的記錄模式都爲Info
,所以前綴是對的,而且咱們是入參有問題,也把錯誤記錄下來了,這樣排錯就很方便了!
至此,本節就完成了,這只是一個簡單的擴展,實際上咱們線上項目要使用的文件日誌,是更復雜一些,開動你的大腦 觸類旁通吧!