addTenant api 和 rpc 的实现

上一篇我们说到咱们还剩下 addTenant 功能还未实现,不知道有没有兄弟感兴趣去实验一波的,本篇文章进行简要补充

根据上一篇文章分析,其实我们只需要执行如下几步即可:

  • 编写 tenant.api,提供外部 addTenant 的 http 接口
  • 编写 tenant.api

提供一个 POST http 的接口 / api /tenant/addtenant

  • goctl 生成 api 代码
  • 修改 api 的配置和逻辑层,让 api 层去调用之前写好的 rpc 接口 即可

对于配置可以模仿上一篇文章 order.api 的配置进行修改,另外只需要调整 addTenant 的 logic 层即可

具体的代码案例可以访问地址:github.com/qingconglai…

下面我们来看是 go-zero 中 日志组件 logx 的剖析

logx 日志组件剖析

对于 logx 日志组件,分别从如下几个方面来聊一聊我的理解,如果描述有不当的地方,还请多加评论多加交流

  • Go-zero 中 logx 是如何使用的?
  • Logx 基本的数据结构
  • Logx 的默认接口实现
  • Logx 日志存储位置,以及自定义存储日志位置的实现
  • Logx 实现自定义接口的方式

Go-zero 中 logx 是如何使用的?

我们以之前的 demo ,关于 tenant 的 rpc 部分作为例子,追踪一下代码,是如何走到日志部分的逻辑的

可以看到在 tenant.go 的文件中,做的是服务的启动

zrpc.MustNewServer 实际上是调用 go-zero 的 zrpc 包 的 NewServer 函数,传入的参数是

  • c RpcServerConf , 我们 rpc 服务的配置,就是咱们项目中的 etc/tenant.yaml

今天不聊关于 RpcServerConf 的结构,咱们重点说说 logx

  • register internal.RegisterFn 注册服务的回调函数

NewServer 函数做了如下几件事情:

  • RpcServerConf 配置数据的有效性检查
  • 初始化 metrics 的 options
  • 设置服务名,注册 etcd 服务,服务名就是上述配置文件中的 Name 字段
  • c.SetUp() 启动整个服务

对于 logx 日志组件的启动就是在 c.SetUp() 中完成

Logx 基本的数据结构

继续看到 logx.SetUp() 中的具体实现 , 函数需要传入的数据结构是这样的 LogConf

ServiceNamevolumerest/zrpcrestzrpc
Modeconsole
consolestdout/stderrfilePathvolume
Encodingjson
jsonplain
TimeFormat2006-01-02T15:04:05.000Z07:00
Pathlogs
Levelinfo
infoerrorinfosevereinfoerrorsevere
Compressfile
KeepDaysconsole
StackCooldownMillis

另外对于 SetUp 函数做了如下几件事:

  • 设定日志等级
  • 初始化时间格式
  • 根据编码方式初始化存储日志编码类型
  • 根据设定的模式来初始化 Writer 句柄

Logx 的默认接口实现

对于 logx 打印日志的具体接口定义在:logx 包的 logger.go 文件中

对于上述接口,根据需要传递的参数我们可以分为如下几类:

ErrorInfoSlowfmt.Sprint(...)string
ErrorfInfofSlowf
ErrorvInfovSlowvjson marshal
ErrorwInfowSlowwkey:value
WithContexttrace-idspan-id
WithDurationduration

例如接口名后缀带有 w 的,是需要咱们传入 key:value 的,例如传入的结构是这样的:

实际上我们可以看到在 logx 源码中,其实有很多文件都已经根据自己的使用情况去实现了上述 Logger 接口

举一个 traceLogger 的例子

实际上我们可以直接看到,我们之前实现的 GetTenant rpc 方法

我们可以看到当调用了NewGetTenantLogic 方法之后,实际上是会调用 logx.WithContext(ctx) 初始化一个 traceLogger 的句柄

traceLogger 实现了上述 Logger 接口, 因此,当我们需要在 rpc 中打印日志的时候,我们可以这样来使用

这个时候,实际上是调用的 traceLogger 对应的实现代码

我们可以看到,打印出来的日志,是我们所期望的信息

此处的字段对应含义是这样的:

  • Timestamp

时间戳

  • Level

日志等级

  • Duration

时间间隔

  • Caller

日志调用者

  • Content

具体的日志信息

仔细查看上述日志,我们可以发现还有 trace 和 span 字段也打印出来了,但是 logEntry 为什么没有定义呢

咱们稍微追一下代码,不难看出,是 traceLogger 内部的 info 函数进行日志信息的拼接

Logx 自定义存储日志位置 和 实现自定义接口的方式

Logx 自定义存储日志位置 和 实现自定义接口的方式其实我在这里就不需要过多的解释了,简单说明一下实现手段就可以了,有必要的话咱们可以查看 go-zero 官方文档 go-zero.dev/cn/docs/com…

自定义存储日志位置

对于咱们需要修改日志的输出位置,实际上我们可以仔细思考一下,对于日志的数据,go-zero 还是使用的 golang io 包中的 Writer 接口

咱们只需要定义对象,去实现 Writer 接口 中的 Write(p []byte) (n int, err error) 方法就可以了

官网也给了我们例子,例如咱们实现输出的日志往 kafka 里面吐,我们就可以这样

实现自定义接口

实现自定义接口,咱们其实刚才看 traceLogger 的实现方式,我们就能领悟到, traceLogger 去实现 Logger 接口中的方法,并且加入自己自定义的逻辑,例如加上了 trace 和 span

那么对于我们自定义接口,其实也是非常容易的,照葫芦画瓢即可了