salesforce / grpc-java-contrib

Useful extensions for the grpc-java library
BSD 3-Clause "New" or "Revised" License
219 stars 34 forks source link

Debug interceptor #39

Closed rmichela closed 4 years ago

rmichela commented 7 years ago

Add client and server interceptors that log/print grpc request debug diagnostics.

ozh-dev commented 6 years ago

My some developments in this direction. But it wrote in Kotlin. I hope it's could be usefull.

enum class Level {
    NONE,
    STATUS,
    HEADERS,
    BODY
}

enum class MethodType {
    REQUEST,
    REPLAY
}

class GrpcLoggingInterceptor(private val logger: (String) -> Unit = { Log.i("DEFAULT LOGGER", it) }) : ClientInterceptor {

    var level = Level.NONE

    val logBody
        get() = level == Level.BODY

    val logHeaders
        get() = logBody || level == Level.HEADERS

    val logStatus
        get() = logBody || level == Level.STATUS

    override fun <ReqT : Any, RespT : Any> interceptCall(method: MethodDescriptor<ReqT, RespT>,
                                                         callOptions: CallOptions,
                                                         next: Channel): ClientCall<ReqT, RespT> {

        return object : ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, callOptions)) {

            override fun sendMessage(message: ReqT) {
                super.sendMessage(message)
                if (logBody) {
                    logMessage(REQUEST, method, message)
                }
            }

            override fun start(responseListener: Listener<RespT>, headers: Metadata?) {
                super.start(object : Listener<RespT>() {
                    override fun onReady() {
                        responseListener.onReady()
                    }

                    override fun onMessage(message: RespT) {
                        responseListener.onMessage(message)
                        if (logBody) {
                            logMessage(REPLAY, method, message)
                        }
                    }

                    override fun onHeaders(headers: Metadata?) {
                        responseListener.onHeaders(headers)
                        if (logHeaders) {
                            logHeaders(method, headers)
                        }
                    }

                    override fun onClose(status: Status, trailers: Metadata?) {
                        responseListener.onClose(status, trailers)
                        if (logStatus) {
                            logStatus(method, status)
                        }
                    }
                }, headers)
            }
        }
    }

    private fun <ReqT, RespT> logMessage(methodType: MethodType,
                                         method: MethodDescriptor<ReqT, RespT>,
                                         message: Any) = log(
            getMethodDirection(methodType)
                    .plus(getMethodInfo(method))
                    .plus(message)
    )

    private fun <ReqT, RespT> logStatus(method: MethodDescriptor<ReqT, RespT>,
                                        status: Status) = log(
            getMethodInfo(method)
                    .plus("\n")
                    .plus("server status: ${status.code}")
                    .plus("\n")
                    .plus("status description: ${status.description}")
                    .plus("\n")
                    .plus("cause: ${status.cause}")
    )

    private fun <ReqT, RespT> logHeaders(method: MethodDescriptor<ReqT, RespT>,
                                         headers: Metadata?) = log(
            "HEADERS: "
                    .plus(getMethodInfo(method))
                    .plus(headers)
    )

    private fun <ReqT, RespT> getMethodInfo(method: MethodDescriptor<ReqT, RespT>) = method.fullMethodName.plus(" : [${method.type}] : ")

    private fun getMethodDirection(methodType: MethodType) = when (methodType) {
        REQUEST -> " --> $methodType "
        REPLAY -> " <-- $methodType "
    }

    private fun log(message: String) {
        logger.invoke(message)
    }
}
rmichela commented 6 years ago

Thanks!