agent.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. package trace
  2. import (
  3. "context"
  4. "fmt"
  5. "net/url"
  6. "os"
  7. "sync"
  8. "github.com/wuntsong-org/go-zero-plus/core/lang"
  9. "github.com/wuntsong-org/go-zero-plus/core/logx"
  10. "go.opentelemetry.io/otel"
  11. "go.opentelemetry.io/otel/exporters/jaeger"
  12. "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
  13. "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
  14. "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
  15. "go.opentelemetry.io/otel/exporters/zipkin"
  16. "go.opentelemetry.io/otel/sdk/resource"
  17. sdktrace "go.opentelemetry.io/otel/sdk/trace"
  18. semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
  19. )
  20. const (
  21. kindJaeger = "jaeger"
  22. kindZipkin = "zipkin"
  23. kindOtlpGrpc = "otlpgrpc"
  24. kindOtlpHttp = "otlphttp"
  25. kindFile = "file"
  26. protocolUdp = "udp"
  27. )
  28. var (
  29. agents = make(map[string]lang.PlaceholderType)
  30. lock sync.Mutex
  31. tp *sdktrace.TracerProvider
  32. )
  33. // StartAgent starts an opentelemetry agent.
  34. func StartAgent(c Config) {
  35. if c.Disabled {
  36. return
  37. }
  38. lock.Lock()
  39. defer lock.Unlock()
  40. _, ok := agents[c.Endpoint]
  41. if ok {
  42. return
  43. }
  44. // if error happens, let later calls run.
  45. if err := startAgent(c); err != nil {
  46. return
  47. }
  48. agents[c.Endpoint] = lang.Placeholder
  49. }
  50. // StopAgent shuts down the span processors in the order they were registered.
  51. func StopAgent() {
  52. _ = tp.Shutdown(context.Background())
  53. }
  54. func createExporter(c Config) (sdktrace.SpanExporter, error) {
  55. // Just support jaeger and zipkin now, more for later
  56. switch c.Batcher {
  57. case kindJaeger:
  58. u, err := url.Parse(c.Endpoint)
  59. if err == nil && u.Scheme == protocolUdp {
  60. return jaeger.New(jaeger.WithAgentEndpoint(jaeger.WithAgentHost(u.Hostname()),
  61. jaeger.WithAgentPort(u.Port())))
  62. }
  63. return jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(c.Endpoint)))
  64. case kindZipkin:
  65. return zipkin.New(c.Endpoint)
  66. case kindOtlpGrpc:
  67. // Always treat trace exporter as optional component, so we use nonblock here,
  68. // otherwise this would slow down app start up even set a dial timeout here when
  69. // endpoint can not reach.
  70. // If the connection not dial success, the global otel ErrorHandler will catch error
  71. // when reporting data like other exporters.
  72. opts := []otlptracegrpc.Option{
  73. otlptracegrpc.WithInsecure(),
  74. otlptracegrpc.WithEndpoint(c.Endpoint),
  75. }
  76. if len(c.OtlpHeaders) > 0 {
  77. opts = append(opts, otlptracegrpc.WithHeaders(c.OtlpHeaders))
  78. }
  79. return otlptracegrpc.New(context.Background(), opts...)
  80. case kindOtlpHttp:
  81. // Not support flexible configuration now.
  82. opts := []otlptracehttp.Option{
  83. otlptracehttp.WithInsecure(),
  84. otlptracehttp.WithEndpoint(c.Endpoint),
  85. }
  86. if len(c.OtlpHeaders) > 0 {
  87. opts = append(opts, otlptracehttp.WithHeaders(c.OtlpHeaders))
  88. }
  89. if len(c.OtlpHttpPath) > 0 {
  90. opts = append(opts, otlptracehttp.WithURLPath(c.OtlpHttpPath))
  91. }
  92. return otlptracehttp.New(context.Background(), opts...)
  93. case kindFile:
  94. f, err := os.OpenFile(c.Endpoint, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
  95. if err != nil {
  96. return nil, fmt.Errorf("file exporter endpoint error: %s", err.Error())
  97. }
  98. return stdouttrace.New(stdouttrace.WithWriter(f))
  99. default:
  100. return nil, fmt.Errorf("unknown exporter: %s", c.Batcher)
  101. }
  102. }
  103. func startAgent(c Config) error {
  104. AddResources(semconv.ServiceNameKey.String(c.Name))
  105. opts := []sdktrace.TracerProviderOption{
  106. // Set the sampling rate based on the parent span to 100%
  107. sdktrace.WithSampler(sdktrace.ParentBased(sdktrace.TraceIDRatioBased(c.Sampler))),
  108. // Record information about this application in a Resource.
  109. sdktrace.WithResource(resource.NewSchemaless(attrResources...)),
  110. }
  111. if len(c.Endpoint) > 0 {
  112. exp, err := createExporter(c)
  113. if err != nil {
  114. logx.Error(err)
  115. return err
  116. }
  117. // Always be sure to batch in production.
  118. opts = append(opts, sdktrace.WithBatcher(exp))
  119. }
  120. tp = sdktrace.NewTracerProvider(opts...)
  121. otel.SetTracerProvider(tp)
  122. otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) {
  123. logx.Errorf("[otel] error: %v", err)
  124. }))
  125. return nil
  126. }