zerolog

Hertz interfaces with zerolog and lumberjack.

Logger structure

var _ hlog.FullLogger = (*Logger)(nil)

type Logger struct {
	log     zerolog.Logger
	out     io.Writer
	level   zerolog.Level
	options []Opt
}

New

New returns a new Logger instance via the newLogger function

Function Signature:

func New(options ...Opt) *Logger

Sample code:

package main

import (
    "github.com/cloudwego/hertz/pkg/common/hlog"
    hertzZerolog "github.com/hertz-contrib/logger/zerolog"
)

func main() {
    hlog.SetLogger(hertzZerolog.New())
}

From

From returns a new Logger using an existing Logger via newLogger

Function Signature:

func From(log zerolog.Logger, options ...Opt) *Logger

Sample code:

package main

import (
    "bytes"
    hertzZerolog "github.com/hertz-contrib/logger/zerolog"
    "github.com/rs/zerolog"
)

func main() {
    b := &bytes.Buffer{}
    zl := zerolog.New(b).With().Str("key", "test").Logger()
    l := hertzZerolog.From(zl)
    l.Info("foo")
}

GetLogger

GetLogger returns the default Logger instance and error via the DefaultLogger() method

Function Signature:

func GetLogger() (Logger, error)

Sample code:

package main

import (
    "fmt"
    hertzZerolog "github.com/hertz-contrib/logger/zerolog"
)

func main() {
    logger, err := hertzZerolog.GetLogger()
    if err != nil {
        fmt.Printf("get logger failed")
    }
}

Unwrap

Unwrap returns the underlying zerolog logger

Function Signature:

func (l *Logger) Unwrap() zerolog.Logger

Sample code:

package main

import (
    "fmt"
    hertzZerolog "github.com/hertz-contrib/logger/zerolog"
)

func main() {
    logger, err := hertzZerolog.GetLogger()
    if err != nil {
        fmt.Printf("get logger failed")
    }
    l := logger.Unwrap()
}

Option configuration

WithOutput

WithOutput returns an Opt function through zerolog’s zerolog.Context.Logger().Output(out).With(), allowing to specify the output of the logger. By default, it is set to os.Stdout

Function Signature:

func WithOutput(out io.Writer) Opt 

Sample code:

package main

import (
    "bytes"
    hertzZerolog "github.com/hertz-contrib/logger/zerolog"
)

func main() {
    b := &bytes.Buffer{}
    l := hertzZerolog.New(hertzZerolog.WithOutput(b))
    l.Info("foobar")
}

WithLevel

WithLevel specifies the level of the logger through zerolog’s built-in zerolog.Context.Logger().Level(lvl).With() method. Convert hlog.Level to zerolog.level by matchHlogLevel(). By default it is set to WarnLevel

Function Signature:

func WithLevel(level hlog.Level) Opt 

Sample code:

package main

import (
    "github.com/cloudwego/hertz/pkg/common/hlog"
    hertzZerolog "github.com/hertz-contrib/logger/zerolog"
)

func main() {
    l := hertzZerolog.New(hertzZerolog.WithLevel(hlog.LevelDebug))
    l.Debug("foobar")
}

WithField

WithField adds a field to the logger’s context through zerolog’s zerolog.Context.Interface(name, value) method

Function Signature:

func WithField(name string, value interface{}) Opt 

Sample code:

package main

import (
    "bytes"
    "github.com/cloudwego/hertz/pkg/common/json"
    hertzZerolog "github.com/hertz-contrib/logger/zerolog"
)

func main() {
    b := &bytes.Buffer{}
    l := hertzZerolog.New(hertzZerolog.WithField("service", "logging"))
    l.SetOutput(b)

    l.Info("foobar")

    type Log struct {
        Level   string `json:"level"`
        Service string `json:"service"`
        Message string `json:"message"`
    }

    log := &Log{}

    err := json.Unmarshal(b.Bytes(), log)//log.service=="logging"
}

WithFields

WithFields adds some fields to the logger’s context through zerolog’s zerolog.Context.Fields(fields)

Function Signature:

func WithFields(fields map[string]interface{}) Opt 

Sample code:

package main

import (
    "bytes"
    hertzZerolog "github.com/hertz-contrib/logger/zerolog"
)

func main() {
    b := &bytes.Buffer{}
    l := hertzZerolog.New(hertzZerolog.WithFields(map[string]interface{}{
        "host": "localhost",
        "port": 8080,
    })) //...
}

WithTimestamp

WithTimestamp adds the timestamp field to the logger’s context through zerolog’s zerolog.Context.Timestamp()

Function Signature:

func WithTimestamp() Opt 

Sample code:

package main

import (
    hertzZerolog "github.com/hertz-contrib/logger/zerolog"
)

func main() {
    l := hertzZerolog.New(hertzZerolog.WithTimestamp())
    l.Info("foobar")
}

WithFormattedTimestamp

WithFormattedTimestamp is similar to WithTimestamp, adding a formatted timestamp field to the logger’s context

Function Signature:

func WithFormattedTimestamp(format string) Opt 

Sample code:

package main

import (
    hertzZerolog "github.com/hertz-contrib/logger/zerolog"
    "time"
)

func main() {
    l := hertzZerolog.New(hertzZerolog.WithFormattedTimestamp(time.RFC3339Nano))
    l.Info("foobar")
}

