Go语言中log日志库的介绍
本文给大家介绍Go语言中log日志库的概念使用技巧,log包定义了Logger类型,该类型提供了一些格式化输出的方法,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
一、标准库log介绍
Go语言内置的log包实现了简单的日志服务。
1、使用Logger
log包定义了Logger类型,该类型提供了一些格式化输出的方法。
log包也提供了一个预定义的“标准”logger,可以通过调用函数Print系列(Print|Printf|Println)、Fatal系列(Fatal|Fatalf|Fatalln)、和Panic系列(Panic|Panicf|Panicln)来使用,比自行创建一个logger对象更容易使用。
logger会打印每条日志信息的日期、时间,默认输出到系统的标准错误。
Fatal系列函数会在写入日志信息后调用os.Exit(1)。
Panic系列函数会在写入日志信息后panic。
示例:
1 2 3 4 5 6 7 8 | // 直接调用print来输出日志到终端 func main() { log.Println("这是一条很普通的日志。") v := "很普通的" log.Printf("这是一条%s日志。\n", v) log.Fatalln("这是一条会触发fatal的日志。") log.Panicln("这是一条会触发panic的日志。") } |
2、配置logger
2.1、标准logger的配置
默认情况下的logger只会提供日志的时间信息,但是很多情况下我们希望得到更多信息,比如记录该日志的文件名和行号等。
log标准库中为我们提供了定制这些设置的方法。log标准库中的Flags函数会返回标准logger的输出配置,而SetFlags函数用来设置标准logger的输出配置。
二、自定义日志库
1、需要满足的需求
1、支持网不同地方输出日志
2、日志要支持开关控制
3、完整的日志要包含时间、行号、文件名、日志级别、日志信息
4、日志文件要能切割
5、日志级别
Debug
Trace
Info
Warning
Error
Fatal
2、了解下runtime包
runtime.Caller() 方法,会返回函数调用的信息,在()内传入int类型数据,代表调用的层数(0代表第一层被调用,1代表第二层,一次类推)。runtime.Caller()返回如下四个参数:
pc: 记录了调用的函数消息,如函数名等
file: 调用的文件名
line: 行数
OK: 返回的bool值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // 获取代码行号 func getinfo(n int) { /* runtime.Caller()返回四个参数: pc: 记录了调用的函数消息,如函数名等 file: 调用的文件名 line: 行数 OK: 返回的bool值 */ pc, file, line, ok := runtime.Caller(n) if !ok { fmt.Printf("runtime.Caller() failed\n") return } fmt.Println(pc) // 用于调用函数消息 funcName := runtime.FuncForPC(pc).Name() // 利用 runtime.FuncForPC()方法,参数pc序号,可以调出函数名 fmt.Println(funcName) fmt.Println(file) // 打印出执行的文件的绝对路径 fmt.Println(path.Base(file)) // path.Base()方法可以获取传入的绝对路径的最后一个文件名称 fmt.Println(line) // 打印出执行的行号 } func main() { getinfo(0) } |
3、自定义日志库
1、在 E:\GoProject\src\gitee.com 项目目录下,新建 mylogger 包目录,存放日志库代码
在mylogger包目录下console.go文件中,放将日志输出到终端的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | # E:\GoProject\src\gitee.com\mylogger\console.go package mylogger import ( "fmt" "strings" "time" ) // 终端输出的日志内容 // Logger 日志结构体 type ConsoleLogger struct { Level LogLevel } // 构造函数 func NewLog(levelStr string) ConsoleLogger { level, err := parseLogLevel(levelStr) if err != nil { panic(err) } return ConsoleLogger{ Level: level, } } // 定义日志等级比较 func (c ConsoleLogger) enable(level LogLevel) bool { return c.Level <= level } // 定义一个日志打印方法 func (c ConsoleLogger) outLog(lv LogLevel, format string, a ...interface{}) { level := parseLogLevelStr(lv) if c.enable(lv) { msg := fmt.Sprintf(format, a...) // 支持格式化操作,a...表示对接收的接口进行展开 level = strings.ToUpper(level) fileName, funcName, linenu := getinfo(3) fmt.Printf("[%s] [%s] [FileName:%s lineNum:%d:%s] %s\n", time.Now().Format("2006-01-02 15:04:05"), level, fileName, linenu, funcName, msg) } } // Debug日志 func (c ConsoleLogger) Debug(msg string, a ...interface{}) { c.outLog(DEBUG, msg, a...) } // Info日志 func (c ConsoleLogger) Info(msg string, a ...interface{}) { c.outLog(INFO, msg, a...) } // Warning日志 func (c ConsoleLogger) Warning(msg string, a ...interface{}) { c.outLog(WARNING, msg, a...) } // Error日志 func (c ConsoleLogger) Error(msg string, a ...interface{}) { c.outLog(ERROR, msg, a...) } // Fatal日志 func (c ConsoleLogger) Fatal(msg string, a ...interface{}) { c.outLog(FATAL, msg, a...) } |
在mylogger包目录下logfile.go文件中,放将日志输出到文件的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | # E:\GoProject\src\gitee.com\mylogger\logfile.go package mylogger // 向文件中写日志的相关操作 import ( "fmt" "os" "path" "strings" "time" ) // Logger 日志结构体 type FileLogger struct { Level LogLevel logPath string // 日志文件目录 fileName string // 日志文件名称 maxFileSize int64 // 日志文件大小 fileObj *os.File errFileObj *os.File } // 构造函数 func NewFileLog(levelStr, fp, fn string, maxSize int64) *FileLogger { level, err := parseLogLevel(levelStr) if err != nil { panic(err) } f1 := &FileLogger{ Level: level, logPath: fp, fileName: fn, maxFileSize: maxSize, } err = f1.initFile() if err != nil { panic(err) } return f1 } // 定义文件初始化方法,打开文件 func (f *FileLogger) initFile() error { // 打开文件 fullPath := path.Join(f.logPath, f.fileName) fileObj, err := os.OpenFile(fullPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) if err != nil { fmt.Printf("open logFile failed,Err:%v", err) return err } // 打开错误日志文件 errFileObj, err := os.OpenFile("error-"+fullPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) if err != nil { fmt.Printf("open errorLogFile failed,Err:%v", err) return err } // 将文件句柄赋值给结构体中的元素 f.fileObj = fileObj f.errFileObj = errFileObj return nil } // 关闭文件方法 func (f *FileLogger) closeFile() { f.fileObj.Close() f.errFileObj.Close() } // 定义日志等级比较 func (f *FileLogger) enable(level LogLevel) bool { return f.Level <= level } // 定义检查日志大小进行切割的方法 func (f *FileLogger) checkLogSize(file *os.File) bool { // 利用os.Open()方法打开的文件句柄类型都是 *os.File 这种指针类型 fileInfo, err := file.Stat() if err != nil { fmt.Printf("get file info failed,err:%v\n", err) return false // panic(err) } if fileInfo.Size() > f.maxFileSize { return true } return false } func (f *FileLogger) cutFile(file *os.File) (*os.File, error) { // 获取当前文件名 fileInfo, err := file.Stat() if err != nil { fmt.Printf("get file info failed,err:%v\n", err) return nil, err } append := "_" + time.Now().Format("2006-01-02-15-04-05") + ".log" // 时间戳后缀 fileName := fileInfo.Name() newFileName := fileName + append oldPath := path.Join(f.logPath, fileName) // 日志文件的全路径 newPath := path.Join(f.logPath, newFileName) // 关闭当前文件句柄 file.Close() // 进行更名备份 os.Rename(oldPath, newPath) // 再打开一个文件 fileObj, err := os.OpenFile(oldPath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644) if err != nil { fmt.Printf("open logFile failed,Err:%v\n", err) return nil, err } return fileObj, nil } // 定义一个日志输出的方法 func (f *FileLogger) outLog(lv LogLevel, format string, a ...interface{}) { level := parseLogLevelStr(lv) if f.enable(lv) { msg := fmt.Sprintf(format, a...) // 支持格式化操作,a...表示对接收的接口进行展开 level = strings.ToUpper(level) fileName, funcName, linenu := getinfo(3) // 判断日志是否需要切割 if f.checkLogSize(f.fileObj) { fileobj, err := f.cutFile(f.fileObj) if err != nil { fmt.Printf("log cut failed,err:%v\n", err) } f.fileObj = fileobj } // 将日志记录在日志文件中 fmt.Fprintf(f.fileObj, "[%s] [%s] [FileName:%s lineNum:%d:%s] %s\n", time.Now().Format("2006-01-02 15:04:05"), level, fileName, linenu, funcName, msg) if lv >= ERROR { // 判断错误日志是否需要切割 if f.checkLogSize(f.errFileObj) { fileobj, err := f.cutFile(f.errFileObj) if err != nil { fmt.Printf("log cut failed,err:%v\n", err) } f.errFileObj = fileobj } // 将日志等级大于ERROR的在错误日志中再记录一遍 fmt.Fprintf(f.errFileObj, "[%s] [%s] [FileName:%s lineNum:%d:%s] %s\n", time.Now().Format("2006-01-02 15:04:05"), level, fileName, linenu, funcName, msg) } } } // Debug日志 func (f *FileLogger) Debug(msg string, a ...interface{}) { f.outLog(DEBUG, msg, a...) } // Info日志 func (f *FileLogger) Info(msg string, a ...interface{}) { f.outLog(INFO, msg, a...) } // Warning日志 func (f *FileLogger) Warning(msg string, a ...interface{}) { f.outLog(WARNING, msg, a...) } // Error日志 func (f *FileLogger) Error(msg string, a ...interface{}) { f.outLog(ERROR, msg, a...) } // Fatal日志 func (f *FileLogger) Fatal(msg string, a ...interface{}) { f.outLog(FATAL, msg, a...) } |
在mylogger包目录下创建mylogger.go文件,放包内共用的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 | # E:\GoProject\src\gitee.com\mylogger\mylogger.go package mylogger import ( "errors" "fmt" "path" "runtime" "strings" ) // 设置日志级别 type LogLevel uint16 const ( UNKNOWN LogLevel = iota DEBUG TRACE INFO WARNING ERROR FATAL ) // 解析日志等级,将字符串解析成int func parseLogLevel(s string) (LogLevel, error) { s = strings.ToLower(s) // 将字符串全部转为小写 switch s { case "debug": return DEBUG, nil case "trace": return TRACE, nil case "info": return INFO, nil case "warning": return WARNING, nil case "error": return ERROR, nil case "fatal": return FATAL, nil default: err := errors.New("无效日志等级") return UNKNOWN, err } } // 将日志等级解析成字符串 func parseLogLevelStr(lv LogLevel) (s string) { s = strings.ToLower(s) // 将字符串全部转为小写 switch lv { case DEBUG: return "DEBUG" case TRACE: return "TRACE" case INFO: return "INFO" case WARNING: return "WARNING" case ERROR: return "ERROR" case FATAL: return "FATAL" default: return "DEBUG" } } // 获取代码行号 func getinfo(n int) (fileName, funcName string, line int) { /* runtime.Caller()返回四个参数: pc: 记录了调用的函数消息,如函数名等 file: 调用的文件名 line: 行数 OK: 返回的bool值 */ pc, file, line, ok := runtime.Caller(n) if !ok { fmt.Printf("runtime.Caller() failed\n") return } fileName = path.Base(file) funcName = runtime.FuncForPC(pc).Name() return fileName, funcName, line } |
2、调用日志库代码测试
需要注意,导入mylogger包,是从$GOPATH路径开始的(即从src目录开始的相对路径)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # E:\GoProject\src\gitee.com\LTP\logging package main import ( "time" mylogger "gitee.com/mylogger" ) func main() { id := 10 name := "小明" flogger := mylogger.NewFileLog("info", "./", "app.log", 1024) for { flogger.Warning("这是个Warning日志") flogger.Error("这是个Error日志,id:%d name:%s", id, name) time.Sleep(2 * time.Second) } } |
到此这篇关于Go log日志库的文章就介绍到这了