分享
  1. 首页
  2. 文章

Gin实践 连载六 搭建Blog API's(五)

EDDYCJY · · 5874 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

API's - logging

在上一节中,我们解决了API's可以任意访问的问题,那么我们现在还有一个问题。

就是我们的日志,都是输出到控制台上的,这显然对于一个项目来说是不合理的,因此我们这一节简单封装log库,使其支持简单的文件日志!


新建logging

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

  1. file.go:
package logging
import (
 "os"
 "time"
 "fmt"
 "log"
)
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 os.IsNotExist(err):
 mkDir(getLogFilePath())
 case os.IsPermission(err):
 log.Fatalf("Permission :%v", err)
 }
 handle, 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 handle
}
func mkDir(filePath string) {
 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:能够接受ErrNotExistsyscall的一些错误,它会返回一个布尔值,能够得知文件不存在或目录不存在
  • os.IsPermission:能够接受ErrPermissionsyscall的一些错误,它会返回一个布尔值,能够得知权限是否满足
  • 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
  1. log.go
package logging
import (
 "log"
 "os"
 "runtime"
 "path/filepath"
 "fmt"
)
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
 WARNING
 ERROR
 FATAL
)
func init() {
 filePath := getLogFileFullPath()
 F = openLogFile(filePath)
 logger = log.New(F, DefaultPrefix, log.LstdFlags)
}
func Debug(v ...interface{}) {
 setPrefix(DEBUG)
 logger.Println(v)
}
func Info(v ...interface{}) {
 setPrefix(INFO)
 logger.Println(v)
}
func Warn(v ...interface{}) {
 setPrefix(WARNING)
 logger.Println(v)
}
func Error(v ...interface{}) {
 setPrefix(ERROR)
 logger.Println(v)
}
func Fatal(v ...interface{}) {
 setPrefix(FATAL)
 logger.Fatalln(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
 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.gotag.goauth.go
  2. log包的引用删除,修改引用我们自己的日志包为gin-blog/pkg/logging
  3. 将原本的log.Println(...)改为log.Info(...)

例如auth.go文件的修改内容:

package api
import (
 "net/http"
 "github.com/gin-gonic/gin"
 "github.com/astaxie/beego/validation"
 "gin-blog/pkg/e"
 "gin-blog/pkg/util"
 "gin-blog/models"
 "gin-blog/pkg/logging"
)
...
func GetAuth(c *gin.Context) {
 ...
 code := e.INVALID_PARAMS
 if ok {
 ...
 } else {
 for _, err := range valid.Errors {
 logging.Info(err.Key, err.Message)
 }
 }
 c.JSON(http.StatusOK, gin.H{
 "code" : code,
 "msg" : e.GetMsg(code),
 "data" : data,
 })
}

验证功能

修改文件后,重启服务,我们来试试吧!

获取到API的Token后,我们故意传错误URL参数给接口,如:http://127.0.0.1:8000/api/v1/articles?tag_id=0&state=9999999&token=eyJhbG..

然后我们到$GOPATH/gin-blog/runtime/logs查看日志:

$ tail -f log20180216.log 
[INFO][article.go:79]2018年02月16日 18:33:12 [state 状态只允许0或1]
[INFO][article.go:79]2018年02月16日 18:33:42 [state 状态只允许0或1]
[INFO][article.go:79]2018年02月16日 18:33:42 [tag_id 标签ID必须大于0]
[INFO][article.go:79]2018年02月16日 18:38:39 [state 状态只允许0或1]
[INFO][article.go:79]2018年02月16日 18:38:39 [tag_id 标签ID必须大于0]

日志结构一切正常,我们的记录模式都为Info,因此前缀是对的,并且我们是入参有问题,也把错误记录下来了,这样排错就很方便了!

至此,本节就完成了,这只是一个简单的扩展,实际上我们线上项目要使用的文件日志,是更复杂一些,开动你的大脑 举一反三吧!

参考

本系列示例代码


有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:EDDYCJY

查看原文:Gin实践 连载六 搭建Blog API's(五)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
5874 次点击
被以下专栏收入,发现更多相似内容
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