摘要:源碼剖析標(biāo)準(zhǔn)庫原文地址源碼剖析標(biāo)準(zhǔn)庫日志輸出構(gòu)成日期空格時分秒空格內(nèi)容源碼剖析互斥鎖,用于確保原子的寫入每行需寫入的日志前綴內(nèi)容設(shè)置日志輔助信息時間文件名行號的寫入。
Golang 源碼剖析:log 標(biāo)準(zhǔn)庫
原文地址:Golang 源碼剖析:log 標(biāo)準(zhǔn)庫
日志 輸出2018/09/28 20:03:08 EDDYCJY Blog...構(gòu)成
[日期]<空格>[時分秒]<空格>[內(nèi)容]
type Logger struct { mu sync.Mutex prefix string flag int out io.Writer buf []byte }
(1) mu:互斥鎖,用于確保原子的寫入
(2) prefix:每行需寫入的日志前綴內(nèi)容
(3) flag:設(shè)置日志輔助信息(時間、文件名、行號)的寫入??蛇x如下標(biāo)識位:
const ( Ldate = 1 << iota // value: 1 Ltime // value: 2 Lmicroseconds // value: 4 Llongfile // value: 8 Lshortfile // value: 16 LUTC // value: 32 LstdFlags = Ldate | Ltime // value: 3 )
Ldate:當(dāng)?shù)貢r區(qū)的格式化日期:2009/01/23
Ltime:當(dāng)?shù)貢r區(qū)的格式化時間:01:23:23
Lmicroseconds:在 Ltime 的基礎(chǔ)上,增加微秒的時間數(shù)值顯示
Llongfile:完整的文件名和行號:/a/b/c/d.go:23
Lshortfile:當(dāng)前文件名和行號:d.go:23,會覆蓋 Llongfile 標(biāo)識
LUTC:如果設(shè)置 Ldate 或 Ltime,且設(shè)置 LUTC,則優(yōu)先使用 UTC 時區(qū)而不是本地時區(qū)
LstdFlags:Logger 的默認(rèn)初始值(Ldate 和 Ltime)
(4) out:io.Writer
(5) buf:用于存儲將要寫入的日志內(nèi)容
func New(out io.Writer, prefix string, flag int) *Logger { return &Logger{out: out, prefix: prefix, flag: flag} } var std = New(os.Stderr, "", LstdFlags)
New 方法用于初始化 Logger,接受三個初始參數(shù),可以定制化而在 log 包內(nèi)默認(rèn)會初始一個 std,它指向標(biāo)準(zhǔn)輸入流。而默認(rèn)的標(biāo)準(zhǔn)輸出、標(biāo)準(zhǔn)錯誤就是顯示器(輸出到屏幕上),標(biāo)準(zhǔn)輸入就是鍵盤。輔助的時間信息默認(rèn)為 Ldate | Ltime,也就是 2009/01/23 01:23:23
// os var ( Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin") Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout") Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr") )
Stdin:標(biāo)準(zhǔn)輸入
Stdout:標(biāo)準(zhǔn)輸出
Stderr:標(biāo)準(zhǔn)錯誤
GetterFlags
Prefix
SetterSetFlags
SetPrefix
SetOutput
Print.., Fatal.., Panic..func Print(v ...interface{}) { std.Output(2, fmt.Sprint(v...)) } func Printf(format string, v ...interface{}) { std.Output(2, fmt.Sprintf(format, v...)) } func Println(v ...interface{}) { std.Output(2, fmt.Sprintln(v...)) } func Fatal(v ...interface{}) { std.Output(2, fmt.Sprint(v...)) os.Exit(1) } func Panic(v ...interface{}) { s := fmt.Sprint(v...) std.Output(2, s) panic(s) } ...
這一部分介紹最常用的日志寫入方法,從源碼可得知 Xrintln、Xrintf 函數(shù) 換行、可變參數(shù)都是通過 fmt 標(biāo)準(zhǔn)庫的方法去實現(xiàn)的
Fatal 和 Panic 是通過 os.Exit(1)、panic(s) 集成實現(xiàn)的。而具體的組裝邏輯是通過 Output 方法實現(xiàn)的
Logger.Outputfunc (l *Logger) Output(calldepth int, s string) error { now := time.Now() // get this early. var file string var line int l.mu.Lock() defer l.mu.Unlock() if l.flag&(Lshortfile|Llongfile) != 0 { // Release lock while getting caller info - it"s expensive. l.mu.Unlock() var ok bool _, file, line, ok = runtime.Caller(calldepth) if !ok { file = "???" line = 0 } l.mu.Lock() } l.buf = l.buf[:0] l.formatHeader(&l.buf, now, file, line) l.buf = append(l.buf, s...) if len(s) == 0 || s[len(s)-1] != " " { l.buf = append(l.buf, " ") } _, err := l.out.Write(l.buf) return err }
Output 方法,簡單來講就是將寫入的日志事件信息組裝并輸出,它會根據(jù) flag 標(biāo)識位的不同來使用 runtime.Caller 去獲取當(dāng)前 goroutine 所執(zhí)行的函數(shù)文件、行號等調(diào)用信息(log 標(biāo)準(zhǔn)庫中默認(rèn)深度為 2)。另外如果結(jié)尾不是換行符 ,將自動補(bǔ)全一個換行
Logger.formatHeaderfunc (l *Logger) formatHeader(buf *[]byte, t time.Time, file string, line int) { *buf = append(*buf, l.prefix...) if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 { if l.flag&LUTC != 0 { t = t.UTC() } if l.flag&Ldate != 0 { year, month, day := t.Date() itoa(buf, year, 4) *buf = append(*buf, "/") itoa(buf, int(month), 2) *buf = append(*buf, "/") itoa(buf, day, 2) *buf = append(*buf, " ") } if l.flag&(Ltime|Lmicroseconds) != 0 { hour, min, sec := t.Clock() itoa(buf, hour, 2) *buf = append(*buf, ":") itoa(buf, min, 2) *buf = append(*buf, ":") itoa(buf, sec, 2) if l.flag&Lmicroseconds != 0 { *buf = append(*buf, ".") itoa(buf, t.Nanosecond()/1e3, 6) } *buf = append(*buf, " ") } } if l.flag&(Lshortfile|Llongfile) != 0 { if l.flag&Lshortfile != 0 { short := file for i := len(file) - 1; i > 0; i-- { if file[i] == "/" { short = file[i+1:] break } } file = short } *buf = append(*buf, file...) *buf = append(*buf, ":") itoa(buf, line, -1) *buf = append(*buf, ": "...) } }
該方法主要是用于格式化日志頭(前綴),根據(jù)入?yún)⒉煌臉?biāo)識位,添加分隔符和對應(yīng)的值到日志信息中。執(zhí)行流程如下:
(1)如果不是空值,則將 prefix 寫入 buf
(2)如果設(shè)置 Ldate、Ltime、Lmicroseconds,則對應(yīng)將日期和時間寫入 buf
(3)如果設(shè)置 Lshortfile、Llongfile,則對應(yīng)將文件和行號信息寫入 buf
Logger.itoafunc itoa(buf *[]byte, i int, wid int) { // Assemble decimal in reverse order. var b [20]byte bp := len(b) - 1 for i >= 10 || wid > 1 { wid-- q := i / 10 b[bp] = byte("0" + i - q*10) bp-- i = q } // i < 10 b[bp] = byte("0" + i) *buf = append(*buf, b[bp:]...) }
該方法主要用于將整數(shù)轉(zhuǎn)換為定長的十進(jìn)制 ASCII,同時給出負(fù)數(shù)寬度避免左側(cè)補(bǔ) 0。另外會以相反的順序組合十進(jìn)制
如何定制化 Logger在標(biāo)準(zhǔn)庫內(nèi),可通過其開放的 New 方法來實現(xiàn)各種各樣的自定義 Logger 組件,但是為什么也可以直接 log.Print* 等方法呢?
func New(out io.Writer, prefix string, flag int) *Logger
其實是在標(biāo)準(zhǔn)庫內(nèi),如果你剛剛細(xì)心的看了前面的小節(jié),不難發(fā)現(xiàn)其默認(rèn)實現(xiàn)了一個 Logger 組件
var std = New(os.Stderr, "", LstdFlags)
這也是一個小小的精妙之處 ??
總結(jié)通過查閱 log 標(biāo)準(zhǔn)庫的源碼,可得知最簡單的一個日志包應(yīng)該如何編寫。另外 log 包是在所有涉及到 Logger 的地方都對 sync.Mutex 進(jìn)行操作(以此解決原子問題),其余邏輯均為組裝日志信息和轉(zhuǎn)換數(shù)值格式,該包較為經(jīng)典,可以多讀幾遍
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/29470.html
摘要:源碼剖析標(biāo)準(zhǔn)庫是怎么樣輸出的原文地址源碼剖析標(biāo)準(zhǔn)庫前言標(biāo)準(zhǔn)開場見多了,那內(nèi)部標(biāo)準(zhǔn)庫又是怎么輸出這段英文的呢今天一起來圍觀下源碼吧 Golang 源碼剖析:fmt 標(biāo)準(zhǔn)庫 --- Print* 是怎么樣輸出的? 原文地址:Golang 源碼剖析:fmt 標(biāo)準(zhǔn)庫 前言 package main import ( fmt ) func main() { fmt.Print...
摘要:目錄結(jié)構(gòu)說明集多編程范式之大成者,使開發(fā)者能夠快速的開發(fā)測試部署程序,支持全平臺靜態(tài)編譯。上目錄位置主要目錄包含如下圖,分別進(jìn)行說明文件夾存放檢查器的輔助文件。工作區(qū)有個子目錄目錄目錄和目錄。目錄用于以代碼包的形式組織并保存源碼文件。 go 目錄結(jié)構(gòu)說明 ??golang集多編程范式之大成者,使開發(fā)者能夠快速的開發(fā)、測試、部署程序,支持全平臺靜態(tài)編譯。go具有優(yōu)秀的依賴管理,高效的運行...
摘要:首先讀取請求內(nèi)容,解析請求,接著匹配相應(yīng)的路由項,隨后調(diào)用路由項的回調(diào)函數(shù)來處理。每一個路由項由請求方法和回調(diào)函數(shù)組成將監(jiān)聽地址作為參數(shù),最終執(zhí)行開始服務(wù)于外部請求創(chuàng)建對象首先,實例化對象。我們可以看到一條項由和對應(yīng)的回調(diào)函數(shù)組成。 作者:Derek 簡介 Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com...
摘要:大殺器之性能剖析原文地址大殺器之性能剖析前言寫了幾噸代碼,實現(xiàn)了幾百個接口。功能測試也通過了,終于成功的部署上線了結(jié)果,性能不佳,什么鬼 Golang 大殺器之性能剖析 PProf 原文地址:Golang 大殺器之性能剖析 PProf 前言 寫了幾噸代碼,實現(xiàn)了幾百個接口。功能測試也通過了,終于成功的部署上線了 結(jié)果,性能不佳,什么鬼?
閱讀 860·2021-11-24 10:44
閱讀 2798·2021-11-11 16:54
閱讀 3216·2021-10-08 10:21
閱讀 2113·2021-08-25 09:39
閱讀 2917·2019-08-30 15:56
閱讀 3474·2019-08-30 13:46
閱讀 3505·2019-08-23 18:09
閱讀 2099·2019-08-23 17:05