首页 > 系统相关 >高性能、零内存分配的Go日志库--Zerolog

高性能、零内存分配的Go日志库--Zerolog

时间:2025-01-14 19:12:17浏览次数:1  
标签:log -- zerolog time Go 日志 Zerolog message op

简介

Zerolog 是一个高性能、零内存分配的 Go 日志库。

它为不需要垃圾回收的延迟敏感型应用程序提供结构化日志记录功能。

您可以以完全零分配的方式使用,这样在初始化记录器对象后,堆上不会再分配其他对象,从而防止触发垃圾回收。

Zerolog 包提供了一个专用于 JSON 输出的快速而简单的记录器。

Zerolog 的 API 旨在提供出色的开发人员体验和惊人的性能。其独特的链接 API 允许 zerolog 通过避免分配和反射来编写 JSON(或 CBOR)日志事件。

Uber 的zap库率先采用了这种方法。Zerolog 正在通过更易于使用的 API 和更出色的性能将这一概念提升到一个新的水平。

为了保持代码库和 API 的简单性,zerolog 只专注于高效的结构化日志记录。使用提供的(但效率低下)可以实现控制台上的漂亮日志记录zerolog.ConsoleWriter

官网

https://github.com/rs/zerolog

性能

基准测试结果

所有操作均无需分配(这些数字包括JSON 编码)

BenchmarkLogEmpty-8        100000000    19.1 ns/op     0 B/op       0 allocs/op
BenchmarkDisabled-8        500000000    4.07 ns/op     0 B/op       0 allocs/op
BenchmarkInfo-8            30000000     42.5 ns/op     0 B/op       0 allocs/op
BenchmarkContextFields-8   30000000     44.9 ns/op     0 B/op       0 allocs/op
BenchmarkLogFields-8       10000000     184 ns/op      0 B/op       0 allocs/op

记录一条消息和 10 个字段:

Library Time Bytes Allocated Objects Allocated
zerolog 767 ns/op 552 B/op 6 allocs/op
⚡ zap 848 ns/op 704 B/op 2 allocs/op
⚡ zap (sugared) 1363 ns/op 1610 B/op 20 allocs/op
go-kit 3614 ns/op 2895 B/op 66 allocs/op
lion 5392 ns/op 5807 B/op 63 allocs/op
logrus 5661 ns/op 6092 B/op 78 allocs/op
apex/log 15332 ns/op 3832 B/op 65 allocs/op
log15 20657 ns/op 5632 B/op 93 allocs/op

使用已经有10个上下文字段的记录器记录消息:

Library Time Bytes Allocated Objects Allocated
zerolog 52 ns/op 0 B/op 0 allocs/op
⚡ zap 283 ns/op 0 B/op 0 allocs/op
⚡ zap (sugared) 337 ns/op 80 B/op 2 allocs/op
lion 2702 ns/op 4074 B/op 38 allocs/op
go-kit 3378 ns/op 3046 B/op 52 allocs/op
logrus 4309 ns/op 4564 B/op 63 allocs/op
apex/log 13456 ns/op 2898 B/op 51 allocs/op
log15 14179 ns/op 2642 B/op 44 allocs/op

记录一个静态字符串,没有任何上下文或printf风格的模板:

Library Time Bytes Allocated Objects Allocated
zerolog 50 ns/op 0 B/op 0 allocs/op
⚡ zap 236 ns/op 0 B/op 0 allocs/op
standard library 453 ns/op 80 B/op 2 allocs/op
⚡ zap (sugared) 337 ns/op 80 B/op 2 allocs/op
go-kit 508 ns/op 656 B/op 13 allocs/op
lion 771 ns/op 1224 B/op 10 allocs/op
logrus 1244 ns/op 1505 B/op 27 allocs/op
apex/log 2751 ns/op 584 B/op 11 allocs/op
log15 5181 ns/op 1592 B/op 26 allocs/op

安装

go get -u github.com/rs/zerolog/log

缺省Logger

简单打印

package main

import (
	"github.com/rs/zerolog/log"
)

func main() {
	// 简单打印 默认是debug级别
	log.Print("Hello, World!") // {"level":"debug","time":"2025-01-14T16:35:03+08:00","message":"Hello, World!"}
}

带日志级别的日志

package main

