zoukankan      html  css  js  c++  java
  • go语言web开发系列之六:gin使用zap记录http服务的访问日志(access log)并按日期分割

    一,安装用到的库:

    1,安装zap日志库:

    liuhongdi@ku:/data/liuhongdi/zaplog$ go get -u go.uber.org/zap

    2,安装go-file-rotatelogs库

    liuhongdi@ku:/data/liuhongdi/zaplog2$ go get -u github.com/lestrrat/go-file-rotatelogs

    说明:刘宏缔的go森林是一个专注golang的博客,
              地址:https://blog.csdn.net/weixin_43881017

    说明:作者:刘宏缔 邮箱: 371125307@qq.com

    二,演示项目的相关信息:

    1,项目地址:

    https://github.com/liuhongdi/digv06

    2,功能说明:记录http服务的访问日志access log

    3,项目结构:如图:

    三,go代码说明

    1,config/config.yaml

    1.  
      Database:
    2.  
      DBType: mysql
    3.  
      UserName: root
    4.  
      Password: password
    5.  
      Host: 127.0.0.1:3306
    6.  
      DBName: dig
    7.  
      Charset: utf8
    8.  
      ParseTime: True
    9.  
      MaxIdleConns: 10
    10.  
      MaxOpenConns: 30
    11.  
      Server:
    12.  
      RunMode: debug
    13.  
      HttpPort: 8000
    14.  
      ReadTimeout: 60
    15.  
      WriteTimeout: 60
    16.  
      Log:
    17.  
      LogFilePath: /data/gologs/logs
    18.  
      LogInfoFileName: info
    19.  
      LogWarnFileName: warn
    20.  
      LogFileExt: log
    21.  
      AccessLog:
    22.  
      LogFilePath: /data/gologs/logs
    23.  
      LogFileName: access
    24.  
      LogFileExt: log

    2,global/setting.go

    1.  
      package global
    2.  
       
    3.  
      import (
    4.  
      "fmt"
    5.  
      "github.com/liuhongdi/digv06/pkg/setting"
    6.  
      "time"
    7.  
      )
    8.  
      //服务器配置
    9.  
      type ServerSettingS struct {
    10.  
      RunMode string
    11.  
      HttpPort string
    12.  
      ReadTimeout time.Duration
    13.  
      WriteTimeout time.Duration
    14.  
      }
    15.  
      //数据库配置
    16.  
      type DatabaseSettingS struct {
    17.  
      DBType string
    18.  
      UserName string
    19.  
      Password string
    20.  
      Host string
    21.  
      DBName string
    22.  
      Charset string
    23.  
      ParseTime bool
    24.  
      MaxIdleConns int
    25.  
      MaxOpenConns int
    26.  
      }
    27.  
      //日志配置
    28.  
      type LogSettingS struct {
    29.  
      LogFilePath string //保存到的目录
    30.  
      LogInfoFileName string //info级日志文件的名字
    31.  
      LogWarnFileName string //warn级日志文件的名字
    32.  
      LogAccessFileName string //Access日志文件的名字
    33.  
      LogFileExt string //文件的扩展名
    34.  
      }
    35.  
      //访问日志配置
    36.  
      type AccessLogSettingS struct {
    37.  
      LogFilePath string //保存到的目录
    38.  
      LogFileName string //Access日志文件的名字
    39.  
      LogFileExt string //文件的扩展名
    40.  
      }
    41.  
      //定义全局变量
    42.  
      var (
    43.  
      ServerSetting *ServerSettingS
    44.  
      DatabaseSetting *DatabaseSettingS
    45.  
      LogSetting *LogSettingS
    46.  
      AccessLogSetting *AccessLogSettingS
    47.  
      )
    48.  
       
    49.  
      //读取配置到全局变量
    50.  
      func SetupSetting() error {
    51.  
      s, err := setting.NewSetting()
    52.  
      if err != nil {
    53.  
      return err
    54.  
      }
    55.  
      err = s.ReadSection("Database", &DatabaseSetting)
    56.  
      if err != nil {
    57.  
      return err
    58.  
      }
    59.  
       
    60.  
      err = s.ReadSection("Server", &ServerSetting)
    61.  
      if err != nil {
    62.  
      return err
    63.  
      }
    64.  
       
    65.  
      err = s.ReadSection("Log", &LogSetting)
    66.  
      if err != nil {
    67.  
      return err
    68.  
      }
    69.  
       
    70.  
      err = s.ReadSection("AccessLog", &AccessLogSetting)
    71.  
      if err != nil {
    72.  
      return err
    73.  
      }
    74.  
       
    75.  
      fmt.Println("setting:")
    76.  
      fmt.Println(ServerSetting)
    77.  
      fmt.Println(DatabaseSetting)
    78.  
      fmt.Println(LogSetting)
    79.  
      fmt.Println(AccessLogSetting)
    80.  
      return nil
    81.  
      }

    3,global/accessLog.go

    1.  
      package global
    2.  
       
    3.  
      import (
    4.  
      "fmt"
    5.  
      "github.com/liuhongdi/digv06/pkg/setting"
    6.  
      "time"
    7.  
      )
    8.  
      //服务器配置
    9.  
      type ServerSettingS struct {
    10.  
      RunMode string
    11.  
      HttpPort string
    12.  
      ReadTimeout time.Duration
    13.  
      WriteTimeout time.Duration
    14.  
      }
    15.  
      //数据库配置
    16.  
      type DatabaseSettingS struct {
    17.  
      DBType string
    18.  
      UserName string
    19.  
      Password string
    20.  
      Host string
    21.  
      DBName string
    22.  
      Charset string
    23.  
      ParseTime bool
    24.  
      MaxIdleConns int
    25.  
      MaxOpenConns int
    26.  
      }
    27.  
      //日志配置
    28.  
      type LogSettingS struct {
    29.  
      LogFilePath string //保存到的目录
    30.  
      LogInfoFileName string //info级日志文件的名字
    31.  
      LogWarnFileName string //warn级日志文件的名字
    32.  
      LogAccessFileName string //Access日志文件的名字
    33.  
      LogFileExt string //文件的扩展名
    34.  
      }
    35.  
      //访问日志配置
    36.  
      type AccessLogSettingS struct {
    37.  
      LogFilePath string //保存到的目录
    38.  
      LogFileName string //Access日志文件的名字
    39.  
      LogFileExt string //文件的扩展名
    40.  
      }
    41.  
      //定义全局变量
    42.  
      var (
    43.  
      ServerSetting *ServerSettingS
    44.  
      DatabaseSetting *DatabaseSettingS
    45.  
      LogSetting *LogSettingS
    46.  
      AccessLogSetting *AccessLogSettingS
    47.  
      )
    48.  
       
    49.  
      //读取配置到全局变量
    50.  
      func SetupSetting() error {
    51.  
      s, err := setting.NewSetting()
    52.  
      if err != nil {
    53.  
      return err
    54.  
      }
    55.  
      err = s.ReadSection("Database", &DatabaseSetting)
    56.  
      if err != nil {
    57.  
      return err
    58.  
      }
    59.  
       
    60.  
      err = s.ReadSection("Server", &ServerSetting)
    61.  
      if err != nil {
    62.  
      return err
    63.  
      }
    64.  
       
    65.  
      err = s.ReadSection("Log", &LogSetting)
    66.  
      if err != nil {
    67.  
      return err
    68.  
      }
    69.  
       
    70.  
      err = s.ReadSection("AccessLog", &AccessLogSetting)
    71.  
      if err != nil {
    72.  
      return err
    73.  
      }
    74.  
       
    75.  
      fmt.Println("setting:")
    76.  
      fmt.Println(ServerSetting)
    77.  
      fmt.Println(DatabaseSetting)
    78.  
      fmt.Println(LogSetting)
    79.  
      fmt.Println(AccessLogSetting)
    80.  
      return nil
    81.  
      }

    4,middelware/accesslog.go

    1.  
      package middleware
    2.  
       
    3.  
      import (
    4.  
      "bytes"
    5.  
      "github.com/liuhongdi/digv06/global"
    6.  
      "github.com/liuhongdi/digv06/pkg/util"
    7.  
      "time"
    8.  
      "github.com/gin-gonic/gin"
    9.  
      )
    10.  
       
    11.  
      type AccessLogWriter struct {
    12.  
      gin.ResponseWriter
    13.  
      body *bytes.Buffer
    14.  
      }
    15.  
       
    16.  
      func (w AccessLogWriter) Write(p []byte) (int, error) {
    17.  
      if n, err := w.body.Write(p); err != nil {
    18.  
      return n, err
    19.  
      }
    20.  
      return w.ResponseWriter.Write(p)
    21.  
      }
    22.  
       
    23.  
      func AccessLog() gin.HandlerFunc {
    24.  
      return func(c *gin.Context) {
    25.  
      bodyWriter := &AccessLogWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
    26.  
      c.Writer = bodyWriter
    27.  
       
    28.  
      beginTime := time.Now().UnixNano()
    29.  
      c.Next()
    30.  
      endTime := time.Now().UnixNano()
    31.  
      duration:=endTime-beginTime
    32.  
       
    33.  
      s := "%s %s "%s %s" "+
    34.  
      "%s %d %d %dµs "+
    35.  
      ""%s""
    36.  
       
    37.  
      layout := "2006-01-02 15:04:05"
    38.  
      timeNow := time.Now().Format(layout)
    39.  
       
    40.  
      global.AccessLogger.Infof(s,
    41.  
      util.GetRealIp(c),
    42.  
      timeNow,
    43.  
      c.Request.Method,
    44.  
      c.Request.RequestURI,
    45.  
      c.Request.Proto,
    46.  
      bodyWriter.Status(),
    47.  
      bodyWriter.body.Len(),
    48.  
      duration/1000,
    49.  
      c.Request.Header.Get("User-Agent"),
    50.  
       
    51.  
      );
    52.  
      }
    53.  
      }

    5,pkg/zaplog/zaplog.go

    1.  
      package zaplog
    2.  
       
    3.  
      import (
    4.  
      rotatelogs "github.com/lestrrat/go-file-rotatelogs"
    5.  
      "go.uber.org/zap"
    6.  
      "go.uber.org/zap/zapcore"
    7.  
      "io"
    8.  
      "time"
    9.  
      )
    10.  
       
    11.  
      //get logger
    12.  
      func GetInitLogger(filepath,infofilename,warnfilename,fileext string) (*zap.SugaredLogger,error) {
    13.  
      encoder := getEncoder()
    14.  
      //两个判断日志等级的interface
    15.  
      //warnlevel以下属于info
    16.  
      infoLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
    17.  
      return lvl < zapcore.WarnLevel
    18.  
      })
    19.  
      //warnlevel及以上属于warn
    20.  
      warnLevel := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
    21.  
      return lvl >= zapcore.WarnLevel
    22.  
      })
    23.  
       
    24.  
      infoWriter,err := getLogWriter(filepath+"/"+infofilename,fileext)
    25.  
      if (err != nil) {
    26.  
      return nil,err
    27.  
      }
    28.  
      warnWriter,err2 := getLogWriter(filepath+"/"+warnfilename,fileext)
    29.  
      if (err2 != nil) {
    30.  
      return nil,err2
    31.  
      }
    32.  
       
    33.  
      //创建具体的Logger
    34.  
      core := zapcore.NewTee(
    35.  
      zapcore.NewCore(encoder, infoWriter, infoLevel),
    36.  
      zapcore.NewCore(encoder, warnWriter, warnLevel),
    37.  
      )
    38.  
      loggerres := zap.New(core, zap.AddCaller())
    39.  
       
    40.  
      return loggerres.Sugar(),nil
    41.  
      }
    42.  
       
    43.  
      //get logger
    44.  
      func GetInitAccessLogger(filepath,filename,fileext string) (*zap.SugaredLogger,error) {
    45.  
       
    46.  
      warnWriter,err2 := getLogWriter(filepath+"/"+filename,fileext)
    47.  
      if (err2 != nil) {
    48.  
      return nil,err2
    49.  
      }
    50.  
       
    51.  
      var cfg zap.Config
    52.  
      cfg =zap.Config{
    53.  
      Level: zap.NewAtomicLevelAt(zap.DebugLevel),
    54.  
      Development: true,
    55.  
      Encoding: "console",
    56.  
      EncoderConfig: zapcore.EncoderConfig{
    57.  
      MessageKey: "msg",
    58.  
      },
    59.  
      OutputPaths: []string{"stdout", "./log.txt"},
    60.  
      ErrorOutputPaths: []string{"stderr"},
    61.  
      }
    62.  
       
    63.  
      l, err := cfg.Build(SetOutput(warnWriter, cfg))
    64.  
      if err != nil {
    65.  
      panic(err)
    66.  
      }
    67.  
       
    68.  
      return l.Sugar(),nil
    69.  
      }
    70.  
       
    71.  
       
    72.  
      func SetOutput(ws zapcore.WriteSyncer, conf zap.Config) zap.Option {
    73.  
      var enc zapcore.Encoder
    74.  
      switch conf.Encoding {
    75.  
      case "json":
    76.  
      enc = zapcore.NewJSONEncoder(conf.EncoderConfig)
    77.  
      case "console":
    78.  
      enc = zapcore.NewConsoleEncoder(conf.EncoderConfig)
    79.  
      default:
    80.  
      panic("unknown encoding")
    81.  
      }
    82.  
      return zap.WrapCore(func(core zapcore.Core) zapcore.Core {
    83.  
      return zapcore.NewCore(enc, ws, conf.Level)
    84.  
      })
    85.  
      }
    86.  
       
    87.  
       
    88.  
      //Encoder
    89.  
      func getEncoder() zapcore.Encoder {
    90.  
      encoderConfig := zap.NewProductionEncoderConfig()
    91.  
      encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
    92.  
      encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
    93.  
      return zapcore.NewConsoleEncoder(encoderConfig)
    94.  
      }
    95.  
       
    96.  
      //LogWriter
    97.  
      func getLogWriter(filePath,fileext string) (zapcore.WriteSyncer,error) {
    98.  
      warnIoWriter,err := getWriter(filePath,fileext)
    99.  
      if (err != nil) {
    100.  
      return nil,err
    101.  
      }
    102.  
      return zapcore.AddSync(warnIoWriter),nil
    103.  
      }
    104.  
       
    105.  
      //日志文件切割,按天
    106.  
      func getWriter(filename,fileext string) (io.Writer,error) {
    107.  
      // 保存30天内的日志,每24小时(整点)分割一次日志
    108.  
      hook, err := rotatelogs.New(
    109.  
      filename+"_%Y%m%d."+fileext,
    110.  
      rotatelogs.WithLinkName(filename),
    111.  
      rotatelogs.WithMaxAge(time.Hour*24*30),
    112.  
      rotatelogs.WithRotationTime(time.Hour*24),
    113.  
      )
    114.  
      /*
    115.  
      //供测试用,保存30天内的日志,每1分钟(整点)分割一次日志
    116.  
      hook, err := rotatelogs.New(
    117.  
      filename+"_%Y%m%d%H%M.log",
    118.  
      rotatelogs.WithLinkName(filename),
    119.  
      rotatelogs.WithMaxAge(time.Hour*24*30),
    120.  
      rotatelogs.WithRotationTime(time.Minute*1),
    121.  
      )
    122.  
      */
    123.  
      if err != nil {
    124.  
      //panic(err)
    125.  
      return nil,err
    126.  
      }
    127.  
      return hook,nil
    128.  
      }

    6,router/router.go

    1.  
      package router
    2.  
       
    3.  
      import (
    4.  
      "github.com/gin-gonic/gin"
    5.  
      "github.com/liuhongdi/digv06/controller"
    6.  
      "github.com/liuhongdi/digv06/global"
    7.  
      "github.com/liuhongdi/digv06/middleware"
    8.  
      "github.com/liuhongdi/digv06/pkg/result"
    9.  
      "runtime/debug"
    10.  
      )
    11.  
       
    12.  
      func Router() *gin.Engine {
    13.  
      router := gin.Default()
    14.  
      //处理异常
    15.  
      router.NoRoute(HandleNotFound)
    16.  
      router.NoMethod(HandleNotFound)
    17.  
      router.Use(middleware.AccessLog())
    18.  
      router.Use(Recover)
    19.  
       
    20.  
      // 路径映射
    21.  
      articlec:=controller.NewArticleController()
    22.  
      router.GET("/article/getone/:id", articlec.GetOne);
    23.  
      router.GET("/article/list", articlec.GetList);
    24.  
      return router
    25.  
      }
    26.  
       
    27.  
      //404
    28.  
      func HandleNotFound(c *gin.Context) {
    29.  
      global.Logger.Errorf("handle not found: %v", c.Request.RequestURI)
    30.  
      //global.Logger.Errorf("stack: %v",string(debug.Stack()))
    31.  
      result.NewResult(c).Error(404,"资源未找到")
    32.  
      return
    33.  
      }
    34.  
       
    35.  
      //500
    36.  
      func Recover(c *gin.Context) {
    37.  
      defer func() {
    38.  
      if r := recover(); r != nil {
    39.  
      //打印错误堆栈信息
    40.  
      //log.Printf("panic: %v ", r)
    41.  
      global.Logger.Errorf("panic: %v", r)
    42.  
      //log stack
    43.  
      global.Logger.Errorf("stack: %v",string(debug.Stack()))
    44.  
      //print stack
    45.  
      debug.PrintStack()
    46.  
      //return
    47.  
      result.NewResult(c).Error(500,"服务器内部错误")
    48.  
      }
    49.  
      }()
    50.  
      //继续后续接口调用
    51.  
      c.Next()
    52.  
      }

    7,main.go

    1.  
      package main
    2.  
       
    3.  
      import (
    4.  
      "github.com/gin-gonic/gin"
    5.  
      _ "github.com/jinzhu/gorm/dialects/mysql"
    6.  
      "github.com/liuhongdi/digv06/global"
    7.  
      "github.com/liuhongdi/digv06/router"
    8.  
      "log"
    9.  
      )
    10.  
       
    11.  
      //init
    12.  
      func init() {
    13.  
      //setting
    14.  
      err := global.SetupSetting()
    15.  
      if err != nil {
    16.  
      log.Fatalf("init.setupSetting err: %v", err)
    17.  
      }
    18.  
       
    19.  
      //logger
    20.  
      err = global.SetupLogger()
    21.  
      if err != nil {
    22.  
      log.Fatalf("init.SetupLogger err: %v", err)
    23.  
      }
    24.  
       
    25.  
      //access logger
    26.  
      err = global.SetupAccessLogger()
    27.  
      if err != nil {
    28.  
      log.Fatalf("init.SetupAccessLogger err: %v", err)
    29.  
      }
    30.  
       
    31.  
      //db
    32.  
      err = global.SetupDBLink()
    33.  
      if err != nil {
    34.  
      log.Fatalf("init.SetupLogger err: %v", err)
    35.  
      global.Logger.Fatalf("init.setupDBEngine err: %v", err)
    36.  
      }
    37.  
       
    38.  
      global.Logger.Infof("------应用init结束")
    39.  
      //global.Logger.
    40.  
      }
    41.  
       
    42.  
      func main() {
    43.  
      global.Logger.Infof("------应用main函数开始")
    44.  
      //设置运行模式
    45.  
      gin.SetMode(global.ServerSetting.RunMode)
    46.  
      //引入路由
    47.  
      r := router.Router()
    48.  
      //run
    49.  
      r.Run(":"+global.ServerSetting.HttpPort)
    50.  
      }

    8,其他代码请参见github

    四,测试效果

    访问url:

    http://127.0.0.1:8000/article/getone/1

    然后查看日志:

    1.  
      root@ku:/data/gologs/logs# more access_20201216.log
    2.  
      127.0.0.1 2020-12-16 15:41:08 "GET /article/getone/1" HTTP/1.1 200 571 13710µs "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0"
    3.  
      127.0.0.1 2020-12-16 15:41:11 "GET /article/getone/1" HTTP/1.1 200 571 2162µs "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0"
    4.  
      127.0.0.1 2020-12-16 15:41:16 "GET /article/getone/100" HTTP/1.1 200 52 770µs "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:83.0) Gecko/20100101 Firefox/83.0"

    五,查看各个库的版本:

    1.  
      module github.com/liuhongdi/digv06
    2.  
       
    3.  
      go 1.15
    4.  
       
    5.  
      require (
    6.  
      github.com/gin-gonic/gin v1.6.3
    7.  
      github.com/go-playground/universal-translator v0.17.0
    8.  
      github.com/go-playground/validator/v10 v10.2.0
    9.  
      github.com/jinzhu/gorm v1.9.16
    10.  
      github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f
    11.  
      github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect
    12.  
      github.com/magiconair/properties v1.8.4 // indirect
    13.  
      github.com/mitchellh/mapstructure v1.3.3 // indirect
    14.  
      github.com/pelletier/go-toml v1.8.1 // indirect
    15.  
      github.com/pkg/errors v0.9.1 // indirect
    16.  
      github.com/spf13/afero v1.4.1 // indirect
    17.  
      github.com/spf13/cast v1.3.1 // indirect
    18.  
      github.com/spf13/jwalterweatherman v1.1.0 // indirect
    19.  
      github.com/spf13/pflag v1.0.5 // indirect
    20.  
      github.com/spf13/viper v1.7.1
    21.  
      go.uber.org/multierr v1.6.0 // indirect
    22.  
      go.uber.org/zap v1.16.0
    23.  
      golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
    24.  
      golang.org/x/text v0.3.4 // indirect
    25.  
      gopkg.in/ini.v1 v1.62.0 // indirect
    26.  
      gopkg.in/yaml.v2 v2.3.0 // indirect
    27.  
      )
  • 相关阅读:
    基于WINCE6.0+S3C2443的camera驱动
    WINCE下基于SL031的RFID功能的实现
    WINCE6.0+S3C2443下的activesync问题
    WINCE6.0+S3C2443下WatchDog学习
    错误的抉择,痛悔
    WINCE5.0下SQL server compact版本更新
    做生意和打工的区别
    WINCE基于MC8630的3G模块的控制和加载
    Xml Tips
    VB.NET and C# Comparison
  • 原文地址:https://www.cnblogs.com/ExMan/p/14312182.html
Copyright © 2011-2022 走看看