golang中l(wèi)og包自定義輸出日志格式與寫入到文件
1.背景:
平時開發(fā)項(xiàng)目時打印日志用到logrus包,但是覺得logrus配置比較麻煩,于是想著直接使用go自帶的log包輸出日志,其提供了一些配置,比如SetPrefix(), 可以讓我們自己二次封裝,讓自己的日志內(nèi)容更鮮明些。
2.代碼:
package log import ( "fmt" "github.com/robfig/cron/v3" "io" "log" "os" "my_log/config" "runtime" "strconv" "strings" "sync" "time" ) var ( debug *log.Logger info *log.Logger warn *log.Logger error *log.Logger dayChangeLock sync.RWMutex ) const ( debugLevel = iota //iota=0 infoLevel warnLevel errorLevel ) func init() { dayChangeLock = sync.RWMutex{} createLogFile() go logJob() } func createLogFile() { dayChangeLock.Lock() defer dayChangeLock.Unlock() now := time.Now() postFix := now.Format("20060102") logFile := "plume_log_" + postFix + ".log" logOut, err := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm) if err != nil { panic(err) } else { multiWriter := io.MultiWriter(os.Stdout, logOut) debug = log.New(multiWriter, "[DEBUG] ", log.Ldate|log.Ltime) info = log.New(multiWriter, "[INFO] ", log.Ldate|log.Ltime) warn = log.New(multiWriter, "[WARN] ", log.Ldate|log.Ltime) error = log.New(multiWriter, "[ERROR] ", log.Ldate|log.Ltime) } } func Debug(format string, v ...any) { if config.Conf.Level <= debugLevel { debug.Printf(getLineNo()+format, v...) } } func Info(format string, v ...any) { if config.Conf.Level <= infoLevel { info.Printf(getLineNo()+format, v...) } } func Warn(format string, v ...any) { if config.Conf.Level <= warnLevel { warn.Printf(getLineNo()+format, v...) } } func Error(format string, v ...any) { if config.Conf.Level <= errorLevel { error.Printf(getLineNo()+format, v...) } } func getLineNo() string { _, file, line, ok := runtime.Caller(2) if ok { split := strings.Split(file, "/") file = split[len(split)-1] fileLine := file + ":" + strconv.Itoa(line) + " " return fileLine } return "" } // logJob 定時操作日志 func logJob() { c := cron.New(cron.WithSeconds()) c.AddFunc("@daily", func() { Info("執(zhí)行l(wèi)og定時任務(wù)。。。") now := time.Now() createLogFile() closeYesterdayLogFile := fmt.Sprintf("plume_log_%s.log", now.Add(-24*time.Hour).Format("20060102")) file, _ := os.Open(closeYesterdayLogFile) file.Sync() file.Close() // 刪除n天前的日志 removeLogFile := fmt.Sprintf("plume_log_%s.log", time.Now().Add(time.Duration(config.Conf.Log.KeepDays)*-24*time.Hour).Format("20060102")) open, err := os.Open(removeLogFile) if err != nil { Error(err.Error()) return } go func () { // 設(shè)置for select 的原因是文件雖然被關(guān)閉了,但文件所占的process還在進(jìn)行中,每10秒輪詢一次,執(zhí)行刪除操作,確保文件有被刪除 loop: for { select { case <-time.After(10 * time.Second): removeErr := os.Remove(removeLogFile) if removeErr != nil { Error(removeErr.Error()) } else { Info("刪除日志成功:%s", removeLogFile) break loop } } } }() }) c.Start() } //var ( // kernel32 = syscall.NewLazyDLL(`kernel32.dll`) // proc = kernel32.NewProc(`SetConsoleTextAttribute`) // CloseHandle = kernel32.NewProc(`CloseHandle`) // // 給字體顏色對象賦值 // FontColor = Color{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} //) //type Color struct { // black int // 黑色 // blue int // 藍(lán)色 // green int // 綠色 // cyan int // 青色 // red int // 紅色 // purple int // 紫色 // yellow int // 黃色 // light_gray int // 淡灰色(系統(tǒng)默認(rèn)值) // gray int // 灰色 // light_blue int // 亮藍(lán)色 // light_green int // 亮綠色 // light_cyan int // 亮青色 // light_red int // 亮紅色 // light_purple int // 亮紫色 // light_yellow int // 亮黃色 // white int // 白色 //} // 輸出有顏色的字體 //func ColorPrint4Window(s, t string) { // switch t { // case "DEBUG": // proc.Call(uintptr(syscall.Stdout), uintptr(FontColor.light_cyan)) // Debug(s) // case "INFO": // proc.Call(uintptr(syscall.Stdout), uintptr(FontColor.green)) // Info(s) // case "WARN": // proc.Call(uintptr(syscall.Stdout), uintptr(FontColor.light_yellow)) // Warn(s) // case "ERROR": // proc.Call(uintptr(syscall.Stdout), uintptr(FontColor.red)) // Error(s) // default: // proc.Call(uintptr(syscall.Stdout), uintptr(FontColor.black)) // Info(s) // } //}
使用viper讀取配置:
package config import ( "github.com/spf13/viper" "os" ) var Conf *Config type Config struct { Log } type Log struct { Level int KeepDays int Prefix string } func init() { Conf = &Config{} config := viper.New() path, _ := os.Getwd() config.SetConfigName("config") // 配置文件名字,注意沒有擴(kuò)展名 config.SetConfigType("toml") config.AddConfigPath(path) if err := config.ReadInConfig(); err != nil { panic(err) } Conf.Level = config.GetInt("log.level") Conf.Log.KeepDays = config.GetInt("log.keep-days") Conf.Log.Prefix = config.GetString("log.prefix") }
package main import ( "my_log/log" ) func main() { log.Debug("我是debug日志") log.Info("我是info日志") log.Warn("我是warn日志") log.Error("我是error日志") }
日志配置文件(config.toml):
[log] level = 0 keep-days = 7 prefix = "test_"
控制臺輸出:
生成的日志文件內(nèi)容:
碰到的問題:
The process cannot access the file because it is being used by another process.
// 問題的產(chǎn)生: file := "test_log.log" os.Open(file) file.Close() os.Remove(file) // 因?yàn)槌绦蜻€在運(yùn)行中,該日志文件所占的process還未停止 // 解決辦法: // 延遲刪除文件,比如time.Sleep() // 推薦使用:label:for + select 輪詢刪除,刪除完畢 break:label // 示例代碼: loop: for { select { case <-time.After(10 * time.Second): removeErr := os.Remove(file) if removeErr != nil { Error(removeErr.Error()) } else { Info("刪除日志成功:%s", file) break loop } } }
總結(jié)
到此這篇關(guān)于golang中l(wèi)og包自定義輸出日志格式與寫入到文件的文章就介紹到這了,更多相關(guān)go log包自定義輸出日志格式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go語言csrf庫使用實(shí)現(xiàn)原理示例解析
這篇文章主要為大家介紹了go語言csrf庫使用實(shí)現(xiàn)原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10GoLang職責(zé)鏈模式代碼實(shí)現(xiàn)介紹
這篇文章主要介紹了GoLang職責(zé)鏈模式代碼實(shí)現(xiàn),職責(zé)鏈模式是一種常用的設(shè)計(jì)模式,可以提高代碼的靈活性與可維護(hù)性,職責(zé)鏈模式將請求和處理分離,可以讓請求在處理鏈中依次經(jīng)過多個處理者,直到找到能夠處理請求的處理者為止2023-05-05Go語言bufio庫的全面指南與實(shí)戰(zhàn)技巧詳解
這篇文章主要為大家全面介紹一下?bufio?庫的核心組件與功能,包括?Reader、Writer?和?Scanner?等并深入探討它們在實(shí)際編程中的運(yùn)用場景和技巧,感興趣的可以了解下2024-01-01