国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Golang 源碼剖析:log 標準庫

stackvoid / 2244人閱讀

摘要:源碼剖析標準庫原文地址源碼剖析標準庫日志輸出構成日期空格時分秒空格內容源碼剖析互斥鎖,用于確保原子的寫入每行需寫入的日志前綴內容設置日志輔助信息時間文件名行號的寫入。

Golang 源碼剖析:log 標準庫

原文地址:Golang 源碼剖析:log 標準庫

日志 輸出
2018/09/28 20:03:08 EDDYCJY Blog...
構成

[日期]<空格>[時分秒]<空格>[內容]

源碼剖析 Logger
type Logger struct {
    mu     sync.Mutex 
    prefix string
    flag   int
    out    io.Writer
    buf    []byte
}

(1) mu:互斥鎖,用于確保原子的寫入
(2) prefix:每行需寫入的日志前綴內容
(3) flag:設置日志輔助信息(時間、文件名、行號)的寫入。可選如下標識位:

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:當地時區的格式化日期:2009/01/23

Ltime:當地時區的格式化時間:01:23:23

Lmicroseconds:在 Ltime 的基礎上,增加微秒的時間數值顯示

Llongfile:完整的文件名和行號:/a/b/c/d.go:23

Lshortfile:當前文件名和行號:d.go:23,會覆蓋 Llongfile 標識

LUTC:如果設置 Ldate 或 Ltime,且設置 LUTC,則優先使用 UTC 時區而不是本地時區

LstdFlags:Logger 的默認初始值(Ldate 和 Ltime)

(4) out:io.Writer
(5) buf:用于存儲將要寫入的日志內容

New
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,接受三個初始參數,可以定制化而在 log 包內默認會初始一個 std,它指向標準輸入流。而默認的標準輸出、標準錯誤就是顯示器(輸出到屏幕上),標準輸入就是鍵盤。輔助的時間信息默認為 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:標準輸入

Stdout:標準輸出

Stderr:標準錯誤

Getter

Flags

Prefix

Setter

SetFlags

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)
}

...

這一部分介紹最常用的日志寫入方法,從源碼可得知 XrintlnXrintf 函數 換行可變參數都是通過 fmt 標準庫的方法去實現的

FatalPanic 是通過 os.Exit(1)panic(s) 集成實現的。而具體的組裝邏輯是通過 Output 方法實現的

Logger.Output
func (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 方法,簡單來講就是將寫入的日志事件信息組裝并輸出,它會根據 flag 標識位的不同來使用 runtime.Caller 去獲取當前 goroutine 所執行的函數文件、行號等調用信息(log 標準庫中默認深度為 2)。另外如果結尾不是換行符 ,將自動補全一個換行

Logger.formatHeader
func (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, ": "...)
    }
}

該方法主要是用于格式化日志頭(前綴),根據入參不同的標識位,添加分隔符和對應的值到日志信息中。執行流程如下:

(1)如果不是空值,則將 prefix 寫入 buf

(2)如果設置 LdateLtimeLmicroseconds,則對應將日期和時間寫入 buf

(3)如果設置 LshortfileLlongfile,則對應將文件和行號信息寫入 buf

Logger.itoa
func 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:]...)
}

該方法主要用于將整數轉換為定長的十進制 ASCII,同時給出負數寬度避免左側補 0。另外會以相反的順序組合十進制

如何定制化 Logger

在標準庫內,可通過其開放的 New 方法來實現各種各樣的自定義 Logger 組件,但是為什么也可以直接 log.Print* 等方法呢?

func New(out io.Writer, prefix string, flag int) *Logger

其實是在標準庫內,如果你剛剛細心的看了前面的小節,不難發現其默認實現了一個 Logger 組件

var std = New(os.Stderr, "", LstdFlags)

這也是一個小小的精妙之處 ??

總結

通過查閱 log 標準庫的源碼,可得知最簡單的一個日志包應該如何編寫。另外 log 包是在所有涉及到 Logger 的地方都對 sync.Mutex 進行操作(以此解決原子問題),其余邏輯均為組裝日志信息和轉換數值格式,該包較為經典,可以多讀幾遍

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/29470.html

相關文章

  • Golang 源碼剖析:fmt 標準 --- Print* 是怎么樣輸出的?

    摘要:源碼剖析標準庫是怎么樣輸出的原文地址源碼剖析標準庫前言標準開場見多了,那內部標準庫又是怎么輸出這段英文的呢今天一起來圍觀下源碼吧 Golang 源碼剖析:fmt 標準庫 --- Print* 是怎么樣輸出的? 原文地址:Golang 源碼剖析:fmt 標準庫 前言 package main import ( fmt ) func main() { fmt.Print...

    charles_paul 評論0 收藏0
  • 重拾golang - go目錄結構說明

    摘要:目錄結構說明集多編程范式之大成者,使開發者能夠快速的開發測試部署程序,支持全平臺靜態編譯。上目錄位置主要目錄包含如下圖,分別進行說明文件夾存放檢查器的輔助文件。工作區有個子目錄目錄目錄和目錄。目錄用于以代碼包的形式組織并保存源碼文件。 go 目錄結構說明 ??golang集多編程范式之大成者,使開發者能夠快速的開發、測試、部署程序,支持全平臺靜態編譯。go具有優秀的依賴管理,高效的運行...

    zhisheng 評論0 收藏0
  • Derek解讀Bytom源碼-Api Server接口服務

    摘要:首先讀取請求內容,解析請求,接著匹配相應的路由項,隨后調用路由項的回調函數來處理。每一個路由項由請求方法和回調函數組成將監聽地址作為參數,最終執行開始服務于外部請求創建對象首先,實例化對象。我們可以看到一條項由和對應的回調函數組成。 作者:Derek 簡介 Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com...

    GitCafe 評論0 收藏0
  • PHPer書單

    摘要:想提升自己,還得多看書多看書多看書下面是我收集到的一些程序員應該看得書單及在線教程,自己也沒有全部看完。共勉吧當然,如果你有好的書想分享給大家的或者覺得書單不合理,可以去通過進行提交。講師溫銘,軟件基金會主席,最佳實踐作者。 想提升自己,還得多看書!多看書!多看書!下面是我收集到的一些PHP程序員應該看得書單及在線教程,自己也沒有全部看完。共勉吧!當然,如果你有好的書想分享給大家的或者...

    jimhs 評論0 收藏0
  • Golang 大殺器之性能剖析 PProf

    摘要:大殺器之性能剖析原文地址大殺器之性能剖析前言寫了幾噸代碼,實現了幾百個接口。功能測試也通過了,終于成功的部署上線了結果,性能不佳,什么鬼 Golang 大殺器之性能剖析 PProf 原文地址:Golang 大殺器之性能剖析 PProf 前言 寫了幾噸代碼,實現了幾百個接口。功能測試也通過了,終于成功的部署上線了 結果,性能不佳,什么鬼?

    leeon 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<