ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Gin实践 连载五 编写一个简单的文件日志

2022-09-09 11:32:41  阅读:179  来源: 互联网

标签:文件 连载 func go Gin 日志 os log


编写一个简单的文件日志

在上一节中,我们解决了API’s可以任意访问的问题,那么我们现在还有一个问题。
就是我们的日志,都是输出到控制台上的,这显然对于一个项目来说是不合理的,因此我们这一节简单封装log库,使其支持简单的文件日志!

新建logging包

我们在pkg下新建logging目录,新建file.go和log.go文件,写入内容:

编写file文件
  1. file.go
package logging

import (
	"errors"
	"fmt"
	"io/fs"
	"log"
	"os"
	"time"
)

var (
	LogSavePath = "runtime/logs/"
	LogSaveName = "log"
	LogFileExt = "log"
	TimeFormat = "20060102"
)
func getLogFilePath() string {
	return fmt.Sprintf("%s", LogSavePath)
}
func getLogFileFullPath() string {
	prefixPath := getLogFilePath()
	suffixPath := fmt.Sprintf("%s%s.%s", LogSaveName, time.Now().Format(TimeFormat), LogFileExt)
	return fmt.Sprintf("%s%s", prefixPath, suffixPath)
}
func openLogFile(filePath string) *os.File {
	_, err := os.Stat(filePath)
	switch {
	case errors.Is(err, fs.ErrNotExist):
		mkDir()
	case errors.Is(err, fs.ErrPermission):
		log.Fatalf("Permission:%v", err)
	}

	handler, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalf("Fail to OpenFile: %v", err)
	}

	return handler
}
func mkDir() {
	dir, _ := os.Getwd()
	err := os.MkdirAll(dir + "/" + getLogFilePath(), os.ModePerm)
	if err != nil {
		panic(err)
	}
}

os.Stat:返回文件信息结构描述文件,如果出现错误,会返回 *PathError

  type PathError struct {
      Op   string
      Path string
      Err  error
  }
  • os.IsNotExist:能够接受ErrNotExist、syscall的一些错误,它会返回一个布尔值,能够得知文件不存在或目录不存在
  • os.IsPermission:能够接受ErrPermission、syscall的一些错误,它会返回一个布尔值,能够得知权限是否满足
  • os.OpenFile:调用文件,支持传入文件名称、指定的模式调用文件、文件权限,返回的文件的方法可以用于I/O。如果出现错误,则为*PathError。
  const (
      // Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
      O_RDONLY int = syscall.O_RDONLY // 以只读模式打开文件
      O_WRONLY int = syscall.O_WRONLY // 以只写模式打开文件
      O_RDWR   int = syscall.O_RDWR   // 以读写模式打开文件
      // The remaining values may be or'ed in to control behavior.
      O_APPEND int = syscall.O_APPEND // 在写入时将数据追加到文件中
      O_CREATE int = syscall.O_CREAT  // 如果不存在,则创建一个新文件
      O_EXCL   int = syscall.O_EXCL   // 使用O_CREATE时,文件必须不存在
      O_SYNC   int = syscall.O_SYNC   // 同步IO
      O_TRUNC  int = syscall.O_TRUNC  // 如果可以,打开时
  )
  • os.Getwd:返回与当前目录对应的根路径名
  • os.MkdirAll:创建对应的目录以及所需的子目录,若成功则返回nil,否则返回error
  • os.ModePerm:const定义ModePerm FileMode = 0777

编写log文件

  1. log.go
package logging

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"runtime"
)

type Level int
var (
	F *os.File

	DefaultPrefix = ""
	DefaultCallerDepth = 2

	logger *log.Logger
	logPrefix = ""
	levelFlags = []string{"DEBUG", "INFO", "WARN", "ERROR", "FATAL"}
)
const (
	DEBUG Level = iota
	INFO
	WARN
	ERROR
	FATAL
)

func init() {
	filePath := getLogFileFullPath()
	F = openLogFile(filePath)
	logger = log.New(F, DefaultPrefix, log.LstdFlags)
}
func Debug(v ...any) {
	setPrefix(DEBUG)
	logger.Println(v)
}
func Info(v ...any) {
	setPrefix(INFO)
	logger.Println(v)
}
func Warn(v ...any) {
	setPrefix(WARN)
	logger.Println(v)
}
func Error(v ...any) {
	setPrefix(ERROR)
	logger.Println(v)
}
func Fatal(v ...any) {
	setPrefix(FATAL)
	logger.Fatal(v)
}
func setPrefix(level Level) {
	_, file, line, ok := runtime.Caller(DefaultCallerDepth)
	if ok {
		logPrefix = fmt.Sprintf("[%s][%s:%d]", levelFlags[level], filepath.Base(file), line)
	}else {
		logPrefix = fmt.Sprintf("[%s]", levelFlags[level])
	}
	logger.SetPrefix(logPrefix)
}

