package grpc import ( "context" "strings" "github.com/rs/zerolog" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/metric" "go.opentelemetry.io/otel/trace" "google.golang.org/grpc" appotel "gitea.libretechconsulting.com/rmcguire/go-app/pkg/otel" "gitea.libretechconsulting.com/rmcguire/go-app/pkg/srv/grpc/opts" ) type appGRPCServer struct { ctx context.Context tracer trace.Tracer meter metric.Meter opts *opts.GRPCOpts serverOpts []grpc.ServerOption logger *zerolog.Logger server *grpc.Server shutdownFunc func(context.Context) error doneChan chan error } // Returns a shutdown func, a channel indicating done / error, // and an up-front error if server fails to start func InitGRPCServer(ctx context.Context, opts *opts.GRPCOpts) ( func(context.Context) error, <-chan error, error, ) { appGRPC := &appGRPCServer{ ctx: ctx, tracer: appotel.GetTracer(ctx, "grpc"), meter: appotel.GetMeter(ctx, "grpc"), opts: opts, serverOpts: make([]grpc.ServerOption, 0), logger: zerolog.Ctx(ctx), doneChan: make(chan error), } ctx, span := appGRPC.tracer.Start(ctx, "appgrpc.init") defer span.End() // Prepare grpc.Server for use if err := appGRPC.prepGRPCServer(ctx); err != nil { span.RecordError(err) span.SetStatus(codes.Error, "failed to prepare GRPC Server") return nil, nil, err } // Load span with server info span.SetAttributes(appGRPC.getServerAttributes()...) err := appGRPC.runGRPCServer(ctx) if err != nil { span.RecordError(err) span.SetStatus(codes.Error, "failed to start GRPC Server") return nil, nil, err } appGRPC.logger.Debug().Msg("GRPC Server Started") span.SetStatus(codes.Ok, "") return appGRPC.shutdownFunc, appGRPC.doneChan, nil } // Convert grpc.ServiceInfo map to []attribute.KeyValue func (a *appGRPCServer) getServerAttributes() []attribute.KeyValue { var attrs []attribute.KeyValue for serviceName, info := range a.server.GetServiceInfo() { // Add the service name attrs = append(attrs, attribute.String("grpc.service", serviceName)) // Convert methods into a comma-separated string var methods []string for _, method := range info.Methods { methods = append(methods, method.Name) } // Add methods if present if len(methods) > 0 { attrs = append(attrs, attribute.String("grpc.service.methods", strings.Join(methods, ","))) } // If metadata is a string, store it if metadata, ok := info.Metadata.(string); ok && metadata != "" { attrs = append(attrs, attribute.String("grpc.service.metadata", metadata)) } } return attrs }