builders.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. package otel
  2. import (
  3. "context"
  4. "crypto/tls"
  5. "crypto/x509"
  6. "errors"
  7. "fmt"
  8. "time"
  9. "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc"
  10. "go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp"
  11. "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
  12. "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
  13. "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
  14. sdkmetric "go.opentelemetry.io/otel/sdk/metric"
  15. "google.golang.org/grpc/credentials"
  16. )
  17. // buildProtocolExporter builds trace and metric exporters based on the provided configuration.
  18. func buildProtocolExporter(config *Config) (te *otlptrace.Exporter, me sdkmetric.Exporter, err error) {
  19. switch config.Protocol {
  20. case "grpc":
  21. te, me, err = buildGRPCExporters(config)
  22. case "http/protobuf", "http", "https":
  23. te, me, err = buildHTTPExporters(config)
  24. default:
  25. err = fmt.Errorf("unsupported OpenTelemetry protocol: %s", config.Protocol)
  26. }
  27. return
  28. }
  29. // buildGRPCExporters builds GRPC exporters based on the provided configuration.
  30. func buildGRPCExporters(config *Config) (*otlptrace.Exporter, sdkmetric.Exporter, error) {
  31. tracerOpts := []otlptracegrpc.Option{}
  32. meterOpts := []otlpmetricgrpc.Option{}
  33. if tlsConf, err := buildTLSConfig(config); tlsConf != nil && err == nil {
  34. creds := credentials.NewTLS(tlsConf)
  35. tracerOpts = append(tracerOpts, otlptracegrpc.WithTLSCredentials(creds))
  36. meterOpts = append(meterOpts, otlpmetricgrpc.WithTLSCredentials(creds))
  37. } else if err != nil {
  38. return nil, nil, err
  39. }
  40. // This context limits connect timeout, not the whole lifetime of the exporter
  41. trctx, trcancel := context.WithTimeout(
  42. context.Background(), withDefaultTimeout(config, config.TracesConnTimeout),
  43. )
  44. defer trcancel()
  45. traceExporter, err := otlptracegrpc.New(trctx, tracerOpts...)
  46. if err != nil {
  47. err = fmt.Errorf("can't connect to OpenTelemetry collector: %s", err)
  48. }
  49. if !config.EnableMetrics {
  50. return traceExporter, nil, err
  51. }
  52. // This context limits connect timeout, not the whole lifetime of the exporter
  53. mtctx, mtcancel := context.WithTimeout(
  54. context.Background(), withDefaultTimeout(config, config.MetricsConnTimeout),
  55. )
  56. defer mtcancel()
  57. metricExporter, err := otlpmetricgrpc.New(mtctx, meterOpts...)
  58. if err != nil {
  59. err = fmt.Errorf("can't connect to OpenTelemetry collector: %s", err)
  60. }
  61. return traceExporter, metricExporter, err
  62. }
  63. // buildHTTPExporters builds HTTP exporters based on the provided configuration.
  64. func buildHTTPExporters(config *Config) (*otlptrace.Exporter, sdkmetric.Exporter, error) {
  65. tracerOpts := []otlptracehttp.Option{}
  66. meterOpts := []otlpmetrichttp.Option{}
  67. if tlsConf, err := buildTLSConfig(config); tlsConf != nil && err == nil {
  68. tracerOpts = append(tracerOpts, otlptracehttp.WithTLSClientConfig(tlsConf))
  69. meterOpts = append(meterOpts, otlpmetrichttp.WithTLSClientConfig(tlsConf))
  70. } else if err != nil {
  71. return nil, nil, err
  72. }
  73. // This context limits connect timeout, not the whole lifetime of the exporter
  74. trctx, trcancel := context.WithTimeout(
  75. context.Background(), withDefaultTimeout(config, config.TracesConnTimeout),
  76. )
  77. defer trcancel()
  78. traceExporter, err := otlptracehttp.New(trctx, tracerOpts...)
  79. if err != nil {
  80. err = fmt.Errorf("can't connect to OpenTelemetry collector: %s", err)
  81. }
  82. if !config.EnableMetrics {
  83. return traceExporter, nil, err
  84. }
  85. // This context limits connect timeout, not the whole lifetime of the exporter
  86. mtctx, mtcancel := context.WithTimeout(
  87. context.Background(), withDefaultTimeout(config, config.MetricsConnTimeout),
  88. )
  89. defer mtcancel()
  90. metricExporter, err := otlpmetrichttp.New(mtctx, meterOpts...)
  91. if err != nil {
  92. err = fmt.Errorf("can't connect to OpenTelemetry collector: %s", err)
  93. }
  94. return traceExporter, metricExporter, err
  95. }
  96. // buildTLSConfig constructs a tls.Config based on the provided configuration.
  97. func buildTLSConfig(config *Config) (*tls.Config, error) {
  98. // If no server certificate is provided, we assume no TLS is needed
  99. if len(config.ServerCert) == 0 {
  100. return nil, nil
  101. }
  102. // Attach root CAs
  103. certPool := x509.NewCertPool()
  104. if !certPool.AppendCertsFromPEM(config.ServerCert) {
  105. return nil, errors.New("can't load OpenTelemetry server cert")
  106. }
  107. tlsConf := tls.Config{RootCAs: certPool}
  108. // If there is not client cert or key, return the config with only root CAs
  109. if len(config.ClientCert) == 0 || len(config.ClientKey) == 0 {
  110. return &tlsConf, nil
  111. }
  112. cert, err := tls.X509KeyPair(config.ClientCert, config.ClientKey)
  113. if err != nil {
  114. return nil, fmt.Errorf("can't load OpenTelemetry client cert/key pair: %s", err)
  115. }
  116. tlsConf.Certificates = []tls.Certificate{cert}
  117. return &tlsConf, nil
  118. }
  119. func withDefaultTimeout(config *Config, timeout time.Duration) time.Duration {
  120. // In case, timeout is zero or negative we assume it was not set
  121. // (or was set to invalid value) and use the default timeout
  122. if timeout <= 0 {
  123. return config.ConnTimeout
  124. }
  125. return timeout
  126. }