go 微服务框架 kratos 日志库使用方法及原理探究

一、Kratos 日志设计理念

kratos 日志库相关的官方文档:日志 | Kratos

Kratos的日志库主要有如下特性:

  • Logger用于对接各种日志库或日志平台,可以用现成的或者自己实现
  • Helper是在您的项目代码中实际需要调用的,用于在业务代码里打日志
  • Filter用于对输出日志进行过滤或魔改(通常用于日志脱敏)
  • Valuer用于绑定一些全局的固定值或动态值(比如时间戳、traceID或者实例id之类的东西)到输出日志中

Logger统一了日志的接入方式,Helper接口统一的日志库的调用方式。

二、Kratos 使用标准内置日志库的方法

1、打印日志到控制台的步骤 

①导入kratos日志包

import "github.com/go-kratos/kratos/v2/log"

//若导入失败,则需先获取包
go get github.com/go-kratos/kratos/v2/log

②使用 kratos 内置标准输出创建日志对象 

logger := log.NewStdLogger(os.Stdout)

③用 Log 方法打印日志,需要传入日志级别 

logger.Log(log.LevelInfo, "msg", "logger.Log 打印的日志")

 ④为了简化写日志时需传入的参数,kratos 用 Helper 对 Logger 进行了包装,建议用新建 helper 对象写日志,方法如下:

//对 logger 进行包装,简化写日志时需传入的参数
h := log.NewHelper(logger)

//写入信息级别的日志
h.Info("使用 kratos 内置标准输出记录的日志")

//写入错误级别的日志
h.Errorf("用户名【%s】不存在", "张三")

 ⑤可通过 log.With() 方法绑定全局字段到 Vauler,用来打印全局信息到日志中

//通过 log.With 方法绑定全局字段到 Vauler,用来打印全局信息到日志中
logger = log.With(logger,
	"ts", log.DefaultTimestamp,
	"caller", log.DefaultCaller,
	"trace_id", tracing.TraceID(),
	"span_id", tracing.SpanID(),
)
h = log.NewHelper(logger)
h.Info("绑定了全局信息到日志中")

⑥用 log.NewFilter() 方法对日志输出进行过滤

// 日志过滤,显示特定级别的日志,或对敏感信息脱敏
h = log.NewHelper(
	log.NewFilter(logger,
		// 按等级过滤
		//log.FilterLevel(log.LevelError),

		// 按key遮蔽
		log.FilterKey("username"),

		// 按value遮蔽
		log.FilterValue("hello"),
	),
)
h.Warn("warn log")
h.Infow("password", "123456")
//日志中 kratos 会变为 ***
h.Infow("username", "kratos")
//日志中 hello 会变为 ***
h.Info("hello")

 效果演示:

 完整代码:

package main

import (
	"os"

	"github.com/go-kratos/kratos/v2"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-kratos/kratos/v2/middleware/tracing"
	"github.com/go-kratos/kratos/v2/transport/http"
)

func main() {
	//使用 kratos 内置标准输出创建日志对象
	logger := log.NewStdLogger(os.Stdout)
	// 用 Log 方法打印日志,需要传入日志级别,不建议使用这种方式,而建议使用 helper 打印日志
	logger.Log(log.LevelInfo, "msg", "logger.Log 打印的日志")
	//对 logger 进行包装,简化写日志时需传入的参数
	h := log.NewHelper(logger)
	//写入信息级别的日志
	h.Info("使用 kratos 内置标准输出记录的日志")
	//写入错误级别的日志
	h.Errorf("用户名【%s】不存在", "张三")

	// 通过 log.With 方法绑定全局字段到 Vauler,用来打印全局信息到日志中
	logger = log.With(logger,
		"ts", log.DefaultTimestamp,
		"caller", log.DefaultCaller,
		"trace_id", tracing.TraceID(),
		"span_id", tracing.SpanID(),
	)
	h = log.NewHelper(logger)
	h.Info("绑定了全局信息到日志中")

	// 日志过滤,显示特定级别的日志,或对敏感信息脱敏
	h = log.NewHelper(
		log.NewFilter(logger,
			// 按等级过滤
			//log.FilterLevel(log.LevelError),

			// 按key遮蔽
			log.FilterKey("username"),

			// 按value遮蔽
			log.FilterValue("hello"),
		),
	)
	h.Warn("warn log")
	h.Infow("password", "123456")
	//日志中 kratos 会变为 ***
	h.Infow("username", "kratos")
	//日志中 hello 会变为 ***
	h.Info("hello")


	//创建 kratos http server 及 app
	httpSrv := http.NewServer(
		http.Address(":8080"),
	)

	app := kratos.New(
		kratos.Name("测试log"),
		kratos.Server(
			httpSrv,
		),
	)

	if err := app.Run(); err != nil {
		log.Fatal(err)
	}
}

 

