github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/conf/tracer/tracer.go (about) 1 package tracer 2 3 import ( 4 "context" 5 "log" 6 "os" 7 "os/signal" 8 "syscall" 9 10 "github.com/google/uuid" 11 "github.com/pkg/errors" 12 "go.opentelemetry.io/otel" 13 "go.opentelemetry.io/otel/attribute" 14 "go.opentelemetry.io/otel/exporters/otlp/otlptrace" 15 "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" 16 "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" 17 "go.opentelemetry.io/otel/propagation" 18 "go.opentelemetry.io/otel/sdk/resource" 19 "go.opentelemetry.io/otel/sdk/trace" 20 semconv "go.opentelemetry.io/otel/semconv/v1.16.0" 21 "google.golang.org/grpc/credentials" 22 23 "github.com/machinefi/w3bstream/pkg/depends/base/consts" 24 "github.com/machinefi/w3bstream/pkg/depends/base/types" 25 "github.com/machinefi/w3bstream/pkg/depends/conf/logger" 26 conftls "github.com/machinefi/w3bstream/pkg/depends/conf/tls" 27 "github.com/machinefi/w3bstream/pkg/depends/kit/sqlx/datatypes" 28 ) 29 30 type Config struct { 31 // GrpcEndpoint provider grpc endpoint 32 GrpcEndpoint types.Endpoint `env:""` 33 // HttpEndpoint provider http endpoint 34 HttpEndpoint types.Endpoint `env:""` 35 // ServiceName service name, default use env `PRJ_NAME` 36 ServiceName string `env:""` 37 // ServiceVersion service version, default use env `PRJ_VERSION` 38 ServiceVersion string `env:""` 39 // InstanceID the unique id of current service, default gen uuid when trace config init 40 InstanceID string `env:""` 41 // DebugMode if enable debug mode 42 DebugMode datatypes.Bool `env:""` 43 // TLS config for connecting provider Endpoint 44 TLS *conftls.X509KeyPair 45 46 provider *trace.TracerProvider 47 } 48 49 func (c *Config) IsZero() bool { 50 return c.GrpcEndpoint.IsZero() && c.HttpEndpoint.IsZero() 51 } 52 53 func (c *Config) SetDefault() { 54 if !c.GrpcEndpoint.IsZero() && c.GrpcEndpoint.Port == 0 { 55 c.GrpcEndpoint.Port = 4317 56 } 57 if !c.HttpEndpoint.IsZero() && c.HttpEndpoint.Port == 0 { 58 c.HttpEndpoint.Port = 4318 59 } 60 if c.DebugMode == 0 { 61 c.DebugMode = datatypes.FALSE 62 } 63 } 64 65 func (c *Config) grpcExporter() (*otlptrace.Exporter, error) { 66 options := []otlptracegrpc.Option{otlptracegrpc.WithEndpoint(c.GrpcEndpoint.Host())} 67 if c.GrpcEndpoint.IsTLS() { 68 if !c.TLS.IsZero() { 69 options = append(options, otlptracegrpc.WithTLSCredentials( 70 credentials.NewClientTLSFromCert(c.TLS.TLSConfig().RootCAs, ""), 71 )) 72 } 73 } else { 74 options = append(options, otlptracegrpc.WithInsecure()) 75 } 76 77 return otlptracegrpc.New(context.Background(), options...) 78 } 79 80 func (c *Config) httpExporter() (*otlptrace.Exporter, error) { 81 options := []otlptracehttp.Option{otlptracehttp.WithEndpoint(c.HttpEndpoint.Host())} 82 if c.GrpcEndpoint.IsTLS() { 83 if !c.TLS.IsZero() { 84 options = append(options, otlptracehttp.WithTLSClientConfig(c.TLS.TLSConfig())) 85 } 86 } else { 87 options = append(options, otlptracehttp.WithInsecure()) 88 } 89 90 return otlptracehttp.New(context.Background(), options...) 91 } 92 93 func (c *Config) Init() error { 94 if c.IsZero() { 95 return nil // return nil and using global.defaultTracerValue 96 } 97 98 if c.ServiceName == "" { 99 c.ServiceName = os.Getenv(consts.EnvProjectName) 100 } 101 if c.ServiceVersion == "" { 102 c.ServiceVersion = os.Getenv(consts.EnvProjectVersion) 103 } 104 if c.InstanceID == "" { 105 c.InstanceID = uuid.NewString() 106 } 107 108 var ( 109 exp trace.SpanExporter 110 err error 111 ep types.Endpoint 112 ) 113 114 if !c.GrpcEndpoint.IsZero() { 115 ep = c.GrpcEndpoint 116 exp, err = c.grpcExporter() 117 } else { 118 ep = c.HttpEndpoint 119 exp, err = c.httpExporter() 120 } 121 if err != nil { 122 return errors.Errorf("new exporter failed: ep[%v] err[%v]", ep, err) 123 } 124 125 // resource 126 res := resource.NewWithAttributes( 127 semconv.SchemaURL, 128 semconv.ServiceNameKey.String(c.ServiceName), 129 semconv.ServiceVersionKey.String(c.ServiceVersion), 130 attribute.String("instance", c.InstanceID), 131 ) 132 if err != nil { 133 return errors.Errorf("new otlp resource failed: %v", err) 134 } 135 136 options := []trace.TracerProviderOption{ 137 trace.WithSampler(trace.AlwaysSample()), 138 trace.WithResource(res), 139 trace.WithSyncer(logger.WithSpanMapExporter(logger.OutputFilter())(logger.StdoutSpanExporter())), 140 } 141 if c.DebugMode == datatypes.TRUE { 142 options = append(options, 143 trace.WithBatcher(logger.WithSpanMapExporter(logger.OutputFilter())(exp)), 144 ) 145 } else { 146 options = append(options, 147 trace.WithBatcher(logger.WithSpanMapExporter(logger.OutputFilter())(logger.WithErrIgnoreExporter()(exp))), 148 ) 149 } 150 151 c.provider = trace.NewTracerProvider(options...) 152 153 // set global trace provider 154 otel.SetTracerProvider(c.provider) 155 otel.SetTextMapPropagator(propagation.Baggage{}) 156 157 log.Printf("Trace provider for service `%s@%s` initialized\n", c.ServiceName, c.ServiceVersion) 158 go func() { 159 stopCh := make(chan os.Signal, 1) 160 signal.Notify(stopCh, os.Interrupt, syscall.SIGTERM) 161 <-stopCh 162 err := c.Shutdown(context.Background()) 163 log.Printf("Trace provider for service `%s@%s` shutdown: %v\n", c.ServiceName, c.ServiceVersion, err) 164 }() 165 166 return nil 167 } 168 169 func (c *Config) Shutdown(ctx context.Context) error { 170 err := c.provider.Shutdown(ctx) 171 log.Printf("Trace provider for service `%s@%s` shutdown: %v\n", c.ServiceName, c.ServiceVersion, err) 172 return err 173 }