ginzap

package module
v1.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 23, 2024 License: MIT Imports: 11 Imported by: 305

README

zap

Run Tests Go Report Card GoDoc

Alternative logging through zap. Thanks for Pull Request from @yezooz

Requirement

Require Go 1.19 or later.

Usage

Start using it

Download and install it:

go get github.com/gin-contrib/zap

Import it in your code:

import "github.com/gin-contrib/zap"

Example

See the example.

package main

import (
  "fmt"
  "time"

  ginzap "github.com/gin-contrib/zap"
  "github.com/gin-gonic/gin"
  "go.uber.org/zap"
)

func main() {
  r := gin.New()

  logger, _ := zap.NewProduction()

  // Add a ginzap middleware, which:
  //   - Logs all requests, like a combined access and error log.
  //   - Logs to stdout.
  //   - RFC3339 with UTC time format.
  r.Use(ginzap.Ginzap(logger, time.RFC3339, true))

  // Logs all panic to error log
  //   - stack means whether output the stack info.
  r.Use(ginzap.RecoveryWithZap(logger, true))

  // Example ping request.
  r.GET("/ping", func(c *gin.Context) {
    c.String(200, "pong "+fmt.Sprint(time.Now().Unix()))
  })

  // Example when panic happen.
  r.GET("/panic", func(c *gin.Context) {
    panic("An unexpected error happen!")
  })

  // Listen and Server in 0.0.0.0:8080
  r.Run(":8080")
}

Skip logging

When you want to skip logging for specific path, please use GinzapWithConfig

r.Use(GinzapWithConfig(utcLogger, &Config{
  TimeFormat: time.RFC3339,
  UTC: true,
  SkipPaths: []string{"/no_log"},
}))

Custom Zap fields

example for custom log request body, response request ID or log Open Telemetry TraceID.

func main() {
  r := gin.New()

  logger, _ := zap.NewProduction()

  r.Use(ginzap.GinzapWithConfig(logger, &ginzap.Config{
    UTC:        true,
    TimeFormat: time.RFC3339,
    Context: ginzap.Fn(func(c *gin.Context) []zapcore.Field {
      fields := []zapcore.Field{}
      // log request ID
      if requestID := c.Writer.Header().Get("X-Request-Id"); requestID != "" {
        fields = append(fields, zap.String("request_id", requestID))
      }

      // log trace and span ID
      if trace.SpanFromContext(c.Request.Context()).SpanContext().IsValid() {
        fields = append(fields, zap.String("trace_id", trace.SpanFromContext(c.Request.Context()).SpanContext().TraceID().String()))
        fields = append(fields, zap.String("span_id", trace.SpanFromContext(c.Request.Context()).SpanContext().SpanID().String()))
      }

      // log request body
      var body []byte
      var buf bytes.Buffer
      tee := io.TeeReader(c.Request.Body, &buf)
      body, _ = io.ReadAll(tee)
      c.Request.Body = io.NopCloser(&buf)
      fields = append(fields, zap.String("body", string(body)))

      return fields
    }),
  }))

  // Example ping request.
  r.GET("/ping", func(c *gin.Context) {
    c.Writer.Header().Add("X-Request-Id", "1234-5678-9012")
    c.String(200, "pong "+fmt.Sprint(time.Now().Unix()))
  })

  r.POST("/ping", func(c *gin.Context) {
    c.Writer.Header().Add("X-Request-Id", "9012-5678-1234")
    c.String(200, "pong "+fmt.Sprint(time.Now().Unix()))
  })

  // Listen and Server in 0.0.0.0:8080
  if err := r.Run(":8080"); err != nil {
    panic(err)
  }
}

Custom skipper function

Example for custom skipper function

r.Use(GinzapWithConfig(logger, &Config{
  TimeFormat: time.RFC3339,
  UTC: true,
  Skipper: func(c *gin.Context) bool {
    return c.Request.URL.Path == "/ping" && c.Request.Method == "GET"
  },
}))

Full example

package main

import (
  "fmt"
  "time"

  ginzap "github.com/gin-contrib/zap"

  "github.com/gin-gonic/gin"
  "go.uber.org/zap"
)

