Why does this hang?
import cats.effect.IO
import cats.effect.unsafe.implicits.global
import com.typesafe.config.ConfigFactory
import io.circe.config.parser
import org.typelevel.log4cats._
import org.typelevel.log4cats.slf4j._
object ItsElenApp extends App:
private val logger: SelfAwareStructuredLogger[IO] = LoggerFactory[IO].getLogger
val ops = for
_ <- logger.info("aa")
_ <- IO(println("bcc"))
_ <- logger.error("bb")
yield ()
ops.unsafeRunSync()
println("end")
It prints:
INFO 2022-06-19 11:56:25,303 ItsElenApp - aa
bcc
and keeps running. Is it the log4cats library, or am I using the App object in a wrong way. Or do I have to close an execution context?
The recommended way of running cats.effect.IO
-based apps is using cats.effect.IOApp
(or cats.effect.ResourceApp
):
object MyApp extends IOApp:
// notice it takes List[String] rather than Array[String]
def run(args: List[String]): IO[ExitCode] = ...
this would run the application, handle setting up exit code, etc. It closes the app when run
reaches the end, which might be necessary if the default thread pool is non-daemon. If you don't want to use IOApp
you might need to close JVM manually, also taking into consideration that exception might have been thrown on .unsafeRunSync()
.
Extending App
is on the other hand not recommended in general. Why? It uses special Delayed
mechanism where the whole body of a class (its constructor) is lazy. This makes it harder to reason about the initialization, which is why this mechanism became deprecated/discouraged. If you are not using IOApp it is better to implement things like:
object MyProgram:
// Java requires this signature
def main(args: Array[String]): Unit = ...
which in your case could look like
object ItsElenApp:
private val logger: SelfAwareStructuredLogger[IO] = LoggerFactory[IO].getLogger
def main(args: Array[String]): Unit =
val ops = for
_ <- logger.info("aa")
_ <- IO(println("bcc"))
_ <- logger.error("bb")
yield ()
// IOApp could spare you this
try
ops.unsafeRunSync()
finally
println("end")
sys.exit(0)