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>"),
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>"},
attribute.KeyValue{Key: "service.name", Value: "<servceName>"},
attribute.KeyValue{Key: "host.name", Value: "<hostName>"},
),
}...)
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()
otelShutdown, err := setupOTelSDK(ctx)
if err != nil {
return
}
defer func() {
err = errors.Join(err, otelShutdown(context.Background()))
}()
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)) {
handler := otelhttp.WithRouteTag(pattern, http.HandlerFunc(handlerFunc))
mux.Handle(pattern, handler)
}
handleFunc("/simple", simpleIOHandler)
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
func InitRedis() *redis.Client {
rdb := redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
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,
},
})
if err != nil {
panic(err)
}
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) {
_, span := tracer.Start(r.Context(), "internalInvoke")
defer span.End()
span.SetAttributes(attribute.KeyValue{
Key: "label-key-1",
Value: attribute.StringValue("label-value-1"),
})
}
Was this page helpful?