zoukankan      html  css  js  c++  java
  • Go语言基础之log实现

    目录结构

    my_logger.go

    package mylogger
    
    import (
    	"path"
    	"runtime"
    	"strings"
    )
    
    type LogLevel uint8 // 值越小, 表示权限越大
    
    const (
    	DEBUG = iota
    	INFO
    	WARNING
    	ERROR
    	FATAL
    )
    
    // 获取运行时的一些信息
    func getRunInfo(skip int) (funcName, fileName string, lineNo int) {
    	pc, file, lineNo, ok := runtime.Caller(skip)
    	if !ok {
    		return
    	}
    	funcName = strings.Split(runtime.FuncForPC(pc).Name(), ".")[1]
    	fileName = path.Base(file)
    	return
    }
    

    console_logger.go

    package mylogger
    
    import (
    	"fmt"
    	"strings"
    	"time"
    )
    
    type ConsoleLogger struct {
    	level LogLevel
    }
    
    func NewConsoleLogger(level string) ConsoleLogger {
    	var tmp LogLevel
    	switch level := strings.ToLower(level); level {
    	case "debug":
    		tmp = DEBUG
    	case "info":
    		tmp = INFO
    	case "warning":
    		tmp = WARNING
    	case "error":
    		tmp = ERROR
    	case "fatal":
    		tmp = FATAL
    	default:
    		panic("不支持的级别类型")
    	}
    	return ConsoleLogger{tmp}
    }
    
    func log(l ConsoleLogger, level LogLevel, message string, a ...interface{}) {
    	message = fmt.Sprintf(message, a...)
    
    	keyMap := map[LogLevel]string{
    		DEBUG:   "DEBUG",
    		INFO:    "INFO",
    		WARNING: "WARING",
    		ERROR:   "ERROR",
    		FATAL:   "FATAL",
    	}
    
    	strLevel := keyMap[level]
    
    	// 获取运行时的一些信息
    	funcName, fileName, lineNo := getRunInfo(3)
    
    	if l.level <= level {
    		fmt.Printf("[%s][%s][%s/%s:%d]: %s 
    ", time.Now().Format("2006/01/02 15:04:05"), strLevel, funcName, fileName, lineNo, message)
    	}
    }
    
    func (l ConsoleLogger) Debug(message string, a ...interface{}) {
    	//if l.level <= DEBUG {
    	//	fmt.Printf("[%s][%s]: %s 
    ", time.Now().Format("2006/01/02 15:04:05"), "DEBUG", message)
    	//}
    	log(l, DEBUG, message, a...)
    }
    
    func (l ConsoleLogger) Info(message string, a ...interface{}) {
    	//if l.level <= INFO {
    	//	fmt.Printf("[%s][%s]: %s 
    ", time.Now().Format("2006/01/02 15:04:05"), "INFO", message)
    	//}
    	log(l, INFO, message, a...)
    }
    
    func (l ConsoleLogger) Warning(message string, a ...interface{}) {
    	//if l.level <= WARNING {
    	//	fmt.Printf("[%s][%s]: %s 
    ", time.Now().Format("2006/01/02 15:04:05"), "WARNING", message)
    	//}
    	log(l, WARNING, message, a...)
    }
    
    func (l ConsoleLogger) Error(message string, a ...interface{}) {
    	//if l.level <= ERROR {
    	//	fmt.Printf("[%s][%s]: %s 
    ", time.Now().Format("2006/01/02 15:04:05"), "ERROR", message)
    	//}
    	log(l, ERROR, message, a...)
    }
    
    func (l ConsoleLogger) Fatal(message string, a ...interface{}) {
    	//if l.level <= FATAL {
    	//	fmt.Printf("[%s][%s]: %s 
    ", time.Now().Format("2006/01/02 15:04:05"), "FATAL", message)
    	//}
    	log(l, FATAL, message, a...)
    }
    

    file_logger.go

    package mylogger
    
    import (
    	"fmt"
    	"os"
    	"path"
    	"strings"
    	"time"
    )
    
    type FileLogger struct {
    	level       LogLevel
    	basePath    string
    	fileName    string
    	errFileName string
    	fileObj     *os.File
    	errFileObj  *os.File
    	fileMaxSize int64
    }
    
    func NewFileLogger(level, basePath, fileName, errFileName string, fileMaxSize int64) (FileLogger, error) {
    	var tmp LogLevel
    	switch level := strings.ToLower(level); level {
    	case "debug":
    		tmp = DEBUG
    	case "info":
    		tmp = INFO
    	case "warning":
    		tmp = WARNING
    	case "error":
    		tmp = ERROR
    	case "fatal":
    		tmp = FATAL
    	default:
    		panic("不支持的级别类型")
    	}
    
    	fileLogger := FileLogger{
    		level:       tmp,
    		basePath:    basePath,
    		fileName:    fileName,
    		errFileName: errFileName,
    		fileMaxSize: fileMaxSize,
    	}
    
    	// 初始化`[标准日志]文件`和`[错误日志]文件`
    	// 打开[标准日志]文件
    	fileObj, err := os.OpenFile(path.Join(basePath, fileName), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    	if err != nil {
    		fmt.Println("打开文件[fileObj]失败")
    		return fileLogger, err
    	}
    	fileLogger.fileObj = fileObj
    	// 打开[错误日志]文件
    	errFileObj, err := os.OpenFile(path.Join(basePath, errFileName), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    	if err != nil {
    		fmt.Println("打开文件[errFileObj]失败")
    		return fileLogger, err
    	}
    	fileLogger.errFileObj = errFileObj
    
    	return fileLogger, nil
    }
    
    func splitFile(basePath, fileName string, fileObj *os.File, fileMaxSize int64) *os.File {
    	// ----------做文件截断操作------------
    	// todo 做文件截断操作
    	// 1. 判断当前打开的文件的大小是否超过设置的长度限制
    	fileInfo, err2 := fileObj.Stat()
    	if err2 != nil {
    		fmt.Println("获取文件信息失败")
    		fmt.Println("err", err2)
    		panic("程序又崩溃了")
    	}
    	fileSize := fileInfo.Size()
    	if fileSize >= fileMaxSize{
    		fmt.Println("正在将文件截断")
    		// 2. 如果超过, 则 2.1:关闭当前文件操作句柄, 2.2:并重命名当前文件  2.3:打开一个新的文件操作句柄
    		// 2.1
    		err2 := fileObj.Close()
    		if err2 != nil {
    			fmt.Println("文件关闭失败[1]")
    			panic("程序报错了[2]")
    		}
    		// 2.2
    		timeStr := time.Now().Format("20060102150405000")
    		oldName := path.Join(basePath, fileName)
    		newName := oldName + ".bak_"+timeStr
    		err2 = os.Rename(oldName, newName)
    		if err2 != nil {
    			fmt.Println("文件重命名失败[1]")
    			fmt.Println("err", err2)
    			panic("重命名失败[1]")
    		}
    		// 2.3
    		newFileObj, err2 := os.OpenFile(path.Join(basePath, fileName), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    		if err2 != nil {
    			fmt.Println("文件已被截断, 重新打开新文件失败[1]")
    			panic("程序又又蹦啦")
    		}
    		//l.fileObj = newFileObj
    		return newFileObj
    	}
    	// 3. 长度未超出限制, 则啥事也不做, pass
    
    	// ----------做文件截断操作------------
    	return nil
    }
    
    func fileLog(l *FileLogger, level LogLevel, message string, a ...interface{}) {
    	message = fmt.Sprintf(message, a...)
    
    	keyMap := map[LogLevel]string{
    		DEBUG:   "DEBUG",
    		INFO:    "INFO",
    		WARNING: "WARING",
    		ERROR:   "ERROR",
    		FATAL:   "FATAL",
    	}
    
    	strLevel := keyMap[level]
    
    	// 获取运行时的一些信息
    	funcName, fileName, lineNo := getRunInfo(3)
    
    	// 写入[标准日志]
    	if l.level <= level {  // 判断log句柄有没有权限, 如果有权限, 则输出
    
    		// 截断文件
    		newFileObj := splitFile(l.basePath, l.fileName, l.fileObj, l.fileMaxSize)
    		if newFileObj != nil{
    			l.fileObj = newFileObj
    		}
    
    		_, err := fmt.Fprintf(l.fileObj, "[%s][%s][%s/%s:%d]: %s 
    ", time.Now().Format("2006/01/02 15:04:05"), strLevel, funcName, fileName, lineNo, message)
    		if err != nil {
    			fmt.Println("文件写入失败")
    			return
    		}
    	}
    	// 写入[错误日志]
    	if level >= ERROR{
    		// todo 做文件截断
    		// 截断文件
    		newFileObj := splitFile(l.basePath, l.errFileName, l.errFileObj, l.fileMaxSize)
    		if newFileObj != nil{
    			l.errFileObj = newFileObj
    		}
    
    		_, err := fmt.Fprintf(l.errFileObj, "[%s][%s][%s/%s:%d]: %s 
    ", time.Now().Format("2006/01/02 15:04:05"), strLevel, funcName, fileName, lineNo, message)
    		if err != nil {
    			fmt.Println("文件写入失败")
    			return
    		}
    	}
    }
    
    func (l *FileLogger) Debug(message string, a ...interface{}) {
    	fileLog(l, DEBUG, message, a...)
    }
    
    func (l *FileLogger) Info(message string, a ...interface{}) {
    	fileLog(l, INFO, message, a...)
    }
    
    func (l *FileLogger) Warning(message string, a ...interface{}) {
    	fileLog(l, WARNING, message, a...)
    }
    
    func (l *FileLogger) Error(message string, a ...interface{}) {
    	fileLog(l, ERROR, message, a...)
    }
    
    func (l *FileLogger) Fatal(message string, a ...interface{}) {
    	fileLog(l, FATAL, message, a...)
    }
    

    测试

    package main
    
    import (
    	"fmt"
    	"time"
    
    	"code.xxx.com/studygolang/day06/mylogger"
    )
    
    func main() {
    	// 1. 向控制台输出的
    	//log := mylogger.NewConsoleLogger("error") // 初始化
    
    	// 2. 向文件输出的
    	log, err := mylogger.NewFileLogger("info", "./resources/logs/", "mylogger.txt", "errorlogger.txt", 1024*1024*10)
    	if err != nil {
    		fmt.Println("日志文件初始化失败")
    		return
    	}
    
    	for {
    		// 打印日志
    		log.Info("你好呀, 这是一条info信息")
    		log.Debug("你好呀, 这是一条debug信息")
    		log.Warning("你好呀, 这是一条waring信息")
    		log.Error("你好呀, 这是一条error信息  id: %d  name: %s", 1, "sch")
    		log.Fatal("你好呀, 这是一条fatal信息")
    		time.Sleep(1000)
    	}
    }
    
  • 相关阅读:
    Android Studio 使用 Gradle 打包 Jar
    图片相关
    判断SIM卡状态,获取SIM卡信息
    SwipeRefreshLayout完美添加及完善上拉加载功能
    环境变量配置文件,本地登录前提示信息/etc/issue
    变量声明declare,简单运算符运算,变量测试与内容替换
    变量:用户自定义变量(本地变量),环境变量,位置参数变量,预定义变量
    多命令顺序执行,dd命令,管道|,grep,通配符,其他特殊符号
    历史命令~/.bash_history,查看所有别名alias,命令执行顺序,命令行常用快捷键,输入输出重定向,wc统计字节单词行数
    查看当前支持的shell,echo -e相关转义符,一个简单shell脚本,dos2unix命令把windows格式转为Linux格式
  • 原文地址:https://www.cnblogs.com/sunch/p/14725131.html
Copyright © 2011-2022 走看看