2、将日志写入到本地文件的方法

 ① 通过 os.OpenFile 新建文件,获取到 os.File 对象

f, err := os.OpenFile("../test.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
	return
}

② 通过 log.NewStdLogger(w io.Writer) 新建 logger 时,传入上述 os.File 对象 f

//输出到控制台,传入os.Stdout;输出到文件,则传文件指针
logger := log.NewStdLogger(f)

注意:os.File 实现了  io.Writer 接口的 Write(p []byte) (n int, err error) 方法,所以能将 f 作为输入参数传入 log.NewStdLogger() 函数

③用 log.NewHelper 包装 logger,并打印日志

h1 := log.NewHelper(logger1)
h1.Info("输出到日志文件中的日志信息")

效果演示:

代码实现:

package main

import (
	"os"

	"github.com/go-kratos/kratos/v2"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-kratos/kratos/v2/transport/http"
)

func main() {
	
	// 通过 log.NewStdLogger(w io.Writer) 中的 io.Writer 设置日志输出方式
	// 将日志输出到 test.log 文件
	f, err := os.OpenFile("../test.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		return
	}
	logger1 := log.NewStdLogger(f)
	h1 := log.NewHelper(logger1)
	h1.Info("输出到日志文件中的日志信息")

	//创建 kratos http server 及 app
	httpSrv := http.NewServer(
		http.Address(":8080"),
	)

	app := kratos.New(
		kratos.Name("测试log"),
		kratos.Server(
			httpSrv,
		),
	)

	if err := app.Run(); err != nil {
		log.Fatal(err)
	}
}

三、 zap 日志库的使用方法

1、zap简介

Zap 是 uber 用 go 语言实现的超快、结构化、分级的日志记录库,性能极高,比其他结构化日志记录包快4-10倍。日志记录机构化,以JSON等格式输出。

有关详细介绍见:zap package - go.uber.org/zap - Go Packages

2、使用方法

①安装 zap 包

go get -u go.uber.org/zap

②导入 zap 包

import "go.uber.org/zap"

③用 NewProduction 等方法创建 zap 日志对象

// 创建 zap 日志对象
zaplogger, _ := zap.NewProduction()
defer logger.Sync()

 ④调用 Info、Error 等方法打印日志

//使用 zap 写日志
zaplogger.Info("failed to fetch URL",
	zap.String("url", "http://dddd.com"),
	zap.Int("attempt", 3),
	zap.Duration("backoff", time.Second),
)

效果演示:

 完整代码:

package main

import (
	"time"

	kzap "github.com/go-kratos/kratos/contrib/log/zap/v2"
	"github.com/go-kratos/kratos/v2"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-kratos/kratos/v2/transport/http"
	"go.uber.org/zap"
)

func main() {
	// 创建 zap 日志对象,并打印日志
	zaplogger, _ := zap.NewProduction()
	defer zaplogger.Sync()
	//使用 zap 写日志
	zaplogger.Info("failed to fetch URL",
		zap.String("url", "http://dddd.com"),
		zap.Int("attempt", 3),
		zap.Duration("backoff", time.Second))


	httpSrv := http.NewServer(
		http.Address(":8080"),
	)

	app := kratos.New(
		kratos.Name("测试log"),
		kratos.Server(
			httpSrv,
		),
	)

	if err := app.Run(); err != nil {
		log.Fatal(err)
	}
}

⑤如果需要将日志输出到文件,则使用如下方法:

package main