import (
	"github.com/rs/zerolog/log"
)

func main() {
	// 带日志级别的日志
	// debug
	log.Debug().Msg("Hello, World!") // {"level":"debug","time":"2025-01-14T16:35:03+08:00","message":"Hello, World!"}
	// info
	log.Info().Msg("Hello, World!") // {"level":"info","time":"2025-01-14T16:35:03+08:00","message":"Hello, World!"}
	// warn
	log.Warn().Msg("Hello, World!") // {"level":"warn","time":"2025-01-14T16:35:03+08:00","message":"Hello, World!"}
	// error
	log.Error().Msg("Hello, World!") // {"level":"error","time":"2025-01-14T16:35:03+08:00","message":"Hello, World!"}
}

设置日志级别

级别

  • panic (zerolog.PanicLevel, 5)
  • fatal (zerolog.FatalLevel, 4)
  • error (zerolog.ErrorLevel, 3)
  • warn (zerolog.WarnLevel, 2)
  • info (zerolog.InfoLevel, 1)
  • debug (zerolog.DebugLevel, 0)
  • trace (zerolog.TraceLevel, -1)
package main

import (
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
)

func main() {
	// 设置日志级别
	log.Logger = log.Level(zerolog.InfoLevel)
	log.Debug().Msg("Hello, World!") // 不会输出
	log.Info().Msg("Hello, World!")  // {"level":"info","time":"2025-01-14T16:35:03+08:00","message":"Hello, World!"}
	log.Warn().Msg("Hello, World!")  // {"level":"warn","time":"2025-01-14T16:35:03+08:00","message":"Hello, World!"}
}

设置时间格式

package main

import (
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
)

func main() {
	log.Info().Msg("Hello, World!") // {"level":"info","time":"2025-01-14T16:46:32+08:00","message":"Hello, World!"}
	// 设置时间格式
	zerolog.TimeFieldFormat = "2006/01/02T15:04:05.9999"
	log.Info().Msg("Hello, World!") // {"level":"info","time":"2025/01/14T16:46:32.6623","message":"Hello, World!"}
	// 设置时间为时间戳
	zerolog.TimeFieldFormat = zerolog.TimeFormatUnixMs
	log.Info().Msg("Hello, World!") // {"level":"info","time":1736844392662,"message":"Hello, World!"}
}

自定义添加字段

在输出日志的时候添加字段

package main

import (
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"time"
)

func main() {
	// 在输出日志的时候添加字符串字段
	log.Info().Str("foo", "bar").Msg("Hello, World!")
	// {"level":"info","foo":"bar","time":"2025-01-14T16:56:49+08:00","message":"Hello, World!"}
	// 在输出日志的时候添加整型字段
	log.Info().Int("foo", 1).Msg("Hello, World!")
	// {"level":"info","foo":1,"time":"2025-01-14T16:56:49+08:00","message":"Hello, World!"}
	// 在输出日志的时候添加浮点型字段
	log.Info().Float64("foo", 1.2).Msg("Hello, World!")
	// {"level":"info","foo":1.2,"time":"2025-01-14T16:56:49+08:00","message":"Hello, World!"}
	// 在输出日志的时候添加布尔型字段
	log.Info().Bool("foo", true).Msg("Hello, World!")
	// {"level":"info","foo":true,"time":"2025-01-14T16:56:49+08:00","message":"Hello, World!"}
	// 在输出日志的时候添加时间字段
	log.Info().Time("foo", time.Now()).Msg("Hello, World!")
	// {"level":"info","foo":"2025-01-14T16:56:49+08:00","time":"2025-01-14T16:56:49+08:00","message":"Hello, World!"}
	// 在输出日志的时候字典
	log.Info().Dict("foo", zerolog.Dict().Str("bar", "baz")).Msg("Hello, World!")
	// {"level":"info","foo":{"bar":"baz"},"time":"2025-01-14T16:56:49+08:00","message":"Hello, World!"}
}

将上下文字段添加到全局记录器

package main

import (
	"github.com/rs/zerolog/log"
)

func main() {
     // 修改logger,在全局中添加字段,后面的日志都会携带这个字段
	log.Logger = log.With().Str("foo", "bar").Logger()
	log.Info().Msg("Hello, World!")
	// {"level":"info","foo":"bar","time":"2025-01-14T17:01:58+08:00","message":"Hello, World!"}
}

