Golang log日志

有一天我在goland里看著輸出日志,很想知道它是哪個(gè)文件,第多少行打出來(lái)的,然后就找到了log功能。
TRACE: 2019/05/24 17:23:38 main.go:22: begin connect
Name: superWang
Phone: 13478808311
看代碼吧:
func init() {
log.SetPrefix("TRACE: ")
log.SetFlags(log.LstdFlags | log.Lshortfile)
}
log.Println("begin connect")
fmt.Println("Name:", result.Name)
fmt.Println("Phone:", result.Phone)

一、官方自帶log
1.如何使用最基本的 log 包
// This sample program demonstrates how to use the base log package.
package main

import (
    "log"
)

func init() {
    log.SetPrefix("TRACE: ")
    log.SetFlags(log.Ldate | log.Lmicroseconds | log.Llongfile)
}

func main() {
    // Println writes to the standard logger.
    log.Println("message")

    // Fatalln is Println() followed by a call to os.Exit(1).
    log.Fatalln("fatal message")

    // Panicln is Println() followed by a call to panic().
    log.Panicln("panic message")
}
-------------------------------
TRACE: 2019/04/09 14:24:32.868375 D:/go/TestFile/src/main/TestLog.go:15: message
TRACE: 2019/04/09 14:24:32.962329 D:/go/TestFile/src/main/TestLog.go:18: fatal message

Process finished with exit code 1

log.SetPrefix("TRACE: ")設(shè)置了一個(gè)字符串,作為每個(gè)日志項(xiàng)的前綴。這個(gè)字符串應(yīng)該是能讓用戶從一般的程序輸出中分辨出日志的字符串。傳統(tǒng)上這個(gè)字符串的字符會(huì)全部大寫(xiě)

golang.org/src/log/log.go

const (
// 將下面的位使用或運(yùn)算符連接在一起,可以控制要輸出的信息。沒(méi)有
// 辦法控制這些信息出現(xiàn)的順序(下面會(huì)給出順序)或者打印的格式
// (格式在注釋里描述)。這些項(xiàng)后面會(huì)有一個(gè)冒號(hào):
// 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
// 日期: 2009/01/23
Ldate = 1 << iota
// 時(shí)間: 01:23:23
Ltime
// 毫秒級(jí)時(shí)間: 01:23:23.123123。該設(shè)置會(huì)覆蓋 Ltime 標(biāo)志
Lmicroseconds
// 完整路徑的文件名和行號(hào): /a/b/c/d.go:23
Llongfile
// 最終的文件名元素和行號(hào): d.go:23
// 覆蓋 Llongfile
Lshortfile
// 標(biāo)準(zhǔn)日志記錄器的初始值
LstdFlags = Ldate | Ltime
)

log 包有一個(gè)很方便的地方就是,這些日志記錄器是多 goroutine 安全的。這意味著在多個(gè)goroutine 可以同時(shí)調(diào)用來(lái)自同一個(gè)日志記錄器的這些函數(shù),而不會(huì)有彼此間的寫(xiě)沖突。標(biāo)準(zhǔn)日志記錄器具有這一性質(zhì),用戶定制的日志記錄器也應(yīng)該滿足這一性質(zhì)。

2.定制的日志記錄器
// 這個(gè)示例程序展示如何創(chuàng)建定制的日志記錄器
package main

import (
    "io"
    "io/ioutil"
    "log"
    "os"
)

var (
    Trace   *log.Logger // 記錄所有日志
    Info    *log.Logger // 重要的信息
    Warning *log.Logger // 需要注意的信息
    Error   *log.Logger // 非常嚴(yán)重的問(wèn)題
)