WithCaller

WithCaller adds a caller to the context of the logger through zerolog’s built-in zerolog.Context.Caller(), and the caller will report the caller’s information

Function Signature:

func WithCaller() Opt 

Sample code:

//get the last element of the path
package main

import (
    "bytes"
    "encoding/json"
    "github.com/hertz-contrib/logger/zerolog"
    "path/filepath"
    "strings"
)

func main() {
    b := &bytes.Buffer{}
    l := zerolog.New(zerolog.WithCaller())//add a caller
    l.SetOutput(b)
    l.Info("foobar")
    type Log struct {
        Level   string `json:"level"`
        Caller  string `json:"caller"`
        Message string `json:"message"`
    }

    log := &Log{}

    err := json.Unmarshal(b.Bytes(), log)
    if err!=nil {
        //...
    }

    segments := strings.Split(log.Caller, ":")
    filePath := filepath.Base(segments[0]) //filepath=="logger.go"
}

WithCallerSkipFrameCount

WithCallerSkipFrameCount adds caller to logger’s Context. CallerWithSkipFrameCount is a method of zerolog’s Context structure. It is used to add the caller’s file name and line number in the log record, and uses zerolog.CallerFieldName as key name

This method accepts a skipFrameCount parameter, which specifies the number of stack frames to skip to determine the correct caller location. If the skipFrameCount parameter is set to -1, the global CallerSkipFrameCount value is used

After calling the CallerWithSkipFrameCount method, a new Context structure is created, and the newCallerHook method is used to create a new hook and add it to the logger

Function Signature:

func WithCallerSkipFrameCount(skipFrameCount int) Opt 

Sample code:

package main

import (
    "github.com/hertz-contrib/logger/zerolog"
)

func main() {
    l := zerolog.New(zerolog.WithCallerSkipFrameCount(-1))
}

WithHook

WithHook adds a hook to the context of logger through zerolog’s zerolog.Context.Logger().Hook(hook).With()

Function Signature:

func WithHook(hook zerolog.Hook) Opt 

Sample code:

package main

import (
    hertzZerolog "github.com/hertz-contrib/logger/zerolog"
    "github.com/rs/zerolog"
)

type Hook struct {
    logs []HookLog
}
type HookLog struct {
    level   zerolog.Level
    message string
}

func main() {
    h := Hook{}
    l := hertzZerolog.New(hertzZerolog.WithHook(h))

    l.Info("Foo")
    l.Warn("Bar")
    //h.logs[0].level==zerolog.InfoLevel
    //h.logs[0].message=="Foo"
    //h.logs[1].level==zerolog.WarnLevel
    //h.logs[1].message=="Bar"
}

WithHookFunc

WithHookFunc is similar to WithHook, adding a hook function to the context of logger

Function Signature:

func WithHookFunc(hook zerolog.HookFunc) Opt

Sample code:

package main

import (
    hertzZerolog "github.com/hertz-contrib/logger/zerolog"
    "github.com/rs/zerolog"
)

type HookLog struct {
    level   zerolog.Level
    message string
}

func main() {
    logs := make([]HookLog, 0, 2)
    l := hertzZerolog.New(hertzZerolog.WithHookFunc(func(e *zerolog.Event, level zerolog.Level, message string) {
        logs = append(logs, HookLog{
            level:   level,
            message: message,
        })
    }))

    l.Info("Foo")
    l.Warn("Bar")
    //h.logs[0].level==zerolog.InfoLevel
    //h.logs[0].message=="Foo"
    //h.logs[1].level==zerolog.WarnLevel
    //h.logs[1].message=="Bar"
}

A complete logrus example

package main

import (
	"context"
	"log"
	"os"
	"path"
	"time"

	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/common/hlog"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
	hertzZerolog "github.com/hertz-contrib/logger/zerolog"
	"gopkg.in/natefinch/lumberjack.v2"
)

func main() {
	h := server.Default()

    // Customizable output directory.
	var logFilePath string
	dir := "./hlog"
	logFilePath = dir + "/logs/"
	if err := os.MkdirAll(logFilePath, 0o777); err != nil {
		log.Println(err.Error())
		return
	}

    // set filename to date
	logFileName := time.Now().Format("2006-01-02") + ".log"
	fileName := path.Join(logFilePath, logFileName)
	if _, err := os.Stat(fileName); err != nil {
		if _, err := os.Create(fileName); err != nil {
			log.Println(err.Error())
			return
		}
	}

	logger := hertzZerolog.New()
    // Provides compression and deletion
	lumberjackLogger := &lumberjack.Logger{
		Filename:   fileName,
		MaxSize:    20,   // A file can be up to 20M.
		MaxBackups: 5,    // Save up to 5 files at the same time
		MaxAge:     10,   // A file can be saved for up to 10 days.
		Compress:   true, // Compress with gzip.
	}

	logger.SetOutput(lumberjackLogger)
	logger.SetLevel(hlog.LevelDebug)

	hlog.SetLogger(logger)

	h.GET("/hello", func(ctx context.Context, c *app.RequestContext) {
		hlog.Info("Hello, hertz")
		c.String(consts.StatusOK, "Hello hertz!")
	})

	h.Spin()
}

For more details on how to adapt the interface of hlog, see hertz-contrib/logger/zerolog