Gin Framework – Part 10

The framework comes with

The Gin framework has its own logging function, which records log information through fmt.Fprint and fmt.Fprintf. The default is to write the log to the terminal, which can be set to write to a file gin.DefaultWriter.

Terminal disable color

gin.DisableConsoleColor()

Only write to file

package main

import (
	"fmt"
	"io"
	"os"

	"github.com/gin-gonic/gin"
)

// Logging uses
func main() {
	// Disable console color, no console color is required when writing logs to file
	gin.DisableConsoleColor()
	// Log to specified file
	f, _ := os.Create("gin.log")
	gin.DefaultWriter = io.MultiWriter(f)
	// create container
	engine := gin.Default()
	engine.GET("/log", func(context *gin.Context) {
		// log
		fmt.Fprint(gin.DefaultWriter, "[gin-log] log test use \n ")
		fmt.Fprintf(gin.DefaultWriter, "[gin-log] Method: %v \n ", context.Request.Method)
		fmt.Fprintf(gin.DefaultWriter, "[gin-log] Url: %v \n ", context.Request.URL)
		fmt.Fprintf(gin.DefaultWriter, "[gin-log] Header: %v \n", context.Request.Header)
		context.JSON(200, gin.H{"msg": "success"})
	})
	// start the service
	_ = engine.Run(":9090")
}
  • Request Effect
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /log                      --> main.main.func1 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on :9090
[gin-log] log test use 
 [gin-log] Method: GET 
 [gin-log] Url: /log 
 [gin-log] Header: map[Accept:[text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8] Accept-Encoding:[gzip, deflate] Accept-Language:[en-US,en;q=0.5] Connection:[keep-alive] Cookie:[_ga=GA1.1.141663794.1636793811] Sec-Fetch-Dest:[document] Sec-Fetch-Mode:[navigate] Sec-Fetch-Site:[none] Sec-Fetch-User:[?1] Upgrade-Insecure-Requests:[1] User-Agent:[Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:97.0) Gecko/20100101 Firefox/97.0]] 
[GIN] 2021/06/30 - 14:49:16 | 200 |     182.996µs |       127.0.0.1 | GET      "/log"
  • Write to file and terminal at the same time

If you want to write the log to the file and the terminal at the same time, you only need to modify the above code io.MultiWriter(f) and modify it to the following content.

func main(){
    ... 
	f,_ := os.Create("gin.log")
  	// write log to both file and console
	gin.DefaultWriter = io.MultiWriter(f, os.Stdout)
	... 
	// start the service
	_ = engine.Run(":9090")
}

Integrate logrus

logrus is probably the most popular Go third-party logging library out there. At the moment, it has 17.9K Github Stars, and the source code is available here.

Installation

go get -u github.com/sirupsen/logrus

Setting properties

Create a logrus_use.go file and write the code as follows:

package main

import (
	"os"
	"path"

	"github.com/sirupsen/logrus"
)

var (
	logPath = "./log"
	logFile = "gin.log"
)
var LogInstance = logrus.New()

func init() {
	// open a file
	logFileName := path.Join(logPath, logFile)
	fileWriter, err := os.OpenFile(logFileName, os.O_APPEND|os.O_WRONLY|os.O_CREATE, os.ModePerm)
	if err != nil {
		panic(err)
	}
	// Set log output to file
	LogInstance.SetOutput(fileWriter)
	// Set log output format
	LogInstance.SetFormatter(&logrus.JSONFormatter{})
	// Set logging level
	LogInstance.SetLevel(logrus.DebugLevel)
}

Use

package main

import (
	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
)

func main() {
	engine := gin.Default()
	engine.GET("/log", func(context *gin.Context) {
		// Warning level log
		LogInstance.WithFields(logrus.Fields{
			"Method": context.Request.Method,
		}).Warning("Warning level log")

		// Error level log
		LogInstance.WithFields(logrus.Fields{
			"param-name": context.DefaultQuery("name", ""),
		}).Error("Error level log")

		if context.DefaultQuery("key", "") == "1" {
			// Fatal level log (such logs will end the service)
			LogInstance.WithFields(logrus.Fields{
				"Host": context.Request.Host,
			}).Fatal("Fatal level log")
		}

		// Info level log
		LogInstance.WithFields(logrus.Fields{
			"code":   context.Writer.Status(),
			"url":    context.Request.URL.Path,
			"method": context.Request.Method,
		}).Info(" info level log")
		context.JSON(200, gin.H{"msg": " success"})
	})
	_ = engine.Run(":9090")
}

Log content

log file: log/gin.log

JSON format

{"Method":"GET","level":"warning","msg":"Warning level log","time":"2021-06-30T16:06:30+08:00"}
{"level":"error","msg":"Error level log","param-name":"","time":"2021-06-60T16:06:30+08:00"}
{"code":200,"level":"info","method":"GET","msg":"Info level log","time":"2021-06-30T16:06:30+08:00","url":"/log"}

