tencent cloud

Feedback

Connecting Go Applications Using OpenTelemetry-Go (Recommended)

Last updated: 2024-06-19 16:31:30
    Note:
    OpenTelemetry is a collection of tools, APIs, and SDKs for monitoring, generating, collecting, and exporting telemetry data (metrics, logs, and traces) to help users analyze the performance and behaviors of the software. For more information about OpenTelemetry, see the OpenTelemetry Official Website.
    The OpenTelemetry community is active, with rapid technological changes, and widely compatible with mainstream programming languages, components, and frameworks, making its link-tracing capability highly popular for cloud-native microservices and container architectures.
    This document will introduce how to connect Go applications with the community's OpenTelemetry-Go scheme. OpenTelemetry-Go provides a series of APIs so that users can send performance data to the observability platform's server. This document introduces how to connect Tencent Cloud APM based on OpenTelemetry Go through the most common application behaviors, such as HTTP services and database access. For more uses of OpenTelemetry-Go, see the Project Homepage.

    Prerequisites

    This scheme supports the officially supported versions of Go, currently 1.21 and 1.22. For lower versions, the connection is theoretically possible, but the community does not maintain full compatibility. For specific information, see the community's Compatibility Description.

    Preliminary steps: Get the connect point and Token.

    1. Log in to the TCOP console.
    2. In the left menu column, select Application Performance Management > Application monitoring, and click Application list > Access application.
    3. In the Data access drawer that pops up on the right, click the Go language.
    4. On the Access Go application page, select the Region and Business System you want to connect.
    5. Select Access protocol type as OpenTelemetry.
    6. Reporting method Choose your desired reporting method, and obtain your Access Point and Token.
    Note:
    Private network reporting: Using this reporting method, your service needs to run in the Tencent Cloud VPC. Through VPC connecting directly, you can avoid the security risks of public network communication and save on reporting traffic overhead.
    Public network reporting: If your service is deployed locally or in non-Tencent Cloud VPC, you can report data in this method. However, it involves security risks in public network communication and incurs reporting traffic fees.

    Connecting Go Applications

    Step 1: Introduce OpenTelemetry-related dependencies to implement the SDK initialization logic.

    package main import ( "context" "errors" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" "go.opentelemetry.io/otel/propagation" "go.opentelemetry.io/otel/sdk/resource" "go.opentelemetry.io/otel/sdk/trace" sdktrace "go.opentelemetry.io/otel/sdk/trace" "log" )
    func setupOTelSDK(ctx context.Context) (*trace.TracerProvider, error) { opts := []otlptracegrpc.Option{ otlptracegrpc.WithEndpoint("<endpoint>"), // Replace <endpoint> with the reporting address otlptracegrpc.WithInsecure(), } exporter, err := otlptracegrpc.New(ctx, opts...) if err != nil { log.Fatal(err) } r, err := resource.New(ctx, []resource.Option{ resource.WithAttributes( attribute.KeyValue{Key: "token", Value: "<token>"}, // Replace <token> with the business system Token attribute.KeyValue{Key: "service.name", Value: "<servceName>"}, // Replace <serviceName> with the application name attribute.KeyValue{Key: "host.name", Value: "<hostName>"}, // Replace <hostName> with the IP address ), }...) if err != nil { log.Fatal(err) } tp := sdktrace.NewTracerProvider( sdktrace.WithSampler(sdktrace.AlwaysSample()), sdktrace.WithBatcher(exporter), sdktrace.WithResource(r), ) otel.SetTracerProvider(tp) otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) return tp, nil }
    The corresponding field descriptions are as follows, replace them according to actual conditions.
    <serviceName>: Application name. Multiple application processes connecting with the same serviceName are displayed as multiple instances under the same application in APM. The application name can be up to 63 characters and can only contain lowercase letters, digits, and the separator (-), and it must start with a lowercase letter and end with a digit or lowercase letter.
    <token>: The business system Token obtained in the preliminary steps.
    <hostName>: The hostname of this instance, which is the unique identifier of the application instance. It can usually be set to the IP address of the application instance.
    <endpoint>: The connect point obtained in the preliminary steps.

    Step 2: SDK initialization and start the HTTP service.

    package main
    
    import ( "context" "errors" "fmt" "log" "net" "net/http" "os" "os/signal" "time" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" )
    func main() { if err := run(); err != nil { log.Fatalln(err) } } func run() (err error) { ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) defer stop() // Initialize SDK otelShutdown, err := setupOTelSDK(ctx) if err != nil { return } // Graceful shutdown defer func() { err = errors.Join(err, otelShutdown(context.Background())) }() // Start HTTP service srv := &http.Server{ Addr: ":8080", BaseContext: func(_ net.Listener) context.Context { return ctx }, ReadTimeout: time.Second, WriteTimeout: 10 * time.Second, Handler: newHTTPHandler(), } srvErr := make(chan error, 1) go func() { srvErr <- srv.ListenAndServe() }()
    select { case err = <-srvErr: return case <-ctx.Done(): stop() } err = srv.Shutdown(context.Background()) return }
    If implementing an HTTP service through frameworks like Gin, the Event Tracking method will differ. See the community's Framework List for details on other frameworks's Event Tracking methods.

    Step 3: Enhanced Event Tracking for HTTP APIs.

    func newHTTPHandler() http.Handler { mux := http.NewServeMux() handleFunc := func(pattern string, handlerFunc func(http.ResponseWriter, *http.Request)) { // HTTP routes Event Tracking handler := otelhttp.WithRouteTag(pattern, http.HandlerFunc(handlerFunc)) mux.Handle(pattern, handler) } // Register APIs handleFunc("/simple", simpleIOHandler) // Enhanced Event Tracking for all APIs handler := otelhttp.NewHandler(mux, "/") return handler }
    
    func simpleIOHandler(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "ok") }

    Connection Verification

    After you start the Go application, access the corresponding API through port 8080, for example, https://localhost:8080/simple, the application reports HTTP request-related link data to APM. In normal traffic cases, the connected applications will be displayed in APM > Application monitoring > Application list and the connected application instances will be displayed in APM > Application monitoring > Application details > Instance monitoring. Since there is a certain latency in the processing of observable data, if the application or instance does not appear in the console after connecting, wait for about 30 seconds.

    More Event Tracking Sample

    Accessing Redis

    Initialization
    import (
    "github.com/redis/go-redis/v9"
    "github.com/redis/go-redis/extra/redisotel/v9"
    )
    
    var rdb *redis.Client
    
    // InitRedis initializing Redis client.
    func InitRedis() *redis.Client {
    rdb := redis.NewClient(&redis.Options{
    Addr: "127.0.0.1:6379",
    Password: "", // no password
    })
    if err := redisotel.InstrumentTracing(rdb); err != nil {
    panic(err)
    }
    if err := redisotel.InstrumentMetrics(rdb); err != nil {
    panic(err)
    }
    return rdb
    }
    Data Access
    func redisRequest(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    rdb := InitRedis()
    val, err := rdb.Get(ctx, "foo").Result()
    if err != nil {
    log.Printf("redis err......")
    panic(err)
    }
    fmt.Println("redis res: ", val)
    }

    Accessing MySQL

    Initialization
    import (
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "gorm.io/gorm/schema"
    "gorm.io/plugin/opentelemetry/tracing"
    )
    
    var GormDB *gorm.DB
    
    type TableDemo struct {
    ID int gorm:"column:id"
    Value string gorm:"column:value"
    }
    
    func InitGorm() {
    var err error
    
    dsn := "root:4T$er3deffYuD#9Q@tcp(127.0.0.1:3306)/db_demo?charset=utf8mb4&parseTime=True&loc=Local"
    GormDB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
    NamingStrategy: schema.NamingStrategy{
    SingularTable: true, // Use singular table names.
    },
    })
    if err != nil {
    panic(err)
    }
    // Add tracing reporting logic.
    //Fill in DBName based on actual conditions. In the APM topology diagram, identify the node using the DBName field. In this example, use mockdb-mysql.
    if err = GormDB.Use(tracing.NewPlugin(tracing.WithoutMetrics(),tracing.WithDBName("mockdb-mysql"))); err != nil {
    panic(err)
    }
    }

    Data Access

    func gormRequest(ctx context.Context) {
    var val string
    if err := gormclient.GormDB.WithContext(ctx).Model(&gormclient.TableDemo{}).Where("id = ?", 1).Pluck("value", &val).Error; err != nil {
    panic(err)
    }
    fmt.Println("MySQL query result: ", val)
    }

    Internal method Event Tracking and setting Span attributes.

    The following code demonstrates internal method Event Tracking by inserting an Internal type Span into the current link context.
    func internalSpanFunc(w http.ResponseWriter, r *http.Request) {
    internalInvoke(r)
    io.WriteString(w, "ok")
    }
    
    func internalInvoke(r *http.Request) {
    // Create an Internal Span.
    _, span := tracer.Start(r.Context(), "internalInvoke")
    defer span.End()
    // Business logic is omitted.
    // Set Span Attributes.
    span.SetAttributes(attribute.KeyValue{
    Key: "label-key-1",
    Value: attribute.StringValue("label-value-1"),
    })
    }
    
    
    Contact Us

    Contact our sales team or business advisors to help your business.

    Technical Support

    Open a ticket if you're looking for further assistance. Our Ticket is 7x24 avaliable.

    7x24 Phone Support