import (
	"os"

	kzap "github.com/go-kratos/kratos/contrib/log/zap/v2"
	"github.com/go-kratos/kratos/v2"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-kratos/kratos/v2/transport/http"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

func main() {
	// 配置Encoder
	encoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())

	// 配置 WriteSyncer,将日志写入文件
	f, err := os.OpenFile("zaptest.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		panic(err)
	}
	defer f.Close()

	writeSyncer := zapcore.AddSync(f)

	// 设置LogLevel
	level := zap.NewAtomicLevelAt(zapcore.InfoLevel)

	// 创建Core
	core := zapcore.NewCore(encoder, writeSyncer, level)

	// 构建Logger
	zaplogger := zap.New(core)
	logger := kzap.NewLogger(zaplogger)

	// 将 zap 日志对象适配到 kratos 的日志库,以便直接使用 kratos 的日志方法
	h := log.NewHelper(logger)
	h.Info("msg", "kratos适配的zap, 写入日志到文件")

	httpSrv := http.NewServer(
		http.Address(":8080"),
	)

	app := kratos.New(
		kratos.Name("测试log"),
		kratos.Server(
			httpSrv,
		),
	)

	if err := app.Run(); err != nil {
		log.Fatal(err)
	}
}

 效果演示:

四、Kratos 适配 zap 日志库的方法

kratos 在 contrib/log  实现好了一些插件,用于适配目前常用的日志库。

  • std 标准输出,Kratos内置
  • fluent 输出到fluentd
  • zap 适配了uber的zap日志库
  • aliyun 输出到阿里云日志

使用方法:

①安装 kratos 的 zap 适配包

go get github.com/go-kratos/kratos/contrib/log/zap/v2

②导入 zap 适配包

import (
	kzap "github.com/go-kratos/kratos/contrib/log/zap/v2"
)

 ③创建 zap 日志对象

// 创建 zap 日志对象,并打印日志
zaplogger, _ := zap.NewProduction()
defer zaplogger.Sync()

④调用函数 NewLogger(zlog *zap.Logger) 将创建的 zap 日志对象适配到 kratos 的日志操作对象

// 将 zap 日志对象适配到 kratos 的日志库,以便直接使用 kratos 的日志方法
logger := kzap.NewLogger(zaplogger)

 ⑤调用函数 log.NewHelper 将 logger 进行包装,简化日志写入操作

h := log.NewHelper(logger)
h.Info("kratos适配的zap日志库")

 

效果演示:

代码实现:

package main

import (
	kzap "github.com/go-kratos/kratos/contrib/log/zap/v2"
	"github.com/go-kratos/kratos/v2"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/go-kratos/kratos/v2/transport/http"
	"go.uber.org/zap"
)

func main() {
	// 创建 zap 日志对象,并打印日志
	zaplogger, _ := zap.NewProduction()
	defer zaplogger.Sync()

	// 将 zap 日志对象适配到 kratos 的日志库,以便直接使用 kratos 的日志方法
	logger := kzap.NewLogger(zaplogger)
	h := log.NewHelper(logger)
	h.Info("kratos适配的zap日志库")

	httpSrv := http.NewServer(
		http.Address(":8080"),
	)

	app := kratos.New(
		kratos.Name("测试log"),
		kratos.Server(
			httpSrv,
		),
	)

	if err := app.Run(); err != nil {
		log.Fatal(err)
	}
}

五、Kratos 适配第三方日志库的实现原理

kratos 底层日志接口 Logger,可用于快速适配各种日志库到框架中来,仅需要实现一个最简单的Log方法。

// Logger is a logger interface.
type Logger interface {
	Log(level Level, keyvals ...interface{}) error
}

 以适配 uber 的 zap 日志库为例,kratos 在 contrib/log/zap/zap.go 中定义了新的日志结构体 Logger,并实现了方法 Log(level log.Level, keyvals ...interface{}) error。其源码如下:

package zap

import (
	"fmt"

	"go.uber.org/zap"

	"github.com/go-kratos/kratos/v2/log"
)

var _ log.Logger = (*Logger)(nil)

type Logger struct {
	log    *zap.Logger
	msgKey string
}

type Option func(*Logger)

// WithMessageKey with message key.
func WithMessageKey(key string) Option {
	return func(l *Logger) {
		l.msgKey = key
	}
}

func NewLogger(zlog *zap.Logger) *Logger {
	return &Logger{
		log:    zlog,
		msgKey: log.DefaultMessageKey,
	}
}

func (l *Logger) Log(level log.Level, keyvals ...interface{}) error {
	var (
		msg    = ""
		keylen = len(keyvals)
	)
	if keylen == 0 || keylen%2 != 0 {
		l.log.Warn(fmt.Sprint("Keyvalues must appear in pairs: ", keyvals))
		return nil
	}

	data := make([]zap.Field, 0, (keylen/2)+1)
	for i := 0; i < keylen; i += 2 {
		if keyvals[i].(string) == l.msgKey {
			msg, _ = keyvals[i+1].(string)
			continue
		}
		data = append(data, zap.Any(fmt.Sprint(keyvals[i]), keyvals[i+1]))
	}

	switch level {
	case log.LevelDebug:
		l.log.Debug(msg, data...)
	case log.LevelInfo:
		l.log.Info(msg, data...)
	case log.LevelWarn:
		l.log.Warn(msg, data...)
	case log.LevelError:
		l.log.Error(msg, data...)
	case log.LevelFatal:
		l.log.Fatal(msg, data...)
	}
	return nil
}

