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>"), // <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>"}, // <token>替换为业务系统Token attribute.KeyValue{Key: "service.name", Value: "<servceName>"}, // <serviceName>替换为应用名 attribute.KeyValue{Key: "host.name", Value: "<hostName>"}, // <hostName>替换为IP地址 ), }...) 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 }
<serviceName>
:应用名,多个使用相同 serviceName 接入的应用进程,在 APM 中会表现为相同应用下的多个实例。应用名最长63个字符,只能包含小写字母、数字及分隔符“ - ”,且必须以小写字母开头,数字或小写字母结尾。<token>
:前置步骤中拿到业务系统 Token。<hostName>
:该实例的主机名,是应用实例的唯一标识,通常情况下可以设置为应用实例的 IP 地址。<endpoint>
:前置步骤中拿到的接入点。package mainimport ( "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() // 初始化 SDK otelShutdown, err := setupOTelSDK(ctx) if err != nil { return } // 优雅关闭 defer func() { err = errors.Join(err, otelShutdown(context.Background())) }() // 启动HTTP服务 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 }
func newHTTPHandler() http.Handler { mux := http.NewServeMux() handleFunc := func(pattern string, handlerFunc func(http.ResponseWriter, *http.Request)) { // 对HTTP路由进行埋点 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") }
https://localhost:8080/simple
,应用就会向 APM 上报处理 HTTP 请求相关的链路数据。在有正常流量的情况下,应用性能监控 > 应用监控 > 应用列表 中将展示接入的应用,应用性能监控 > 应用监控 > 应用详情 > 实例监控中将展示接入的应用实例。由于可观测数据的处理存在一定延时,如果接入后在控制台没有查询到应用或实例,请等待30秒左右。import ("github.com/redis/go-redis/v9""github.com/redis/go-redis/extra/redisotel/v9")var rdb *redis.Client// InitRedis 初始化Redis客户端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}
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)}
import ("gorm.io/driver/mysql""gorm.io/gorm""gorm.io/gorm/schema""gorm.io/plugin/opentelemetry/tracing")var GormDB *gorm.DBtype TableDemo struct {ID int `gorm:"column:id"`Value string `gorm:"column:value"`}func InitGorm() {var err errordsn := "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)}//加入tracing上报逻辑//需要根据实际情况填入DBName,在APM的拓扑图中,通过DBName字段确认节点,在本示例中使用"mockdb-mysql"if err = GormDB.Use(tracing.NewPlugin(tracing.WithoutMetrics(),tracing.WithDBName("mockdb-mysql"))); err != nil {panic(err)}}
func gormRequest(ctx context.Context) {var val stringif 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)}
func internalSpanFunc(w http.ResponseWriter, r *http.Request) {internalInvoke(r)io.WriteString(w, "ok")}func internalInvoke(r *http.Request) {// 创建一个 Internal Span_, span := tracer.Start(r.Context(), "internalInvoke")defer span.End()// 业务逻辑省略// 设置Span的Attributesspan.SetAttributes(attribute.KeyValue{Key: "label-key-1",Value: attribute.StringValue("label-value-1"),})}
本页内容是否解决了您的问题?