1.目录结构

2.mylogger/concole.go
package mylogger
import (
"fmt"
"time"
)
//往终端写日志相关内容
//Logger日志结构体
type ConsoleLogger struct {
Level LogLevel
}
//NewLog 构造函数
func NewConsoleLogger(LevelStr string) ConsoleLogger {
level, err := parseLogLevel(LevelStr)
if err != nil {
panic(err)
}
return ConsoleLogger{
Level: level,
}
}
func (c ConsoleLogger) enable(loglevel LogLevel) bool {
return loglevel >= c.Level
}
func (c ConsoleLogger) log(lv LogLevel, format string, a ...interface{}) {
if c.enable(lv) {
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 (c ConsoleLogger) Debug(format string, a ...interface{}) {
c.log(DEBUG, format, a...)
}
func (c ConsoleLogger) Info(format string, a ...interface{}) {
c.log(INFO, format, a...)
}
func (c ConsoleLogger) Warning(format string, a ...interface{}) {
c.log(WARNING, format, a...)
}
func (c ConsoleLogger) Error(format string, a ...interface{}) {
c.log(ERROR, format, a...)
}
func (c ConsoleLogger) Fatal(format string, a ...interface{}) {
c.log(FATAL, format, a...)
}
3.mylogger/file.go
package mylogger
import (
"fmt"
"os"
"path"
"time"
)
//aa 往文件里面写日志相关代码
type FileLogger struct {
Level LogLevel
filePath string //日志文件保存的路径
fileName string //日志文件保存的文件名
fileObj *os.File
errFileObj *os.File
maxFileSize int64
logChan chan *logMsg
}
type logMsg struct {
level LogLevel
msg string
funcName string
fileName string
timestamp string
line int
}
// NewFileLogger 构造函数
func NewFileLogger(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,
logChan: make(chan *logMsg, 50000),
}
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)
}
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
}
//日志文件都已打开了
f.fileObj = fileObj
f.errFileObj = errFileObj
//开启一个后台的goroutine去往文件里写日志
go f.writeLogBackground()
return nil
}
func (f *FileLogger) enable(logLevel LogLevel) bool {
return logLevel >= f.Level
}
func (f *FileLogger) checkSize(file *os.File) bool {
fileInfo, err := file.Stat()
if err != nil {
fmt.Printf("get file info failed, err:%v\n",err)
}
//如果当前文件大小 大于等于 日志文件的最大值 就应该返回true
return fileInfo.Size() >= f.maxFileSize
}
func (f *FileLogger) splitFile(file *os.File) (*os.File, error){
//需要切割日志文件
nowStr := time.Now().Format("20060102150405000")
fileInfo, err := file.Stat()
if err != nil {
fmt.Printf("get file info failed, err:%v\n", err)
}
logName := path.Join(f.filePath,fileInfo.Name())
newLogName := fmt.Sprintf("%s.bak%s", logName, nowStr)
//1.关闭当前的日志文件
file.Close()
//2.备份一下 rename xx.log ->xx.log.bak2019...
os.Rename(logName, newLogName)
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) writeLogBackground(){
for {
if f.checkSize(f.fileObj) {
newFile, err := f.splitFile(f.fileObj)
if err != nil {
return
}
f.fileObj = newFile
}
select {
case logTmp := <- f.logChan:
//把日志拼出来
logInfo := fmt.Sprintf("[%s] [%s] [%s:%s:$d] [%s]\n",logTmp.timestamp, getLogString(logTmp.level), logTmp.fileName, logTmp.funcName,logTmp.line, logTmp.msg)
fmt.Fprintf(f.fileObj,logInfo)
if logTmp.level >= ERROR {
if f.checkSize(f.errFileObj) {
newFile, err := f.splitFile(f.errFileObj) //日志文件
if err != nil{
return
}
f.errFileObj = newFile
}
//如果要记录的日志大于等于ERROR级别,我还要在err日志文件中再记录一遍
fmt.Fprintf(f.errFileObj,logInfo) //
}
default:
//取不到日志先休息500毫秒
time.Sleep(time.Millisecond * 500)
}
}
}
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)
//先把日志发送到通道中
logTmp := &logMsg{
level: lv,
msg: msg,
funcName: funcName,
fileName: fileName,
timestamp: now.Format("2016-01-02 15:04:05"),
line: lineNo,
}
select {
case f.logChan <- logTmp:
default:
}
}
}
func (f *FileLogger) Debug(format string, a ...interface{}){
f.log(DEBUG, format, a...)
}
func (f *FileLogger) Info(format string, a ...interface{}){
f.log(INFO, format, a...)
}
func (f *FileLogger) Warning(format string, a ...interface{}){
f.log(WARNING, format, a...)
}
func (f *FileLogger) Error(format string, a ...interface{}){
f.log(ERROR, format, a...)
}
func (f *FileLogger) Fatal(format string, a ...interface{}){
f.log(FATAL, format, a...)
}
4.mylogger/mylogger.go
package mylogger
import (
"errors"
"fmt"
"path"
"runtime"
"strings"
)
//自定义一个日志库
type LogLevel uint16
//Logger接口
type Logger interface {
Debug(format string, a ...interface{})
Info(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
WARNING
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 "warning":
return WARNING, 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 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)
if !ok {
fmt.Printf("runtime.Caller() failed\n")
return
}
funcName = runtime.FuncForPC(pc).Name()
fileName = path.Base(file)
funcName = strings.Split(funcName, ".")[1]
return
}
5.mylogger_test
package main
import (
"day06/mylogger"
)
var log mylogger.Logger
//测试我们自己写的日志库
func main(){
log = mylogger.NewConsoleLogger("Info")
log = mylogger.NewFileLogger("Info", "./","zhoulinwan.log", 10*1024*1024)
for {
log.Debug("这是一条Debug日志")
log.Info("这是一条info日志")
log.Warning("这是一条warning日志")
id := 10010
name := "理想"
log.Error("这是一条Error日志,id:%d,name:%s", id, name)
log.Fatal("这是一条Fatal日志")
}
}
版权声明:本文为huha666原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。