func (l *Logger) Sync() error {
	return l.log.Sync()
}

func (l *Logger) Close() error {
	return l.Sync()
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/638406.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

正点原子LWIP学习笔记(三)PHY芯片简介

PHY芯片简介 一、PHY芯片简介&#xff08;了解&#xff09;二、LAN8720A与YT8512C简介&#xff08;熟悉&#xff09;三、PHY地址的设置&#xff08;掌握&#xff09;四、RMII模式选择&#xff08;熟悉&#xff09;五、PHY寄存器简介&#xff08;熟悉&#xff09; 一、PHY芯片简…

AIGC绘画设计基础-建筑设计应用

一、AI及AIGC 对于AI大家都不陌生&#xff0c;但是AIGC这个概念好多人其实不大清楚。“AI”是指人工智能技术本身&#xff0c;而“AIGC”是指基于人工智能技术而生成的内容。 生成式人工智能——AIGC&#xff08;Artificial Intelligence Generated Content&#xff09;&…

Java类和对象(五)—— 抽象类、接口、Object类和内部类

抽象类 在继承体系下&#xff0c;父类有些方法可能是要被重写的&#xff0c;如果我们事先就知道某些方法需要重写的话&#xff0c;我们可以不用在父类里面具体实现这个方法&#xff0c;这时候我们会用到抽象方法&#xff0c;这时候我们会用到关键字abstract关键字来修饰 publ…

Thinkphp内核开发盲盒商城源码v2.0 对接易支付/阿里云短信/七牛云存储

源码简介 这套系统是我从以前客户手里拿到的,100完整可用,今天测试防红链接失效了,需要修改防红API即可!前端页面展示我就不放了,懂的都懂 优点是Thinkphp开发的&#xff0c;二开容易。 源码图片 资源获取&#xff1a;Thinkphp内核开发盲盒商城源码v2.0 对接易支付/阿里云短…

有些错误,常犯常新、常新常犯:记录一个使用element-plus的tooltip组件的错误

使用element-plus的tooltip组件&#xff0c;最开始的写法是这样的&#xff1a; <el-tooltipclass"box-item"effect"dark"content"tooltip content" ><el-button v-if"isDisabled" :underline"false" type"pr…

【实战】SpringBoot整合Websocket、Redis实现Websocket集群负载均衡

文章目录 前言技术积累什么是Websocket什么是Redis发布订阅Redis发布订阅与消息队列的区别 实战演示SpringBoot整合WebsoketWebsoket集群负载均衡 实战测试IDEA启动两台服务端配置nginx负载均衡浏览器访问模拟对话 前言 相信很多同学都用过websocket来实现服务端主动向客户端推…

Web前端一套全部清晰 ⑨ day5 CSS.4 标准流、浮动、Flex布局

我走我的路&#xff0c;有人拦也走&#xff0c;没人陪也走 —— 24.5.24 一、标准流 标准流也叫文档流&#xff0c;指的是标签在页面中默认的排布规则&#xff0c;例如:块元素独占一行&#xff0c;行内元素可以一行显示多个。 二、浮动 作用: 让块级元素水平排列。 属性名:floa…

瑞米派Ubuntu系统移植指南-米尔RemiPi

1.概述 Linux系统平台上有许多开源的系统构建框架&#xff0c;这些框架方便了开发者进行嵌入式系统的构建和定制化开发&#xff0c;目前比较常见的有Buildroot, Yocto, OpenEmbedded等等。 同时更多的传统的桌面系统也加入到嵌入式环境体系中&#xff0c;如Ubuntu&#xff0c…

启动docker报错:Failed to listen on Docker Socket for the API.

说明&#xff1a; 1、安装部署docker完成后&#xff0c;启动docker报错&#xff1a;Failed to listen on Docker Socket for the API&#xff0c;如下图所示&#xff1a; 2、将SocketGroupdocker更改成&#xff1a;SocketGrouproot即可 一、解决方法&#xff1a; 1、执行命令…

用智能插件(Fitten Code: Faster and Better AI Assistant)修改好了可以持久保存的vue3留言板

天际 第一修改是选项式&#xff1a; <!-- 模板结构 --> <template><div><textarea placeholder"请输入备注内容" v-model"newItem"></textarea><button click"addItem">添加</button><hr><…

Redis 主从复制、哨兵与集群

一、Redis 主从复制 1. 主从复制的介绍 主从复制&#xff0c;是指将一台Redis服务器的数据&#xff0c;复制到其他的Redis服务器。前者称为主节点(Master)&#xff0c;后者称为从节点(Slave)&#xff1b;数据的复制是单向的&#xff0c;只能由主节点到从节点。 默认情况下&a…

【MySQL精通之路】InnoDB(6)-磁盘结构(2)-索引

主博客&#xff1a; 【MySQL精通之路】InnoDB(6)-磁盘上的InnoDB结构-CSDN博客 上一篇&#xff1a; 下一篇&#xff1a; 【MySQL精通之路】磁盘上的InnoDB结构-表空间-CSDN博客 目录 1.聚集索引和二级索引 1.1 Innodb 如何建立聚集索引 1.2 聚集索引如何加快查询速度 1…

大语言模型的工程技巧(一)——GPU计算

相关说明 这篇文章的大部分内容参考自我的新书《解构大语言模型&#xff1a;从线性回归到通用人工智能》&#xff0c;欢迎有兴趣的读者多多支持。 本文涉及到的代码链接如下&#xff1a;regression2chatgpt/ch07_autograd/gpu.ipynb 本文将讨论如何利用PyTorch实现GPU计算。本…

第十一节 SpringBoot Starter 面试题

一、面试题 很多同学的简历都写着熟悉 SpringBoot&#xff0c; 而 Starter 的实现原理被当作的考题的的情况越来越多。 来源牛客网关于 starter 的一些面试题 情景一、路虎一面 情景二、蔚来 情景三、同花顺 Starter 频频出现&#xff0c;因此在面试准备时&#xff0c;这道题…

Qt_电脑wifi相关操作

项目描述: 在做项目时用到了获取wifi的操作。在网上查找了好久资料,这里做一些总结。 这里有显示当前电脑wifi连接状态,列出wifi列表,连接断开wifi等函数。欢迎大家留言添加文章内容。 使用范围: windows电脑(中文的环境) 使用技术:windows的cmd命令。和对字符串的解析…

概念解析 | 3D Referring Expression Comprehension (3D-REC):让计算机“听懂“人类的3D语言指令

注1:本文系"概念解析"系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:3D Referring Expression Comprehension (3D-REC)。 概念解析 | 3D Referring Expression Comprehension (3D-REC):让计算机"听懂"人类的3D语言指令 PDF]…