无级别或者无消息日志

无消息

package main

import (
	"github.com/rs/zerolog/log"
)

func main() {
	log.Info().Msg("") // {"level":"info","time":"2025-01-14T16:49:28+08:00"}
	log.Info().Send()  // {"level":"info","time":"2025-01-14T16:49:28+08:00"}
}

无级别

package main

import (
	"github.com/rs/zerolog/log"
)

func main() {
	log.Log().Msg("") // {"time":"2025-01-14T16:49:28+08:00"}
	log.Log().Send()  // {"time":"2025-01-14T16:49:28+08:00"}
}

错误日志

package main

import (
	"fmt"
	"github.com/rs/zerolog/log"
)

func main() {
	// 普通错误日志
	log.Error().Msg("Hello World") // {"level":"error","time":"2025-01-14T17:05:07+08:00","message":"Hello World"}

	// 创建异常对象
	err := fmt.Errorf("Hello World")

	// 直接传出异常对象生成日志 有error字段,没有 message 字段的错误日志
	log.Err(err).Send()
	// {"level":"error","error":"Hello World","time":"2025-01-14T17:05:07+08:00"}

	// 直接传出异常对象生成日志 有error字段,有 message 字段的错误日志
	log.Err(err).Msg("这是错误消息的message字段值")
	// {"level":"error","error":"Hello World","time":"2025-01-14T17:05:07+08:00","message":"这是错误消息的message字段值"}
}

将文件和行号添加到日志中

长文件名格式

package main

import (
	"github.com/rs/zerolog/log"
)

func main() {
	log.Logger = log.With().Caller().Logger()
	log.Info().Msg("hello world")

	// Output: {"level": "info", "message": "hello world", "caller": "/go/src/your_project/main.go:21"}
}

短文件格式

package main

import (
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"path/filepath"
	"strconv"
)

func main() {
	// 设置 行号序列化的方法
	zerolog.CallerMarshalFunc = func(pc uintptr, file string, line int) string {
		return filepath.Base(file) + ":" + strconv.Itoa(line)
	}
	log.Logger = log.With().Caller().Logger()
	log.Info().Msg("hello world")

	// Output: {"level": "info", "message": "hello world", "caller": "main.go:21"}
}

根据上下文传递子Logger

package main

import (
	"context"
	"github.com/rs/zerolog/log"
)

func main() {
	ctx := log.With().Str("component", "module").Logger().WithContext(context.TODO())

	log.Ctx(ctx).Info().Msg("hello world")

	// Output: {"component":"module","level":"info","message":"hello world"}
}

自定义主要字段的名称

package main

import (
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
)

func main() {
	zerolog.TimestampFieldName = "t"
	zerolog.LevelFieldName = "l"
	zerolog.MessageFieldName = "m"

	log.Info().Msg("hello world")

	// Output: {"l":"info","t":"2025-01-14T17:38:55+08:00","m":"hello world"}
}

集成 net/http

package main

import (
    "github.com/rs/zerolog"
    "github.com/rs/zerolog/hlog"
    "github.com/justinas/alice"
    "net/http"
    "os"
    "time"
)

