日志库

作者: 雪上霜 | 来源:发表于2020-04-24 22:44 被阅读0次
image.png
  • 终端日志
//console.go
package logger

import (
    "fmt"
    "time"
)


//往终端上写

//日志结构体
type Logger struct {
    Level   LogLevel
}



//构造函数
func NewLog(levelStr string) Logger {
    level,err := parseLogLevel(levelStr)
    if err != nil{
        panic(err)
    }

    return Logger{
        Level: level,
    }
}

//判断当前方法的Level与传入logLevel的高低。
func (l Logger) enable(logLevel LogLevel) bool{
    return l.Level <= logLevel
}

//打印格式
//参考Printf参数。
//func Printf(format string, a ...interface{}) (n int, err error)
//Printf根据format参数生成格式化的字符串并写入标准输出。返回写入的字节数和遇到的任何错误。
func (l Logger)log(lv LogLevel,format string,a ...interface{}){
    if l.enable(lv){
        //func Sprintf(format string, a ...interface{}) string
        //Sprintf根据format参数生成格式化的字符串并返回该字符串。
        msg := fmt.Sprintf(format,a...)
        now := time.Now()
        funcName,fileName,lineNo := getInfo(3)//3个函数调用最后的Caller,因此传入3。
        fmt.Printf("[%s] [%s][%s:%s:%d] %s\n",now.Format("2006-01-02 15:04:05"),getLogString(lv),funcName,fileName,lineNo,msg)
    }
}

//Debug方法
func (l Logger) Debug(format string,a ...interface{}) {
        l.log(DEBUG,format,a...)
}

//Trace方法
func (l Logger) Trace(format string,a ...interface{}) {
        l.log(TRACE,format,a...)
}

//Error方法
func (l Logger) Error(format string,a ...interface{}) {
        l.log(ERROR,format,a...)    
}

//Info方法
func (l Logger) Info(format string,a ...interface{}) {
    l.log(INFO,format,a...)
}

////Warning方法
func (l Logger) Warning(format string,a ...interface{}) {
    l.log(WARNING,format,a...)
}

//Fatal方法
func (l Logger) Fatal(format string,a ...interface{}) {
    l.log(FATAL,format,a...)
}
  • 文件日志
//file.go
package logger

import (
    "os"
    "path"
    "fmt"
    "time"
)

//往文件中写日志


//日志结构体
type FileLogger struct {
    Level   LogLevel
    filePath string     //日志文件保存路径
    fileName string     //日志文件保存文件名
    fileObj *os.File
    errFileObj  *os.File
    maxFileSize int64
    curDate time.Time   //当前时间
}



//构造函数
func NewFileLogMaxSize(levelStr,fp,fn string,maxSize int64) (*FileLogger) {
    logLevel,err := parseLogLevel(levelStr)
    if err != nil{
        panic(err)
    }
    f1 := &FileLogger{
        Level: logLevel,
        filePath:   fp,
        fileName:   fn,
        maxFileSize:    maxSize,
    }
    err = f1.initFile()
    if err != nil{
        panic(err)
    }
    return f1
}

//构造函数2
func NewFileLogTime(levelStr,fp,fn string,curDate time.Time) (*FileLogger) {
    logLevel,err := parseLogLevel(levelStr)
    if err != nil{
        panic(err)
    }
    f1 := &FileLogger{
        Level: logLevel,
        filePath:   fp,
        fileName:   fn,
        curDate:    curDate,
    }
    err = f1.initFile()
    if err != nil{
        panic(err)
    }
    return f1
}

