欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go語(yǔ)言編程實(shí)現(xiàn)支持六種級(jí)別的日志庫(kù)?

 更新時(shí)間:2022年07月04日 09:28:37   作者:波斯馬  
這篇文章主要為大家介紹了使用Golang編寫(xiě)一個(gè)支持六種級(jí)別的日志庫(kù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

Golang標(biāo)準(zhǔn)日志庫(kù)提供的日志輸出方法有Print、Fatal、Panic等,沒(méi)有常見(jiàn)的Debug、Info、Error等日志級(jí)別,用起來(lái)不太順手。這篇文章就來(lái)手?jǐn)]一個(gè)自己的日志庫(kù),可以記錄不同級(jí)別的日志。

其實(shí)對(duì)于追求簡(jiǎn)單來(lái)說(shuō),Golang標(biāo)準(zhǔn)日志庫(kù)的三個(gè)輸出方法也夠用了,理解起來(lái)也很容易:

  • Print用于記錄一個(gè)普通的程序日志,開(kāi)發(fā)者想記點(diǎn)什么都可以。
  • Fatal用于記錄一個(gè)導(dǎo)致程序崩潰的日志,并會(huì)退出程序。
  • Panic用于記錄一個(gè)異常日志,并觸發(fā)panic。

不過(guò)對(duì)于用慣了Debug、Info、Error的人來(lái)說(shuō),還是有點(diǎn)不習(xí)慣;對(duì)于想更細(xì)致的區(qū)分日志級(jí)別的需求,標(biāo)準(zhǔn)日志庫(kù)還提供了一個(gè)通用的Output方法,開(kāi)發(fā)者在要輸出的字符串中加入級(jí)別也是可以的,但總是有點(diǎn)別扭,不夠直接。

目前市面上也已經(jīng)有很多優(yōu)秀的三方日志庫(kù),比如uber開(kāi)源的zap,常見(jiàn)的還有zerolog、logrus等。不過(guò)我這里還是想自己手?jǐn)]一個(gè),因?yàn)榇蠖鄶?shù)開(kāi)源產(chǎn)品都不會(huì)完全貼合自己的需求,有很多自己用不上的功能,這會(huì)增加系統(tǒng)的復(fù)雜性,有沒(méi)有隱藏的坑也很難說(shuō),當(dāng)然自己入坑的可能性也很大;再者看了官方日志庫(kù)的實(shí)現(xiàn)之后,感覺(jué)可以簡(jiǎn)單封裝下即可實(shí)現(xiàn)自己想要的功能,能夠hold住。

初始需求

我這里的初始需求是:

  • 將日志寫(xiě)入磁盤(pán)文件,每個(gè)月一個(gè)文件夾,每個(gè)小時(shí)一個(gè)文件。
  • 支持常見(jiàn)日志級(jí)別:Trace、Debug、Info、Warn、Error、Fatal,并且程序能夠設(shè)置日志級(jí)別。

我給這個(gè)日志庫(kù)取名為ylog,預(yù)期的使用方法如下:

ylog.SetLevel(LevelInfo)
ylog.Debug("I am a debug log.")
ylog.Info("I am a Info log.")

技術(shù)實(shí)現(xiàn)

類型定義

需要定義一個(gè)結(jié)構(gòu)體,保存日志級(jí)別、要寫(xiě)入的文件等信息。

type FileLogger struct {
	lastHour int64
	file     *os.File
	Level    LogLevel
	mu       sync.Mutex
	iLogger  *log.Logger
	Path     string
}

來(lái)看一下這幾個(gè)參數(shù):

lastHour 用來(lái)記錄創(chuàng)建日志文件時(shí)的小時(shí)數(shù),如果小時(shí)變了,就要?jiǎng)?chuàng)建新的日志文件。

file 當(dāng)前使用的日志文件。

Level 當(dāng)前使用的日志級(jí)別。

mu 因?yàn)榭赡茉诓煌膅o routine中寫(xiě)日志,需要一個(gè)互斥體保證日志文件不會(huì)重復(fù)創(chuàng)建。

iLogger 標(biāo)準(zhǔn)日志庫(kù)實(shí)例,因?yàn)檫@里是封裝了標(biāo)準(zhǔn)日志庫(kù)。

Path 日志輸出的最上層目錄,比如程序根目錄下的logs目錄,這里就保存一個(gè)字符串:logs。

日志級(jí)別

先把日志級(jí)別定義出來(lái),這里日志級(jí)別其實(shí)是int類型,從0到5,級(jí)別不斷升高。

如果設(shè)置為T(mén)oInfo,則Info級(jí)別及比Info級(jí)別高的日志都能輸出。

type LogLevel int
const (
	LevelTrace LogLevel = iota
	LevelDebug
	LevelInfo
	LevelWarn
	LevelError
	LevelFatal
)

上文提到可以在Output方法的參數(shù)中加入日志級(jí)別,這里就通過(guò)封裝Output方法來(lái)實(shí)現(xiàn)不同級(jí)別的日志記錄方法。這里貼出其中一個(gè)方法,封裝的方式都一樣,就不全都貼出來(lái)了:

func (l *FileLogger) CanInfo() bool {
	return l.Level <= LevelInfo 
}
func (l *FileLogger) Info(v ...any) {
	if l.CanInfo() {
		l.ensureFile()
		v = append([]any{"Info "}, v...)
		l.iLogger.Output(2, fmt.Sprintln(v...))
	}
}

輸出日志前做了三件事:

  • 判斷日志級(jí)別,如果設(shè)置的日志級(jí)別小于等于當(dāng)前輸出級(jí)別,則可以輸出。
  • 確保日志文件已經(jīng)創(chuàng)建好,后邊會(huì)講如何確保。
  • 將日志級(jí)別前插到日志字符串中。

然后調(diào)用標(biāo)準(zhǔn)庫(kù)的Output函數(shù)輸出日志,這里第一個(gè)參數(shù)是為了獲取到當(dāng)前正在寫(xiě)日志的程序文件名,傳入的是在程序調(diào)用棧中進(jìn)行查找的深度值,這里用2就正好。

寫(xiě)到文件

標(biāo)準(zhǔn)庫(kù)的log是支持輸出到多種目標(biāo)的,只要實(shí)現(xiàn)了io.Write接口:

type Writer interface {
	Write(p []byte) (n int, err error)
}

因?yàn)槲募?duì)象也實(shí)現(xiàn)了這個(gè)接口,所以這里可以創(chuàng)建os.File的實(shí)例,并把它設(shè)置到內(nèi)嵌的標(biāo)準(zhǔn)日志庫(kù)實(shí)例,也就是設(shè)置到前邊創(chuàng)建的FileLogger中的iLogger中。這個(gè)操作在ensureFile方法中,看一下這個(gè)文件的實(shí)現(xiàn):

func (l *FileLogger) ensureFile() (err error) {
	currentTime := time.Now()
	if l.file == nil {
		l.mu.Lock()
		defer l.mu.Unlock()
		if l.file == nil {
			l.file, err = createFile(&l.Path, &currentTime)
			l.iLogger.SetOutput(l.file)
			l.iLogger.SetFlags(log.Lshortfile | log.Ldate | log.Ltime | log.Lmicroseconds)
			l.lastHour = getTimeHour(&currentTime)
		}
		return
	}
	currentHour := getTimeHour(&currentTime)
	if l.lastHour != currentHour {
		l.mu.Lock()
		defer l.mu.Unlock()
		if l.lastHour != currentHour {
			_ = l.file.Close()
			l.file, err = createFile(&l.Path, &currentTime)
			l.iLogger.SetOutput(l.file)
			l.iLogger.SetFlags(log.Llongfile | log.Ldate | log.Ltime)
			l.lastHour = getTimeHour(&currentTime)
		}
	}
	return
}

這里稍微有點(diǎn)復(fù)雜,基本邏輯是:如果文件實(shí)例不存在,則創(chuàng)建;如果需要?jiǎng)?chuàng)建新的文件,則先關(guān)閉舊的文件再創(chuàng)建新的文件。

更改文件實(shí)例時(shí)需要加鎖,否則可能多次操作,出現(xiàn)預(yù)期之外的情況。

設(shè)置輸出到文件后,標(biāo)準(zhǔn)log庫(kù)的Output方法就會(huì)將日志輸出到這個(gè)文件了。

默認(rèn)實(shí)現(xiàn)

經(jīng)過(guò)上邊一系列操作,這個(gè)FileLogger就可以使用了:

var logger = NewFileLogger(LevelInfo, "logs")
logger.Info("This is a info.")

不過(guò)和最初設(shè)想的用法有點(diǎn)差別:ylog.Info("xxxx")

這需要在ylog包中再定義一個(gè)名為Info的公開(kāi)函數(shù),可以在這個(gè)公開(kāi)函數(shù)中調(diào)用一個(gè)默認(rèn)創(chuàng)建的FileLogger實(shí)例,代碼是這樣的:

var stdPath = "logs"
var std = NewFileLogger(LevelInfo, stdPath)
func Trace(v ...any) {
	if std.CanTrace() {
		std.ensureFile()
		v = append([]any{"Trace"}, v...)
		std.iLogger.Output(2, fmt.Sprintln(v...))
	}
}

注意這里沒(méi)有調(diào)用std的Trace方法,這是因?yàn)镺utput中的第一個(gè)參數(shù),如果嵌套調(diào)用std.Trace,則多了一層,這個(gè)參數(shù)就得設(shè)置為3,但是自己創(chuàng)建實(shí)例調(diào)用Trace時(shí)這個(gè)參數(shù)需要為2,這就產(chǎn)生沖突了。

經(jīng)過(guò)以上這些操作,就可以實(shí)現(xiàn)預(yù)期的日志操作了:

ylog.SetLevel(LevelInfo)
ylog.Debug("I am a debug log.")
ylog.Info("I am a Info log.")

完整的程序代碼:https://github.com/bosima/ylog/tree/v1.0.1

