You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
go-zero/core/trace/agent.go

150 lines
4.0 KiB
Go

package trace
import (
"context"
"fmt"
"net/url"
"os"
"sync"
"github.com/zeromicro/go-zero/core/lang"
"github.com/zeromicro/go-zero/core/logx"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/exporters/jaeger"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
"go.opentelemetry.io/otel/exporters/zipkin"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
)
const (
kindJaeger = "jaeger"
kindZipkin = "zipkin"
kindOtlpGrpc = "otlpgrpc"
kindOtlpHttp = "otlphttp"
kindFile = "file"
protocolUdp = "udp"
)
var (
agents = make(map[string]lang.PlaceholderType)
lock sync.Mutex
tp *sdktrace.TracerProvider
)
// StartAgent starts an opentelemetry agent.
func StartAgent(c Config) {
if c.Disabled {
return
}
lock.Lock()
defer lock.Unlock()
_, ok := agents[c.Endpoint]
if ok {
return
}
// if error happens, let later calls run.
if err := startAgent(c); err != nil {
return
}
agents[c.Endpoint] = lang.Placeholder
}
// StopAgent shuts down the span processors in the order they were registered.
func StopAgent() {
lock.Lock()
defer lock.Unlock()
if tp != nil {
_ = tp.Shutdown(context.Background())
tp = nil
}
}
func createExporter(c Config) (sdktrace.SpanExporter, error) {
// Just support jaeger and zipkin now, more for later
switch c.Batcher {
case kindJaeger:
u, err := url.Parse(c.Endpoint)
if err == nil && u.Scheme == protocolUdp {
return jaeger.New(jaeger.WithAgentEndpoint(jaeger.WithAgentHost(u.Hostname()),
jaeger.WithAgentPort(u.Port())))
}
return jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(c.Endpoint)))
case kindZipkin:
return zipkin.New(c.Endpoint)
case kindOtlpGrpc:
// Always treat trace exporter as optional component, so we use nonblock here,
// otherwise this would slow down app start up even set a dial timeout here when
// endpoint can not reach.
// If the connection not dial success, the global otel ErrorHandler will catch error
// when reporting data like other exporters.
opts := []otlptracegrpc.Option{
otlptracegrpc.WithInsecure(),
otlptracegrpc.WithEndpoint(c.Endpoint),
}
if len(c.OtlpHeaders) > 0 {
opts = append(opts, otlptracegrpc.WithHeaders(c.OtlpHeaders))
}
return otlptracegrpc.New(context.Background(), opts...)
case kindOtlpHttp:
// Not support flexible configuration now.
opts := []otlptracehttp.Option{
otlptracehttp.WithInsecure(),
otlptracehttp.WithEndpoint(c.Endpoint),
}
if len(c.OtlpHeaders) > 0 {
opts = append(opts, otlptracehttp.WithHeaders(c.OtlpHeaders))
}
if len(c.OtlpHttpPath) > 0 {
opts = append(opts, otlptracehttp.WithURLPath(c.OtlpHttpPath))
}
return otlptracehttp.New(context.Background(), opts...)
case kindFile:
f, err := os.OpenFile(c.Endpoint, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
return nil, fmt.Errorf("file exporter endpoint error: %s", err.Error())
}
return stdouttrace.New(stdouttrace.WithWriter(f))
default:
return nil, fmt.Errorf("unknown exporter: %s", c.Batcher)
}
}
func startAgent(c Config) error {
AddResources(semconv.ServiceNameKey.String(c.Name))
opts := []sdktrace.TracerProviderOption{
// Set the sampling rate based on the parent span to 100%
sdktrace.WithSampler(sdktrace.ParentBased(sdktrace.TraceIDRatioBased(c.Sampler))),
// Record information about this application in a Resource.
sdktrace.WithResource(resource.NewSchemaless(attrResources...)),
}
if len(c.Endpoint) > 0 {
exp, err := createExporter(c)
if err != nil {
logx.Error(err)
return err
}
// Always be sure to batch in production.
opts = append(opts, sdktrace.WithBatcher(exp))
}
tp = sdktrace.NewTracerProvider(opts...)
otel.SetTracerProvider(tp)
otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) {
logx.Errorf("[otel] error: %v", err)
}))
return nil
}