FIFO-Diffusion,一个无需额外训练即可生成长视频的框架。通过确保每个帧引用足够多的先前帧来生成高质量、一致的长视频。

简单来讲&#xff0c;FIFO-Diffusion先通过一些模型如VideoCraft2、zeroscope、Opem-Sora Plan等与FIFO-Diffusion的组合生成短视频&#xff0c;然后取结尾的帧&#xff08;也可以取多帧&#xff09;&#xff0c;再用这一帧的图片生成另一段短视频&#xff0c;然后拼接起来。FI…

工大智信智能听诊器:开启个人健康管理的全新模式

工大智信智能听诊器&#xff1a;开启个人健康管理的全新模式 在快节奏的现代生活中&#xff0c;健康管理已成为人们关注的焦点。工大智信智能听诊器&#xff0c;作为一款创新的医疗设备&#xff0c;不仅提供高级数据管理功能&#xff0c;而且成为了个人健康管理的得力助手。 这…

6款网站登录页(附带源码)

6款网站登录页 效果图及部分源码123456 领取源码下期更新预报 效果图及部分源码 1 部分源码 <style>* {margin: 0;padding: 0;}html {height: 100%;}body {height: 100%;}.container {height: 100%;background-image: linear-gradient(to right, #fbc2eb, #a6c1ee);}.l…

Application Development using Large Language Models笔记

诸神缄默不语-个人CSDN博文目录 这是2023年NeurIPS Andrew Ng和Isa Fulford做的tutorial&#xff0c;关于如何用LLM来开发新产品的技术和思路&#xff1a;NeurIPS Tutorial Application Development using Large Language Models 文章目录 1. LLM基础2. 提示工程技巧3. 微调4.…