一、背景和意义
kratos是go语言中常用的微服务框架,该框架自带有log组件。在打印日志时如果输出traceId就可以方便地追踪同一请求的日志,kratos官方的traceId相关示例比较复杂,对于一个简单的、不涉及大量上下游服务的提供给前端页面调用的http服务,文本提供一个更简单使用示例。
二、创建运行简单的kratos应用
执行如下命令安装kratos工具:
bash
go install github.com/go-kratos/kratos/cmd/kratos/v2@latest
接下来创建一个简单项目:
bash
kratos new kratos-demo -r https://gitee.com/go-kratos/kratos-layout.git
将相关依赖下载到本地:
bash
go mod download
生成代码:
bash
go generate ./...
运行代码:
bash
kratos run
用浏览器打开链接:http://localhost:8000/helloworld/kratos'
。正常情况下会返回:{"message":"Hello kratos"}
同时,服务器上会输出如下一行日志:
log
INFO ts=2024-03-03T18:10:58+08:00 caller=biz/greeter.go:43 service.id=xxxxxx service.name= service.version= trace.id= span.id= msg=CreateGreeter: kratos
对应的打印日志的代码是在internal/biz/greeter.go
这个文件中的如下代码:
go
// CreateGreeter creates a Greeter, and returns the new Greeter.
func (uc *GreeterUsecase) CreateGreeter(ctx context.Context, g *Greeter) (*Greeter, error) {
uc.log.WithContext(ctx).Infof("CreateGreeter: %v", g.Hello)
return uc.repo.Save(ctx, g)
}
在前面输出的日志中,trace.id还是空的。
三、增加traceId输出
修改internal/server/http.go
文件,添加一个增加traceid的中间件方法simpleTraceHandler
:
go
func simpleTraceHandler(handler middleware.Handler) middleware.Handler {
return func(ctx context.Context, req interface{}) (reply interface{}, err error) {
ctx = context.WithValue(ctx, "simpleTraceId", uuid.NewString())
return handler(ctx, req)
}
}
// NewHTTPServer new an HTTP server.
func NewHTTPServer(c *conf.Server, greeter *service.GreeterService, logger log.Logger) *http.Server {
var opts = []http.ServerOption{
http.Middleware(
recovery.Recovery(),
simpleTraceHandler,
),
}
if c.Http.Network != "" {
opts = append(opts, http.Network(c.Http.Network))
}
if c.Http.Addr != "" {
opts = append(opts, http.Address(c.Http.Addr))
}
if c.Http.Timeout != nil {
opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration()))
}
srv := http.NewServer(opts...)
v1.RegisterGreeterHTTPServer(srv, greeter)
return srv
}
simpleTraceHandler
方法使用uuid作为traceId。
接下来修改cmd/kratos-demo/main.go
的代码,增加一个traceId
函数用于获取前面中间件设置的traceId,另外简单应用暂时不需要输出spanId,将其去掉:
go
func traceId() log.Valuer {
return func(ctx context.Context) interface{} {
traceId := ctx.Value("simpleTraceId")
if traceId == nil {
return ""
} else {
return traceId
}
}
}
func main() {
flag.Parse()
logger := log.With(log.NewStdLogger(os.Stdout),
"ts", log.DefaultTimestamp,
"caller", log.DefaultCaller,
"service.id", id,
"service.name", Name,
"service.version", Version,
"trace.id", traceId(),
)
c := config.New(
config.WithSource(
file.NewSource(flagconf),
),
)
defer c.Close()
if err := c.Load(); err != nil {
panic(err)
}
var bc conf.Bootstrap
if err := c.Scan(&bc); err != nil {
panic(err)
}
app, cleanup, err := wireApp(bc.Server, bc.Data, logger)
if err != nil {
panic(err)
}
defer cleanup()
// start and wait for stop signal
if err := app.Run(); err != nil {
panic(err)
}
}
为了验证,我们在internal/service/greeter.go
中也增加一行日志输入,以确认同一请求的traceId是一致的:
go
// GreeterService is a greeter service.
type GreeterService struct {
v1.UnimplementedGreeterServer
uc *biz.GreeterUsecase
log *log.Helper
}
// NewGreeterService new a greeter service.
func NewGreeterService(uc *biz.GreeterUsecase, logger log.Logger) *GreeterService {
return &GreeterService{uc: uc, log: log.NewHelper(logger)}
}
// SayHello implements helloworld.GreeterServer.
func (s *GreeterService) SayHello(ctx context.Context, in *v1.HelloRequest) (*v1.HelloReply, error) {
g, err := s.uc.CreateGreeter(ctx, &biz.Greeter{Hello: in.Name})
if err != nil {
return nil, err
}
s.log.WithContext(ctx).Info("Say Hello get Greeter: %v", g)
return &v1.HelloReply{Message: "Hello " + g.Hello}, nil
}
改好上述代码之后,运行go generate ./...
和kratos run
,访问http://localhost:8000/helloworld/kratos'
两次,可看到如下日志输出:
log
INFO ts=2024-03-03T18:24:01+08:00 caller=biz/greeter.go:43 service.id=xxxxxx service.name= service.version= trace.id=91ad483f-d724-4055-906d-34d255c93b01 msg=CreateGreeter: kratos
INFO ts=2024-03-03T18:24:01+08:00 caller=service/greeter.go:30 service.id=xxxxxx service.name= service.version= trace.id=91ad483f-d724-4055-906d-34d255c93b01 msg=Say Hello get Greeter: %v&{kratos}
INFO ts=2024-03-03T18:24:09+08:00 caller=biz/greeter.go:43 service.id=xxxxxx service.name= service.version= trace.id=c52f4d91-6a7d-4c6d-a69b-7c3f496bc8fe msg=CreateGreeter: kratos
INFO ts=2024-03-03T18:24:09+08:00 caller=service/greeter.go:30 service.id=xxxxxx service.name= service.version= trace.id=c52f4d91-6a7d-4c6d-a69b-7c3f496bc8fe msg=Say Hello get Greeter: %v&{kratos}
第一次请求的traceId为91ad483f-d724-4055-906d-34d255c93b01
,而第二次为c52f4d91-6a7d-4c6d-a69b-7c3f496bc8fe