grpc-spring-boot-starter
grpc-spring-boot-starter copied to clipboard
GRpcExceptionHandler doesn't work [Kotlin]
Hi, thanks for the work you've done. It's really great starter!
I have some trouble, maybe it related with Kotlin CoroutineImpl. The interceptor below does not catch exception:
@GRpcService
class TempUrlService() : TempUrlServiceGrpcKt.TempUrlServiceCoroutineImplBase() {
private val logger = KotlinLogging.logger {}
@GRpcExceptionHandler
fun anotherHandler(e: NullPointerException, scope: GRpcExceptionScope): Status {
logger.warn { "NPE!" }
return Status.INTERNAL
}
@Throws(Throwable::class)
override suspend fun createThumbnailTempUrl(request: ThumbRequest): ThumbResponse {
throw NullPointerException("HELLO")
}
}
Using 4.5.10 version.
Can you please try to debug to see the private handler discovered here for your TempUrlService
service bean ?
The actual handler then resolved here
@jvmlet
My handler are presented, but method resolveMethodByThrowable
isn't invoked.
I could not find the entry point where interceptor should be invoked :(
for unary call it's called from here
Ohh, I think the issue is this line , I should also check the privateResolvers
Meanwhile, please add dummy global handler as temporary workaround to proceed here ..
Hi, do you mean GRpcServiceAdvice
or GRpcGlobalInterceptor
?
First doesn't work too for me
While I wrapped in try..catch :)
The problem is this line , it should check private resolvers as well. So, meanwhile, you can workaround the bug by adding dummy @GRpcServiceAdvice
with @GRpcServiceAdvice
method, it will force interceptor to proceed (here) and you private handler should be invoked
Please tell, is code ok?
Exception
class TestException(message: String) : Exception(message)
Service
@GRpcService
class FileService() : FileServiceGrpcKt.FileServiceCoroutineImplBase() {
override suspend fun getInfo(request: FileInfoRequest): FileInfoResponse {
throw TestException("Hello")
}
}
Advice
@GRpcServiceAdvice
class GRpcErrorInterceptor {
private val logger = KotlinLogging.logger {}
@GRpcExceptionHandler
fun throwableExceptionHandler(e: TestException, scope: GRpcExceptionScope): Status {
logger.error(e) { "gRPC unexpected error" }
return Status.INTERNAL.withCause(e) // should be code 13
}
}
But no logs in application, and status differs:
hett@STORM:~ $ grpc_cli call 172.23.0.1:9090 getInfo ""
connecting to 172.23.0.1:9090
Rpc failed with status code 2, error message:
I can provide demo app.
Hmmm, I suspected that the problem is in the coroutines, but not. This doesn't work too:
@GRpcService()
class FileService() : FileServiceGrpc.FileServiceImplBase() {
private val logger = KotlinLogging.logger {}
override fun getInfo(request: FileInfoRequest, responseObserver: StreamObserver<FileInfoResponse>) {
throw TestException("Hello")
}
}
You should have both @GRpcExceptionHandler
: global and private. Your private handler will be called
You also should wrap your exception with throw new GRpcRuntimeExceptionWrapper(new TestException(),"myHint")
, the RuntimeException
is caught...
Bingo, with runtime exception it is works!
The problem is that the Kotlin does not have throws
signature and not differs checked/non-checked exceptions. In may case I testing java checked exception.
If declared next exception, no any logs will produced on error and it big problem:
class TestException(message: String) : java.lang.Exception(message)
The next code with runtime exception works (global handler registered too):
class TestException(message: String) : java.lang.RuntimeException(message)
@GRpcService()
class FileService() : FileServiceGrpc.FileServiceImplBase() {
@GRpcExceptionHandler
fun anotherHandler(e: TestException, scope: GRpcExceptionScope): Status {
logger.warn { "error!" }
return Status.INTERNAL
}
override fun getInfo(request: FileInfoRequest, responseObserver: StreamObserver<FileInfoResponse>) {
throw TestException("Hello")
}
}
But if trying to extend kotlin corutine impl we get silence again in the logs :(
@GRpcService()
class FileService() : FileServiceGrpcKt.FileServiceCoroutineImplBase() {
@GRpcExceptionHandler
fun anotherHandler(e: TestException, scope: GRpcExceptionScope): Status {
logger.warn { "error!" }
return Status.INTERNAL
}
override suspend fun getInfo(request: FileInfoRequest): FileInfoResponse {
throw TestException("Hello")
}
}
I suspect that it is different troubles.
I created demo project which reproduces both problems https://github.com/TheHett/grpc_demo/tree/master/src/main/kotlin/com/example/demo/grpc
It should be call manually because I doesn't know how to write test for this
grpc_cli call 127.0.0.1:6565 demo.api.DemoService/test ""
You can have a look at demo module in this repo to get the idea how to develop unit tests
Hi, It doesn't works for me as for @TheHett. I have checked methodResolver
attribute content inside the GRpcExceptionHandlerInterceptor
after its initialisation through the constructor. It contains as expected in the mapperHandlers the @GRpcExceptionHandler
method defined inside @GRpcServiceAdvice
classes, and each grpc services (privateResolvers
) contains to their @GRpcExceptionHandler
method inside its respective mapperHandlers attribute.
At runtime, when I look at this condition for check global exception handler (declared via @GRpcServiceAdvice
) it's return true but when I throw the exception (runtimeEx or not) it's not cached, and when I look too the same place for local exception handler (from @GrpcService
) it's return false, and btw exception are not cached too.
Ps: for test with exception, i did used the wrapper
as doc advices it.
@jvmlet have you an idea about how I could work around this issue please ?
Any idea @jvmlet please ?
i have the same problem with kotlin, @GrpcExceptionHandler in service or @GRpcServiceAdvice on single class doesn't work
Still relevant with 4.9.1, any updates here?
Fixed in latest 5.1.5-SNAPSHOT
, please verify and confirm.
5.1.5
was released
I am still getting it in 5.1.5
Maybe it's somehow interfere with the order of interceptors?
grpc.security.auth.interceptor-order=-4
grpc.recovery.interceptor-order=-2
grpc.validation.interceptor-order=-1