//文件的初始化,创建打开文件。
func (f *FileLogger)initFile()(error){
    fullFileName := path.Join(f.filePath,f.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,err1 := os.OpenFile(fullFileName+".err",os.O_APPEND | os.O_CREATE | os.O_WRONLY,0644)
    if err1 != nil{
        fmt.Printf("open log file failed,err :%v\n",err1)
        return err1
    }

    //日志文件都打开了
    f.fileObj = fileObj
    f.errFileObj = errFileObj
    return nil
}

//文件关闭方法。
func (f *FileLogger)Close(){
    f.fileObj.Close()
    f.errFileObj.Close()
}

//判断是否写入err日志文件中。
func (f *FileLogger) enable(logLevel LogLevel) bool{
    return f.Level <= logLevel
}

//检查文件大小
func (f *FileLogger)checkSize(file *os.File) bool{
    //func (f *File) Stat() (fi FileInfo, err error)
    //Stat返回描述文件f的FileInfo类型值。如果出错,错误底层类型是*PathError。
    fileInfo,err := file.Stat()
    if err != nil{
        fmt.Printf("get file info failed,err:%v\n",err)
        return false
    }
    return fileInfo.Size() >= f.maxFileSize
}


//分割文件
func (f *FileLogger) splitFile(file *os.File) (*os.File,error) {
    //需要切割日志文件
            //1、备份一下,rename
            nowStr := time.Now().Format("20060102150405000")
            fileInfo,err := file.Stat()
            if err != nil{
                fmt.Printf("get file info failed,err:%v",err)
                return nil,err
            }
            logName := path.Join(f.filePath,fileInfo.Name())    //拿到当前的日志文件完整路径
            newLogName := fmt.Sprintf("%s/%s.bak%s",f.filePath,f.fileName,nowStr)//拼接一个日志文件备份的名字
            
            
            //2、关闭当前日志文件
            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.Printf("open new log file failed,err:%v\n",err)
                return  nil,err
            }
            //4、将打开的新日志文件对象赋值给f.fileObj
            return fileObj,nil
}

//根据文件大小分隔写入文件中的日志格式
func (f *FileLogger)log(lv LogLevel,format string,a ...interface{}){
    if f.enable(lv){
        msg := fmt.Sprintf(format,a...)
        now := time.Now()
        funcName,fileName,lineNo := getInfo(3)
        if f.maxFileSize > 0{
            if f.checkSize(f.fileObj){
                newFile,err := f.splitFile(f.fileObj)
                if err != nil{
                    return
                }
                f.fileObj = newFile
            }
            fmt.Fprintf(f.fileObj,"[%s] [%s][%s:%s:%d] %s\n",now.Format("2006-01-02 15:04:05"),getLogString(lv),funcName,fileName,lineNo,msg)
            if lv >= ERROR{
                //记录的日志大于等于ERROR级别,还要将err日志文件再记录一遍
                if f.checkSize(f.errFileObj){
                    newFile,err := f.splitFile(f.errFileObj)
                    if err != nil{
                        return
                    }
                    f.errFileObj = newFile
                }
                fmt.Fprintf(f.errFileObj,"[%s] [%s][%s:%s:%d] %s\n",now.Format("2006-01-02 15:04:05"),getLogString(lv),funcName,fileName,lineNo,msg)    
            }

        }else {
            //func Since(t Time) Duration
        //Since返回从t到现在经过的时间,等价于time.Now().Sub(t)。
        if time.Since(f.curDate) >= 3 * time.Second {
            newFile,err := f.splitFile(f.fileObj)
            f.curDate = time.Now()
            if err != nil{
                return
            }
            f.fileObj = newFile
        }
        fmt.Fprintf(f.fileObj,"[%s] [%s][%s:%s:%d] %s\n",now.Format("2006-01-02 15:04:05"),getLogString(lv),funcName,fileName,lineNo,msg)
        if lv >= ERROR{
            //记录的日志大于等于ERROR级别,还要将err日志文件再记录一遍
            if time.Since(f.curDate)  >= 3 * time.Second{
                newFile,err := f.splitFile(f.errFileObj)
                f.curDate = time.Now()
                if err != nil{
                    return
                }
                f.errFileObj = newFile
            }
            fmt.Fprintf(f.errFileObj,"[%s] [%s][%s:%s:%d] %s\n",now.Format("2006-01-02 15:04:05"),getLogString(lv),funcName,fileName,lineNo,msg)    
            }
        }
    }
}

//Debug方法
func (f *FileLogger) Debug(format string,a ...interface{}) {
    f.log(DEBUG,format,a...)
}

//Trace方法
func (f *FileLogger) Trace(format string,a ...interface{}) {
    f.log(TRACE,format,a...)    
}

//Error方法
func (f *FileLogger) Error(format string,a ...interface{}) {
    f.log(ERROR,format,a...)    
}

//Info方法
func (f *FileLogger) Info(format string,a ...interface{}) {
    f.log(INFO,format,a...)
}

//Warning方法
func (f *FileLogger) Warning(format string,a ...interface{}) {
    f.log(WARNING,format,a...)
}

//Fatal方法
func (f *FileLogger) Fatal(format string,a ...interface{}) {
    f.log(FATAL,format,a...)
}
  • 共同函数
//logger.go
package logger

import (
    "strings"
    "errors"
    "fmt"
    "path"
    "runtime"
)

type LogLevel uint16