func main() {
    host := "local-hostname" // 定义主机名

    // 初始化 zerolog,添加时间戳和服务信息
    log := zerolog.New(os.Stdout).With().
        Timestamp().
        Str("role", "my-service").
        Str("host", host).
        Logger()

    // 创建 alice 中间件链
    c := alice.New()

    // 添加日志处理器,将日志输出到控制台
    c = c.Append(hlog.NewHandler(log))

    // 添加访问日志处理器,记录请求方法、URL、状态码、响应大小和耗时
    c = c.Append(hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
        hlog.FromRequest(r).Info().
            Str("method", r.Method).
            Str("url", r.URL.String()).
            Int("status", status).
            Int("size", size).
            Dur("duration", duration).
            Msg("")
    }))

    // 添加额外日志处理器,记录客户端 IP、User-Agent、Referer 和请求 ID
    c = c.Append(hlog.RemoteAddrHandler("ip"))
    c = c.Append(hlog.UserAgentHandler("user_agent"))
    c = c.Append(hlog.RefererHandler("referer"))
    c = c.Append(hlog.RequestIDHandler("req_id", "Request-Id"))

    // 最终请求处理器,记录自定义日志信息
    h := c.Then(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        hlog.FromRequest(r).Info().
            Str("user", "current user").
            Str("status", "ok").
            Msg("Something happened")
    }))

    // 注册路由并启动 HTTP 服务
    http.Handle("/", h)
    if err := http.ListenAndServe(":8080", nil); err != nil {
        log.Fatal().Err(err).Msg("Startup failed") // 如果启动失败,记录错误日志
    }
}
  1. 初始化日志:使用 zerolog 初始化日志,并添加时间戳和服务信息。
  2. 中间件链:使用 alice 创建中间件链,逐步添加日志处理器。
  3. 访问日志:记录每个请求的关键信息,如方法、URL、状态码等。
  4. 额外日志:记录客户端 IP、User-Agent、Referer 和请求 ID,丰富日志内容。
  5. 请求处理:在请求处理函数中记录自定义日志信息。
  6. 启动服务:注册路由并启动 HTTP 服务,处理启动失败的情况。

运行后,只要有访问8080端口的,都会打印日志

{
  "level": "info",
  "role": "my-service",
  "host": "local-hostname",
  "ip": "127.0.0.1:30305",
  "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
  "req_id": "cu33ouao7p78o742c5t0",
  "user": "current user",
  "status": "ok",
  "time": "2025-01-14T18:29:13+08:00",
  "message": "Something happened"
}
{
  "level": "info",
  "role": "my-service",
  "host": "local-hostname",
  "ip": "127.0.0.1:30305",
  "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
  "req_id": "cu33ouao7p78o742c5t0",
  "method": "GET",
  "url": "/",
  "status": 0,
  "size": 0,
  "duration": 15.1797,
  "time": "2025-01-14T18:29:13+08:00"
}
{
  "level": "info",
  "role": "my-service",
  "host": "local-hostname",
  "ip": "127.0.0.1:30305",
  "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
  "referer": "http://127.0.0.1:8080/",
  "req_id": "cu33ouao7p78o742c5tg",
  "user": "current user",
  "status": "ok",
  "time": "2025-01-14T18:29:13+08:00",
  "message": "Something happened"
}
{
  "level": "info",
  "role": "my-service",
  "host": "local-hostname",
  "ip": "127.0.0.1:30305",
  "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",
  "referer": "http://127.0.0.1:8080/",
  "req_id": "cu33ouao7p78o742c5tg",
  "method": "GET",
  "url": "/favicon.ico",
  "status": 0,
  "size": 0,
  "duration": 0,
  "time": "2025-01-14T18:29:13+08:00"
}

钩子

Zerolog 提供了一种通过 Hook 接口挂接到日志记录过程的方法

定义如下:

type Hook interface {
    // Run runs the hook with the event.
    Run(e *zerolog.Event, level zerolog.Level, message string)
}

在具体类型上实现 Hook 接口时,可以使用 Logger.Hook() 方法将其应用于 Logger,以便在每次记录日志时执行其 Run() 方法。

然后,您可以根据事件的日志级别或一些其他条件运行不同的操作。

示例

package main

import (
	"fmt"
	"github.com/rs/zerolog"
	"os"
)

// SeverityHook 是一个自定义的日志钩子,用于添加日志级别到日志条目中
type SeverityHook struct{}

