Golang实现redis系列-(1)日志组件的封装

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: Golang实现redis系列-(1)日志组件的封装

日志滚动

前言

虽然zap+Lumberjack可以实现日志切割与归档,但是在项目中用不到太多(越简单越好),此时去学习两个库还不如自己封装一层go内置的log包,以后有了新需求再加或者再重构

需求

  1. 要求每日日志存在xxxx/xx/xx的文件夹中
  2. 日志文件名为filename_n.log
  3. 日志文件的大小有要求
  4. 日志要输出到文件和stdout中

代码

package log
import (
  "fmt"
  "io"
  "log"
  "os"
  "strconv"
  "sync"
  "time"
)
// Settings 存储logger的配置信息,为func Setup提供配置
type Settings struct {
  //文件所在路径
  Path string
  //文件名
  Name string
  //文件后缀
  Ext string
  //时间格式
  TimeFormat string
  //文件夹内文件最大数量
  MaxBackups uint32
  //文件的大小(k)
  MaxSize uint32
  //用于计数到第几个文件
  Cnt int
}
var (
  logger             *log.Logger
  defaultPrefix      = ""
  defaultCallerDepth = 2
  mu                 sync.Mutex
  logPrefix          = ""
  levelFlags         = []string{"ERROR", "FATAL"}
  flags              = log.LstdFlags | log.Lmicroseconds | log.Lshortfile
  filePCB            *os.File
  config             *Settings
  fileStat           os.FileInfo
)
type logLevel int
// log levels
const (
  ERROR logLevel = iota
  FATAL
)
func init() {
  logger = log.New(os.Stdout, defaultPrefix, flags)
}
//检查目录是否存在,true为不存在
func checkNotExist(src string) bool {
  _, err := os.Stat(src)
  return os.IsNotExist(err)
}
//检查目录是否有权限,true为没权限
func checkNotPermission(src string) bool {
  _, err := os.Stat(src)
  return os.IsPermission(err)
}
func isNotExistMkDir(src string) error {
  if notExist := checkNotExist(src); notExist {
    if err := mkDir(src); err != nil {
      return err
    }
  }
  return nil
}
func mkDir(src string) error {
  err := os.MkdirAll(src, os.ModePerm) //Unix permission bits, 0o777
  if err != nil {
    return err
  }
  return nil
}
func mustOpen(fileName, dir string) (*os.File, error) {
  perm := checkNotPermission(dir)
  if perm {
    return nil, fmt.Errorf("permission denied dir: %s", dir)
  }
  err := isNotExistMkDir(dir)
  if err != nil {
    return nil, fmt.Errorf("error during make dir %s, err: %s", dir, err)
  }
  f, err := os.OpenFile(dir+string(os.PathSeparator)+fileName, os.O_TRUNC|os.O_CREATE|os.O_RDWR, 0644)
  if err != nil {
    return nil, fmt.Errorf("fail to open file, err: %s", err)
  }
  return f, nil
}
// Setup 配置logger
func Setup(settings *Settings) {
  config = settings
  if settings.Cnt == 11 {
    settings.Cnt = 1
  }
  if filePCB != nil {
    filePCB.Close()
    filePCB = nil
  }
  dir := settings.Path + time.Now().Format(settings.TimeFormat)
  fileName := fmt.Sprintf("%s_%s.%s",
    settings.Name,
    strconv.Itoa(settings.Cnt),
    settings.Ext)
  logFile, err := mustOpen(fileName, dir)
  if err != nil {
    log.Fatalf("logging.Setup err: %s", err)
  }
  filePCB = logFile
  mw := io.MultiWriter(os.Stdout, logFile)
  logger = log.New(mw, defaultPrefix, flags)
  settings.Cnt++
}
//写入文件时检查文件大小和日期(mustOpen中其实已经会检查日期了)
func checkFileSize() {
  fileStat, _ = filePCB.Stat()
  if fileStat.Size() > int64(config.MaxSize*1024) {
    Setup(config)
  }
}
//设置logger的前缀
func setPrefix(level logLevel) {
  logPrefix = fmt.Sprintf("[%s] ", levelFlags[level])
  logger.SetPrefix(logPrefix)
}
func Error(v ...interface{}) {
  mu.Lock()
  defer mu.Unlock()
  setPrefix(ERROR)
  checkFileSize()
  logger.Println(v...)
}
// Fatal 打印错误日志,然后停止程序
func Fatal(v ...interface{}) {
  mu.Lock()
  defer mu.Unlock()
  setPrefix(FATAL)
  checkFileSize()
  logger.Fatalln(v...)
}
package log_test
import (
  "Gotosp/pkg/log"
  "testing"
)
func TestSetup(t *testing.T) {
  log.Setup(&log.Settings{
    Path:       "logs",
    Name:       "filename",
    Ext:        "log",
    TimeFormat: "2006-01-02",
    MaxBackups: 20, //最多MaxBackups个文件
    MaxSize:    10, //文件最大MaxSize(k)
    Cnt:        1,  //从filename_cnt开始,后期做config配置
  })
  for i := 0; i < 10000; i++ {
    log.Error("ERROR")
  }
  log.Fatal("stop")
}

测试


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
4月前
|
存储 NoSQL 关系型数据库
Redis 日志篇:无畏宕机快速恢复的杀手锏
Redis 日志篇:无畏宕机快速恢复的杀手锏
79 0
|
4月前
|
缓存 NoSQL 应用服务中间件
2.2.2 redis,memcached,nginx网络组件
2.2.2 redis,memcached,nginx网络组件
|
4月前
|
NoSQL Java 应用服务中间件
4.网络设计与redis、memcached、nginx组件(二)
4.网络设计与redis、memcached、nginx组件(二)
28 0
|
4月前
|
存储 NoSQL 应用服务中间件
4.网络设计与redis、memcached、nginx组件(一)
4.网络设计与redis、memcached、nginx组件(一)
76 0
|
4月前
|
NoSQL Go Redis
Golang实现redis系列-(3)封装RESP协议
Golang实现redis系列-(3)封装RESP协议
36 0
|
2月前
|
缓存 NoSQL Java
springboot中集成redis,二次封装成工具类
springboot中集成redis,二次封装成工具类
176 0
|
4月前
|
Go
Golang日志切割归档
Golang日志切割归档
31 0
|
4月前
|
JSON Go 开发工具
Golang日志库Zap基本使用
Golang日志库Zap基本使用
38 0
|
28天前
|
Java
使用Java代码打印log日志
使用Java代码打印log日志
89 1
|
1月前
|
Linux Shell
Linux手动清理Linux脚本日志定时清理日志和log文件执行表达式
Linux手动清理Linux脚本日志定时清理日志和log文件执行表达式
81 1