golang中log包自定义输出日志格式与写入到文件

 更新时间:2023年06月30日 16:07:19   作者:Json_Marz  
这篇文章主要给大家介绍了关于golang中log包自定义输出日志格式与写入到文件的相关资料,日志输出在任何项目中都极其重要,是有助于后续我们排查解决程序BUG,需要的朋友可以参考下

1.背景:

平时开发项目时打印日志用到logrus包,但是觉得logrus配置比较麻烦,于是想着直接使用go自带的log包输出日志,其提供了一些配置,比如SetPrefix(), 可以让我们自己二次封装,让自己的日志内容更鲜明些。

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("执行log定时任务。。。")
    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 () {
        // 设置for select 的原因是文件虽然被关闭了,但文件所占的process还在进行中,每10秒轮询一次,执行删除操作,确保文件有被删除
        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 // 蓝色
//    green        int // 绿色
//    cyan         int // 青色
//    red          int // 红色
//    purple       int // 紫色
//    yellow       int // 黄色
//    light_gray   int // 淡灰色(系统默认值)
//    gray         int // 灰色
//    light_blue   int // 亮蓝色
//    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") // 配置文件名字,注意没有扩展名
    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_"

控制台输出:

生成的日志文件内容:

碰到的问题:

The process cannot access the file because it is being used by another process.

// 问题的产生:
file := "test_log.log"
os.Open(file)
file.Close()
os.Remove(file)
// 因为程序还在运行中,该日志文件所占的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
                }
            }
        }

总结

到此这篇关于golang中log包自定义输出日志格式与写入到文件的文章就介绍到这了,更多相关go log包自定义输出日志格式内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • GOLang IO接口与工具使用方法讲解

    GOLang IO接口与工具使用方法讲解

    这篇文章主要介绍了GOLang IO接口与工具使用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-03-03
  • Golang pipe在不同场景下远程交互

    Golang pipe在不同场景下远程交互

    这篇文章主要介绍了Golang pipe在不同场景下远程交互,pipe实现从一个进程重定向至另一个进程,它是双向数据通道,用于实现进行间通信
    2023-03-03
  • go语言中切片Slice与数组Array对比以及panic: runtime error: index out of range问题解决

    go语言中切片Slice与数组Array对比以及panic: runtime error: index out 

    go语言中数组与其他语言有在显著的不同,包括其不能够进行添加,以及值拷贝的特性,下面这篇文章主要给大家介绍了关于go语言中切片Slice与数组Array对比以及panic: runtime error: index out of range问题解决的相关资料,需要的朋友可以参考下
    2022-07-07
  • Golang控制通道实现协程等待详解

    Golang控制通道实现协程等待详解

    这篇文章主要介绍了Golang控制通道实现协程等待,通道是Go语言程序的并发体goroutine是它们之间的通信机制。一个通道是一个通信机制,它可以让一个goroutine通过它给另一个goroutine发送值信息。每个通道都有一个特殊的类型,也就是channels可发送数据的类型
    2022-11-11
  • Go使用database/sql操作数据库的教程指南

    Go使用database/sql操作数据库的教程指南

    Go 语言中,有一个名为database/sql的标准库,提供了统一的编程接口,使开发人员能够以一种通用的方式与各种关系型数据库进行交互,本文就来和大家讲讲它的具体操作吧
    2023-06-06
  • go 类型转换方式(interface 类型的转换)

    go 类型转换方式(interface 类型的转换)

    这篇文章主要介绍了go 类型转换方式(interface 类型的转换),具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-05-05
  • golang使用bcrypt包对密码进行加密的方法实现

    golang使用bcrypt包对密码进行加密的方法实现

    本文主要介绍了golang使用bcrypt包对密码进行加密的方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • Go语言学习之反射的用法详解

    Go语言学习之反射的用法详解

    反射指的是运行时动态的获取变量的相关信息。本文将为大家详细介绍Go语言中反射的用法,文中的示例代码讲解详细,感兴趣的可以了解一下
    2022-04-04
  • Go语言Elasticsearch数据清理工具思路详解

    Go语言Elasticsearch数据清理工具思路详解

    这篇文章主要介绍了Go语言Elasticsearch数据清理工具思路详解,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-10-10
  • Go设计模式之享元模式讲解和代码示例

    Go设计模式之享元模式讲解和代码示例

    享元是一种结构型设计模式,它允许你在消耗少量内存的情况下支持大量对象,模式通过共享多个对象的部分状态来实现上述功能,换句话来说,享元会将不同对象的相同数据进行缓存以节省内存,本文就将通过代码示例给大家详细介绍一下享元模式
    2023-06-06

最新评论