package client import ( "context" "fmt" "golang.conradwood.net/go-easyops/auth" "golang.conradwood.net/go-easyops/cmdline" "golang.conradwood.net/go-easyops/common" "golang.conradwood.net/go-easyops/ctx" pctx "golang.conradwood.net/go-easyops/ctx" "golang.conradwood.net/go-easyops/ctx/shared" pp "golang.conradwood.net/go-easyops/profiling" "golang.conradwood.net/go-easyops/prometheus" "golang.conradwood.net/go-easyops/rpc" "golang.conradwood.net/go-easyops/tokens" "golang.conradwood.net/go-easyops/utils" // "reflect" "google.golang.org/grpc" // "google.golang.org/grpc/metadata" "time" ) // called for each outbound rpc func ClientMetricsUnaryInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { if ctx == nil { return fmt.Errorf("missing context, will not make outbound call without context") } pp.ClientRpcEntered() defer pp.ClientRpcDone() start := time.Now() s, m, err := splitMethodAndService(method) // keep prometheus happy: (not to panic) if s == "" { s = "unknown" } if m == "" { m = "unknown" } // bad programmer! - log it prominently (often) if err != nil { fmt.Printf("[go-easyops] invalid fqdn method: \"%s\": %s\n", method, err) } if cmdline.IsDebugRPCClient() && !isKnownNotAuthRPCs(s, m) { ls := pctx.GetLocalState(ctx) // technically.. this is _wrong_. the CallingService() is the service which called us. if ls.CallingService() == nil && ls.User() == nil { utils.PrintStack("[go-easyops] outbound context issue") fmt.Printf("[go-easyops] WARNING calling another service (%s.%s) with a context without authentication (%s)\n", s, m, pctx.Context2String(ctx)) } else if ls.CallingService() == nil && tokens.GetServiceTokenParameter() != "" { utils.PrintStack("[go-easyops] outbound context issue") fmt.Printf("[go-easyops] WARNING calling another service (%s.%s) with a context without calling service information (%s)\n", s, m, pctx.Context2String(ctx)) } /* _, ex := metadata.FromOutgoingContext(ctx) if !ex { if *debug_rpc_client { fmt.Printf("Context: %#v\n", ctx) utils.PrintStack("No metadata:") } fmt.Printf("[go-easyops] WARNING - calling external method %s.%s without metadata (authentication)\n", s, m) } */ } print_debug_client(ctx, fmt.Sprintf("%s.%s()", s, m)) grpc_client_sent.With(prometheus.Labels{"method": m, "servicename": s}).Inc() // generated by gRPC, calls the gRPC method err = invoker(ctx, method, req, reply, cc, opts...) observeRPC(start, s, m) // record time.Since(start) into metric dur := time.Since(start) if err != nil { grpc_client_failed.With(prometheus.Labels{"method": m, "servicename": s}).Inc() if *dialer_debug || cmdline.IsDebugRPCClient() { fmt.Printf("Invoked remote method=%s duration=%0.2fs error=%v (Method: \"%s\" in Service: \"%s\")\n", method, dur.Seconds(), err, m, s) utils.PrintStack("method %s/%s invoked at:\n", s, m) } } else if cmdline.IsDebugRPCClient() { fmt.Printf("Invoked method %s.%s (%0.2fs)...\n", s, m, dur.Seconds()) } return err } func print_debug_client(ictx context.Context, targetname string) { if !*dialer_debug && !cmdline.IsDebugRPCClient() { return } if cmdline.ContextWithBuilder() { ls := ctx.GetLocalState(ictx) /* if ls == nil || ls.CallingService() == nil && strings.Contains(targetname, "Mail") { panic("no calling service") } */ us := auth.UserIDString(common.VerifySignedUser(ls.User())) sv := auth.UserIDString(common.VerifySignedUser(ls.CallingService())) cmdline.DebugfContext("Invoking method %s as %s (service %s)...\n", targetname, us, sv) cmdline.DebugfContext("Outbound context:\n") cmdline.DebugfContext("Loccalstate: %s\n", shared.LocalState2string(ls)) return } cs := rpc.CallStateFromContext(ictx) if cs == nil { // utils.PrintStack("no callstate") fmt.Printf("[go-easyops] WARNING - calling external method %s without callstate (%s)\n", targetname, utils.CallingFunction()) } else { // cs.PrintContext() us := "none" u := cs.User() if u != nil { us = fmt.Sprintf("UserID=%s, Email=%s", u.ID, u.Email) } fmt.Printf("Invoking method as %s...%s\n", targetname, us) } //fmt.Printf("Contype: %s (%T)\n", reflect.TypeOf(cc), cc) // fmt.Printf("Ctx: %#v\n", ctx) }