下篇文章將繼續(xù)改造這個(gè)日志庫(kù),支持輸出Json格式的日志,以及輸出日志到Kafka,更多關(guān)于Golang日志庫(kù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 淺析Go語(yǔ)言的數(shù)據(jù)類型及數(shù)組

    淺析Go語(yǔ)言的數(shù)據(jù)類型及數(shù)組

    Golang是一種靜態(tài)強(qiáng)類型、編譯型語(yǔ)言。Go?語(yǔ)言語(yǔ)法與?C?相近,但功能上有:內(nèi)存安全,GC(垃圾回收),結(jié)構(gòu)形態(tài)及?CSP-style?并發(fā)計(jì)算。本文主要和大家聊聊Go語(yǔ)言的數(shù)據(jù)類型及數(shù)組,希望對(duì)大家有所幫助
    2022-11-11
  • Go存儲(chǔ)基礎(chǔ)使用direct io方法實(shí)例

    Go存儲(chǔ)基礎(chǔ)使用direct io方法實(shí)例

    這篇文章主要介紹了Go存儲(chǔ)基礎(chǔ)之如何使用direct io方法實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • golang利用不到20行代碼實(shí)現(xiàn)路由調(diào)度詳解

    golang利用不到20行代碼實(shí)現(xiàn)路由調(diào)度詳解

    這篇文章主要給大家介紹了關(guān)于golang利用不到20行代碼實(shí)現(xiàn)路由調(diào)度的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-08-08
  • 基于Golang實(shí)現(xiàn)YOLO目標(biāo)檢測(cè)算法

    基于Golang實(shí)現(xiàn)YOLO目標(biāo)檢測(cè)算法

    目標(biāo)檢測(cè)是計(jì)算機(jī)視覺(jué)領(lǐng)域的重要任務(wù),它不僅可以識(shí)別圖像中的物體,還可以標(biāo)記出物體的位置和邊界框,YOLO是一種先進(jìn)的目標(biāo)檢測(cè)算法,以其高精度和實(shí)時(shí)性而聞名,本文將介紹如何使用Golang實(shí)現(xiàn)YOLO目標(biāo)檢測(cè)算法,文中有相關(guān)的代碼示例供大家參考,需要的朋友可以參考下
    2023-11-11
  • Go語(yǔ)言網(wǎng)站使用異步編程和Goroutine提高Web的性能

    Go語(yǔ)言網(wǎng)站使用異步編程和Goroutine提高Web的性能

    作為一門(mén)現(xiàn)代化編程語(yǔ)言,Go語(yǔ)言提供了強(qiáng)大的異步編程能力,使得程序員可以以更高效的方式處理并發(fā)任務(wù),在Go語(yǔ)言中,使用Goroutine在單個(gè)進(jìn)程中實(shí)現(xiàn)多任務(wù)并行處理,以及如何使用協(xié)程池來(lái)進(jìn)一步提高Web服務(wù)器的處理能力,
    2024-01-01
  • Go語(yǔ)言中的匿名結(jié)構(gòu)體用法實(shí)例

    Go語(yǔ)言中的匿名結(jié)構(gòu)體用法實(shí)例

    這篇文章主要介紹了Go語(yǔ)言中的匿名結(jié)構(gòu)體用法,實(shí)例分析了匿名結(jié)構(gòu)體的使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • Go中匿名結(jié)構(gòu)體的使用技巧

    Go中匿名結(jié)構(gòu)體的使用技巧

    這篇文章主要給大家分享一個(gè)使用匿名結(jié)構(gòu)體,提升Go編程效率的小技巧,沒(méi)什么技術(shù)深度,屬于在日常寫(xiě)代碼過(guò)程中積累下來(lái)的一個(gè)提升自己編程效率的小經(jīng)驗(yàn)
    2023-08-08
  • Golang?WaitGroup?底層原理及源碼解析

    Golang?WaitGroup?底層原理及源碼解析

    WaitGroup?是?Golang?中最常見(jiàn)的并發(fā)控制技術(shù)之一,它的作用我們可以簡(jiǎn)單類比為其他語(yǔ)言中多線程并發(fā)控制中的?join(),這篇文章主要介紹了Golang?WaitGroup?底層原理及源碼詳解,需要的朋友可以參考下
    2023-04-04
  • Go語(yǔ)言圖片處理和生成縮略圖的方法

    Go語(yǔ)言圖片處理和生成縮略圖的方法

    這篇文章主要介紹了Go語(yǔ)言圖片處理和生成縮略圖的方法,涉及Go語(yǔ)言針對(duì)圖片操作的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • Golang中crypto/rand庫(kù)的使用技巧與最佳實(shí)踐

    Golang中crypto/rand庫(kù)的使用技巧與最佳實(shí)踐

    在Golang的眾多隨機(jī)數(shù)生成庫(kù)中,crypto/rand?是一個(gè)專為加密安全設(shè)計(jì)的庫(kù),本文主要介紹了Golang中crypto/rand庫(kù)的使用技巧與最佳實(shí)踐,感興趣的可以了解一下
    2024-02-02

最新評(píng)論