func init() {
    file, err := os.OpenFile("errors.txt",
        os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err != nil {
        log.Fatalln("Failed to open error log file:", err)
    }

    Trace = log.New(ioutil.Discard,
        "TRACE: ",
        log.Ldate|log.Ltime|log.Lshortfile)

    Info = log.New(os.Stdout,
        "INFO: ",
        log.Ldate|log.Ltime|log.Lshortfile)

    Warning = log.New(os.Stdout,
        "WARNING: ",
        log.Ldate|log.Ltime|log.Lshortfile)

    Error = log.New(io.MultiWriter(file, os.Stderr),
        "ERROR: ",
        log.Ldate|log.Ltime|log.Lshortfile)
}

func main() {
    Trace.Println("I have something standard to say")
    Info.Println("Special Information")
    Warning.Println("There is something you need to know about")
    Error.Println("Something has failed")
}
------------------------------------
INFO: 2019/04/09 14:37:11 TestCustomLog.go:44: Special Information
ERROR: 2019/04/09 14:37:11 TestCustomLog.go:46: Something has failed
WARNING: 2019/04/09 14:37:11 TestCustomLog.go:45: 
There is something you need to know about

golang.org/src/log/log.go

// New 創(chuàng)建一個(gè)新的 Logger。out 參數(shù)設(shè)置日志數(shù)據(jù)將被寫(xiě)入的目的地
// 參數(shù) prefix 會(huì)在生成的每行日志的最開(kāi)始出現(xiàn)
// 參數(shù) flag 定義日志記錄包含哪些屬性
func New(out io.Writer, prefix string, flag int) *Logger {
return &Logger{out: out, prefix: prefix, flag: flag}
}

Discard 是一個(gè) io.Writer 接口,調(diào)用它的 Write 方法將不做任何事情并且始終成功返回。當(dāng)某個(gè)等級(jí)的日志不重要時(shí),使用 Discard 變量可以禁用這個(gè)等級(jí)的日志。

// devNull 是一個(gè)用 int 作為基礎(chǔ)類型的類型
type devNull int
// Discard 是一個(gè) io.Writer,所有的 Write 調(diào)用都不會(huì)有動(dòng)作,但是會(huì)成功返回
var Discard io.Writer = devNull(0)
// io.Writer 接口的實(shí)現(xiàn)
func (devNull) Write(p []byte) (int, error) {
return len(p), nil
}

golang.org/src/os/file.go

// Stdin、Stdout 和 Stderr 是已經(jīng)打開(kāi)的文件,分別指向標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和
// 標(biāo)準(zhǔn)錯(cuò)誤的文件描述符
var (
Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)
os/file_unix.go
// NewFile 用給出的文件描述符和名字返回一個(gè)新 File
func NewFile(fd uintptr, name string) *File {

可以看到 3 個(gè)變量的聲明,分別表示所有操作系統(tǒng)里都有的 3 個(gè)標(biāo)準(zhǔn)輸入/輸出,即 Stdin、Stdout 和 Stderr。這 3 個(gè)變量都被聲明為 File 類型的指針,這個(gè)類型實(shí)現(xiàn)了 io.Writer 接口。

MultiWriter 函數(shù)調(diào)用會(huì)返回一個(gè) io.Writer接口類型值,這個(gè)值包含之前打開(kāi)的文件file,以及 stderr。MultiWriter 函數(shù)是一個(gè)變參函數(shù),可以接受任意個(gè)實(shí)現(xiàn)了io.Writer 接口的值。這個(gè)函數(shù)會(huì)返回一個(gè)io.Writer 值,這個(gè)值會(huì)把所有傳入的 io.Writer 的值綁在一起。當(dāng)對(duì)這個(gè)返回值進(jìn)行寫(xiě)入時(shí),會(huì)向所有綁在一起的io.Writer 值做寫(xiě)入。這讓類似log.New 這樣的函數(shù)可以同時(shí)向多個(gè)Writer 做輸出?,F(xiàn)在,當(dāng)我們使用Error 記錄器記錄日志時(shí),輸出會(huì)同時(shí)寫(xiě)到文件和stderr。

每個(gè)記錄器變量都包含一組方法,這組方法與 log 包里實(shí)現(xiàn)的那組函數(shù)完全一致

func (l *Logger) Fatal(v ...interface{})
func (l *Logger) Fatalf(format string, v ...interface{})
func (l *Logger) Fatalln(v ...interface{})
func (l *Logger) Flags() int
func (l *Logger) Output(calldepth int, s string) error
func (l *Logger) Panic(v ...interface{})
func (l *Logger) Panicf(format string, v ...interface{})
func (l *Logger) Panicln(v ...interface{})
func (l *Logger) Prefix() string
func (l *Logger) Print(v ...interface{})
func (l *Logger) Printf(format string, v ...interface{})
func (l *Logger) Println(v ...interface{})
func (l *Logger) SetFlags(flag int)
func (l *Logger) SetPrefix(prefix string)
二、第三方log庫(kù)
1.簡(jiǎn)要匯總

參考
golang中的log庫(kù)
Golang的log包哪個(gè)好用?
在Github中最受歡迎的Go日志庫(kù)集合

截止2019.5.24star數(shù)統(tǒng)計(jì)

  • logrus 11018star 近期還在更新
  • zap 6901star 近期還在更新
  • oklog 2781star 近期還在更新
  • glog 2214star 已經(jīng)很久不更新
  • seelog 1312star 已經(jīng)很久不更新
  • zerolog 1981star 近期還在更新
2.選擇JSON格式的日志

王?。鹤罴讶罩緦?shí)踐
最佳日志實(shí)踐(v2.0)
達(dá)達(dá) JSON日志文件

舉個(gè)例子,在引入JSON格式的日志之后,我很快就用PHP做了一個(gè)日志查看頁(yè)面,因?yàn)樾畔⑹墙Y(jié)構(gòu)化的,這個(gè)查看頁(yè)面要做過(guò)濾篩選或者特殊顯示就變得方便許多,不是先前做不到,而是做起來(lái)很繁瑣。但是JSON日志比起二進(jìn)制文件格式來(lái)說(shuō),有個(gè)明顯的問(wèn)題是JSON導(dǎo)致的文件體積膨脹。

