最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go語言編程實現(xiàn)支持六種級別的日志庫?

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

前言

Golang標準日志庫提供的日志輸出方法有Print、Fatal、Panic等,沒有常見的Debug、Info、Error等日志級別,用起來不太順手。這篇文章就來手擼一個自己的日志庫,可以記錄不同級別的日志。

其實對于追求簡單來說,Golang標準日志庫的三個輸出方法也夠用了,理解起來也很容易:

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

不過對于用慣了Debug、Info、Error的人來說,還是有點不習慣;對于想更細致的區(qū)分日志級別的需求,標準日志庫還提供了一個通用的Output方法,開發(fā)者在要輸出的字符串中加入級別也是可以的,但總是有點別扭,不夠直接。

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

初始需求

我這里的初始需求是:

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

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

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

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

類型定義

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

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

來看一下這幾個參數(shù):

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

file 當前使用的日志文件。

Level 當前使用的日志級別。

mu 因為可能在不同的go routine中寫日志,需要一個互斥體保證日志文件不會重復(fù)創(chuàng)建。

iLogger 標準日志庫實例,因為這里是封裝了標準日志庫。

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

日志級別

先把日志級別定義出來,這里日志級別其實是int類型,從0到5,級別不斷升高。

如果設(shè)置為ToInfo,則Info級別及比Info級別高的日志都能輸出。

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

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

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

輸出日志前做了三件事:

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

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

寫到文件

標準庫的log是支持輸出到多種目標的,只要實現(xiàn)了io.Write接口:

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

因為文件對象也實現(xiàn)了這個接口,所以這里可以創(chuàng)建os.File的實例,并把它設(shè)置到內(nèi)嵌的標準日志庫實例,也就是設(shè)置到前邊創(chuàng)建的FileLogger中的iLogger中。這個操作在ensureFile方法中,看一下這個文件的實現(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
}

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

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

設(shè)置輸出到文件后,標準log庫的Output方法就會將日志輸出到這個文件了。

默認實現(xiàn)

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

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

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

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

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

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

經(jīng)過以上這些操作,就可以實現(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ù)改造這個日志庫,支持輸出Json格式的日志,以及輸出日志到Kafka,更多關(guān)于Golang日志庫的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • go編譯so庫讓python引用編譯后沒有.h文件的問題

    go編譯so庫讓python引用編譯后沒有.h文件的問題

    有時python需要引用go的一些開源庫,這時就需要go編譯成python可調(diào)用的庫,本文給大家介紹了go編譯so庫讓python引用,編譯后沒有.h文件的問題,需要的朋友可以參考下
    2024-02-02
  • golang gin框架獲取參數(shù)的操作

    golang gin框架獲取參數(shù)的操作

    這篇文章主要介紹了golang gin框架獲取參數(shù)的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go語言中的通道channel詳情

    Go語言中的通道channel詳情

    這篇文章主要介紹了Go語言中的通道channel,在Go語言中管道類似于一個數(shù)據(jù)流,每次放入或者取出一部分數(shù)據(jù),數(shù)據(jù)取出后原通道內(nèi)的數(shù)據(jù)就刪除掉,在linux操作系統(tǒng)中管道會將函數(shù)的返回結(jié)果作為下一個函數(shù)的參數(shù),下文詳細內(nèi)容需要的朋友可以參考下
    2022-02-02
  • go中空接口的具體使用

    go中空接口的具體使用

    空接口是一種特殊的接口類型,它不包含任何方法,本文主要介紹了go中空接口的具體使用,具有一定的參考價值,感興趣的可以了解一下
    2025-04-04
  • go zero微服務(wù)高在請求量下如何優(yōu)化

    go zero微服務(wù)高在請求量下如何優(yōu)化

    這篇文章主要為大家介紹了go zero微服務(wù)高在請求量下的優(yōu)化處理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • Go語言中io.Reader和io.Writer的詳解與實現(xiàn)

    Go語言中io.Reader和io.Writer的詳解與實現(xiàn)

    在Go語言的實際編程中,幾乎所有的數(shù)據(jù)結(jié)構(gòu)都圍繞接口展開,接口是Go語言中所有數(shù)據(jù)結(jié)構(gòu)的核心。在使用Go語言的過程中,無論你是實現(xiàn)web應(yīng)用程序,還是控制臺輸入輸出,又或者是網(wǎng)絡(luò)操作,不可避免的會遇到IO操作,使用到io.Reader和io.Writer接口。下面來詳細看看。
    2016-09-09
  • Golang unsafe包中的類型和函數(shù)詳解

    Golang unsafe包中的類型和函數(shù)詳解

    Golang中的unsafe包用于在運行時進行低級別的操作,這些操作通常是不安全的,因為可以打破Golang的類型安全性和內(nèi)存安全性,使用 unsafe包的程序可能會影響可移植性和兼容性,接下來看下unsafe包中的類型和函數(shù)
    2023-08-08
  • Go sync WaitGroup使用深入理解

    Go sync WaitGroup使用深入理解

    這篇文章主要為大家介紹了Go sync WaitGroup使用深入理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-10-10
  • Go實現(xiàn)凱撒密碼加密解密

    Go實現(xiàn)凱撒密碼加密解密

    這篇文章主要為大家介紹了Go實現(xiàn)凱撒密碼加密解密示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • 手把手帶你走進Go語言之條件表達式

    手把手帶你走進Go語言之條件表達式

    條件表達式由條件運算符構(gòu)成,并常用條件表達式構(gòu)成一個賦值語句,本文給大家介紹了在Go語言中條件表達式的具體用法,講述的非常詳細,對大家的學(xué)習或工作具有一定的參考借鑒價值
    2021-09-09

最新評論

鸡西市| 武胜县| 武邑县| 托克托县| 山阳县| 东海县| 鹤庆县| 互助| 岳阳市| 阳原县| 东阳市| 讷河市| 陵川县| 张家港市| 攀枝花市| 长乐市| 福州市| 南汇区| 明星| 卓尼县| 金昌市| 义马市| 清水河县| 凤山市| 济宁市| 彩票| 南丹县| 邹平县| 富平县| 松滋市| 邢台市| 福鼎市| 闽清县| 监利县| 深水埗区| 封开县| 青河县| 青田县| 虞城县| 华宁县| 武隆县|