Giter Club home page Giter Club logo

zap's Introduction

zap

Run Tests Go Report Card GoDoc Join the chat at https://gitter.im/gin-gonic/gin

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)
  }
}

zap's People

Contributors

appleboy avatar csbzy avatar dependabot[bot] avatar dreamacro avatar easonlin404 avatar erdnaxeli avatar hannes-tallied avatar kevin-lindsay-1 avatar linkinstars avatar ota42y avatar rymurr avatar sambitdash avatar thinkerou avatar tic8 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zap's Issues

need to set log level for specific path beside skipping

thank you for this valuable work it really works and saves us from log complexity of gin while using zap for the rest of the project

I guess it would be nice to have a feature to set the log level for specific path to something different from info which is default currently

for the workaround, I configure for skip and add log manually in the handler.

Make a release

As this repo has no any version, so any log line contains "caller":"[email protected]/zap.go:46"

Would you like please to make a tag so this string will be shorter or remove caller even

Ginzap does not log error from gin.Context

Hi,

I am using ginzap and trying to get it log to stdout error from gin.Context but it seems does not work. Can anyone help me point out where I use the middleware wrong.

package http

import (
	"net/http/httputil"
	"net/url"
	"time"

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

// Server is HTTP server of gateway service.
type Server struct {
	r    *gin.Engine
	addr string
}

// NewServer creates new instance of gateway HTTP server.
func NewServer(addr string,
	auth *httpsign.Authenticator,
	perm gin.HandlerFunc,
	logger *zap.Logger,
	options ...Option,
) (*Server, error) {
	r := gin.Default()
	r.Use(ginzap.Ginzap(logger, time.RFC3339, true))
	r.Use(ginzap.RecoveryWithZap(logger, true))
	r.Use(auth.Authenticated())

	server := Server{
		addr: addr,
		r:    r,
	}

	for _, opt := range options {
		if err := opt(&server); err != nil {
			return nil, err
		}
	}
	return &server, nil
}

// Start runs the HTTP gateway server.
func (svr *Server) Start() error {
	return svr.r.Run(svr.addr)
}

I test by requests and got 401 unauthorized. From httpsign.Authenticated function it return c.AbortWithError(err). I think it should put error to c.Context. However, ginzap only log Info instead log Error as expected. I cannot find where I used the middleware wrong yet.

Can't use wrapped logger as the logger

ZapLogger is defined as having Info and Error methods. I have a wrapper around zap that I want to pass in which works except line 116 of zap.go attempts to cast as a literal zap logger. Why have the interface if what you're going to do disallows it? My suggestions are to either expand the interface to add the Log method as part of it so you don't need to cast or instead of calling Error on line 120, call info.

For broken pipe, determine whether it can be encapsulated in the gin framework?

zap/zap.go

Line 72 in 062a8ac

if ne, ok := err.(*net.OpError); ok {

I have also seen such code snippets in the gin framework. Here you have done this again. Is it possible to encapsulate an IsBroken func instead of writing this every time.

Just like below:

// IsBroken check for a broken connection, as it is not really a
// condition that warrants a panic stack trace.
func IsBroken(err interface{}) bool {
	if ne, ok := err.(*net.OpError); ok {
		if se, ok := ne.Err.(*os.SyscallError); ok {
			errMsg := strings.ToLower(se.Error())
			debugPrintf("os syscall error:%s", errMsg)

			if strings.Contains(errMsg, "broken pipe") ||
				strings.Contains(errMsg, "reset by peer"){
				return true
			}
		}
	}

	return false
}

can't use otelzap as logger for Ginzap

otelzap is a OpenTelemetry instrumentations for zap logger.
as Ginzap want actually zap.Logger, we can't use the otelzap one,

the fix is actually simple, use interface instead of the zap.Logger struct.

Remove open-telemetry dependencies

@appleboy

I use mod tidy and it installs "go.opentelemetry.io/otel/trace" as an indirect dependency. I think this comes from the example folder.

How do I use this package without installing the opentelemetry package?

Custom fields in log statement?

Hi, I'm wondering if you'd consider a PR for adding functionality to make it possible to add custom fields to the log statement? E.g. by having something like:

func WithCustomFields(logger *zap.Logger,
	timeFormat string,
	utc bool,
	customFields ...func(c *gin.Context) zap.Field,
) gin.HandlerFunc {

...

Our use-case is that we need to log the return headers.

request body Custom Zap fields can't get body

PUT request,zap log output "body": "",but request body is json

r.Use(ginzap.GinzapWithConfig(logger.GetRootLogger().With(zap.String("component", "gin")), &ginzap.Config{
	TimeFormat: logger.LogTimeFmt,
	UTC:        false,
	Context: func(c *gin.Context) []zapcore.Field {
		var fields []zapcore.Field

		// 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
	}}))

how to display console recovery log with RecoveryWithZap()

Hi, When I use default gin.Recovery(), I can set display error into console and write log file:

gin.DefaultErrorWriter = io.MultiWriter(logger, os.Stderr)
r.Use(gin.Recovery())

Now,When I use RecoveryWithZap(), it will only write log into the zap logger file,how to display console recovery log too?

Thanks, Best Wishes.

GinzapWithConfig is not available in v0.0.1

I don't see the GinzapWithConfig in zap.go when I go get github.com/gin-contrib/zap

I am trying to use the skipPaths options but the GinzapWithConfig isn't there

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.