Docker启动jaeger链路追踪,并在kratos中使用jaeger

发布时间 2023-10-12 16:01:42作者: 左耳钢琴右耳吉他

1. 拉取并启动docker镜像

docker run --rm --name jaeger \
  -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 4317:4317 \
  -p 4318:4318 \
  -p 14250:14250 \
  -p 14268:14268 \
  -p 14269:14269 \
  -p 9411:9411 \
  jaegertracing/all-in-one:1.50

image

2. 访问UI界面

http://localhost:16686
image

3. 在kratos项目中使用jaeger

  • 在main.go文件中加入代码,初始化provider
点击查看代码
import (
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/exporters/jaeger"
	"go.opentelemetry.io/otel/sdk/resource"
	"go.opentelemetry.io/otel/sdk/trace"
	semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
)

func tracerProvider(url string) (*trace.TracerProvider, error) {
	// Create the Jaeger exporter
	exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
	if err != nil {
		return nil, err
	}
	tp := trace.NewTracerProvider(
		// Always be sure to batch in production.
		trace.WithBatcher(exp),
		// Record information about this application in a Resource.
		trace.WithResource(resource.NewWithAttributes(
			semconv.SchemaURL,
			semconv.ServiceName(conf.ServiceName),
			semconv.ServiceVersion(conf.Version),
			attribute.String("environment", conf.Config.Service.Env),
		)),
	)
	return tp, nil
}

func init() {
	conf.NewViperConfig()
	// 初始化jaeger
	tp, err := tracerProvider(conf.Config.Jaeger.Url)
	if err != nil {
		log.Fatal(err)
	}
	otel.SetTracerProvider(tp)

}
  • 在接口中注入链路追踪代码, 通过传递上下文参数将各层函数链接起来
newCtx, span := otel.Tracer("jaeger").Start(ctx, "service.QueryJaegerDemo")
defer span.End()
  • service层注入,eg:
点击查看代码
func (s *DemoService) QueryJaegerDemoRecord(ctx context.Context, req *v1.QueryJaegerDemoRequest) (*v1.QueryJaegerDemoResponse, error) {
	newCtx, span := otel.Tracer("jaeger").Start(ctx, "service.QueryJaegerDemo")
	defer span.End()

	// 通过请求参数查询数据
	deviceData, err := s.activity.QueryJaegerDemo(newCtx, req.GetName())
	if err != nil {
		return nil, err
	}
	// 数据赋值
	tmp := v1.JaegerDemo{
		Id:         deviceData.ID,
		Name:       deviceData.Name,
		Age:        deviceData.Age,
		CreateTime: timestamppb.New(deviceData.CreateTime),
	}
	// 填充响应体
	response := &v1.QueryJaegerDemoResponse{Data: &tmp}
	utils.DefaultSuccessResponse(response)
	// 返回响应
	return response, nil
}
  • biz层注入,eg:
点击查看代码
func (uc *ActivityUsecase) QueryJaegerDemo(ctx context.Context, name string) (*JaegerDemo, error) {
	newCtx, span := otel.Tracer("jaeger").Start(ctx, "biz.QueryJaegerDemo")
	defer span.End()

	coinInfo, err := uc.repo.QueryJaegerDemo(newCtx, name)
	// 查询不到数据
	if errors.Is(err, gorm.ErrRecordNotFound) {
		// 将查询参数填入错误信息中
		metaData := map[string]string{
			"name": name,
		}
		return nil, v1.ErrorErrorReasonNotFound("Not found info").WithMetadata(metaData)
	}
	if err != nil {
		uc.log.Errorf("option database error: %s", err.Error())
		return nil, v1.ErrorErrorReasonInternalError(errcode.InternalErrorDatabase)
	}

	return &coinInfo, err
}
  • data层注入,eg:
点击查看代码
func (r *activityRepo) QueryJaegerDemo(ctx context.Context, name string) (biz.JaegerDemo, error) {
	_, span := otel.Tracer("jaeger").Start(ctx, "data.QueryJaegerDemo")
	defer span.End()

	var res biz.JaegerDemo
	sql := r.data.LocalBase.
		Table("test_table").
		Select([]string{"id", "name", "age", "create_time"}).
		Where("name = ?", name)
	err := sql.Take(&res).Error

	return res, err
}

4. 请求接口,在UI界面查看链路情况

image

  • 通过链路追踪可以看到各层函数的执行情况
    image