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
  1. 获取当前事件
  2. 对 Logger实例进行加锁操作
  3. 判断Logger的标志位是否包含 Lshortfile 或 Llongfile, 如果包含进入步骤4, 如果不包含进入步骤5
  4. 获取当前函数调用所在的文件和行号信息
  5. 格式化数据,并将数据写入到 l.out 中,完成输出
  6. 解锁操作

log包整体结构还是很简单的,有兴趣的小伙伴可以再自己多看一下源码。