// Run 方法实现了 zerolog.Hook 接口,会在每次日志记录时调用
func (h SeverityHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {
	if level != zerolog.NoLevel {
		e.Str("severity", level.String()) // 添加 "severity" 字段,值为日志级别
	}
	fmt.Println("=========================================", msg, level)
}

func main() {
	// 初始化 zerolog,输出到控制台
	log := zerolog.New(os.Stdout).With().Timestamp().Logger()

	// 将自定义钩子添加到日志实例中
	hooked := log.Hook(SeverityHook{})

	// 记录一条警告日志
	hooked.Warn().Msg("这是告警信息")
	// 输出: ========================================= 这是告警信息 warn
	// {"level":"warn","time":"2025-01-14T18:35:31+08:00","severity":"warn","message":"这是告警信息"}

	hooked.Debug().Msg("这是debug信息")
	// 输出: ========================================= 这是debug信息 debug
	// {"level":"debug","time":"2025-01-14T18:35:31+08:00","severity":"debug","message":"这是debug信息"}
}

日志采样

采样是一种用于有意删除重复日志条目的技术,以便只保留和处理其中的一部分。

当您的高流量应用程序生成大量记录时,这很有帮助,并且存储每一条记录将导致过高的存储和处理成本,这可能是不可取的。

采样通过防止每秒记录数百或数千次相同的日志来解决此问题,从而防止资源浪费。

基本采样 (BasicSampler)

BasicSampler 是最简单的采样方式,它会每隔 N 条日志记录一次。

以下是使用 Zerolog 对日志进行采样的最基本方法:

package main

import (
	"github.com/rs/zerolog"
	"os"
	"time"
)

func main() {
	// 初始化 zerolog,输出到控制台
	log := zerolog.New(os.Stdout).With().Timestamp().Logger()

	// 使用 BasicSampler,每 10 条日志记录一次
	sampled := log.Sample(&zerolog.BasicSampler{N: 10})

	// 模拟日志输出
	for i := 1; i <= 30; i++ {
		sampled.Info().Msgf("This is log message %d", i)
		time.Sleep(100 * time.Millisecond) // 模拟日志间隔
	}
}

输出结果

由于采样设置为每 10 条日志记录一次,因此只有第 1、11、21 条日志会被输出:

{"level":"info","time":"2025-01-14T18:40:29+08:00","message":"This is log message 1"}
{"level":"info","time":"2025-01-14T18:40:30+08:00","message":"This is log message 11"}
{"level":"info","time":"2025-01-14T18:40:31+08:00","message":"This is log message 21"}

适用场景

  • 适用于需要简单控制日志量的场景。
  • 例如,每 100 条日志记录一次,减少日志输出。

高级采样 (LevelSampler 和 BurstSampler)

LevelSampler 允许对不同日志级别设置不同的采样策略。

BurstSampler 是一种更灵活的采样方式,允许在指定时间窗口内记录一定数量的日志,超过限制后按比例采样。

package main

import (
	"github.com/rs/zerolog"
	"os"
	"time"
)

func main() {
	// 初始化 zerolog,输出到控制台
	log := zerolog.New(os.Stdout).With().Timestamp().Logger()

	// 设置高级采样策略
	sampled := log.Sample(
		zerolog.LevelSampler{
			DebugSampler: &zerolog.BurstSampler{
				Burst:       5,                             // 每秒最多记录 5 条 Debug 日志
				Period:      1 * time.Second,               // 时间窗口为 1 秒
				NextSampler: &zerolog.BasicSampler{N: 100}, // 超过 5 条后,每 100 条记录一次
			},
		},
	)

	// 模拟日志输出
	for i := 1; i <= 200; i++ {
		sampled.Debug().Msgf("This is debug log message %d", i)
		time.Sleep(10 * time.Millisecond) // 模拟日志间隔
	}
}

输出结果

  1. 每秒的前 5 条 Debug 日志会被记录。
  2. 超过 5 条后,每 100 条 Debug 日志记录一次。
  3. 其他级别的日志不受影响(如果有)。

示例输出:

{"level":"debug","time":"2025-01-14T18:44:55+08:00","message":"This is debug log message 1"}
{"level":"debug","time":"2025-01-14T18:44:55+08:00","message":"This is debug log message 2"}
{"level":"debug","time":"2025-01-14T18:44:55+08:00","message":"This is debug log message 3"}
{"level":"debug","time":"2025-01-14T18:44:55+08:00","message":"This is debug log message 4"}
{"level":"debug","time":"2025-01-14T18:44:56+08:00","message":"This is debug log message 5"}
{"level":"debug","time":"2025-01-14T18:44:56+08:00","message":"This is debug log message 6"}
{"level":"debug","time":"2025-01-14T18:44:56+08:00","message":"This is debug log message 95"}
{"level":"debug","time":"2025-01-14T18:44:56+08:00","message":"This is debug log message 96"}
{"level":"debug","time":"2025-01-14T18:44:56+08:00","message":"This is debug log message 97"}
{"level":"debug","time":"2025-01-14T18:44:56+08:00","message":"This is debug log message 98"}
{"level":"debug","time":"2025-01-14T18:44:56+08:00","message":"This is debug log message 99"}
{"level":"debug","time":"2025-01-14T18:44:57+08:00","message":"This is debug log message 111"}
{"level":"debug","time":"2025-01-14T18:44:57+08:00","message":"This is debug log message 191"}
{"level":"debug","time":"2025-01-14T18:44:57+08:00","message":"This is debug log message 192"}
{"level":"debug","time":"2025-01-14T18:44:57+08:00","message":"This is debug log message 193"}
{"level":"debug","time":"2025-01-14T18:44:57+08:00","message":"This is debug log message 194"}
{"level":"debug","time":"2025-01-14T18:44:58+08:00","message":"This is debug log message 195"}

适用场景

  • 适用于需要精细控制日志级别的场景。
  • 例如,Debug 日志量很大,但希望每秒最多记录 5 条,超过后按比例采样。

自定义Logger

logger := zerolog.New(os.Stderr).With().Timestamp().Logger()

logger = logger.Level(zerolog.InfoLevel)
logger.Info().Str("foo", "bar").Msg("hello world")

// Output: {"level":"info","time":1494567715,"message":"hello world","foo":"bar"}

定制漂亮的日志记录

要记录人性化的彩色输出,请使用zerolog.ConsoleWriter

package main

import (
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"os"
)

func main() {
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})

	log.Info().Str("foo", "bar").Msg("Hello world")

	// Output: 3:04PM INF Hello World foo=bar
}

要自定义配置和格式:

package main

import (
	"fmt"
	"github.com/rs/zerolog"
	"os"
	"strings"
	"time"
)

func main() {
	// 配置控制台日志输出格式 设置时间格式
	output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}

	// 自定义日志级别显示格式,如 "| INFO  |"
	output.FormatLevel = func(i interface{}) string {
		return strings.ToUpper(fmt.Sprintf("| %-6s|", i))
	}

	// 自定义日志消息显示格式,如 "***message****"
	output.FormatMessage = func(i interface{}) string {
		return fmt.Sprintf("***%s****", i)
	}

	// 自定义字段名显示格式,如 "field:"
	output.FormatFieldName = func(i interface{}) string {
		return fmt.Sprintf("%s:", i)
	}

	// 自定义字段值显示格式,转为大写,如 "VALUE"
	output.FormatFieldValue = func(i interface{}) string {
		return strings.ToUpper(fmt.Sprintf("%s", i))
	}

	// 创建日志记录器,自动添加时间戳
	log := zerolog.New(output).With().Timestamp().Logger()

	log.Info().Str("foo", "bar").Msg("Hello World")

	// Output: 2025-01-14T17:18:53+08:00 | INFO  | ***Hello World**** foo:BAR
}

在开发环境中,您可能会发现以更易于阅读的格式从应用程序输出日志条目会很有帮助,这样就可以轻松发现各种事件,而不会被不相关的符号和字段分散注意力。

Zerolog 提供了一个 ConsoleWriter 类型,用于解析原始 JSON 条目,并将其以彩色格式输出到控制台。

package main

import (
	"github.com/rs/zerolog"
	"os"
	"runtime/debug"
	"time"
)

func main() {
	buildInfo, _ := debug.ReadBuildInfo()
	logger := zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}).
		Level(zerolog.TraceLevel).
		With().
		Timestamp().
		Caller().
		Int("pid", os.Getpid()).
		Str("go_version", buildInfo.GoVersion).
		Logger()
	logger.Trace().Msg("trace message")
	logger.Debug().Msg("debug message")
	logger.Info().Msg("info message")
	logger.Warn().Msg("warn message")
	logger.Error().Msg("error message")
	logger.WithLevel(zerolog.FatalLevel).Msg("fatal message")
	logger.WithLevel(zerolog.PanicLevel).Msg("panic message")
}

将日志写入到文件

package main

import (
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"os"
)

func main() {
	file, err := os.OpenFile("log.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatal().Err(err).Msg("Failed to open log file")
	}
	defer file.Close()
	logger := zerolog.New(file).Level(zerolog.ErrorLevel).With().Timestamp().Logger()
	logger.Info().Msg("Hello, world!")
}

多日志输出

zerolog.MultiLevelWriter可用于将日志消息发送到多个输出。

