初始化项目

This commit is contained in:
皮蛋13361098506
2025-01-06 16:01:02 +08:00
commit 1b77f62820
575 changed files with 69193 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
package impl_localfile
import "sync"
func (l *Logger) InfoLog(format string, args ...interface{}) {
l.log(getLog(format, args...), info, true)
}
func (l *Logger) WarnLog(format string, args ...interface{}) {
l.log(getLog(format, args...), warn, true)
}
func (l *Logger) DebugLog(format string, args ...interface{}) {
l.log(getLog(format, args...), debug, true)
}
func (l *Logger) ErrorLog(format string, args ...interface{}) {
l.log(getLog(format, args...), _error, true)
}
func (l *Logger) FatalLog(format string, args ...interface{}) {
l.log(getLog(format, args...), fatal, true)
}
func (l *Logger) CloseLog(waitFinish bool) {
wg := sync.WaitGroup{}
// 关闭所有的file
for _, logger := range l.loggerMap {
wg.Add(1)
go logger.Close(&wg, waitFinish)
}
wg.Wait()
}

View File

@@ -0,0 +1,3 @@
package impl_localfile
// 包名不能采用中分线而且前面的包已经说明了是log包这个子文件夹不需要在加上log

View File

@@ -0,0 +1,203 @@
package impl_localfile
import (
"fmt"
"log"
"os"
"path/filepath"
"strconv"
"sync"
"time"
)
const (
con_FILE_SUFFIX = "txt"
defaultPanicNum = 10 // 默认支持的连续panic次数
defaultPanicIntervals = 5 // 秒
fileMaxSize = 1 << 27 // 128M
)
type fileLog struct {
logPath string
// fileDir + fileName 用于判断是否需要切换日志文件
fileDir string
fileName string
lv levelType
msgChan chan logObject
f *os.File
// 如果出现未知panic并一直发生那么应该要真正的panic
panicNum int
panicTime time.Time
// 当前已经写入的大小
curWriteSize int64
}
func newLog(logPath string, lv levelType) *fileLog {
f := &fileLog{
logPath: logPath,
fileDir: "",
fileName: "",
msgChan: make(chan logObject, 10240),
lv: lv,
f: nil,
}
go f.loop()
return f
}
func (f *fileLog) loop() {
defer func() {
if r := recover(); r != nil {
// 将错误输出,而不是记录到文件,是因为可能导致死循环
fmt.Println("log file lv:", f.lv, " err:", r)
// 超过间隔时间重置
if time.Since(f.panicTime)/time.Second > defaultPanicIntervals {
f.panicNum = 0
f.panicTime = time.Now()
}
// 这里处理连续panic 也是防止循环调用形成死循环
f.panicNum++
if f.panicNum >= defaultPanicNum {
panic(r)
}
}
go f.loop()
}()
for logItem := range f.msgChan {
f.writeLog(logItem)
}
}
func (f *fileLog) writeLog(logObj logObject) {
// 由于不使用直接 logUtil 调用logUtilPlus 存在终端打印的控制,所以 logUtil 只作为纯文件日志组件
// if PrintImportantLog && (logObj.level == warn || logObj.level == _error || logObj.level == fatal) {
// fmt.Println(logObj.logInfo)
// }
// 检查是否需要去切换文件或者创建文件
f.checkFileAndLoop(logObj.level, logObj.ifIncludeHour)
f.curWriteSize += int64(len(logObj.logInfo))
_, _ = f.f.WriteString(logObj.logInfo)
}
// 检查文件是否需要切换
func (f *fileLog) checkFileAndLoop(level levelType, ifIncludeHour bool) {
// 获取当前时间
now := time.Now()
fileAbsoluteDirectory := filepath.Join(f.logPath, strconv.Itoa(now.Year()), strconv.Itoa(int(now.Month())))
fileName := ""
if ifIncludeHour {
fileName = fmt.Sprintf("%s.%s.%s", time.Now().Format("2006-01-02-15"), level, con_FILE_SUFFIX)
} else {
fileName = fmt.Sprintf("%s.%s.%s", time.Now().Format("2006-01-02"), level, con_FILE_SUFFIX)
}
// 说明已经存在, 检查文件大小,并切换
if f.f != nil && f.fileName == fileName && f.fileDir == fileAbsoluteDirectory {
if f.curWriteSize < fileMaxSize {
return
}
// 大小超额切换文件
f.switchFile()
return
}
// 创建文件夹
if f.fileDir != fileAbsoluteDirectory {
if err := os.MkdirAll(fileAbsoluteDirectory, os.ModePerm|os.ModeTemporary); err != nil {
log.Println("make dir all is err :", err)
}
f.fileDir = fileAbsoluteDirectory
}
// 创建文件
if f.fileName != fileName {
f.fileName = fileName
}
// 到这里说明要切换了关闭之前的file
if f.f != nil {
_ = f.f.Close()
}
// 正常切换文件
f.createFile()
}
func (f *fileLog) switchFile() {
// 关闭文件
_ = f.f.Close()
// 重命名
curFileName := filepath.Join(f.fileDir, f.fileName)
newFileName := filepath.Join(f.fileDir, fmt.Sprintf("%s.%s.%s", f.fileName, time.Now().Format("15-04-05.999"), con_FILE_SUFFIX))
err := os.Rename(curFileName, newFileName)
if err != nil {
log.Println(err)
}
// 再次创建文件
f.createFile()
}
func (f *fileLog) createFile() {
// 得到最终的文件绝对路径
fileAbsolutePath := filepath.Join(f.fileDir, f.fileName)
// 打开文件(如果文件存在就以读写模式打开,并追加写入;如果文件不存在就创建,然后以写模式打开。)
file, err := os.OpenFile(fileAbsolutePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm|os.ModeTemporary)
if err != nil {
log.Println("open file is err :", err)
return
}
f.f = file
stat, _ := file.Stat()
f.curWriteSize = stat.Size()
}
func (f *fileLog) SetLogPath(logPath string) {
f.logPath = logPath
}
// Close 最后等待 3s 钟
func (f *fileLog) Close(wg *sync.WaitGroup, waitFinish bool) {
ticker := time.NewTicker(3 * time.Second)
defer func() {
ticker.Stop()
if f.f != nil {
_ = f.f.Close()
}
wg.Done()
}()
for {
if !waitFinish || len(f.msgChan) == 0 {
break
}
select {
case <-ticker.C:
fmt.Println("wait close log file timeout:3s")
return
default:
// 1ms 写入文件500条最多等待 15ms 检查一次就行
time.Sleep(15 * time.Millisecond)
}
}
}

