Golang中log日志包的使用
文章目录
1.前言
作为后端开发人员,日志文件记录了发生在操作系统或其他软件运行时的事件或状态。技术人员可以通过日志记录进而判断系统的运行状态,寻找导致系统出错、崩溃的成因等。这是我们分析程序问题常用的手段。
2.log包介绍
logLogger
Logger结构体
// A Logger represents an active logging object that generates lines of
// output to an io.Writer. Each logging operation makes a single call to
// the Writer's Write method. A Logger can be used simultaneously from
// multiple goroutines; it guarantees to serialize access to the Writer.
type Logger struct {
mu sync.Mutex // ensures atomic writes; protects the following fields
prefix string // prefix on each line to identify the logger (but see Lmsgprefix)
flag int // properties
out io.Writer // destination for output
buf []byte // for accumulating text to write
}
io.Writerio.WriterwriteLoggergoroutines
mu :是sync.Mutex,它是一个同步互斥锁,用于保证日志记录的原子性.
prefix :是输入的日志每一行的前缀
flag :是一个标志,用于设置日志的打印格式
out :日志的输出目标,需要是一个实现了 io.Writer接口的对象,如: os.Stdout, os.Stderr, os.File等等
buf :用于缓存数据
flag可选值
其中flag的值在log包中定义了一些常量,它的作用主要是用于标识日志信息附加携带的信息:
// These flags define which text to prefix to each log entry generated by the Logger.
// Bits are or'ed together to control what's printed.
// With the exception of the Lmsgprefix flag, there is no
// control over the order they appear (the order listed here)
// or the format they present (as described in the comments).
// The prefix is followed by a colon only when Llongfile or Lshortfile
// is specified.
// For example, flags Ldate | Ltime (or LstdFlags) produce,
// 2009/01/23 01:23:23 message
// while flags Ldate | Ltime | Lmicroseconds | Llongfile produce,
// 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
const (
Ldate = 1 << iota // the date in the local time zone: 2009/01/23
Ltime // the time in the local time zone: 01:23:23
Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime.
Llongfile // full file name and line number: /a/b/c/d.go:23
Lshortfile // final file name element and line number: d.go:23. overrides Llongfile
LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone
Lmsgprefix // move the "prefix" from the beginning of the line to before the message
LstdFlags = Ldate | Ltime // initial values for the standard logger
)
LstdFlags
3.log包的使用
3.1 日志输出方法
log包中定义了如下的一套日志信息输出方法:
func (l *Logger) Print(v ...interface{
}) //直接打印输出
func (l *Logger) Fatal(v ...interface{
}) //输出日志后立即结束程序
func (l *Logger) Panic(v ...interface{
}) //输出日志后抛出异常
和这三个方法相似的另外的方法都很好理解,就是换行或者格式化输出。
3.2 自定义创建日志对象
New
// New creates a new Logger. The out variable sets the
// destination to which log data will be written.
// The prefix appears at the beginning of each generated log line, or
// after the log header if the Lmsgprefix flag is provided.
// The flag argument defines the logging properties.
func New(out io.Writer, prefix string, flag int) *Logger {
return &Logger{
out: out, prefix: prefix, flag: flag}
}
var std = New(os.Stderr, "", LstdFlags)
如果你刚好只需要日期和时间这两个额外的信息,就可以直接通过log包名调用方法,默认就是使用的这个初始化的std结构体的对应方法,如:
log.Println("hello,world~")
New
out io.Writer:表示输出位置,可选值如 os.Stdout 为系统控制台,os.OpenFile 输出到单独的文件
prefix string: 表示统一前缀,会添加到生成的每一条日志前面,如debug的场景我们可以单独使用一个为[DEBUG]的前缀
flag int:表示额外信息标识,上文介绍过
当然,我们也可以单独调用如下相关的方法来单独设置。
func (l *Logger) SetOutput(w io.Writer)
func (l *Logger) SetFlags(flag int)
func (l *Logger) SetPrefix(prefix string)
3.3 封装自定义日志包
log
package logger
import (
"io"
"log"
"os"
)
const (
flag = log.Ldate | log.Ltime | log.Lshortfile
preDebug = "[DEBUG]"
preInfo = "[INFO]"
preWarning = "[WARNING]"
preError = "[ERROR]"
)
var (
logFile io.Writer
debugLogger *log.Logger
infoLogger *log.Logger
warningLogger *log.Logger
errorLogger *log.Logger
defaultLogFile = "/var/log/web.log"
)
func init() {
var err error
logFile, err = os.OpenFile(defaultLogFile, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
if err != nil {
defaultLogFile = "./web.log"
logFile, err = os.OpenFile(defaultLogFile, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
if err != nil {
log.Fatalf("create log file err %+v", err)
}
}
debugLogger = log.New(logFile, preDebug, flag)
infoLogger = log.New(logFile, preInfo, flag)
warningLogger = log.New(logFile, preWarning, flag)
errorLogger = log.New(logFile, preError, flag)
}
func Debugf(format string, v ...interface{
}) {
debugLogger.Printf(format, v...)
}
func Infof(format string, v ...interface{
}) {
infoLogger.Printf(format, v...)
}
func Warningf(format string, v ...interface{
}) {
warningLogger.Printf(format, v...)
}
func Errorf(format string, v ...interface{
}) {
errorLogger.Printf(format, v...)
}
func SetOutputPath(path string) {
var err error
logFile, err = os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
if err != nil {
log.Fatalf("create log file err %+v", err)
}
debugLogger.SetOutput(logFile)
infoLogger.SetOutput(logFile)
warningLogger.SetOutput(logFile)
errorLogger.SetOutput(logFile)
}
package main
import "yourPath/logger"
func main() {
author := "korbin"
logger.Debugf("hello,%s",author)
logger.Infof("hello,%s",author)
logger.Warningf("hello,%s",author)
logger.Errorf("hello,%s",author)
}
[DEBUG]2020/12/01 11:33:07 logger.go:43: hello,korbin
[INFO]2020/12/01 11:33:07 logger.go:47: hello,korbin
[WARNING]2020/12/01 11:33:07 logger.go:51: hello,korbin
[ERROR]2020/12/01 11:33:07 logger.go:55: hello,korbin
os.Stderr
3.4 log包进一步解析
Loggerfunc (l *Logger) Output(calldepth int, s string) error
- 获取当前事件
- 对 Logger实例进行加锁操作
- 判断Logger的标志位是否包含 Lshortfile 或 Llongfile, 如果包含进入步骤4, 如果不包含进入步骤5
- 获取当前函数调用所在的文件和行号信息
- 格式化数据,并将数据写入到 l.out 中,完成输出
- 解锁操作
log包整体结构还是很简单的,有兴趣的小伙伴可以再自己多看一下源码。