go语言日志实现详解(打印日志、日志写入文件和日志切割)

golang内置了log包,实现简单的日志服务,下面这篇文章主要给大家介绍了关于go语言日志实现(打印日志、日志写入文件和日志切割)的相关资料,需要的朋友可以参考下

Go语言内置的log包实现了简单的日志服务

log包定义了Logger类型,该类型提供了一些格式化输出的方法。本包也提供了一个预定义的“标准”logger,可以通过调用函数Print系列(Print|Printf|Println)、Fatal系列(Fatal|Fatalf|Fatalln)、和Panic系列(Panic|Panicf|Panicln)来使用,比自行创建一个logger对象更容易使用。

Logger

package main

import (
    "log"
)

func main() {
    log.Println("这是一条测试的日志。")
    v := "很普通的"
    log.Printf("这是一条%s日志。\n", v)
    log.Fatalln("这是一条会触发fatal的日志。")
    log.Panicln("这是一条会触发panic的日志。")
}

标准logger的配置

SetFlags函数用来设置标准logger的输出配置。

const (
    // 控制输出日志信息的细节,不能控制输出的顺序和格式。
    // 输出的日志在每一项后会有一个冒号分隔:例如2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
    Ldate         = 1 << iota     // 日期:2009/01/23
    Ltime                         // 时间:01:23:23
    Lmicroseconds                 // 微秒级别的时间:01:23:23.123123(用于增强Ltime位)
    Llongfile                     // 文件全路径名+行号: /a/b/c/d.go:23
    Lshortfile                    // 文件名+行号:d.go:23(会覆盖掉Llongfile)
    LUTC                          // 使用UTC时间
    LstdFlags     = Ldate | Ltime // 标准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函数用来设置标准logger的输出目的地,默认是标准错误输出。

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

创建新logger对象

log标准库中还提供了一个创建新logger对象的构造函数–New,支持我们创建自己的logger示例。New函数的签名如下:

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

New创建一个Logger对象。其中,参数out设置日志信息写入的目的地。参数prefix会添加到生成的每一条日志前面。参数flag定义日志的属性(时间、文件等等)。

举个例子:

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记录的日志。

补充:Go内置的log库功能有限,例如无法满足记录不同级别日志的情况,需要选择使用第三方的日志库,如logrus、zap等。

日志库级别

package log

import (
    "errors"
    "fmt"
    "strings"
    "time"
)

type LogLevel uint16

//日志常量
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("无效的日志级别")
        return UNKNOW,err
    }
}
//定义日志级别机构提
type Logger  struct{
    Level LogLevel
}
//构造器
func NewLog(levelLog string) Logger  {
    level, err := paraLogLevel(levelLog)
    if err !=nil{
        panic(err)
    }
    return Logger{
        Level: level,
    }
}
//是否能够打印某个级别的日志
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日志")
}

打印结果:[2022-08-04 10:41:56] [Debug] 这是debug日志[2022-08-04 10:41:56] [Info] 这是info日志

runtime.Caller

能够拿到文件名函数名和行号

可变参数的日志

//......interface{}表示可变的任意参数,可不传也可传任意长度
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);
    }

}

实现往文件里面写日志

1.新建fileloger.go文件,用来提供写入日志的功能

package log

import (
    "errors"
    "fmt"
    "os"
    "path"
    "strings"
    "time"
)

type LogLevel uint16
//日志级别
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("无效的日志级别")
        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"
    }
}

//定义日志的结构体
type FileLogger  struct{
    Level LogLevel
    filePath string
    fileName string
    //要打开和写入的文件,一个日志文件一个错误日志文件
    fileObj *os.File
    errfileObj *os.File
    maxFileSize int64
}

//构造函数
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

}
//判断级别
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.测试:

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

}

//运行两次后,打印结果:

日志切割(按文件大小切割、按日期切割)

其实就是每次记录文件的大小,超过了就重新写一个文件。

通过Stat()函数拿到文件的一些信息

    open, _:= os.Open("文件名")
    stat, _, := open.Stat()
    stat.Size()//拿到文件大小

日期切割:

拿到文件的名称或者检查下有没有当天的日志文件,没有就创建新增。

总结