Text format

time="2021-06-30T16:35:16+08:00" level=warning msg="Warning level log" Method=GET
time= "2021-06-30T16:35:16+08:00" level=error msg="Error level log" param-name=
time= "2021-06-30T16:35:16+08:00" level=info msg="Info level log" code=200 method=GET url=/log

Compression scrolling

Lumberjack is a Go package for writing logs to rolling files.

Installation

go get -u github.com/natefinch/lumberjack

Import

import "github.com/natefinch/lumberjack"

Use

Modify logrus_use.go file code

package main

import (
	"path"

	"github.com/natefinch/lumberjack"
	"github.com/sirupsen/logrus"
)

var (
	logPath = "./log"
	logFile = "gin.log"
)

var LogInstance = logrus.New()

// log initialization
func init() {
	// open file
	logFileName := path.Join(logPath, logFile)
	// log with rolling compression
	rolling(logFileName)
	// set log output JSON Format
	//LogInstance.SetFormatter(&logrus.JSONFormatter{})
	LogInstance.SetFormatter(&logrus.TextFormatter{})
	//Set logging level
	LogInstance.SetLevel(logrus.DebugLevel)
}

// Log rolling settings
func rolling(logFile string) {
	// Set the output
	LogInstance.SetOutput(&lumberjack.Logger{
		Filename:   logFile, // Log file location
		MaxSize:    1,       // Maximum capacity of a single file, in MB
		MaxBackups: 3,       // The maximum number of retained expired files
		MaxAge:     1,       // The maximum time interval for retaining expired files, the unit is days
		Compress:   true,    // Whether to compress the rolling log, use gzip compression
	})
}
  • main.go code
package main

import (
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
)

func main() {
	engine := gin.Default()
	engine.GET("/log", func(context *gin.Context) {
		// Warning level log
		LogInstance.WithFields(logrus.Fields{
			"Method": context.Request.Method,
		}).Warning("Warning level log")

		// Error level log
		LogInstance.WithFields(logrus.Fields{
			"param-name": context.DefaultQuery("name", ""),
		}).Error("Error level log")

		if context.DefaultQuery("key", "") == "1" {
			// Fatal level log (such logs will end the service)
			LogInstance.WithFields(logrus.Fields{
				"Host": context.Request.Host,
			}).Fatal("Fatal level log")
		}

		// Info level log
		LogInstance.WithFields(logrus.Fields{
			"code":    context.Writer.Status(),
			"url":     context.Request.URL.Path,
			"context": strings.Repeat("Test", 50000), // repeat
		}).Info("info level log")
		context.JSON(200, gin.H{"msg": "success"})
	})
	_ = engine.Run(":9090")
}
  • Put it all together
package main

import (
	"path"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/natefinch/lumberjack"
	"github.com/sirupsen/logrus"
)

var (
	logPath = "./log"
	logFile = "gin.log"
)

var LogInstance = logrus.New()

// log initialization
func init() {
	// open file
	logFileName := path.Join(logPath, logFile)
	// log with rolling compression
	rolling(logFileName)
	// set log output JSON Format
	//LogInstance.SetFormatter(&logrus.JSONFormatter{})
	LogInstance.SetFormatter(&logrus.TextFormatter{})
	//Set logging level
	LogInstance.SetLevel(logrus.DebugLevel)
}

// Log rolling settings
func rolling(logFile string) {
	// Set the output
	LogInstance.SetOutput(&lumberjack.Logger{
		Filename:   logFile, // Log file location
		MaxSize:    1,       // Maximum capacity of a single file, in MB
		MaxBackups: 3,       // The maximum number of retained expired files
		MaxAge:     1,       // The maximum time interval for retaining expired files, the unit is days
		Compress:   true,    // Whether to compress the rolling log, use gzip compression
	})
}

func main() {

	engine := gin.Default()

	engine.GET("/log", func(context *gin.Context) {
		// Warning level log
		LogInstance.WithFields(logrus.Fields{
			"Method": context.Request.Method,
		}).Warning("Warning level log")

		// Error level log
		LogInstance.WithFields(logrus.Fields{
			"param-name": context.DefaultQuery("name", ""),
		}).Error("Error level log")

		if context.DefaultQuery("key", "") == "1" {
			// Fatal level log (such logs will end the service)
			LogInstance.WithFields(logrus.Fields{
				"Host": context.Request.Host,
			}).Fatal("Fatal level log")
		}

		// Info level log
		LogInstance.WithFields(logrus.Fields{
			"code":    context.Writer.Status(),
			"url":     context.Request.URL.Path,
			"context": strings.Repeat("Test", 50000), // repeat
		}).Info("info level log")
		context.JSON(200, gin.H{"msg": "success"})
	})
	_ = engine.Run(":9090")
}

Effects

Hori Systems – Go Gin Framework – Part 1