首页 > 解决方案 > 向 Cloud Run 中的内部方法添加跟踪

问题描述

我们想为部署在 Cloud Run 上的服务中使用的方法添加跟踪。

跟踪已提供 Cloud Run 请求:


假设我们有以下 gRPC 方法:

func (s *myServiceService) SyncTable(ctx context.Context, req *pb.SyncTableRequest) (*longrunning.Operation, error) {

    //.... some stuff here...
    
    // making a call to the internal method, which has a tracing span
    err := dropRequestOnStorage(ctx, ...)
    if err != nil {
        return nil, err
    }
    
    return op, nil
}

这是一个内部方法的示例,我们向其中添加了 Trace span,并由主 gRPC 方法调用:

// dropRequestOnStorage loads the requests on the relevant bucket.
func dropRequestOnStorage(ctx context.Context, filename string, operationID string, req *pb.ExtractDataRequest) error {
 
    // add tracing to this method.
    ctx, span := otel.Tracer("").Start(ctx, "dropRequestOnStorage")
    defer span.End()

    // load json object to storage
    reqByte, err := protojson.Marshal(req)
    if err != nil {
        fmt.Println(err)
    }
    wc := storageClient.Bucket("my-bucket-with-cool-stuff").Object(filename).NewWriter(ctx)
    wc.ContentType = "application/json"
    
    _, err = wc.Write(reqByte)
    if err != nil {
        fmt.Println(err)
    }
    wc.Close()
    fmt.Println(filename)
    return nil
}

查看 Google Cloud Run 的跟踪,我看到了上述方法的跟踪:

谷歌云运行跟踪

尽管context从主 gRPC 传递到内部方法,但 Tracing 并没有被拉到底层内部。内部方法生成的跟踪不会“接收”主 gRPC 跟踪作为父级。

这是因为 Cloud Run 提供的默认跟踪是由 Cloud Run 内部完成的吗?因此在 gRPC 方法的上下文中不可用?

标签: google-cloud-runopen-telemetry

解决方案


使用 gRPC 拦截器进行跟踪


让它工作的唯一方法是添加 gRPC 拦截器来为每个 gRPC 方法创建跟踪跨度。

package main

import (
    "context"
    texporter "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
    "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/propagation"
    sdktrace "go.opentelemetry.io/otel/sdk/trace"
    "google.golang.org/grpc"
    "log"
    "net"
    "os"
)

func init() {

    // Pre-declare err to avoid shadowing.
    var err error

    // initialising tracing exporter
    //exporter, err := stdout.NewExporter(stdout.WithPrettyPrint())
    exporter, err := texporter.NewExporter(texporter.WithProjectID("alis-de"))
    if err != nil {
        log.Fatalf("texporter.NewExporter: %v", err)
    }

    tp := sdktrace.NewTracerProvider(
        sdktrace.WithSampler(sdktrace.AlwaysSample()),
        sdktrace.WithSyncer(exporter),
    )

    otel.SetTracerProvider(tp)
    otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
}

func main() {
    log.Printf("starting server...")
    
    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
        log.Printf("Defaulting to port %s", port)
    }

    listener, err := net.Listen("tcp", ":"+port)
    if err != nil {
        log.Fatalf("net.Listen: %v", err)
    }
    
    // Attaching grpc interceptors to automatically enable tracing at gRCP methods
    grpcServer := grpc.NewServer(
        grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
        grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor()),
    )

    pb.RegisterOperationsServer(grpcServer, &operationsService{})

    if err = grpcServer.Serve(listener); err != nil {
        log.Fatal(err)
    }
}

跟踪现在拉到控制台:

使用 gRPC 拦截器进行跟踪

但是,查看 Traces,现在(不幸的是??)有两个跟踪条目:

  1. Cloud Run 提供的默认跟踪(没有子跟踪)
  2. gRPC 拦截器生成的新跟踪(带有反映内部调用方法的子跟踪)

重复的跟踪条目


推荐阅读