majianyu
2/7/2018 - 3:42 AM

log_go

package minilog

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

type Logger struct {
	Dir      string       //日志存储目录
	File     string       //日志基础名称
	Date     string       //日志当前日期
	Writer   *os.File     //日志文件的句柄
	Buffer   chan logtext //日志的缓冲通道
	close    chan int
	wlnfl    []string //Which Level Need Filename & LineNumber
	Callback callback
}

type logtext struct {
	date  string   //日志日期
	level string   //级别
	time  string   //日志时间
	text  string   //日志内容
	call  callback //回调
}

type callback func(string, string)

const (
	logFlag = os.O_CREATE | os.O_WRONLY | os.O_APPEND //日志 创建 只写 追加
	logMode = 0666                                    //日志 可读 可写
	logDate = "2006-01-02"                            //日期按日切割的FORMAT
	logTime = "15:04:05"                              //时间格式3
)

//创建一个新的日志记录器
func NewLogger(dir string, filename string, bufferSize int64) *Logger {
	l := new(Logger)
	l.Date = time.Now().Format(logDate)
	l.File = filename
	l.Dir = path.Dir(dir + "/") //确保路径格式正确,避免路径分隔符或多或少
	l.Buffer = make(chan logtext, bufferSize)
	l.close = make(chan int)
	if writer, err := l.getWriter(); err != nil {
		panic("日志创建失败:" + err.Error())
	} else {
		l.Writer = writer
	}
	go l.flush()
	return l
}
func (l *Logger) Flush() {
	defer l.Writer.Close()
	l.close <- 1
	close(l.close)
	close(l.Buffer)
	for lt := range l.Buffer {
		l.flushLog(lt)
	}
}
func (l *Logger) WithFileLine(sets ...string) {
	l.wlnfl = sets
}

func (l *Logger) getWriter() (writer *os.File, err error) {
	return os.OpenFile(l.Dir+"/"+l.File+"."+l.Date+".log", logFlag, logMode)
}

// 将文本内容刷入日志buffer中
// 写满前为异步操作
// 写满后则同步堵塞
func (l *Logger) Write(level, text string, c callback) {
	defer func() {
		if r := recover(); r != nil {
			// 忽略关闭 chan 后的写入
		}
	}()
	if len(l.wlnfl) > 0 {
		if ok, _ := inStringArray(l.wlnfl, level); ok {
			_, f, l, _ := runtime.Caller(2)
			text = fmt.Sprintf("%s [%s:%d]", text, f, l)
		}
	}
	t := time.Now()

	l.Buffer <- logtext{
		date:  t.Format(logDate),
		time:  t.Format(logTime),
		level: level,
		text:  text,
		call:  c,
	}

}

func (l *Logger) Info(text string) {
	l.Write("INFO", text, l.Callback)
}

func (l *Logger) Infof(format string, a ...interface{}) {
	l.Write("INFO", fmt.Sprintf(format, a...), l.Callback)
}

func (l *Logger) Error(text string) {
	l.Write("ERRO", text, l.Callback)
}

func (l *Logger) Errorf(format string, a ...interface{}) {
	l.Write("ERRO", fmt.Sprintf(format, a...), l.Callback)
}

func (l *Logger) Debug(text string) {
	l.Write("DEBG", text, l.Callback)
}

func (l *Logger) Fatal(text string) {
	l.Write("FATL", text, l.Callback)
}

func (l *Logger) Access(text string) {
	l.Write("ACES", text, l.Callback)
}

func (l *Logger) AccessCall(text string, c callback) {
	l.Write("ACES", text, c)
}

// 将buffer里的内容逐次刷入磁盘
func (l *Logger) flush() {
	for {
		select {
		case <-l.close:
			return
		case lt := <-l.Buffer: //日志刷入操作
			l.flushLog(lt)
		}
	}
}

func (l *Logger) flushLog(lt logtext) {
	//每次写入前判断一下日期是否一致,不一致则创建新日志文件
	if lt.date != l.Date {
		l.Date = lt.date
		if writer, err := l.getWriter(); err != nil {
			//使用系统 panic log,暴露出本次异常,最好配置报警;
			//errLog.Write("创建次日日志失败: " + m.file + " " + datestr) //发送错误报警
		} else {
			l.Writer.Close()
			l.Writer = writer
		}
	}
	fmt.Fprintf(l.Writer, "%s %s [%s] %s\n", lt.date, lt.time, lt.level, lt.text)
	//如果有后续调用则调用该函数
	if lt.call != nil {
		lt.call(lt.text, lt.date+" "+lt.time)
	}
}

func inStringArray(arr []string, dst string) (bool, int) {
	if len(arr) == 0 {
		return false, -1
	}
	for i, v := range arr {
		if v == dst {
			return true, i
		}
	}
	return false, -1
}
package main

import (
	"minilog"
	"log"
	"time"
)

func main() {
	Logger := minilog.NewLogger("./", "mjy", 5000)
	defer Logger.Flush()
	Logger.Callback = func(text string, date string) {
		log.Printf("%s:%s", date, text)
	}
	go func() {
		for {
			Logger.Info("loop...........")
			time.Sleep(time.Second)
		}
	}()
	Logger.WithFileLine("ERRO")
	Logger.Info("aaaaaaaaaaa")
	Logger.Info("bbbbb")
	Logger.Info("ccccc")
	Logger.Info("dddd")
	Logger.Info("eeeee")
	time.Sleep(10* time.Second)
}