日志
使用Logger
Print系列Fatal系列Panic系列 
log 
package main
import (
	"log"
)
func main() {
	log.Println("这是一条很普通的日志。")
	v := "很普通的"
	log.Printf("这是一条%s日志。\n", v)
	log.Fatalln("这是一条会触发fatal的日志。")
	log.Panicln("这是一条会触发panic的日志。")
}
 
编译并执行上面的代码会得到如下输出:
2022/02/14 21:18:42 这是一条很普通的日志。
2022/02/14 21:18:42 这是一条很普通的日志。     
2022/02/14 21:18:42 这是一条会触发fatal的日志。
 
配置logger
log 
logFlagsSetFlags 
func Flags() int
func SetFlags(flag int)
 
flag选项
log 
 
下面我们在记录日志之前先设置一下标准logger的输出选项如下:
func main() {
	log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)
	log.Println("这是一条很普通的日志。")
}
 
编译执行后得到的输出结果如下:
2022/02/14 21:26:42.546952 D:/Golang/src/go_code/project02/log/test/main.go:9: 这是一条很普通的日志。
 
配置日志前缀
log 
func Prefix() string
func SetPrefix(prefix string)
 
PrefixSetPrefix 
func main() {
	log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)
	log.Println("这是一条很普通的日志。")
	log.SetPrefix("[小王子]")
	log.Println("这是一条很普通的日志。")
}
 
上面的代码输出如下:
[小王子]2022/02/14 21:29:01.392368 D:/Golang/src/go_code/project02/log/test/main.go:11: 这是一条很普通的日志。
 
这样我们就能够在代码中为我们的日志信息添加指定的前缀,方便之后对日志信息进行检索和处理。
配置日志输出位置
func SetOutput(w io.Writer)
 
SetOutput 
xx.log 
func main() {
	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)
	log.Println("这是一条很普通的日志。")
	log.SetPrefix("[小王子]")
	log.Println("这是一条很普通的日志。")
}
 
init 
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
logNewNew 
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>2022/02/14 21:34:36 main.go:11: 这是自定义的logger记录的日志。
 
日志案例
需求分析
- 支持往不同的地方输出日志
 - 日志分级别 
  
- Debug
 - Trace
 - Info
 - Warning
 - Error
 - Fatal
 
 - 日志要支持开关控制,比如说开发的时候什么级别都能输出,但是上线之后只有INFO级别往下的才能输出
 - 日志要有时间、行号、文件名、日志级别、日志信息
 - 日志文件要切割 
  
- 按文件大小切割 
    
- 每次记录日志之前都判断—下当前写的这个文件的文件大小
 
 - 按日期切割 
    
- 在日志结构体中设置一个字段记录上一次切割的小时数
 - 在写日志之前检查一下当前时间的小时数和之前保存的是否一致,不一致就要切割
 
 
 - 按文件大小切割 
    
 
公共函数:
package mylogger
import (
   "errors"
   "fmt"
   "path"
   "runtime"
   "strings"
)
type LogLevel uint16
//接口
type Logger interface {
   Debug(format string, a ...interface{})
   Info(format string, a ...interface{})
   Trace(format string, a ...interface{})
   Warning(format string, a ...interface{})
   Error(format string, a ...interface{})
   Fatal(format string, a ...interface{})
}
const (
   UNKNOWN LogLevel = iota
   DEBUG
   TRACE
   INFO
   WARING
   ERROR
   FATAL
)
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 "waring":
      return WARING, nil
   case "error":
      return ERROR, nil
   case "fatal":
      return FATAL, nil
   default:
      err := errors.New("无效日志级别")
      return UNKNOWN, err
   }
}
func getLogString(lv LogLevel) string {
   switch lv {
   case DEBUG:
      return "DEBUG"
   case TRACE:
      return "TRACE"
   case INFO:
      return "INFO"
   case WARING:
      return "WARING"
   case ERROR:
      return "ERROR"
   case FATAL:
      return "FATAL"
   }
   return "DEBUG"
}
func getInfo(skip int) (funcName, fileName string, lineNo int) {
   pc, file, lineNo, ok := runtime.Caller(skip)
   if !ok {
      fmt.Println("runtime.Caller() failed")
      return
   }
   funcName = runtime.FuncForPC(pc).Name()
   fileName = path.Base(file)
   funcName = strings.Split(funcName, ".")[1]
   return
}
 
Console:
package mylogger
import (
   "fmt"
   "time"
)
//往终端上写
//日志结构体
type ConsoleLogger struct {
   Level LogLevel
}
//构造函数
func NewConsoleLogLog(levelStr string) ConsoleLogger {
   level, err := parseLogLevel(levelStr)
   if err != nil {
      panic(err)
   }
   return ConsoleLogger{
      level,
   }
}
func (l ConsoleLogger) enable(loglevel LogLevel) bool {
   return l.Level <= loglevel
}
func (l ConsoleLogger) log(lv LogLevel, format string, a ...interface{}) {
   if l.enable(DEBUG) {
      msg := fmt.Sprintf(format, a...)
      now := time.Now()
      funcName, fileName, lineNo := getInfo(3)
      fmt.Printf("[%s] [%s] [%s : %s : %d] %s \n", now.Format("2006-01-02 15:04:05 "), getLogString(lv), fileName, funcName, lineNo, msg)
   }
}
func (l ConsoleLogger) Debug(format string, a ...interface{}) {
   l.log(DEBUG, format, a...)
}
func (l ConsoleLogger) Trace(format string, a ...interface{}) {
   l.log(TRACE, format, a...)
}
func (l ConsoleLogger) Info(format string, a ...interface{}) {
   l.log(INFO, format, a...)
}
func (l ConsoleLogger) Warning(format string, a ...interface{}) {
   l.log(WARING, format, a...)
}
func (l ConsoleLogger) Error(format string, a ...interface{}) {
   l.log(ERROR, format, a...)
}
func (l ConsoleLogger) Fatal(format string, a ...interface{}) {
   l.log(FATAL, format, a...)
}
 
