@@ -16,6 +16,7 @@ import misk.web.NetworkChain
16
16
import misk.web.NetworkInterceptor
17
17
import misk.web.Response
18
18
import misk.web.ResponseBody
19
+ import misk.web.interceptors.hooks.RequestResponseLoggedCapture
19
20
import misk.web.mediatype.MediaTypes
20
21
import misk.web.toResponseBody
21
22
import okhttp3.Headers.Companion.toHeaders
@@ -41,9 +42,10 @@ import java.util.Base64
41
42
*
42
43
* TODO(isabel): Set the response body in a ThreadLocal to log in [RequestLoggingInterceptor]
43
44
*/
44
- class ExceptionHandlingInterceptor (
45
+ class ExceptionHandlingInterceptor private constructor (
45
46
private val actionName : String ,
46
- private val mapperResolver : ExceptionMapperResolver
47
+ private val mapperResolver : ExceptionMapperResolver ,
48
+ private val requestResponseLoggedCapture : RequestResponseLoggedCapture ,
47
49
) : NetworkInterceptor {
48
50
49
51
@OptIn(ExperimentalMiskApi ::class )
@@ -60,7 +62,7 @@ class ExceptionHandlingInterceptor(
60
62
val response = toResponse(th, suppressLog = true , mdcTags)
61
63
sendGrpcFailure(chain.httpCall, response.statusCode, toGrpcResponse(th, mdcTags))
62
64
} else {
63
- val response = toResponse(th, suppressLog = false , mdcTags)
65
+ val response = toResponse(th, suppressLog = requestResponseLoggedCapture.isLogged() , mdcTags)
64
66
chain.httpCall.statusCode = response.statusCode
65
67
sendHttpFailure(chain.httpCall, response)
66
68
}
@@ -117,41 +119,40 @@ class ExceptionHandlingInterceptor(
117
119
return buffer.readUtf8()
118
120
}
119
121
120
- private fun toResponse (th : Throwable , suppressLog : Boolean , mdcTags : Set <Tag >): Response <* > {
121
- // If the exception is a reflection wrapper, unwrap first.
122
- when (th) {
123
- is InvocationTargetException -> return toResponse(th.targetException, suppressLog, mdcTags)
124
- is UncheckedExecutionException -> return toResponse(th.cause!! , suppressLog, mdcTags)
125
- }
122
+ private fun toResponse (th : Throwable , suppressLog : Boolean , mdcTags : Set <Tag >): Response <* > =
123
+ unwrappedToResponse(th.unwrap(), suppressLog, mdcTags)
126
124
125
+ private fun unwrappedToResponse (th : Throwable , suppressLog : Boolean , mdcTags : Set <Tag >): Response <* > =
127
126
// Prefer the mapper's response, if one exists.
128
- val mapper = mapperResolver.mapperFor(th)
129
- if (mapper != null ) {
127
+ mapperResolver.mapperFor(th)?.let {
130
128
if (! suppressLog) {
131
129
log.log(
132
- level = mapper .loggingLevel(th),
130
+ level = it .loggingLevel(th),
133
131
th = th,
134
- tags = * mdcTags.toTypedArray(),
132
+ tags = mdcTags.toTypedArray(),
135
133
) { " exception dispatching to $actionName " }
136
134
}
137
- return mapper.toResponse(th)
138
- }
139
135
136
+ it.toResponse(th)
137
+ }
140
138
// Fall back to a default mapping.
141
- return toInternalServerError(th, mdcTags)
142
- }
139
+ ? : toInternalServerError(th, suppressLog , mdcTags)
140
+
143
141
144
- private fun toGrpcResponse (th : Throwable , mdcTags : Set <Tag >): GrpcErrorResponse = when (th) {
142
+ private fun toGrpcResponse (th : Throwable , mdcTags : Set <Tag >): GrpcErrorResponse =
143
+ unwrappedToGrpcResponse(th.unwrap(), mdcTags)
144
+
145
+ private fun unwrappedToGrpcResponse (th : Throwable , mdcTags : Set <Tag >): GrpcErrorResponse = when (th) {
145
146
is UnauthenticatedException -> GrpcErrorResponse (GrpcStatus .UNAUTHENTICATED , th.message)
146
147
is UnauthorizedException -> GrpcErrorResponse (GrpcStatus .PERMISSION_DENIED , th.message)
147
- is InvocationTargetException -> toGrpcResponse(th.targetException, mdcTags)
148
- is UncheckedExecutionException -> toGrpcResponse(th.cause!! , mdcTags)
149
148
else -> mapperResolver.mapperFor(th)?.let {
150
- log.log(
151
- level = it.loggingLevel(th),
152
- th = th,
153
- tags = * mdcTags.toTypedArray(),
154
- ) { " exception dispatching to $actionName " }
149
+ if (! requestResponseLoggedCapture.isLogged()) {
150
+ log.log(
151
+ level = it.loggingLevel(th),
152
+ th = th,
153
+ tags = mdcTags.toTypedArray(),
154
+ ) { " exception dispatching to $actionName " }
155
+ }
155
156
val grpcResponse = it.toGrpcResponse(th)
156
157
if (grpcResponse == null ) {
157
158
val httpResponse = toResponse(th, suppressLog = true , mdcTags)
@@ -162,15 +163,19 @@ class ExceptionHandlingInterceptor(
162
163
} ? : GrpcErrorResponse .INTERNAL_SERVER_ERROR
163
164
}
164
165
165
- private fun toInternalServerError (th : Throwable , mdcTags : Set <Tag >): Response <* > {
166
- log.error(th, * mdcTags.toTypedArray()) { " unexpected error dispatching to $actionName " }
166
+ private fun toInternalServerError (th : Throwable , suppressLog : Boolean , mdcTags : Set <Tag >): Response <* > {
167
+ if (! suppressLog) {
168
+ log.error(th = th, tags = mdcTags.toTypedArray()) { " unexpected error dispatching to $actionName " }
169
+ }
167
170
return INTERNAL_SERVER_ERROR_RESPONSE
168
171
}
169
172
170
173
class Factory @Inject internal constructor(
171
- private val mapperResolver : ExceptionMapperResolver
174
+ private val mapperResolver : ExceptionMapperResolver ,
175
+ private val requestResponseLoggedCapture : RequestResponseLoggedCapture ,
172
176
) : NetworkInterceptor.Factory {
173
- override fun create (action : Action ) = ExceptionHandlingInterceptor (action.name, mapperResolver)
177
+ override fun create (action : Action ) =
178
+ ExceptionHandlingInterceptor (action.name, mapperResolver, requestResponseLoggedCapture)
174
179
}
175
180
176
181
private companion object {
@@ -200,6 +205,20 @@ fun toGrpcStatus(statusCode: Int): GrpcStatus {
200
205
}
201
206
}
202
207
208
+ /* *
209
+ * Unwrap [InvocationTargetException] and [UncheckedExecutionException] to find the root cause.
210
+ */
211
+ internal fun Throwable.unwrap (): Throwable {
212
+ var th = this
213
+ while (true ) {
214
+ th = when (th) {
215
+ is InvocationTargetException -> th.targetException
216
+ is UncheckedExecutionException -> th.cause!!
217
+ else -> return th
218
+ }
219
+ }
220
+ }
221
+
203
222
/* * Convert to a compatible base64-proto-encoded google.rpc.Status-compatible value. */
204
223
private val GrpcErrorResponse .toEncodedStatusProto
205
224
get() : String {
0 commit comments