From 03ace0ef4a3b4196baa821c344d064253eb91380 Mon Sep 17 00:00:00 2001 From: Thanh Le Date: Sat, 11 May 2024 14:29:21 +0700 Subject: [PATCH] Only log request/reponse when errors Add config to show every requests --- modules/app/src/main/scala/app.config.scala | 9 ++++---- modules/app/src/main/scala/app.scala | 2 +- modules/app/src/main/scala/http.logger.scala | 23 +++++++++++++++++++ .../app/src/main/scala/http.middleware.scala | 10 ++++++-- modules/app/src/main/scala/http.routes.scala | 4 ++-- modules/e2e/src/test/scala/CompatSuite.scala | 2 +- 6 files changed, 40 insertions(+), 10 deletions(-) create mode 100644 modules/app/src/main/scala/http.logger.scala diff --git a/modules/app/src/main/scala/app.config.scala b/modules/app/src/main/scala/app.config.scala index 4e834847..64066624 100644 --- a/modules/app/src/main/scala/app.config.scala +++ b/modules/app/src/main/scala/app.config.scala @@ -21,14 +21,15 @@ case class AppConfig( elastic: ElasticConfig ) -case class HttpServerConfig(host: Host, port: Port, shutdownTimeout: Int) +case class HttpServerConfig(host: Host, port: Port, apiLogger: Boolean, shutdownTimeout: Int) object HttpServerConfig: - private def host = env("HTTP_HOST").or(prop("http.host")).as[Host].default(ip"0.0.0.0") - private def port = env("HTTP_PORT").or(prop("http.port")).as[Port].default(port"9673") + private def host = env("HTTP_HOST").or(prop("http.host")).as[Host].default(ip"0.0.0.0") + private def port = env("HTTP_PORT").or(prop("http.port")).as[Port].default(port"9673") + private def logger = env("HTTP_API_LOGGER").or(prop("http.api.logger")).as[Boolean].default(false) private def shutdownTimeout = env("HTTP_SHUTDOWN_TIMEOUT").or(prop("http.shutdown.timeout")).as[Int].default(30) - def config = (host, port, shutdownTimeout).parMapN(HttpServerConfig.apply) + def config = (host, port, logger, shutdownTimeout).parMapN(HttpServerConfig.apply) case class ElasticConfig(uri: String) diff --git a/modules/app/src/main/scala/app.scala b/modules/app/src/main/scala/app.scala index f1e37a4e..15e3e1cf 100644 --- a/modules/app/src/main/scala/app.scala +++ b/modules/app/src/main/scala/app.scala @@ -22,7 +22,7 @@ object App extends IOApp.Simple: class SearchApp(res: AppResources, config: AppConfig)(using Logger[IO]): def run(): Resource[IO, Unit] = for - httpApp <- Routes(res) + httpApp <- Routes(res, config.server) server <- MkHttpServer.apply.newEmber(config.server, httpApp) _ <- Logger[IO].info(s"Starting server on ${config.server.host}:${config.server.port}").toResource yield () diff --git a/modules/app/src/main/scala/http.logger.scala b/modules/app/src/main/scala/http.logger.scala new file mode 100644 index 00000000..3598d059 --- /dev/null +++ b/modules/app/src/main/scala/http.logger.scala @@ -0,0 +1,23 @@ +package lila.search +package app + +import cats.data.Kleisli +import cats.effect.IO +import cats.syntax.all.* +import org.http4s.internal.Logger as Http4sLogger +import org.http4s.{ HttpApp, Request, Response } +import org.typelevel.log4cats.Logger + +object ApiErrorLogger: + + def isResponseError(res: Response[IO]): Boolean = + !res.status.isSuccess && res.status.code != 404 + + private def logError(req: Request[IO], res: Response[IO])(using Logger[IO]): IO[Unit] = + Http4sLogger.logMessage(req)(true, true)(Logger[IO].warn) >> + Http4sLogger.logMessage(res)(true, true)(Logger[IO].warn) + + def instance(using Logger[IO]): HttpApp[IO] => HttpApp[IO] = http => + Kleisli: req => + http(req).flatTap: res => + logError(req, res).whenA(isResponseError(res)) diff --git a/modules/app/src/main/scala/http.middleware.scala b/modules/app/src/main/scala/http.middleware.scala index 9bc8c6ef..4e8ed812 100644 --- a/modules/app/src/main/scala/http.middleware.scala +++ b/modules/app/src/main/scala/http.middleware.scala @@ -5,18 +5,24 @@ import cats.effect.IO import org.http4s.* import org.http4s.implicits.* import org.http4s.server.middleware.* +import org.typelevel.log4cats.Logger import scala.concurrent.duration.* type Middleware = HttpRoutes[IO] => HttpRoutes[IO] -def ApplyMiddleware(routes: HttpRoutes[IO]): HttpApp[IO] = +def ApplyMiddleware(config: HttpServerConfig)(routes: HttpRoutes[IO])(using Logger[IO]): HttpApp[IO] = val autoSlash: Middleware = AutoSlash(_) val timeout: Middleware = Timeout(60.seconds) val middleware = autoSlash.andThen(timeout) - val logger = + + def verboseLogger = RequestLogger.httpApp[IO](true, true) andThen ResponseLogger.httpApp[IO, Request[IO]](true, true) + val logger = + if config.apiLogger then verboseLogger + else ApiErrorLogger.instance + logger(middleware(routes).orNotFound) diff --git a/modules/app/src/main/scala/http.routes.scala b/modules/app/src/main/scala/http.routes.scala index 86bd7edd..c8ea6fa8 100644 --- a/modules/app/src/main/scala/http.routes.scala +++ b/modules/app/src/main/scala/http.routes.scala @@ -9,7 +9,7 @@ import org.http4s.{ HttpApp, HttpRoutes } import org.typelevel.log4cats.Logger import smithy4s.http4s.SimpleRestJsonBuilder -def Routes(resources: AppResources)(using Logger[IO]): Resource[IO, HttpApp[IO]] = +def Routes(resources: AppResources, config: HttpServerConfig)(using Logger[IO]): Resource[IO, HttpApp[IO]] = val healthServiceImpl: HealthService[IO] = new HealthService.Default[IO](IO.stub) @@ -28,4 +28,4 @@ def Routes(resources: AppResources)(using Logger[IO]): Resource[IO, HttpApp[IO]] .sequence .map(_.reduceK) .map(_ <+> docs) - .map(ApplyMiddleware) + .map(ApplyMiddleware(config)) diff --git a/modules/e2e/src/test/scala/CompatSuite.scala b/modules/e2e/src/test/scala/CompatSuite.scala index 1bbdfa80..7a337b76 100644 --- a/modules/e2e/src/test/scala/CompatSuite.scala +++ b/modules/e2e/src/test/scala/CompatSuite.scala @@ -110,7 +110,7 @@ object CompatSuite extends weaver.IOSuite: IO.fromFuture(IO(client.storeBulkTeam(sources))).map(expect.same(_, ())) def testAppConfig = AppConfig( - server = HttpServerConfig(ip"0.0.0.0", port"9999", shutdownTimeout = 1), + server = HttpServerConfig(ip"0.0.0.0", port"9999", false, shutdownTimeout = 1), elastic = ElasticConfig("http://0.0.0.0:9200") )