View File

@@ -0,0 +1,223 @@
package impl_localfile
import (
"fmt"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
"goutil/fileUtil"
"goutil/timeUtil"
)
const (
con_SEPERATOR = "\n------------------------------------------------------\n"
)
// Logger
// @description: 日志对象
type Logger struct {
logPath string
loggerMap map[levelType]*fileLog
bufPool *sync.Pool
// 压缩属性
// 上一次日志压缩的日期 时间戳
preCompressDate int64
// 压缩锁对象
compressLock sync.Mutex
}
// NewLogger
// @description: 构造日志对象
// parameter:
//
// @*Logger:
func NewLogger() *Logger {
log := &Logger{
logPath: "Log",
loggerMap: make(map[levelType]*fileLog, 4),
bufPool: &sync.Pool{
New: func() interface{} {
return &strings.Builder{}
},
},
}
// 初始化基础日志
for lv := range levels {
log.loggerMap[levelType(lv)] = newLog(log.logPath, levelType(lv))
}
return log
}
// log
// @description: 记录日志
// parameter:
//
// @receiver l:
// @logInfo:
// @lv:
// @ifIncludeHour:
//
// return:
func (l *Logger) log(logInfo string, lv levelType, ifIncludeHour bool) {
buf := l.bufPool.Get().(*strings.Builder)
defer l.bufPool.Put(buf)
buf.Reset()
// 组装所有需要写入的内容
// 获取当前时间
buf.WriteString(time.Now().Format("2006-01-02 15:04:05"))
buf.WriteString("---->\n")
buf.WriteString(logInfo)
// 加上最后的分隔符
buf.WriteString(con_SEPERATOR)
// 写入之前检查
l.checkCompress()
// 构造对象并添加到队列中
l.loggerMap[lv].msgChan <- newLogObject(buf.String(), lv, ifIncludeHour)
}
// SetLogPath
// @description: 设置文件路径
// parameter:
//
// @receiver l:
// @_logPath:
//
// return:
func (l *Logger) SetLogPath(_logPath string) {
l.logPath = _logPath
for _, lf := range l.loggerMap {
lf.SetLogPath(_logPath)
}
}
// checkCompress
// @description: 压缩
// parameter:
//
// @receiver l:
//
// return:
func (l *Logger) checkCompress() {
// 不用压缩,直接返回
if !l.preCompress() {
return
}
// 日志压缩
go l.doCompress()
}
// closeLogFile
// @description: 关闭日志文件
// parameter:
//
// @receiver l:
//
// return:
func (l *Logger) closeLogFile() {
// 需要压缩文件,关闭已经打开的文件
for _, lf := range l.loggerMap {
_ = lf.f.Close()
}
}
// preCompress 确定是否要压缩
func (l *Logger) preCompress() bool {
// 检查是否需要进行数据压缩
nowDate := timeUtil.GetDate(time.Now()).Unix()
if nowDate == l.preCompressDate {
return false
}
l.compressLock.Lock()
defer l.compressLock.Unlock()
// 上一次压缩的时间
if nowDate == l.preCompressDate {
return false
}
l.closeLogFile()
l.preCompressDate = nowDate
return true
}
// 日志压缩
func (l *Logger) doCompress() {
defer func() {
if r := recover(); r != nil {
// 将错误输出,而不是记录到文件,是因为可能导致死循环
fmt.Println(r)
}
}()
// 获取昨天的日期,并获取昨天对应的文件夹
yesterday := time.Now().AddDate(0, 0, -1)
dateString := timeUtil.Format(yesterday, "yyyy-MM-dd")
fileAbsoluteDirectory := filepath.Join(l.logPath, strconv.Itoa(yesterday.Year()), strconv.Itoa(int(yesterday.Month())))
// 判断是否已经存在压缩文件
compressFileName := fmt.Sprintf("%s.tar.gz", dateString)
compressAbsolutePath := filepath.Join(fileAbsoluteDirectory, compressFileName)
if exists, err := fileUtil.IsFileExists(compressAbsolutePath); err == nil && exists {
return
}
// 获取昨天的文件列表
fileList, err := fileUtil.GetFileList2(fileAbsoluteDirectory, dateString, con_FILE_SUFFIX)
if err != nil {
fmt.Printf("logUtil.compress.fileUtil.GetFileList2 err:%s\n", err)
return
}
if len(fileList) == 0 {
return
}
// 进行tar操作得到yyyy-MM-dd.tar
tarFileName := fmt.Sprintf("%s.tar", dateString)
tarAbsolutePath := filepath.Join(fileAbsoluteDirectory, tarFileName)
if err := fileUtil.Tar(fileList, tarAbsolutePath); err != nil {
fmt.Printf("logUtil.compress.fileUtil.Tar err:%s\n", err)
}
// 进行gzip操作得到yyyy-MM-dd.tar.gz
if err := fileUtil.Gzip(tarAbsolutePath, ""); err != nil {
fmt.Printf("logUtil.compress.fileUtil.Gzip err:%s\n", err)
}
// 删除原始文件
for _, item := range fileList {
_ = fileUtil.DeleteFile(item)
}
// 删除tar文件
_ = fileUtil.DeleteFile(tarAbsolutePath)
}
// getLog
// @description: 组装日志
// parameter:
//
// @format:
// @args:
//
// return:
//
// @string:
func getLog(format string, args ...interface{}) string {
if len(args) == 0 {
return format
} else {
return fmt.Sprintf(format, args...)
}
}

View File

@@ -0,0 +1,33 @@
package impl_localfile
type levelType int
// 日志等级
const (
// info 信息
info levelType = iota
// warn 警告
warn
// debug 调试
debug
// _error 错误
_error
// fatal 致命
fatal
)
var levels = [...]string{
"info",
"warn",
"debug",
"error",
"fatal",
}
func (t levelType) String() string {
return levels[t]
}

View File

@@ -0,0 +1,20 @@
package impl_localfile
type logObject struct {
// 日志信息
logInfo string
// 日志等级
level levelType
// 日志文件名称是否包含小时
ifIncludeHour bool
}
func newLogObject(logInfo string, level levelType, ifIncludeHour bool) logObject {
return logObject{
logInfo: logInfo,
level: level,
ifIncludeHour: ifIncludeHour,
}
}