func main() {
  r := gin.New()

  logger, _ := zap.NewProduction()

  r.Use(ginzap.GinzapWithConfig(logger, &ginzap.Config{
    UTC:        true,
    TimeFormat: time.RFC3339,
    Skipper: func(c *gin.Context) bool {
      return c.Request.URL.Path == "/ping" && c.Request.Method == "GET"
    },
  }))

  // Example ping request.
  r.GET("/ping", func(c *gin.Context) {
    c.Writer.Header().Add("X-Request-Id", "1234-5678-9012")
    c.String(200, "pong "+fmt.Sprint(time.Now().Unix()))
  })

  r.POST("/ping", func(c *gin.Context) {
    c.Writer.Header().Add("X-Request-Id", "9012-5678-1234")
    c.String(200, "pong "+fmt.Sprint(time.Now().Unix()))
  })

  // Listen and Server in 0.0.0.0:8080
  if err := r.Run(":8080"); err != nil {
    panic(err)
  }
}

Custom SkipPathRegexps function

Example for custom SkipPathRegexps function

rxURL := regexp.MustCompile(`^/ping\s*`)
r.Use(ginzap.GinzapWithConfig(logger, &ginzap.Config{
  UTC:             true,
  TimeFormat:      time.RFC3339,
  SkipPathRegexps: []*regexp.Regexp{rxURL},
}))

Full example

package main

import (
  "fmt"
  "regexp"
  "time"

  ginzap "github.com/gin-contrib/zap"

  "github.com/gin-gonic/gin"
  "go.uber.org/zap"
)

func main() {
  r := gin.New()

  logger, _ := zap.NewProduction()
  rxURL := regexp.MustCompile(`^/ping\s*`)

  r.Use(ginzap.GinzapWithConfig(logger, &ginzap.Config{
    UTC:             true,
    TimeFormat:      time.RFC3339,
    SkipPathRegexps: []*regexp.Regexp{rxURL},
  }))

  // Example ping request.
  r.GET("/ping1234", func(c *gin.Context) {
    c.String(200, "pong "+fmt.Sprint(time.Now().Unix()))
  })

  // Listen and Server in 0.0.0.0:8080
  if err := r.Run(":8080"); err != nil {
    panic(err)
  }
}

Documentation

Overview

Package ginzap provides log handling using zap package. Code structure based on ginrus package.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CustomRecoveryWithZap added in v0.1.0

func CustomRecoveryWithZap(logger ZapLogger, stack bool, recovery gin.RecoveryFunc) gin.HandlerFunc

CustomRecoveryWithZap returns a gin.HandlerFunc (middleware) with a custom recovery handler that recovers from any panics and logs requests using uber-go/zap. All errors are logged using zap.Error(). stack means whether output the stack info. The stack info is easy to find where the error occurs but the stack info is too large.

func Ginzap

func Ginzap(logger ZapLogger, timeFormat string, utc bool) gin.HandlerFunc

Ginzap returns a gin.HandlerFunc (middleware) that logs requests using uber-go/zap.

Requests with errors are logged using zap.Error(). Requests without errors are logged using zap.Info().

It receives:

  1. A time package format string (e.g. time.RFC3339).
  2. A boolean stating whether to use UTC time zone or local.

func GinzapWithConfig added in v0.0.2

func GinzapWithConfig(logger ZapLogger, conf *Config) gin.HandlerFunc

GinzapWithConfig returns a gin.HandlerFunc using configs

func RecoveryWithZap

func RecoveryWithZap(logger ZapLogger, stack bool) gin.HandlerFunc

RecoveryWithZap returns a gin.HandlerFunc (middleware) that recovers from any panics and logs requests using uber-go/zap. All errors are logged using zap.Error(). stack means whether output the stack info. The stack info is easy to find where the error occurs but the stack info is too large.

Types

type Config added in v0.0.2

type Config struct {
	TimeFormat      string
	UTC             bool
	SkipPaths       []string
	SkipPathRegexps []*regexp.Regexp
	Context         Fn
	DefaultLevel    zapcore.Level
	// skip is a Skipper that indicates which logs should not be written.
	// Optional.
	Skipper Skipper
}

Config is config setting for Ginzap

type Fn added in v0.1.0

type Fn func(c *gin.Context) []zapcore.Field

type Skipper added in v1.0.0

type Skipper func(c *gin.Context) bool

Skipper is a function to skip logs based on provided Context

type ZapLogger added in v0.2.0

type ZapLogger interface {
	Info(msg string, fields ...zap.Field)
	Error(msg string, fields ...zap.Field)
}

ZapLogger is the minimal logger interface compatible with zap.Logger

Directories

Path Synopsis
_example

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL