每日一库系列01 go的logrus库介绍

实习过程中功能需求分析:

重构 Log 模块,基于 logrus 搭建多日志客户端并接入飞书 WebHook。

Webhook

飞书允许用户在群组中创建一个自定义机器人,将其他渠道的消息通过Webhook的方式推送至该群组中。

Webhook顾名思义即网络钩子,也称为用户自定义 HTTP回调函数(user-defined HTTP callbacks),通常用于监听某些行为事件,当事件触发时会向用户指定的目标地址发送信息。

这段话对于大多数没有计算机基础知识的读者来说可能难以理解,但却无关紧要,因为我们只需要可以将其理解为「中间人」即可:

Logrus

在这里插入图片描述

go get github.com/sirupsen/logrus
package main

import (
  "github.com/sirupsen/logrus"
)

func Log3() {
  logrus.SetLevel(logrus.TraceLevel)

  logrus.Trace("trace msg")
  logrus.Debug("debug msg")
  logrus.Info("info msg")
  logrus.Warn("warn msg")
  logrus.Error("error msg")
  logrus.Fatal("fatal msg")
  logrus.Panic("panic msg")
}
func main() {
	Log3()
}

在这里插入图片描述

Panic:记录日志,然后panic。

Fatal:致命错误,出现错误时程序无法正常运转。输出日志后,程序退出;

Error:错误日志,需要查看原因;
Warn:警告信息,提醒程序员注意;

Info:关键操作,核心流程的日志;

Debug:一般程序中输出的调试信息;

Trace:很细粒度的信息,一般用不到;

另外,我们观察到输出中有三个关键信息,time、level和msg:
time:输出日志的时间;
level:日志级别;
msg:日志信息。
调用logrus.SetReportCaller(true)设置在输出日志中添加文件名和方法信息
 logrus.SetReportCaller(true)

 logrus.Info("info msg")

在这里插入图片描述

添加自定义字段

有时候需要在输出中添加一些字段,可以通过调用logrus.WithFieldlogrus.WithFields实现。logrus.WithFields接受一个logrus.Fields类型的参数,其底层实际上为map[string]interface{}

logrus.WithFields(logrus.Fields{
    "name": "dj",
    "age": 18,
  }).Info("info msg")

在这里插入图片描述

requestLogger := logrus.WithFields(logrus.Fields{
    "user_id": 10010,
    "ip":      "192.168.32.15",
  })

  requestLogger.Info("info msg")
  requestLogger.Error("error msg")

在这里插入图片描述
在这里插入图片描述

实际上,WithFields返回一个logrus.Entry类型的值,它将logrus.Logger和设置的logrus.Fields保存下来。调用Entry相关方法输出日志时,保存下来的logrus.Fields也会随之输出。

重定向输出
package main

import (
    "bytes"
    "fmt"
    log "github.com/sirupsen/logrus"
    "io"
    "os"
)

func Log4() {
    // 创建一个 bytes.Buffer 用于内存中的日志存储
    var buffer bytes.Buffer
    
    // 打开或创建一个日志文件
    file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err != nil {
        fmt.Println("Failed to log to file:", err)
        return
    }
    defer file.Close()

    // 创建一个 MultiWriter,它可以将日志同时写入多个 io.Writer
    writers := []io.Writer{
        os.Stdout,      // 标准输出
        &buffer,        // 内存中的缓冲区
        file,           // 日志文件
    }

    // 使用 MultiWriter 创建一个新的 Writer
    multiWriter := io.MultiWriter(writers...)

    // 设置 logrus 的输出为 MultiWriter
    log.SetOutput(multiWriter)

    // 设置日志格式为 JSON 格式
    log.SetFormatter(&log.JSONFormatter{})

    // 设置日志级别
    log.SetLevel(log.DebugLevel)

    // 记录一些日志
    log.Info("This is an info message")
    log.Warn("This is a warning message")
    log.Error("This is an error message")

    // 从缓冲区读取日志并打印出来
    fmt.Println("Buffered logs:")
    fmt.Print(buffer.String())
}

在这里插入图片描述

bytes.Buffer 用于内存中的日志存储

bytes.Buffer 是 Go 语言标准库 bytes 包中的一个类型,它提供了一个可变大小的缓冲区,用于高效地读写字节数据。Buffer 实现了 io.Writer 和 io.Reader 接口,因此可以很方便地与需要这些接口的函数或方法一起使用。

package main

import (
	"os"
	"fmt"
	"bytes"
)

func main() {
    file, _ := os.Open("./test.txt")    
    buf := bytes.NewBufferString("Hello world ")    
    buf.ReadFrom(file)              //将text.txt内容追加到缓冲器的尾部    
    fmt.Println(buf.String())
}

在这里插入图片描述

实际上,考虑到易用性,库一般会使用默认值创建一个对象,包最外层的方法一般都是操作这个默认对象。

// github.com/sirupsen/logrus/exported.go
var (
  std = New()
)

func StandardLogger() *Logger {
  return std
}

func SetOutput(out io.Writer) {
  std.SetOutput(out)
}

func SetFormatter(formatter Formatter) {
  std.SetFormatter(formatter)
}

func SetReportCaller(include bool) {
  std.SetReportCaller(include)
}

func SetLevel(level Level) {
  std.SetLevel(level)
}
func New() *Logger {
	return &Logger{
		Out:          os.Stderr,
		Formatter:    new(TextFormatter),
		Hooks:        make(LevelHooks),
		Level:        InfoLevel,
		ExitFunc:     os.Exit,
		ReportCaller: false,
	}
}
创建自己的logger对象

创建自己的Logger对象,使用方式与直接调用logrus的方法类似

package main

import "github.com/sirupsen/logrus"

func main() {
  log := logrus.New()

  log.SetLevel(logrus.InfoLevel)
  log.SetFormatter(&logrus.JSONFormatter{})

  log.Info("info msg")
}

在这里插入图片描述

hook钩子

logrus 中,钩子(Hook)是一个非常强大的特性,它允许你在日志记录的生命周期中插入自定义逻辑。

可以为logrus设置钩子,每条日志输出前都会执行钩子的特定方法

具体来说,钩子可以在日志记录之前或之后执行特定的操作,比如添加额外的字段、根据日志级别将日志输出到不同的目的地,发送错误到错误跟踪服务、将统计信息发送到 StatsD 或在致命错误时转储核心。等。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package logrus

// A hook to be fired when logging on the logging levels returned from
// `Levels()` on your implementation of the interface. Note that this is not
// fired in a goroutine or a channel with workers, you should handle such
// functionality yourself if your call is non-blocking and you don't wish for
// the logging calls for levels returned from `Levels()` to block.(q1)
type Hook interface {
	Levels() []Level
	Fire(*Entry) error
}

// Internal type for storing the hooks on a logger instance.
type LevelHooks map[Level][]Hook

// Add a hook to an instance of logger. This is called with
// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
func (hooks LevelHooks) Add(hook Hook) {
	for _, level := range hook.Levels() {
		hooks[level] = append(hooks[level], hook)
	}
}

// Fire all the hooks for the passed level. Used by `entry.log` to fire
// appropriate hooks for a log entry.
func (hooks LevelHooks) Fire(level Level, entry *Entry) error {
	for _, hook := range hooks[level] {
		if err := hook.Fire(entry); err != nil {
			return err
		}
	}

	return nil
}

q1: 一个钩子(hook)会在日志记录时触发,触发条件是日志级别与你在接口实现中通过 Levels() 方法返回的日志级别相匹配。需要注意的是,这个钩子不会在 goroutine 或带有工作线程的通道中触发,如果你的调用是非阻塞的,并且你不希望由 Levels() 返回的日志级别的日志调用被阻塞,你应该自己处理这种功能。

这段代码描述了一个日志钩子的行为:它会在特定日志级别(由 Levels() 方法返回)的日志记录时触发,但不会在后台的 goroutine 或工作线程中自动执行。如果钩子的操作是非阻塞的,开发者需要自行处理并发或异步逻辑,以避免日志调用被阻塞。

Hook介绍

在这里插入图片描述

Hook: 接口,需实现Levels()和Fire()方法。

Levels()方法返回感兴趣的日志级别,输出其他日志时不会触发钩子。Fire是日志输出前调用的钩子方法。

LevelHooks: 这是一个类型别名,表示一个映射,键是日志级别,值是一个包含多个钩子的切片。通过这种方式,你可以为不同的日志级别注册不同的钩子。

实操

实现Hook接口的结构体,就可以实现钩子功能。

只需要在Fire方法实现中,为entry.Data添加字段就会输出到日志中。

package main

import (
  "github.com/sirupsen/logrus"
)

type AppHook struct {
  AppName string
}

func (h *AppHook) Levels() []logrus.Level {
  return logrus.AllLevels
}

func (h *AppHook) Fire(entry *logrus.Entry) error {
  entry.Data["app"] = h.AppName
  return nil
}

func main() {
  h := &AppHook{AppName: "awesome-web"}
  logrus.AddHook(h)

  logrus.Info("info msg")
}

在这里插入图片描述

logrus的第三方 Hook 很多,我们可以使用一些 Hook 将日志发送到 redis/mongodb 等存储中:

将日志发送到飞书机器人bot

https://www.feishu.cn/content/7271149634339422210

交互式消息卡片

在fire发送post请求给飞书webhook的api。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值