logrus也是支持json格式日志的,可以參考Golang之使用Logrus

logrus 為什么不支持輸出文件名和行號(hào)

3.性能

參考
如何開(kāi)發(fā)高性能 go 組件?
Go零消耗debug log技巧
玩轉(zhuǎn)高性能日志庫(kù)ZAP (1)
玩轉(zhuǎn)高性能日志庫(kù)ZAP (2)
玩轉(zhuǎn)高性能日志庫(kù)ZAP(3)

看到題目,有人肯定會(huì)問(wèn),官方的log模塊不好么? Debug Log一般很長(zhǎng),在生產(chǎn)環(huán)境還輸出的話,也很難找。 再者,log的消耗是比較大的,特別是需要打印行號(hào)時(shí)。如果在代碼中有大量的debug日志,這個(gè)損耗累積起來(lái),那也是相當(dāng)驚人的了。

我們的業(yè)務(wù)通常會(huì)記錄大量的 Debug 日志,但在實(shí)際測(cè)試過(guò)程中,發(fā)現(xiàn)我們使用的日志庫(kù) seelog 性能存在嚴(yán)重的瓶頸,在我們的對(duì)比結(jié)果中發(fā)現(xiàn):zap 表現(xiàn)非常突出,單線程 Qps 也是 logrus、seelog 的數(shù)倍。

其實(shí)我們最初的實(shí)踐中并沒(méi)有意識(shí)到日志框架的性能的重要性,直到開(kāi)發(fā)后期進(jìn)行系統(tǒng)的 benchmark 總是不盡人意,而且在不同的日志級(jí)別下性能差距明顯。通過(guò) go profiling 看到日志組件對(duì)于計(jì)算資源的消耗十分巨大,因此決心將其替換為一個(gè)高性能的日志框架,這也是選擇用 zap 的一個(gè)重要的考量的點(diǎn)。

目前我們使用 zap 已有2年多的時(shí)間,zap 很好地解決了日志組件的低性能的問(wèn)題。目前 zap 也從 beta 發(fā)布到了 1.8版本,對(duì)于 zap 我們不僅僅看到它的高性能,更重要的是理解它的設(shè)計(jì)與工程實(shí)踐。日志屬于 io 密集型的組件,這類組件如何做到高性能低成本,這也將直接影響到服務(wù)成本。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 在我們開(kāi)發(fā)程序后,如果有一些問(wèn)題需要對(duì)程序進(jìn)行調(diào)試的時(shí)候,日志是必不可少的,這是我們分析程序問(wèn)題常用的手段。 日志...
    豆瓣奶茶閱讀 18,472評(píng)論 0 22
  • Lua 5.1 參考手冊(cè) by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 14,235評(píng)論 0 38
  • ORA-00001: 違反唯一約束條件 (.) 錯(cuò)誤說(shuō)明:當(dāng)在唯一索引所對(duì)應(yīng)的列上鍵入重復(fù)值時(shí),會(huì)觸發(fā)此異常。 O...
    我想起個(gè)好名字閱讀 5,918評(píng)論 0 9
  • 這幾日總是睡不好,晚上睡不著,早上起不來(lái)。鬧鐘調(diào)到七點(diǎn),總要撐到七點(diǎn)半起床。幸虧單位離得近,要不然工資要扣光。 早...
    飛飛魚(yú)先生閱讀 179評(píng)論 1 2
  • 南無(wú)阿彌陀佛 自私不得自由,利他才得自由; 自大不得自由,謙卑才得自由; 愛(ài)己不得自由,愛(ài)人才得自由; 虛假不得自...
    釋宗音閱讀 427評(píng)論 0 1

友情鏈接更多精彩內(nèi)容