package main
import (
"context"
"log"
"net/http"
"time"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func main() {
ctx := context.Background()
// 1. 初始化导出器 (连接到本地Collector)
traceExporter, err := otlptracegrpc.New(ctx,
otlptracegrpc.WithEndpoint("localhost:4317"), // OTLP gRPC 收集器端点
otlptracegrpc.WithInsecure(), // 开发环境使用非安全连接
// otlptracegrpc.WithDialOption(grpc.WithBlock()), // 可选:阻塞连接
)
if err != nil {
log.Fatalf("failed to create trace exporter: %v", err)
}
// 2. 配置资源
res, err := resource.New(ctx,
resource.WithAttributes(
semconv.ServiceNameKey.String("my-awesome-go-service"),
semconv.ServiceVersionKey.String("1.0.0"),
),
resource.WithProcess(),
resource.WithHost(),
)
if err != nil {
log.Fatalf("failed to create resource: %v", err)
}
// 3. 创建 BatchSpanProcessor 和 TracerProvider
bsp := sdktrace.NewBatchSpanProcessor(traceExporter)
tp := sdktrace.NewTracerProvider(
sdktrace.WithSampler(sdktrace.AlwaysSample()),
sdktrace.WithResource(res),
sdktrace.WithSpanProcessor(bsp),
)
defer func() {
// 7. 优雅关闭
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
if err := tp.Shutdown(ctx); err != nil {
log.Fatalf("failed to shutdown TracerProvider: %v", err)
}
}()
// 4. 设置全局 TracerProvider 和 Propagator
otel.SetTracerProvider(tp)
otel.SetTextMapPropagator(propagation.TraceContext{})
// 5. 使用自动 Instrumentation 包装 HTTP 处理器
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
// 手动创建一个Span来记录一些操作
tracer := otel.Tracer("example-tracer")
ctx, span := tracer.Start(r.Context(), "my-handler-logic")
defer span.End()
// 模拟一些工作
time.Sleep(100 * time.Millisecond)
span.AddEvent("Did some work!")
w.Write([]byte("Hello, World!"))
})
// 使用 otelhttp 中间件包装整个路由(可选,但推荐用于自动处理HTTP请求的Span)
wrappedHandler := otelhttp.NewHandler(http.DefaultServeMux, "server")
log.Println("Starting server on :8080")
if err := http.ListenAndServe(":8080", wrappedHandler); err != nil {
log.Fatalf("failed to start server: %v", err)
}
}