在此示例中,我们将日志消息发送到os.Stdout内置 ConsoleWriter。

package main

import (
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"os"
)

func main() {
	file, err := os.OpenFile("log.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatal().Err(err).Msg("Failed to open log file")
	}
	defer file.Close()
	// 创建一个日志记录器,将多个写入器组合在一起
	writer := zerolog.MultiLevelWriter(file, os.Stdout)
	logger := zerolog.New(writer).With().Timestamp().Logger()
	// 设置日志级别为InfoLevel
	logger = logger.Level(zerolog.InfoLevel)
	logger.Info().Msg("Hello, world!")
}

使用Lumberjack进行日志切割归档

这个日志程序中唯一缺少的就是日志切割归档功能。

Zerolog本身不支持切割归档日志文件

为了添加日志切割归档功能,我们将使用第三方库Lumberjack来实现。

安装

执行下面的命令安装Lumberjack

go get -u gopkg.in/natefinch/lumberjack.v2

zap logger中加入Lumberjack

infoLumberIO := &lumberjack.Logger{
	Filename:   "./server/info.log",
	MaxSize:    10, // megabytes
	MaxBackups: 10,
	MaxAge:     28,    // days
	Compress:   false, //Compress确定是否应该使用gzip压缩已旋转的日志文件。默认值是不执行压缩。
}
writer := zapcore.AddSync(infoLumberIO)

Lumberjack Logger采用以下属性作为输入:

  • Filename: 日志文件的位置 如果文件目录不存在,会自动创建
  • MaxSize:最大文件大小,日志文件达到多少的时候切割(以MB为单位)
  • MaxBackups:保留旧文件的最大个数
  • MaxAges:保留旧文件的最大天数
  • Compress:是否压缩/归档旧文件

存档的状态

在zerolog中使用lumberjack

package main

import (
	"github.com/rs/zerolog"
	"gopkg.in/natefinch/lumberjack.v2"
	"os"
)

func main() {
	// 配置 lumberjack 实现日志切割
	logFile := &lumberjack.Logger{
		Filename:   "app.log", // 日志文件路径
		MaxSize:    5,         // 每个日志文件的最大大小(MB)
		MaxBackups: 5,         // 保留旧日志文件的最大数量
		MaxAge:     30,        // 保留旧日志文件的最大天数
		Compress:   true,      // 是否压缩旧日志文件
	}

	// 创建 zerolog 的 MultiLevelWriter,同时输出到文件和控制台
	multiWriter := zerolog.MultiLevelWriter(
		zerolog.ConsoleWriter{Out: os.Stdout}, // 输出到控制台
		logFile,                               // 输出到文件
	)

	// 创建 Logger
	logger := zerolog.New(multiWriter).With().Timestamp().Logger()

	// 记录日志
	for i := 0; i < 1000000; i++ {
		logger.Info().Int("index", i).Msg("This is a log message")
	}
}

错误日志与普通日志分开的logger

package main

import (
	"os"
	"time"

	"github.com/rs/zerolog"
	"gopkg.in/natefinch/lumberjack.v2"
)

func main() {
	// 配置普通日志的切割
	normalLogFile := &lumberjack.Logger{
		Filename:   "normal.log", // 普通日志文件路径
		MaxSize:    10,           // 每个日志文件的最大大小(MB)
		MaxBackups: 3,            // 保留旧日志文件的最大数量
		MaxAge:     30,           // 保留旧日志文件的最大天数
		Compress:   true,         // 是否压缩旧日志文件
	}

	// 配置错误日志的切割
	errorLogFile := &lumberjack.Logger{
		Filename:   "error.log", // 错误日志文件路径
		MaxSize:    10,          // 每个日志文件的最大大小(MB)
		MaxBackups: 3,           // 保留旧日志文件的最大数量
		MaxAge:     30,          // 保留旧日志文件的最大天数
		Compress:   true,        // 是否压缩旧日志文件
	}

	// 创建控制台输出器
	consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout}

	// 创建普通日志 Logger,输出到控制台和普通日志文件
	normalLogger := zerolog.New(zerolog.MultiLevelWriter(
		consoleWriter,    // 输出到控制台
		normalLogFile,    // 输出到普通日志文件
	)).With().Timestamp().Logger()

	// 创建错误日志 Logger,输出到控制台和错误日志文件
	errorLogger := zerolog.New(zerolog.MultiLevelWriter(
		consoleWriter,   // 输出到控制台
		errorLogFile,    // 输出到错误日志文件
	)).With().Timestamp().Logger()

	// 记录普通日志
	normalLogger.Info().Msg("This is a normal log message")

	// 记录错误日志
	errorLogger.Error().Msg("This is an error log message")
}

