go語(yǔ)言日志實(shí)現(xiàn)詳解(打印日志、日志寫入文件和日志切割)
Go語(yǔ)言內(nèi)置的log包實(shí)現(xiàn)了簡(jiǎn)單的日志服務(wù)
log包定義了Logger類型,該類型提供了一些格式化輸出的方法。本包也提供了一個(gè)預(yù)定義的“標(biāo)準(zhǔn)”logger,可以通過(guò)調(diào)用函數(shù)Print系列(Print|Printf|Println)、Fatal系列(Fatal|Fatalf|Fatalln)、和Panic系列(Panic|Panicf|Panicln)來(lái)使用,比自行創(chuàng)建一個(gè)logger對(duì)象更容易使用。
Logger
package main
import (
"log"
)
func main() {
log.Println("這是一條測(cè)試的日志。")
v := "很普通的"
log.Printf("這是一條%s日志。\n", v)
log.Fatalln("這是一條會(huì)觸發(fā)fatal的日志。")
log.Panicln("這是一條會(huì)觸發(fā)panic的日志。")
}標(biāo)準(zhǔn)logger的配置
SetFlags函數(shù)用來(lái)設(shè)置標(biāo)準(zhǔn)logger的輸出配置。
const (
// 控制輸出日志信息的細(xì)節(jié),不能控制輸出的順序和格式。
// 輸出的日志在每一項(xiàng)后會(huì)有一個(gè)冒號(hào)分隔:例如2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
Ldate = 1 << iota // 日期:2009/01/23
Ltime // 時(shí)間:01:23:23
Lmicroseconds // 微秒級(jí)別的時(shí)間:01:23:23.123123(用于增強(qiáng)Ltime位)
Llongfile // 文件全路徑名+行號(hào): /a/b/c/d.go:23
Lshortfile // 文件名+行號(hào):d.go:23(會(huì)覆蓋掉Llongfile)
LUTC // 使用UTC時(shí)間
LstdFlags = Ldate | Ltime // 標(biāo)準(zhǔn)logger的初始值
)
func main() {
log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)
log.Println("這是一條很普通的日志。")
}
配置日志前綴(SetPrefix)
func main() {
log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)
log.Println("這是一條很普通的日志。")
log.SetPrefix("[哈哈哈]")
log.Println("這是一條很普通的日志。")
}
配置日志輸出位置
SetOutput函數(shù)用來(lái)設(shè)置標(biāo)準(zhǔn)logger的輸出目的地,默認(rèn)是標(biāo)準(zhǔn)錯(cuò)誤輸出。
func init() {
logFile, err := os.OpenFile("./xx.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
fmt.Println("open log file failed, err:", err)
return
}
log.SetOutput(logFile)
log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)
}
創(chuàng)建新logger對(duì)象
log標(biāo)準(zhǔn)庫(kù)中還提供了一個(gè)創(chuàng)建新logger對(duì)象的構(gòu)造函數(shù)–New,支持我們創(chuàng)建自己的logger示例。New函數(shù)的簽名如下:
func New(out io.Writer, prefix string, flag int) *Logger
New創(chuàng)建一個(gè)Logger對(duì)象。其中,參數(shù)out設(shè)置日志信息寫入的目的地。參數(shù)prefix會(huì)添加到生成的每一條日志前面。參數(shù)flag定義日志的屬性(時(shí)間、文件等等)。
舉個(gè)例子:
func main() {
logger := log.New(os.Stdout, "<New>", log.Lshortfile|log.Ldate|log.Ltime)
logger.Println("這是自定義的logger記錄的日志。")
}
//<New>2017/06/19 14:06:51 main.go:34: 這是自定義的logger記錄的日志。
補(bǔ)充:Go內(nèi)置的log庫(kù)功能有限,例如無(wú)法滿足記錄不同級(jí)別日志的情況,需要選擇使用第三方的日志庫(kù),如logrus、zap等。
日志庫(kù)級(jí)別
package log
import (
"errors"
"fmt"
"strings"
"time"
)
type LogLevel uint16
//日志常量
const (
UNKNOW LogLevel = iota
DEBUG
TRACE
INFO
WARNIG
ERROR
FATAL
)
//解析日志級(jí)別
func paraLogLevel(s string) (LogLevel,error) {
s = strings.ToLower(s)
switch s {
case "debug":
return DEBUG,nil
case "tarce":
return TRACE,nil
case "info":
return INFO,nil
case "warnig":
return WARNIG,nil
case "error":
return ERROR,nil
case "fatal":
return FATAL,nil
default:
err:= errors.New("無(wú)效的日志級(jí)別")
return UNKNOW,err
}
}
//定義日志級(jí)別機(jī)構(gòu)提
type Logger struct{
Level LogLevel
}
//構(gòu)造器
func NewLog(levelLog string) Logger {
level, err := paraLogLevel(levelLog)
if err !=nil{
panic(err)
}
return Logger{
Level: level,
}
}
//是否能夠打印某個(gè)級(jí)別的日志
func (l Logger) enable(logLevel LogLevel) bool {
return l.Level >logLevel
}
func (l Logger) Debug(msg string) {
if l.enable(DEBUG){
now := time.Now()
fmt.Printf("[%s] [Debug] %s",now.Format("2006-01-02 15:04:05"),msg);
}
}
func (l Logger) Info(msg string) {
if l.enable(INFO){
now := time.Now()
fmt.Printf("[%s] [Info] %s",now.Format("2006-01-02 15:04:05"),msg);
}
}
func (l Logger) Warning(msg string) {
if l.enable(WARNIG){
now := time.Now()
fmt.Printf("[%s] [Warning] %s",now.Format("2006-01-02 15:04:05"),msg);
}
}
func (l Logger) Error(msg string) {
if l.enable(ERROR){
now := time.Now()
fmt.Printf("[%s] [Error] %s",now.Format("2006-01-02 15:04:05"),msg);
}
}
func (l Logger) Fatal(msg string) {
if l.enable(FATAL){
now := time.Now()
fmt.Printf("[%s] [Fatal] %s",now.Format("2006-01-02 15:04:05"),msg);
}
}
import "gostudy/log"
func main() {
newLog := log.NewLog("warnig")
newLog.Debug("這是debug日志")
newLog.Info("這是info日志")
newLog.Warning("這是Warning日志")
newLog.Error("這是ERROR日志")
newLog.Fatal("這是FATAL日志")
}
打印結(jié)果:[2022-08-04 10:41:56] [Debug] 這是debug日志[2022-08-04 10:41:56] [Info] 這是info日志
runtime.Caller