log.New创建一个新的日志记录器,out定义要写入日志数据的IO句柄,prefix定义每个生成日志行的开头,flag定义了日志记录属性。

  func New(out io.Writer, prefix string, flag int) *Logger {
      return &Logger{out: out, prefix: prefix, flag: flag}
  }

log.LstdFlags日志记录的格式属性之一,其余的选项如下:

const (
	Ldate         = 1 << iota     // the date in the local time zone: 2009/01/23
	Ltime                         // the time in the local time zone: 01:23:23
	Lmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime.
	Llongfile                     // full file name and line number: /a/b/c/d.go:23
	Lshortfile                    // final file name element and line number: d.go:23. overrides Llongfile
	LUTC                          // if Ldate or Ltime is set, use UTC rather than the local time zone
	Lmsgprefix                    // move the "prefix" from the beginning of the line to before the message
	LstdFlags     = Ldate | Ltime // initial values for the standard logger
)

当前目录结构:

gin-blog/
├── conf
│   └── app.ini
├── main.go
├── middleware
│   └── jwt
│       └── jwt.go
├── models
│   ├── article.go
│   ├── auth.go
│   ├── models.go
│   └── tag.go
├── pkg
│   ├── e
│   │   ├── code.go
│   │   └── msg.go
│   ├── logging
│   │   ├── file.go
│   │   └── log.go
│   ├── setting
│   │   └── setting.go
│   └── util
│       ├── jwt.go
│       └── pagination.go
├── routers
│   ├── api
│   │   ├── auth.go
│   │   └── v1
│   │       ├── article.go
│   │       └── tag.go
│   └── router.go
├── runtime

我们自定义的logging包,已经基本完成了,接下来让它接入到我们的项目之中吧!
我们打开先前包含log包的代码,

  1. 打开routers目录下的article.go、tag.go、auth.go
  2. 将log包的引用删除,修改引用我们自己的日志包为gin-blog/pkg/logging
  3. 将原本的log.Println(...)改为logging.Info(...)

验证功能

修改文件后,重启服务,我们来试试吧!
获取到API的Token后,我们故意传错误URL参数给接口,如:http://127.0.0.1:8000/api/v1/articles?tag_id=0&state=9999999&token=eyJhbG..
然后我们到$GOPATH/gin-blog/runtime/logs查看日志:
log20220910.log

[INFO][article.go:170]2022/09/10 00:00:40 [modified_by 修改人不能为空]
[INFO][article.go:170]2022/09/10 00:00:47 [state 状态只允许0或1]

日志结构一切正常,我们的记录模式都为Info,因此前缀是对的,并且我们是入参有问题,也把错误记录下来了,这样排错就很方便了!
至此,本节就完成了,这只是一个简单的扩展,实际上我们线上项目要使用的文件日志,是更复杂一些,开动你的大脑 举一反三吧!

定时任务实现按天切割日志

虽然我们的日志名称中是按天的格式的,但是上面我们并没有实现按天进行日志的切割,为了实现日志按天切割,我们需要引入一个定时任务:
在pkg目录下创建cron目录,新建cron.go文件:

package cron

import (
	"time"
)

// SetTime 获取到自定时间的Duration 误差在1s内
// 计算设置时间和当前时间的差值,大于当前时间则返回,否则为第二天的时间
func SetTime(hour, minute, second int) (d time.Duration) {
	now := time.Now()
	setTime := time.Date(now.Year(), now.Month(), now.Day(), hour, minute, second, 0, now.Location())
	d = setTime.Sub(now)
	if d > 0 {
		return
	}
	return d + time.Hour*24
}
func ScheduleTask(f func()) {
	timer := time.NewTimer(SetTime(0, 0, 0))
	defer timer.Stop()
	for {
		select {
		case <-timer.C:
			timer.Reset(time.Hour * 24)
			f()
		}
	}
}

pkg/logging/log.go文件修改:

func init() {
	filePath := getLogFileFullPath()
	F = openLogFile(filePath)
	logger = log.New(F, DefaultPrefix, log.LstdFlags)
	go cron.ScheduleTask(updateF)
}
func updateF() {
	filePath := getLogFileFullPath()
	F = openLogFile(filePath)
	logger = log.New(F, DefaultPrefix, log.LstdFlags)
}

这样的话每天00:00:00秒就会创建新的日志文件:

标签:文件,连载,func,go,Gin,日志,os,log
来源: https://www.cnblogs.com/mayanan/p/16672187.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有