diff --git a/cmd/gateway/main.go b/cmd/gateway/main.go index 0e3ce21..2f7225e 100644 --- a/cmd/gateway/main.go +++ b/cmd/gateway/main.go @@ -17,7 +17,10 @@ import ( assetfs "github.com/elazarl/go-bindata-assetfs" grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "github.com/lc-1010/OneBlogService/cmd/internal/middleware" + "github.com/lc-1010/OneBlogService/global" "github.com/lc-1010/OneBlogService/pkg/swagger" + "github.com/lc-1010/OneBlogService/pkg/tracer" pb "github.com/lc-1010/OneBlogService/proto" "github.com/lc-1010/OneBlogService/server" @@ -35,6 +38,22 @@ var port string func init() { flag.StringVar(&port, "port", "8004", "启动端口号") flag.Parse() + err := setupTracer() + if err != nil { + log.Fatalf("init setupTracer err:%v", err) + } +} +func setupTracer() error { + tracerProvider, err := tracer.NewJaegerTrancer( + "grpc", + "127.0.0.1", + "6831", + ) + if err != nil { + return err + } + global.Tracer = tracerProvider + return nil } const SERVICE_NAME = "tag-service" @@ -51,7 +70,11 @@ func grpcHandlderFunc(grpcServer *grpc.Server, otherHander http.Handler) http.Ha func runGrpcServer() *grpc.Server { opts := []grpc.ServerOption{ - grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer()), + grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer( + middleware.Recovery, + middleware.ErrorLog, + middleware.ServerTracing, + )), } s := grpc.NewServer(opts...) diff --git a/cmd/internal/middleware/server_interceptor.go b/cmd/internal/middleware/server_interceptor.go new file mode 100644 index 0000000..6bf7b17 --- /dev/null +++ b/cmd/internal/middleware/server_interceptor.go @@ -0,0 +1,101 @@ +package middleware + +import ( + "context" + "log" + "runtime/debug" + "time" + + "github.com/lc-1010/OneBlogService/global" + "github.com/lc-1010/OneBlogService/pkg/errcode" + "github.com/lc-1010/OneBlogService/pkg/metatext" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + "google.golang.org/grpc" + "google.golang.org/grpc/metadata" +) + +func Recovery(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { + defer func() { + if e := recover(); e != nil { + recoverLog := "recovery log: method:%s, message:%v, stack:%s" + log.Printf(recoverLog, info.FullMethod, + e, string(debug.Stack()[:])) + } + }() + return handler(ctx, req) +} + +// ErrorLog logs any errors that occur during the execution of the grpc server handler function. +// +// ErrorLog takes in the following parameters: +// - ctx: the context.Context object that represents the request context. +// - req: the input request object of any type. +// - info: the *grpc.UnaryServerInfo object that contains information about the server and method being called. +// - handler: the grpc.UnaryHandler function that handles the request and returns a response and an error. +// +// ErrorLog returns the following values: +// - resp: the response object of any type returned by the handler function. +// - err: an error object that contains any error that occurred during the execution of the handler function. +func ErrorLog(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { + resp, err := handler(ctx, req) + if err != nil { + errLog := "error log:method:%s,code :%v,messsage:%v,details:%v" + s := errcode.FromError(err) + log.Printf(errLog, info.FullMethod, s.Code(), s.Err()) + } + return resp, err +} + +func AccessLog(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { + requestLog := "access request log:method :%s,begin_time:%d,request:%v" + beginTime := time.Now().Local().Unix() + log.Printf(requestLog, info.FullMethod, beginTime, req) + + //handler process + resp, err := handler(ctx, req) + + responseLog := "access response log:method:%s,begin_time:%d,end_time:%d response:%v" + endTime := time.Now().Local().Unix() + log.Printf(responseLog, info.FullMethod, beginTime, endTime, resp) + return resp, err +} + +// ServerTracing applies tracing to a gRPC server handler. +// +// It takes a context.Context object, a request object of any type, +// a *grpc.UnaryServerInfo object, and a grpc.UnaryHandler function. +// +// It returns a response object of any type and an error. +func ServerTracing(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + md = metadata.New(nil) + } + // 将请求添加到 metadata + mdmap := metatext.MetadataTextMap{MD: md} + attrs := []attribute.KeyValue{} + + mdmap.Set("method", info.FullMethod) + mdmap.Set("remote-addr", md.Get("x-forwarded-for")[0]) + + _ = mdmap.ForeachKey(func(key, val string) error { + attrs = append(attrs, attribute.String(key, val)) + return nil + }) + + tr := global.Tracer.Tracer("grpc") + spanName := info.FullMethod + attrs = append(attrs, attribute.String("service", "flag-test-ok")) + + spanOpts := []trace.SpanStartOption{ + trace.WithAttributes( + attrs..., + ), + } + + c, span := tr.Start(ctx, spanName, spanOpts...) + defer span.End() + + return handler(c, req) +} diff --git a/pkg/metatext/metadata.go b/pkg/metatext/metadata.go new file mode 100644 index 0000000..35b7364 --- /dev/null +++ b/pkg/metatext/metadata.go @@ -0,0 +1,27 @@ +package metatext + +import ( + "strings" + + "google.golang.org/grpc/metadata" +) + +type MetadataTextMap struct { + metadata.MD +} + +func (m MetadataTextMap) ForeachKey(handler func(key, val string) error) error { + for k, vs := range m.MD { + for _, v := range vs { + if err := handler(k, v); err != nil { + return err + } + } + } + return nil +} + +func (m MetadataTextMap) Set(key, val string) { + key = strings.ToLower(key) + m.MD.Append(key, val) +}