//InLogger接口
type InLogger interface{
    Debug(format string,a ...interface{})
    Trace(format string,a ...interface{})
    Error(format string,a ...interface{})
    Info(format string,a ...interface{})
    Warning(format string,a ...interface{})
    Fatal(format string,a ...interface{})
}

const(
    UNKNOW LogLevel = iota
    DEBUG   
    TRACE
    INFO
    WARNING
    ERROR
    FATAL
)


//将字符串转换成LogLevel类型。
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 UNKNOW,err
    }
}

//将LogLevel类型转换成字符串类型。
func getLogString(lv LogLevel)string{
    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"
    }
    return "DEBUG"
}

//获取文件的信息。
//返回的时调用栈的标识符、文件名、行号。
func getInfo(skip int)(funcName,fileName string, lineNo int){
    pc,file,lineNo,ok := runtime.Caller(skip)   
    //Caller函数报告当前go程的调用栈所执行的文件名和行号信息。
    //func Caller(skip int) (pc uintptr, file string, line int, ok bool)
    if !ok {
        fmt.Println("runtime.Caller() failed\n")
        return
    }

    //func FuncForPC(pc uintptr) *Func
    //FuncForPC返回一个表示调用栈标识符pc对应的调用栈的*Func;如果该调用栈标识符没有对应的调用栈,函数会返回nil。每一个调用栈必然是对某个函数的调用。
    //func (f *Func) Name() string
    //Name返回该调用栈所调用的函数的名字。
    funcName = runtime.FuncForPC(pc).Name()
    //func Base(path string) string
    //Base函数返回路径的最后一个元素。在提取元素前会求掉末尾的斜杠。如果路径是"",会返回".";如果路径是只有一个斜杆构成,会返回"/"。
    //此处即为文件名
    fileName = path.Base(file)
    //func Split(s, sep string) []string
    
    funcName = strings.Split(funcName,".")[1]
    return 
}

  • 测试代码
//main.go
package main

import (
    "mylogger/logger"

    "time"
)

//声明一个全局接口变量
var log logger.InLogger 

//测试写的日志库
func main() {
    //log = logger.NewLog("debug")  //终端日志
    log = logger.NewFileLogMaxSize("Info","./","zhoulin.log",1*1024)//文件日志
    //log = logger.NewFileLogTime("Info","./","zhoulin.log",time.Now())
    
    for {
        id := 100
        name := "af"
        log.Debug("这是一条debug日志。")
        log.Trace("这是一条info日志。")
        log.Info("这是一条info日志。")
        log.Warning("这是一条Warning日志。")
        log.Error("这是一条Error日志%d,%s。",id,name)
        log.Fatal("这是一条Fatal日志。")
        time.Sleep(time.Second)
    }
}

相关文章

  • SQL 磁盘满了,处理方式

    分离数据库 备份日志 删除日志 附加数据库,新生成数据库日志文件

  • ZooKeeper源码分析之数据库日志

    本篇我们来分析ZooKeeper的数据库日志,ZooKeeper的数据库日志分为两类,快照日志和事务日志。快照日志...

  • 日志管理

    搭建rsyslog日志数据库 实现日志web展示通过loganalyzer展示数据库中的日志

  • MySQL-10mysql物理文件

    大家好,本篇文章记录数据库日志跟讲解。(个人理解) 数据库日志种类 数据库的数据存储文件 慢查询日志 错误日志与二...

  • 必须了解的mysql三大日志-binlog、redo log和u

    日志是mysql数据库的重要组成部分,记录着数据库运行期间各种状态信息。mysql日志主要包括错误日志、查询日志、...

  • MySQL中的日志文件解析

    前言 日志文件记录了影响MySQL数据库的各种类型活动,MySQL数据库中常见的日志文件有错误日志,二进制日志,慢...

  • 丰碑

    丰碑系统上线 系统自检中。。。 20% 50% 90% 自检完毕。 检查日志库更新。。。 更新日志库。。。 更新完...

  • 丰碑

    丰碑系统上线 系统自检中。。。 20% 50% 90% 自检完毕。 检查日志库更新。。。 更新日志库。。。 更新完...

  • MySQL主要日志的基本操作与简单解析

    MySQL主要有以下几种日志: 错误日志 通用查询日志 慢查询日志 二进制日志 DDL日志 日志是mysql数据库...

  • 了解mysql三大日志-binlog、redo log和undo

    日志是 mysql 数据库的重要组成部分,记录着数据库运行期间各种状态信息。mysql日志主要包括错误日志、查询日...

网友评论

      本文标题:日志库

      本文链接:https://www.haomeiwen.com/subject/ksufwhtx.html