能夠拿到文件名函數(shù)名和行號(hào)
可變參數(shù)的日志
//......interface{}表示可變的任意參數(shù),可不傳也可傳任意長(zhǎng)度
func (l Logger) Debug(msg string, a ...interface{}) {
msg = fmt.Sprint(msg,a)
if l.enable(DEBUG){
now := time.Now()
fmt.Printf("[%s] [Debug] %s",now.Format("2006-01-02 15:04:05"),msg);
}
}
實(shí)現(xiàn)往文件里面寫日志
1.新建fileloger.go文件,用來(lái)提供寫入日志的功能
package log
import (
"errors"
"fmt"
"os"
"path"
"strings"
"time"
)
type LogLevel uint16
//日志級(jí)別
const (
UNKNOW LogLevel = iota
DEBUG
TRACE
INFO
WARNIG
ERROR
FATAL
)
//解析日志
func paraLogLevel(s string) (LogLevel,error) {
s = strings.ToLower(s)
switch s {
case "debug":
return DEBUG,nil
case "tarce":
return TRACE,nil
case "info":
return INFO,nil
case "warnig":
return WARNIG,nil
case "error":
return ERROR,nil
case "fatal":
return FATAL,nil
default:
err:= errors.New("無(wú)效的日志級(jí)別")
return UNKNOW,err
}
}
//獲取日志的字符串格式
func getLogStr (level LogLevel) string {
switch level {
case DEBUG:
return "debug"
case TRACE:
return "tarce"
case INFO:
return "info"
case WARNIG:
return "warnig"
case ERROR:
return "error"
case FATAL:
return "fatal"
default:
return "unknow"
}
}
//定義日志的結(jié)構(gòu)體
type FileLogger struct{
Level LogLevel
filePath string
fileName string
//要打開和寫入的文件,一個(gè)日志文件一個(gè)錯(cuò)誤日志文件
fileObj *os.File
errfileObj *os.File
maxFileSize int64
}
//構(gòu)造函數(shù)
func NewFlieLogger(LeveStr ,fp,fn string,size int64) *FileLogger{
level, err := paraLogLevel(LeveStr)
if err != nil {
panic(err)
}
f1 := &FileLogger{
Level: level,
filePath: fp,
fileName: fn,
maxFileSize: size,
}
err= f1.initFile()
if err != nil {
panic(err)
}
return f1
}
//初始化要打開和寫入的日志文件的操作
func (f *FileLogger) initFile() (error) {
join := path.Join(f.filePath, f.fileName)
fileObj, err := os.OpenFile(join, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("open log fail ,err: %v\n",err)
return err
}
errFileObj, err := os.OpenFile(join+".err", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("open log fail ,err: %v\n",err)
return err
}
//日志文件都打開
f.fileObj = fileObj;
f.errfileObj = errFileObj;
return nil
}
//判斷級(jí)別
func (l FileLogger) enable(logLevel LogLevel) bool {
return l.Level >logLevel
}
//打印日志操作
func (f *FileLogger) Log(leve LogLevel,msg string) {
now := time.Now()
if f.enable(leve){
fmt.Fprintf(f.fileObj,"[%s] [%s] %s",now.Format("2006-01-02 15:04:05"),getLogStr(leve),msg);
}
if leve >ERROR{
fmt.Fprintf(f.errfileObj,"[%s] [%s] %s",now.Format("2006-01-02 15:04:05"),getLogStr(leve),msg);
}
}
func (l FileLogger) Debug(msg string, a ...interface{}) {
msg = fmt.Sprint(msg,a)
if l.enable(DEBUG){
l.Log(DEBUG,msg)
}
}
func (l FileLogger) Info(msg string, a ...interface{}) {
msg = fmt.Sprint(msg,a)
if l.enable(WARNIG){
l.Log(WARNIG,msg)
}
}
func (l FileLogger) Warning(msg string, a ...interface{}) {
msg = fmt.Sprint(msg,a)
if l.enable(WARNIG){
l.Log(WARNIG,msg)
}
}
func (l FileLogger) Error(msg string, a ...interface{}) {
msg = fmt.Sprint(msg,a)
if l.enable(ERROR){
l.Log(ERROR,msg)
}
}
func (l FileLogger) Fatal(msg string, a ...interface{}) {
msg = fmt.Sprint(msg,a)
if l.enable(FATAL){
l.Log(FATAL,msg)
}
}
func (f *FileLogger) Colse() {
f.fileObj.Close()
f.errfileObj.Close()
}
2.測(cè)試:
func main() {
newLog := log.NewFlieLogger("warnig","./","now.log",100*1024*1024)
newLog.Debug("這是debug日志")
newLog.Info("這是info日志")
newLog.Warning("這是Warning日志")
newLog.Error("這是ERROR日志")
newLog.Fatal("這是FATAL日志")
newLog.Colse()
}
//運(yùn)行兩次后,打印結(jié)果:

