微服務(wù)框架 go-zero logx 日志組件剖析
addTenant api 和 rpc 的實(shí)現(xiàn)
上一篇我們說到咱們還剩下 addTenant 功能還未實(shí)現(xiàn),不知道有沒有兄弟感興趣去實(shí)驗(yàn)一波的,本篇文章進(jìn)行簡要補(bǔ)充
根據(jù)上一篇文章分析,其實(shí)我們只需要執(zhí)行如下幾步即可:
編寫 tenant.api,提供外部 addTenant 的 http 接口
編寫 tenant.api
提供一個(gè) POST http 的接口 / api /tenant/addtenant
type?(
????????AddTenantReq?{
????????????????Name?string?`json:"name"`
????????????????Addr?string?`json:"addr"`
????????}
????????AddTenantRsp?{
????????????????Id?string?`json:"id"`
????????}
)
service?tenant?{
????????@handler?addTenant
????????post?/api/tenant/addtenant(AddTenantReq)?returns?(AddTenantRsp)
goctl 生成 api 代碼
goctl?api?go?-api?tenant.api??-dir?.
修改 api 的配置和邏輯層,讓 api 層去調(diào)用之前寫好的 rpc 接口 即可
對(duì)于配置可以模仿上一篇文章 order.api 的配置進(jìn)行修改,另外只需要調(diào)整 addTenant 的 logic 層即可
func?(l?*AddTenantLogic)?AddTenant(req?*types.AddTenantReq)?(*types.AddTenantRsp,?error)?{
???//?todo:?add?your?logic?here?and?delete?this?line
???rsp,err?:=l.svcCtx.TenantRpc.AddTenant(l.ctx,?&tenant.AddTenantReq{
??????Name:?req.Name,
??????Addr:?req.Addr,
???})
???if?err?!=nil{
??????return?nil,err
???}
???return?&types.AddTenantRsp{Id:?rsp.Id},nil
}
具體的代碼案例可以訪問地址:https://github.com/qingconglaixueit/my_test_Demo
下面我們來看是 go-zero 中 日志組件 logx 的剖析
logx 日志組件剖析
對(duì)于 logx 日志組件,分別從如下幾個(gè)方面來聊一聊我的理解,如果描述有不當(dāng)?shù)牡胤?,還請多加評(píng)論多加交流
Go-zero 中 logx 是如何使用的?
Logx 基本的數(shù)據(jù)結(jié)構(gòu)
Logx 的默認(rèn)接口實(shí)現(xiàn)
Logx 日志存儲(chǔ)位置,以及自定義存儲(chǔ)日志位置的實(shí)現(xiàn)
Logx 實(shí)現(xiàn)自定義接口的方式
Go-zero 中 logx 是如何使用的?
我們以之前的 demo ,關(guān)于 tenant 的 rpc 部分作為例子,追蹤一下代碼,是如何走到日志部分的邏輯的
可以看到在 tenant.go 的文件中,做的是服務(wù)的啟動(dòng)
zrpc.MustNewServer 實(shí)際上是調(diào)用 go-zero 的 zrpc 包 的 NewServer 函數(shù),傳入的參數(shù)是
c RpcServerConf , 我們 rpc 服務(wù)的配置,就是咱們項(xiàng)目中的 etc/tenant.yaml
今天不聊關(guān)于 RpcServerConf 的結(jié)構(gòu),咱們重點(diǎn)說說 logx
register internal.RegisterFn 注冊服務(wù)的回調(diào)函數(shù)
NewServer 函數(shù)做了如下幾件事情:
RpcServerConf 配置數(shù)據(jù)的有效性檢查
初始化 metrics 的 options
設(shè)置服務(wù)名,注冊 etcd 服務(wù),服務(wù)名就是上述配置文件中的 Name 字段
c.SetUp() 啟動(dòng)整個(gè)服務(wù)
對(duì)于 logx 日志組件的啟動(dòng)就是在 c.SetUp() 中完成
Logx 基本的數(shù)據(jù)結(jié)構(gòu)
繼續(xù)看到 logx.SetUp() 中的具體實(shí)現(xiàn) , 函數(shù)需要傳入的數(shù)據(jù)結(jié)構(gòu)是這樣的 LogConf
type?LogConf?struct?{
???ServiceName?????????string?`json:",optional"`
???Mode????????????????string?`json:",default=console,options=[console,file,volume]"`
???Encoding????????????string?`json:",default=json,options=[json,plain]"`
???TimeFormat??????????string?`json:",optional"`
???Path????????????????string?`json:",default=logs"`
???Level???????????????string?`json:",default=info,options=[info,error,severe]"`
???Compress????????????bool???`json:",optional"`
???KeepDays????????????int????`json:",optional"`
???StackCooldownMillis?int????`json:",default=100"`
}
ServiceName
:設(shè)置服務(wù)名稱,可選。在volume
模式下,該名稱用于生成日志文件。在rest/zrpc
服務(wù)中,名稱將被自動(dòng)設(shè)置為rest
或zrpc
的名稱。
Mode
:輸出日志的模式,默認(rèn)是console
console
模式將日志寫到stdout/stderr
file
模式將日志寫到Path
指定目錄的文件中volume
模式在 docker 中使用,將日志寫入掛載的卷中
Encoding
: 指示如何對(duì)日志進(jìn)行編碼,默認(rèn)是json
json
模式以 json 格式寫日志plain
模式用純文本寫日志,并帶有終端顏色顯示
TimeFormat
:自定義時(shí)間格式,可選。默認(rèn)是2006-01-02T15:04:05.000Z07:00
Path
:設(shè)置日志路徑,默認(rèn)為logs
Level
: 用于過濾日志的日志級(jí)別。默認(rèn)為info
info
,所有日志都被寫入error
,info
的日志被丟棄severe
,info
和error
日志被丟棄,只有severe
日志被寫入
Compress
: 是否壓縮日志文件,只在file
模式下工作
KeepDays
:日志文件被保留多少天,在給定的天數(shù)之后,過期的文件將被自動(dòng)刪除。對(duì)console
模式?jīng)]有影響
StackCooldownMillis
:多少毫秒后再次寫入堆棧跟蹤。用來避免堆棧跟蹤日志過多
另外對(duì)于 SetUp 函數(shù)做了如下幾件事:
設(shè)定日志等級(jí)
初始化時(shí)間格式
根據(jù)編碼方式初始化存儲(chǔ)日志編碼類型
根據(jù)設(shè)定的模式來初始化 Writer 句柄
Logx 的默認(rèn)接口實(shí)現(xiàn)
對(duì)于 logx 打印日志的具體接口定義在:logx 包的 logger.go 文件中
對(duì)于上述接口,根據(jù)需要傳遞的參數(shù)我們可以分為如下幾類:
Error
,Info
,Slow
: 將任何類型的信息寫進(jìn)日志,使用fmt.Sprint(...)
來轉(zhuǎn)換為string
Errorf
,Infof
,Slowf
: 將指定格式的信息寫入日志
Errorv
,Infov
,Slowv
: 將任何類型的信息寫入日志,用json marshal
編碼
Errorw
,Infow
,Sloww
: 寫日志,并帶上給定的key:value
字段
WithContext
:將給定的 ctx 注入日志信息,例如用于記錄trace-id
和span-id
WithDuration
: 將指定的時(shí)間寫入日志信息中,字段名為duration
例如接口名后綴帶有 w 的,是需要咱們傳入 key:value 的,例如傳入的結(jié)構(gòu)是這樣的:
實(shí)際上我們可以看到在 logx 源碼中,其實(shí)有很多文件都已經(jīng)根據(jù)自己的使用情況去實(shí)現(xiàn)了上述 Logger 接口
舉一個(gè) traceLogger 的例子
實(shí)際上我們可以直接看到,我們之前實(shí)現(xiàn)的 GetTenant rpc 方法
我們可以看到當(dāng)調(diào)用了NewGetTenantLogic 方法之后,實(shí)際上是會(huì)調(diào)用 logx.WithContext(ctx) 初始化一個(gè) traceLogger 的句柄
traceLogger 實(shí)現(xiàn)了上述 Logger 接口, 因此,當(dāng)我們需要在 rpc 中打印日志的時(shí)候,我們可以這樣來使用
這個(gè)時(shí)候,實(shí)際上是調(diào)用的 traceLogger 對(duì)應(yīng)的實(shí)現(xiàn)代碼
我們可以看到,打印出來的日志,是我們所期望的信息
此處的字段對(duì)應(yīng)含義是這樣的:
Timestamp
時(shí)間戳
Level
日志等級(jí)
Duration
時(shí)間間隔
Caller
日志調(diào)用者
Content
具體的日志信息
仔細(xì)查看上述日志,我們可以發(fā)現(xiàn)還有 trace 和 span 字段也打印出來了,但是 logEntry 為什么沒有定義呢
咱們稍微追一下代碼,不難看出,是 traceLogger 內(nèi)部的 info 函數(shù)進(jìn)行日志信息的拼接
Logx 自定義存儲(chǔ)日志位置 和 實(shí)現(xiàn)自定義接口的方式
Logx 自定義存儲(chǔ)日志位置 和 實(shí)現(xiàn)自定義接口的方式其實(shí)我在這里就不需要過多的解釋了,簡單說明一下實(shí)現(xiàn)手段就可以了,有必要的話咱們可以查看 go-zero 官方文檔 https://go-zero.dev/cn/docs/component/logx/
自定義存儲(chǔ)日志位置
對(duì)于咱們需要修改日志的輸出位置,實(shí)際上我們可以仔細(xì)思考一下,對(duì)于日志的數(shù)據(jù),go-zero 還是使用的 golang io 包中的 Writer 接口
咱們只需要定義對(duì)象,去實(shí)現(xiàn) Writer 接口 中的 Write(p []byte) (n int, err error) 方法就可以了
官網(wǎng)也給了我們例子,例如咱們實(shí)現(xiàn)輸出的日志往 kafka 里面吐,我們就可以這樣
實(shí)現(xiàn)自定義接口
實(shí)現(xiàn)自定義接口,咱們其實(shí)剛才看 traceLogger 的實(shí)現(xiàn)方式,我們就能領(lǐng)悟到, traceLogger 去實(shí)現(xiàn) Logger 接口中的方法,并且加入自己自定義的邏輯,例如加上了 trace 和 span
那么對(duì)于我們自定義接口,其實(shí)也是非常容易的,照葫蘆畫瓢即可了
\
感謝閱讀,歡迎交流,點(diǎn)個(gè)贊,關(guān)注一波 再走吧
歡迎點(diǎn)贊,關(guān)注,收藏
朋友們,你的支持和鼓勵(lì),是我堅(jiān)持分享,提高質(zhì)量的動(dòng)力
好了,本次就到這里
技術(shù)是開放的,我們的心態(tài),更應(yīng)是開放的。擁抱變化,向陽而生,努力向前行。
我是阿兵云原生,歡迎點(diǎn)贊關(guān)注收藏,下次見~
\