![](https://img.haomeiwen.com/i7898366/f8b0650e215d9c67.png)
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)
}
}
网友评论