Home > database >  Reuse log client in interceptor for Golang grpc server method
Reuse log client in interceptor for Golang grpc server method

Time:12-18

I am building a grpc server in Go. Right now the server provides three methods:

  • SubmitJob
  • CancelJob
  • GetJobStatus

I am using Datadog to log few metrics e.g. request count, duration etc. The SubmitJob method looks like:

func (s *myServer) submitJob(ctx context.Context, request *submitJobRequest) (*submitJobResponse, error) {
        s.dd_client.LogRequestCount("SubmitJob")
        start_time := time.Now()
        defer s.dd_client.LogRequestDuration("SubmitJob", time.Since(start_time))

        resp := someFunc()

    return resp, nil
}

The logging code is pretty much the same among those three different server methods. Thus, I wonder how to avoid such duplication.

I noticed that golang grpc has the concept ofinterceptor. Basically I can define a interceptor function, and use that to do pre/post processing of server method call.

Follow the documentation, I wrote an interceptor like:

func unaryInterceptor(ctx context.Context,
    req interface{},
    info *grpc.UnaryServerInfo,
    handler grpc.UnaryHandler,
) (interface{}, error) {
    dd_client := NewDatadogClient()
    defer dd_client.Close()

        dd_client.LogRequestCount(info.FullMethod)

        start_time := time.Now()

    resp, err := handler(ctx, req)

        dd_client.LogRequestDuration(info.FullMethod, time.Since(start_time))

    return resp, err
}

The problem is that, everytime when the interceptor is called, it will create and destory a new Datadog client. I feel this is unnecessary. But since unaryInterceptor is just a method instead of a class, I do not see a way to create the Datadog client once and reuse it later? Is there a way to fulfill what I need?

CodePudding user response:

Yes, instead of creating it on spot, move that to a different file / package, lets call it DataDog.go

On it you need to declar it as a singleton so it's only 1 pointer across all usages something like....

(im using the type 'DataDogType' to refer the data type return by the function 'NewDatadogClient', pls specify the package and version to give a more precise sample code)

//your package code....


var dataDog DataDogType

//access it through a wrapper, the wrapper will init the var the first time, 
//the remaining times it will be reused

func GetDataDog()(*DataDogType) {

if dataDog == nil {
    dataDog = NewDatadogClient()
}


return dataDog

}

Now depending of your use case, if you return it as a pointer and close it you will close it for all usages, so return the pointer it self and omit the part of closing it, or return the value so its a copy of the original

btw could you share the dataDog version and specific package that you are using?

  • Related