标签:log,--,zerolog,time,Go,日志,Zerolog,message,op
From: https://www.cnblogs.com/guangdelw/p/18671412

相关文章

  • 渠道知识库系统系统:代理商与分销商竞争突围的关键密码
    在竞争激烈的市场环境中,代理商与分销商作为连接品牌与终端消费者的桥梁,其竞争力直接关乎企业的市场地位。构建一个全面、智能的渠道知识库系统,是提升代理商与分销商能力、实现差异化竞争的关键。本文将深入探讨如何构建这样的知识库系统,并提及helplook工具在其中的辅助作用,助力企......
  • Jar Analyzer:JAR包分析工具
    工具介绍一个JAR包分析工具,批量分析,SCA漏洞分析,方法调用关系搜索,字符串搜索,Spring组件分析,信息泄露检查,CFG程序分析,JVM栈帧分析,进阶表达式搜索,字节码指令级的动态调试分析,反编译JAR包一键导出,一键提取序列化数据恶意代码,一键分析BCEL字节码。JarAnalyzer的用途场景01:从大量J......
  • 07jdk7u21原生利用链
    JDK7u21反序列化的关键在于找到可以动态方法执行的代码:例如CC链中的Transformer,CB链中的PropertyUtils#getPropertyJDK7u21中动态方法执行的点,AnnotationInvocationHandler#equalsImpl中的hisValue=memberMethod.invoke(o)。privateBooleanequalsImpl(Objecto){......
  • 敏捷团队的进阶之路:工具的实践与应用
    ​在当今快节奏、高竞争的商业环境中,“敏捷”已经从一种开发模式演变为广泛适用于各行业的工作哲学。然而,对于很多团队来说,敏捷并不仅仅是学习几个概念或框架,更是如何在日常实践中将这些理念真正落地。而在这个过程中,敏捷工具的作用显得尤为重要。为什么敏捷工具成为敏捷落地的......
  • 英语语法(介词和连词)
       认识介词 例子: 例子: 识别介词 时间介词at 精确的时间by不缺定的时间for 持续的时间in 固定的时间on某一天的时间since开始时间until 直到 空间介词at  指一个点by 近的意思from从别处到这里in 一个封闭的区域off......
  • 电脑“减肥”利器:两款重复文件查找神器大揭秘
    前言:        随着电脑使用时间的增长,我们往往会不知不觉地积累大量重复的软件和文件。手动一一核对这些重复项,不仅耗时费力,还容易遗漏。今天,我要为大家推荐两款重复文件查找神器,它们能够轻松帮我们清理硬盘空间,让电脑“瘦身”更高效。EasyDuplicateFinder:重复文件......
  • 玩机党的福音,Windows超级管理器!
    前言:        今天,我要向大家隆重推荐一款堪称“神器”的软件——Windows超级管理器。这是一款完全免费且功能强大的系统管理软件,其出色的表现甚至超越了广为人知的鲁大师。Windows超级管理器        这款软件的体积十分小巧,仅有5兆大小,下载后无需安装,打开即......
  • java学生综合测评管理系统论文+源码 2025毕设
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容一、研究背景在现代教育管理体系中,学生综合测评是一项至关重要的工作。随着教育规模的不断扩大和教育理念的持续更新,传统的人工测评管理方式已难以满足需求。......
  • java乡村水费管理系统论文+源码 2025毕设
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容一、研究背景随着城乡一体化进程的加快,乡村地区的整体发展水平不断提升,基础设施建设逐步完善。其中,水费管理在乡村发展中的重要性日益凸显。传统的乡村水费管......
  • java进销存管理系统论文+源码 2025毕设
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容一、研究背景随着企业规模的不断扩大和业务的日益复杂,传统的手工管理方式已经难以满足企业对于采购、销售和库存管理的需求。在过去,企业常常面临着库存积压或......