日志切割(按文件大小切割、按日期切割)
其實(shí)就是每次記錄文件的大小,超過(guò)了就重新寫一個(gè)文件。
通過(guò)Stat()函數(shù)拿到文件的一些信息
open, _:= os.Open("文件名")
stat, _, := open.Stat()
stat.Size()//拿到文件大小
日期切割:
拿到文件的名稱或者檢查下有沒(méi)有當(dāng)天的日志文件,沒(méi)有就創(chuàng)建新增。
總結(jié)
到此這篇關(guān)于go語(yǔ)言日志實(shí)現(xiàn)(打印日志、日志寫入文件和日志切割)的文章就介紹到這了,更多相關(guān)go語(yǔ)言打印日志和日志切割內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go結(jié)合Redis用最簡(jiǎn)單的方式實(shí)現(xiàn)分布式鎖
本文主要介紹了Go結(jié)合Redis用最簡(jiǎn)單的方式實(shí)現(xiàn)分布式鎖示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
使用Lumberjack+zap進(jìn)行日志切割歸檔操作
這篇文章主要介紹了使用Lumberjack+zap進(jìn)行日志切割歸檔操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12
goland 實(shí)現(xiàn)websocket server的示例代碼
本文主要介紹了goland 實(shí)現(xiàn)websocket server的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
詳解golang開發(fā)中http請(qǐng)求redirect的問(wèn)題
這篇文章主要介紹了詳解golang開發(fā)中http請(qǐng)求redirect的問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
Go 自定義package包設(shè)置與導(dǎo)入操作
這篇文章主要介紹了Go 自定義package包設(shè)置與導(dǎo)入操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05
線上問(wèn)題排查之golang使用json進(jìn)行對(duì)象copy
這篇文章主要介紹了線上問(wèn)題排查之golang使用json進(jìn)行對(duì)象copy,文章圍繞golang使用json進(jìn)行對(duì)象copy的內(nèi)存溢出問(wèn)題排查展開詳細(xì)內(nèi)容需要的小伙伴可以參考一下2022-06-06
golang?墻上時(shí)鐘與單調(diào)時(shí)鐘的實(shí)現(xiàn)
本文主要介紹了golang?墻上時(shí)鐘與單調(diào)時(shí)鐘的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
Golang實(shí)現(xiàn)組合模式和裝飾模式實(shí)例詳解
這篇文章主要介紹了Golang實(shí)現(xiàn)組合模式和裝飾模式,本文介紹組合模式和裝飾模式,golang實(shí)現(xiàn)兩種模式有共同之處,但在具體應(yīng)用場(chǎng)景有差異。通過(guò)對(duì)比兩個(gè)模式,可以加深理解,需要的朋友可以參考下2022-11-11

