-
Notifications
You must be signed in to change notification settings - Fork 0
/
ExceptionHandler.scala
63 lines (53 loc) · 2.52 KB
/
ExceptionHandler.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package pl.iterators.stir.server
import cats.effect.IO
import org.http4s.Status.InternalServerError
import org.typelevel.log4cats
import scala.util.control.NonFatal
trait ExceptionHandler extends ExceptionHandler.PF {
/**
* Creates a new [[ExceptionHandler]] which uses the given one as fallback for this one.
*/
def withFallback(that: ExceptionHandler): ExceptionHandler
/**
* "Seals" this handler by attaching a default handler as fallback if necessary.
*/
def seal(logAction: Option[(Throwable, String) => IO[Unit]] = None): ExceptionHandler
}
object ExceptionHandler {
type PF = PartialFunction[Throwable, Route]
private[stir] def errorMessageTemplate(param1: String, param2: String): String = {
s"Error during processing of request: '$param1'. Completing with $param2 response. " +
"To change default exception handling behavior, provide a custom ExceptionHandler."
}
implicit def apply(pf: PF): ExceptionHandler = apply(knownToBeSealed = false)(pf)
private def apply(knownToBeSealed: Boolean)(pf: PF): ExceptionHandler =
new ExceptionHandler {
def isDefinedAt(error: Throwable) = pf.isDefinedAt(error)
def apply(error: Throwable) = pf(error)
def withFallback(that: ExceptionHandler): ExceptionHandler =
if (!knownToBeSealed) ExceptionHandler(knownToBeSealed = false)(this.orElse(that)) else this
def seal(logAction: Option[(Throwable, String) => IO[Unit]] = None): ExceptionHandler =
if (!knownToBeSealed) ExceptionHandler(knownToBeSealed = true)(this.orElse(default(logAction))) else this
}
/**
* Default [[ExceptionHandler]] that discards the request's entity by default.
*/
def default(logAction: Option[(Throwable, String) => IO[Unit]] = None): ExceptionHandler = {
val log = logAction.getOrElse { (t: Throwable, s: String) =>
log4cats.slf4j.Slf4jFactory.create[IO].getLogger.error(t)(s)
}
apply(knownToBeSealed = true) {
case NonFatal(e) => ctx => {
val message = Option(e.getMessage).getOrElse(s"${e.getClass.getName} (No error message supplied)")
log(e, errorMessageTemplate(message, InternalServerError.toString())) *>
ctx.complete(InternalServerError)
}
}
}
/**
* Creates a sealed ExceptionHandler from the given one. Returns the default handler if the given one
* is `null`.
*/
def seal(handler: ExceptionHandler)(logAction: Option[(Throwable, String) => IO[Unit]] = None): ExceptionHandler =
if (handler ne null) handler.seal() else ExceptionHandler.default(logAction)
}