File
package mylogger
import (
   "fmt"
   "os"
   "path"
   "time"
)
//往文件里面写日志相关代码
type FileLogger struct {
   Level       LogLevel
   filePath    string //日志文件保存的路径
   fileName    string //日志文件保存的文件名
   fileObj     *os.File
   errFileObj  *os.File
   maxFileSize int64
}
// 构造函数
func NewFileLogger(levelStr, fp, fn string, maxSize int64) *FileLogger {
   logLevel, err := parseLogLevel(levelStr)
   if err != nil {
      panic(err)
   }
   fl := &FileLogger{
      Level:       logLevel,
      filePath:    fp,
      fileName:    fn,
      maxFileSize: maxSize,
   }
   err = fl.initFile()
   if err != nil {
      panic(err)
   }
   return fl
}
func (l *FileLogger) initFile() error {
   fullFileName := path.Join(l.filePath, l.fileName)
   fileObj, err := os.OpenFile(fullFileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
   if err != nil {
      fmt.Printf("open log file failed ,err:%v\n", err)
      return err
   }
   errFileObj, err := os.OpenFile(fullFileName+".err", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
   if err != nil {
      fmt.Printf("open err log file failed ,err:%v\n", err)
      return err
   }
   l.fileObj = fileObj
   l.errFileObj = errFileObj
   return nil
}
func (l *FileLogger) checkSize(file *os.File) bool {
   fileInfo, err := file.Stat()
   if err != nil {
      fmt.Printf("get file info failed,err:%v\n", err)
      return false
   }
   return fileInfo.Size() >= l.maxFileSize
}
func (l *FileLogger) splitFile(file *os.File) (*os.File, error) {
   //需要切割日志文件
   //2.备份一下 rename
   nowStr := time.Now().Format("20060102150405000")
   fileInfo, err := file.Stat()
   if err != nil {
      fmt.Printf("get file info failed,err:%v\n", err)
      return nil, err
   }
   logName := path.Join(l.filePath, fileInfo.Name())      //拿到当前的日志文件完整路径
   newLogName := fmt.Sprintf("%s.bak%s", logName, nowStr) //拼接一个日志文件备份的名字
   //1.关闭当前的日志文件
   file.Close()
   os.Rename(logName, newLogName)
   //3.打开一个新的日志文件
   fileObj, err := os.OpenFile(logName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
   if err != nil {
      fmt.Println("open new log fail err=", err)
      return nil, err
   }
   //4.将打开的日志文件对象赋值给 fileObj
   return fileObj, err
}
func (l *FileLogger) log(lv LogLevel, format string, a ...interface{}) {
   if l.enable(lv) {
      msg := fmt.Sprintf(format, a...)
      now := time.Now()
      funcName, fileName, lineNo := getInfo(3)
      if l.checkSize(l.fileObj) {
         newFile, err := l.splitFile(l.fileObj)
         if err != nil {
            return
         }
         l.fileObj = newFile
      }
      fmt.Fprintf(l.fileObj, "[%s] [%s] [%s : %s : %d] %s \n", now.Format("2006-01-02 15:04:05 "), getLogString(lv), fileName, funcName, lineNo, msg)
      if lv >= ERROR {
         if l.checkSize(l.errFileObj) {
            newFile, err := l.splitFile(l.errFileObj)
            if err != nil {
               return
            }
            l.errFileObj = newFile
         }
         //如果要记录的日志大于等于ERROR级别,我还要在err目志文件中再记录一遍
         fmt.Fprintf(l.errFileObj, "[%s] [%s] [%s : %s : %d] %s \n", now.Format("2006-01-02 15:04:05 "), getLogString(lv), fileName, funcName, lineNo, msg)
      }
   }
}
func (l *FileLogger) enable(loglevel LogLevel) bool {
   return l.Level <= loglevel
}
func (l *FileLogger) Debug(format string, a ...interface{}) {
   if l.enable(DEBUG) {
      l.log(DEBUG, format, a...)
   }
}
func (l *FileLogger) Trace(format string, a ...interface{}) {
   if l.enable(TRACE) {
      l.log(TRACE, format, a...)
   }
}
func (l *FileLogger) Info(format string, a ...interface{}) {
   if l.enable(INFO) {
      l.log(INFO, format, a...)
   }
}
func (l *FileLogger) Warning(format string, a ...interface{}) {
   if l.enable(WARING) {
      l.log(WARING, format, a...)
   }
}
func (l *FileLogger) Error(format string, a ...interface{}) {
   if l.enable(ERROR) {
      l.log(ERROR, format, a...)
   }
}
func (l *FileLogger) Fatal(format string, a ...interface{}) {
   if l.enable(FATAL) {
      l.log(FATAL, format, a...)
   }
}
func (l *FileLogger) Close() {
   l.fileObj.Close()
   l.errFileObj.Close()
}
 
main:
var log mylogger.Logger
func main() {
   log = mylogger.NewConsoleLogLog("debug")
   log = mylogger.NewFileLogger("info", "./", "1.log", 10*1024)
   for {
      id := 100
      name := "n"
      log.Debug("这是一条 Debug 日志")
      log.Info("这是一条 Info 日志")
      log.Warning("这是一条 Warning 日志")
      log.Error("这是一条 Error  日志 id:%d,name:%s", id, name)
   }
}