diff --git a/client/shared/src/main/scala/org/http4s/client/middleware/Logger.scala b/client/shared/src/main/scala/org/http4s/client/middleware/Logger.scala index 862bfa7bf2f..59b4091ce05 100644 --- a/client/shared/src/main/scala/org/http4s/client/middleware/Logger.scala +++ b/client/shared/src/main/scala/org/http4s/client/middleware/Logger.scala @@ -40,18 +40,32 @@ object Logger { ) ) - def logBodyText[F[_]: Async]( + def logWithEntity[F[_]: Async]( logHeaders: Boolean, - logBody: Stream[F, Byte] => Option[F[String]], + logEntity: Entity[F] => Option[F[String]], redactHeadersWhen: CIString => Boolean = defaultRedactHeadersWhen, logAction: Option[String => F[Unit]] = None, )(client: Client[F]): Client[F] = - ResponseLogger.logBodyText(logHeaders, logBody, redactHeadersWhen, logAction)( - RequestLogger.logBodyText(logHeaders, logBody, redactHeadersWhen, logAction)( + ResponseLogger.logWithEntity(logHeaders, logEntity, redactHeadersWhen, logAction)( + RequestLogger.logWithEntity(logHeaders, logEntity, redactHeadersWhen, logAction)( client ) ) + @deprecated("Use Logger.logWithEntity that utilizes Entity model for a Message body", "1.0.0-M39") + def logBodyText[F[_]: Async]( + logHeaders: Boolean, + logBody: Stream[F, Byte] => Option[F[String]], + redactHeadersWhen: CIString => Boolean = defaultRedactHeadersWhen, + logAction: Option[String => F[Unit]] = None, + )(client: Client[F]): Client[F] = + logWithEntity( + logHeaders, + (entity: Entity[F]) => logBody(entity.body), + redactHeadersWhen, + logAction, + )(client) + def logMessage[F[_]](message: Message[F])( logHeaders: Boolean, logBody: Boolean, diff --git a/client/shared/src/main/scala/org/http4s/client/middleware/RequestLogger.scala b/client/shared/src/main/scala/org/http4s/client/middleware/RequestLogger.scala index b97ca771d82..8c458f52cd6 100644 --- a/client/shared/src/main/scala/org/http4s/client/middleware/RequestLogger.scala +++ b/client/shared/src/main/scala/org/http4s/client/middleware/RequestLogger.scala @@ -45,20 +45,38 @@ object RequestLogger { )(logAction.getOrElse(defaultLogAction[F])) } - def logBodyText[F[_]: Async]( + def logWithEntity[F[_]: Async]( logHeaders: Boolean, - logBody: Stream[F, Byte] => Option[F[String]], + logEntity: Entity[F] => Option[F[String]], redactHeadersWhen: CIString => Boolean = Headers.SensitiveHeaders.contains, logAction: Option[String => F[Unit]] = None, )(client: Client[F]): Client[F] = impl(client, logBody = true) { request => - InternalLogger.logMessageWithBodyText(request)( + InternalLogger.logMessageWithEntity(request)( logHeaders, - logBody, + logEntity, + logAction.getOrElse(defaultLogAction[F]), redactHeadersWhen, - )(logAction.getOrElse(defaultLogAction[F])) + ) } + @deprecated( + "Use RequestLogger.logWithEntity that utilizes Entity model for a Message body", + "1.0.0-M39", + ) + def logBodyText[F[_]: Async]( + logHeaders: Boolean, + logBody: Stream[F, Byte] => Option[F[String]], + redactHeadersWhen: CIString => Boolean = Headers.SensitiveHeaders.contains, + logAction: Option[String => F[Unit]] = None, + )(client: Client[F]): Client[F] = + logWithEntity( + logHeaders, + (entity: Entity[F]) => logBody(entity.body), + redactHeadersWhen, + logAction, + )(client) + def customized[F[_]: Async]( client: Client[F], logBody: Boolean = true, diff --git a/client/shared/src/main/scala/org/http4s/client/middleware/ResponseLogger.scala b/client/shared/src/main/scala/org/http4s/client/middleware/ResponseLogger.scala index 466887d5feb..8d5ab1c9445 100644 --- a/client/shared/src/main/scala/org/http4s/client/middleware/ResponseLogger.scala +++ b/client/shared/src/main/scala/org/http4s/client/middleware/ResponseLogger.scala @@ -48,20 +48,38 @@ object ResponseLogger { )(logAction.getOrElse(defaultLogAction[F])) } - def logBodyText[F[_]: Async]( + def logWithEntity[F[_]: Async]( logHeaders: Boolean, - logBody: Stream[F, Byte] => Option[F[String]], + logEntity: Entity[F] => Option[F[String]], redactHeadersWhen: CIString => Boolean = Headers.SensitiveHeaders.contains, logAction: Option[String => F[Unit]] = None, )(client: Client[F]): Client[F] = impl(client, logBody = true) { response => - InternalLogger.logMessageWithBodyText(response)( + InternalLogger.logMessageWithEntity(response)( logHeaders, - logBody, + logEntity, + logAction.getOrElse(defaultLogAction[F]), redactHeadersWhen, - )(logAction.getOrElse(defaultLogAction[F])) + ) } + @deprecated( + "Use ResponseLogger.logWithEntity that utilizes Entity model for a Message body", + "1.0.0-M39", + ) + def logBodyText[F[_]: Async]( + logHeaders: Boolean, + logBody: Stream[F, Byte] => Option[F[String]], + redactHeadersWhen: CIString => Boolean = Headers.SensitiveHeaders.contains, + logAction: Option[String => F[Unit]] = None, + )(client: Client[F]): Client[F] = + logWithEntity( + logHeaders, + (entity: Entity[F]) => logBody(entity.body), + redactHeadersWhen, + logAction, + )(client) + def customized[F[_]: Async]( client: Client[F], logBody: Boolean = true, diff --git a/core/shared/src/main/scala/org/http4s/internal/Logger.scala b/core/shared/src/main/scala/org/http4s/internal/Logger.scala index 596dedd9594..efde75acb58 100644 --- a/core/shared/src/main/scala/org/http4s/internal/Logger.scala +++ b/core/shared/src/main/scala/org/http4s/internal/Logger.scala @@ -74,16 +74,17 @@ object Logger { logBody: Boolean, redactHeadersWhen: CIString => Boolean = Headers.SensitiveHeaders.contains, )(log: String => F[Unit])(implicit F: Concurrent[F]): F[Unit] = { - val logBodyText = (_: Stream[F, Byte]) => defaultLogBody(message)(logBody) + val logBodyText = (_: Entity[F]) => defaultLogBody(message)(logBody) - logMessageWithBodyText(message)(logHeaders, logBodyText, redactHeadersWhen)(log) + logMessageWithEntity(message)(logHeaders, logBodyText, log, redactHeadersWhen) } - def logMessageWithBodyText[F[_]](message: Message[F])( + def logMessageWithEntity[F[_]](message: Message[F])( logHeaders: Boolean, - logBodyText: Stream[F, Byte] => Option[F[String]], + logEntity: Entity[F] => Option[F[String]], + log: String => F[Unit], redactHeadersWhen: CIString => Boolean = Headers.SensitiveHeaders.contains, - )(log: String => F[Unit])(implicit F: Monad[F]): F[Unit] = { + )(implicit F: Monad[F]): F[Unit] = { def prelude = message match { case req: Request[_] => s"${req.httpVersion} ${req.method} ${req.uri}" @@ -93,7 +94,7 @@ object Logger { val headers: String = defaultLogHeaders(message)(logHeaders, redactHeadersWhen) val bodyText: F[String] = - logBodyText(message.body) match { + logEntity(message.entity) match { case Some(textF) => textF.map(text => s"""body="$text"""") case None => F.pure("") } @@ -105,4 +106,19 @@ object Logger { .flatMap(log) } + @deprecated( + "Use Logger.logMessageWithEntity that utilizes Entity model for a Message body", + "1.0.0-M39", + ) + def logMessageWithBodyText[F[_]](message: Message[F])( + logHeaders: Boolean, + logBodyText: Stream[F, Byte] => Option[F[String]], + redactHeadersWhen: CIString => Boolean = Headers.SensitiveHeaders.contains, + )(log: String => F[Unit])(implicit F: Monad[F]): F[Unit] = + logMessageWithEntity(message)( + logHeaders, + (entity: Entity[F]) => logBodyText(entity.body), + log, + redactHeadersWhen, + ) } diff --git a/server/shared/src/main/scala/org/http4s/server/middleware/Logger.scala b/server/shared/src/main/scala/org/http4s/server/middleware/Logger.scala index 59c0d7af061..37519f8c17e 100644 --- a/server/shared/src/main/scala/org/http4s/server/middleware/Logger.scala +++ b/server/shared/src/main/scala/org/http4s/server/middleware/Logger.scala @@ -50,9 +50,9 @@ object Logger { ) } - def logBodyText[G[_], F[_]]( + def logWithEntity[G[_], F[_]]( logHeaders: Boolean, - logBody: Stream[F, Byte] => Option[F[String]], + logEntity: Entity[F] => Option[F[String]], fk: F ~> G, redactHeadersWhen: CIString => Boolean = defaultRedactHeadersWhen, logAction: Option[String => F[Unit]] = None, @@ -60,11 +60,32 @@ object Logger { val log: String => F[Unit] = logAction.getOrElse { s => logger.info(s).to[F] } - ResponseLogger.impl(logHeaders, Right(logBody), fk, redactHeadersWhen, log.pure[Option])( - RequestLogger.impl(logHeaders, Right(logBody), fk, redactHeadersWhen, log.pure[Option])(http) + ResponseLogger.impl(logHeaders, Right(logEntity), fk, redactHeadersWhen, log.pure[Option])( + RequestLogger.impl(logHeaders, Right(logEntity), fk, redactHeadersWhen, log.pure[Option])( + http + ) ) } + @deprecated( + "Use Logger.logWithEntity that utilizes Entity model for a Message body", + "1.0.0-M39", + ) + def logBodyText[G[_], F[_]]( + logHeaders: Boolean, + logBody: Stream[F, Byte] => Option[F[String]], + fk: F ~> G, + redactHeadersWhen: CIString => Boolean = defaultRedactHeadersWhen, + logAction: Option[String => F[Unit]] = None, + )(http: Http[G, F])(implicit G: MonadCancelThrow[G], F: Async[F]): Http[G, F] = + logWithEntity[G, F]( + logHeaders, + (entity: Entity[F]) => logBody(entity.body), + fk, + redactHeadersWhen, + logAction, + )(http) + def httpApp[F[_]: Async]( logHeaders: Boolean, logBody: Boolean, @@ -73,13 +94,30 @@ object Logger { )(httpApp: HttpApp[F]): HttpApp[F] = apply(logHeaders, logBody, FunctionK.id[F], redactHeadersWhen, logAction)(httpApp) + def httpAppLogEntity[F[_]: Async]( + logHeaders: Boolean, + logEntity: Entity[F] => Option[F[String]], + redactHeadersWhen: CIString => Boolean = defaultRedactHeadersWhen, + logAction: Option[String => F[Unit]] = None, + )(httpApp: HttpApp[F]): HttpApp[F] = + logWithEntity(logHeaders, logEntity, FunctionK.id[F], redactHeadersWhen, logAction)(httpApp) + + @deprecated( + "Use Logger.httpAppLogEntity that utilizes Entity model for a Message body", + "1.0.0-M39", + ) def httpAppLogBodyText[F[_]: Async]( logHeaders: Boolean, logBody: Stream[F, Byte] => Option[F[String]], redactHeadersWhen: CIString => Boolean = defaultRedactHeadersWhen, logAction: Option[String => F[Unit]] = None, )(httpApp: HttpApp[F]): HttpApp[F] = - logBodyText(logHeaders, logBody, FunctionK.id[F], redactHeadersWhen, logAction)(httpApp) + httpAppLogEntity( + logHeaders, + (entity: Entity[F]) => logBody(entity.body), + redactHeadersWhen, + logAction, + )(httpApp) def httpRoutes[F[_]: Async]( logHeaders: Boolean, @@ -89,13 +127,30 @@ object Logger { )(httpRoutes: HttpRoutes[F]): HttpRoutes[F] = apply(logHeaders, logBody, OptionT.liftK[F], redactHeadersWhen, logAction)(httpRoutes) + def httpRoutesLogEntity[F[_]: Async]( + logHeaders: Boolean, + logEntity: Entity[F] => Option[F[String]], + redactHeadersWhen: CIString => Boolean = defaultRedactHeadersWhen, + logAction: Option[String => F[Unit]] = None, + )(httpRoutes: HttpRoutes[F]): HttpRoutes[F] = + logWithEntity(logHeaders, logEntity, OptionT.liftK[F], redactHeadersWhen, logAction)(httpRoutes) + + @deprecated( + "Use Logger.httpRoutesLogEntity that utilizes Entity model for a Message body", + "1.0.0-M39", + ) def httpRoutesLogBodyText[F[_]: Async]( logHeaders: Boolean, logBody: Stream[F, Byte] => Option[F[String]], redactHeadersWhen: CIString => Boolean = defaultRedactHeadersWhen, logAction: Option[String => F[Unit]] = None, )(httpRoutes: HttpRoutes[F]): HttpRoutes[F] = - logBodyText(logHeaders, logBody, OptionT.liftK[F], redactHeadersWhen, logAction)(httpRoutes) + httpRoutesLogEntity( + logHeaders, + (entity: Entity[F]) => logBody(entity.body), + redactHeadersWhen, + logAction, + )(httpRoutes) def logMessage[F[_], A <: Message[F]](message: A)( logHeaders: Boolean, diff --git a/server/shared/src/main/scala/org/http4s/server/middleware/RequestLogger.scala b/server/shared/src/main/scala/org/http4s/server/middleware/RequestLogger.scala index 4dcbbbf1826..fa0a6ddbd64 100644 --- a/server/shared/src/main/scala/org/http4s/server/middleware/RequestLogger.scala +++ b/server/shared/src/main/scala/org/http4s/server/middleware/RequestLogger.scala @@ -51,7 +51,7 @@ object RequestLogger { private[server] def impl[G[_], F[_]]( logHeaders: Boolean, - logBodyText: Either[Boolean, Stream[F, Byte] => Option[F[String]]], + logBodyText: Either[Boolean, Entity[F] => Option[F[String]]], fk: F ~> G, redactHeadersWhen: CIString => Boolean, logAction: Option[String => F[Unit]], @@ -69,7 +69,7 @@ object RequestLogger { Logger.logMessage[F, Request[F]](r)(logHeaders, bool, redactHeadersWhen)(log(_)) case Right(f) => org.http4s.internal.Logger - .logMessageWithBodyText(r)(logHeaders, f, redactHeadersWhen)(log(_)) + .logMessageWithEntity(r)(logHeaders, f, log(_), redactHeadersWhen) } val logBody: Boolean = logBodyText match { @@ -135,25 +135,59 @@ object RequestLogger { )(httpRoutes: HttpRoutes[F]): HttpRoutes[F] = apply(logHeaders, logBody, OptionT.liftK[F], redactHeadersWhen, logAction)(httpRoutes) + def httpAppLogEntity[F[_]: Async]( + logHeaders: Boolean, + logEntity: Entity[F] => Option[F[String]], + redactHeadersWhen: CIString => Boolean = Headers.SensitiveHeaders.contains, + logAction: Option[String => F[Unit]] = None, + )(httpApp: HttpApp[F]): HttpApp[F] = + impl[F, F](logHeaders, Right(logEntity), FunctionK.id[F], redactHeadersWhen, logAction)(httpApp) + + @deprecated( + "Use RequestLogger.httpAppLogEntity that utilizes Entity model for a Message body", + "1.0.0-M39", + ) def httpAppLogBodyText[F[_]: Async]( logHeaders: Boolean, logBody: Stream[F, Byte] => Option[F[String]], redactHeadersWhen: CIString => Boolean = Headers.SensitiveHeaders.contains, logAction: Option[String => F[Unit]] = None, )(httpApp: HttpApp[F]): HttpApp[F] = - impl[F, F](logHeaders, Right(logBody), FunctionK.id[F], redactHeadersWhen, logAction)(httpApp) + httpAppLogEntity[F]( + logHeaders, + (entity: Entity[F]) => logBody(entity.body), + redactHeadersWhen, + logAction, + )(httpApp) - def httpRoutesLogBodyText[F[_]: Async]( + def httpRoutesLogEntity[F[_]: Async]( logHeaders: Boolean, - logBody: Stream[F, Byte] => Option[F[String]], + logEntity: Entity[F] => Option[F[String]], redactHeadersWhen: CIString => Boolean = Headers.SensitiveHeaders.contains, logAction: Option[String => F[Unit]] = None, )(httpRoutes: HttpRoutes[F]): HttpRoutes[F] = impl[OptionT[F, *], F]( logHeaders, - Right(logBody), + Right(logEntity), OptionT.liftK[F], redactHeadersWhen, logAction, )(httpRoutes) + + @deprecated( + "Use RequestLogger.httpRoutesLogEntity that utilizes Entity model for a Message body", + "1.0.0-M39", + ) + def httpRoutesLogBodyText[F[_]: Async]( + logHeaders: Boolean, + logBody: Stream[F, Byte] => Option[F[String]], + redactHeadersWhen: CIString => Boolean = Headers.SensitiveHeaders.contains, + logAction: Option[String => F[Unit]] = None, + )(httpRoutes: HttpRoutes[F]): HttpRoutes[F] = + httpRoutesLogEntity[F]( + logHeaders, + (entity: Entity[F]) => logBody(entity.body), + redactHeadersWhen, + logAction, + )(httpRoutes) } diff --git a/server/shared/src/main/scala/org/http4s/server/middleware/ResponseLogger.scala b/server/shared/src/main/scala/org/http4s/server/middleware/ResponseLogger.scala index 616e8912a4a..0dcf8c78937 100644 --- a/server/shared/src/main/scala/org/http4s/server/middleware/ResponseLogger.scala +++ b/server/shared/src/main/scala/org/http4s/server/middleware/ResponseLogger.scala @@ -50,7 +50,7 @@ object ResponseLogger { private[server] def impl[G[_], F[_], A]( logHeaders: Boolean, - logBodyText: Either[Boolean, Stream[F, Byte] => Option[F[String]]], + logBodyText: Either[Boolean, Entity[F] => Option[F[String]]], fk: F ~> G, redactHeadersWhen: CIString => Boolean, logAction: Option[String => F[Unit]], @@ -66,7 +66,7 @@ object ResponseLogger { Logger.logMessage[F, Response[F]](resp)(logHeaders, bool, redactHeadersWhen)(log(_)) case Right(f) => org.http4s.internal.Logger - .logMessageWithBodyText(resp)(logHeaders, f, redactHeadersWhen)(log(_)) + .logMessageWithEntity(resp)(logHeaders, f, log(_), redactHeadersWhen) } val logBody: Boolean = logBodyText match { @@ -115,13 +115,32 @@ object ResponseLogger { )(httpApp: Kleisli[F, A, Response[F]]): Kleisli[F, A, Response[F]] = apply(logHeaders, logBody, FunctionK.id[F], redactHeadersWhen, logAction)(httpApp) + def httpAppLogEntity[F[_]: Async, A]( + logHeaders: Boolean, + logEntity: Entity[F] => Option[F[String]], + redactHeadersWhen: CIString => Boolean = Headers.SensitiveHeaders.contains, + logAction: Option[String => F[Unit]] = None, + )(httpApp: Kleisli[F, A, Response[F]]): Kleisli[F, A, Response[F]] = + impl[F, F, A](logHeaders, Right(logEntity), FunctionK.id[F], redactHeadersWhen, logAction)( + httpApp + ) + + @deprecated( + "Use ResponseLogger.httpAppLogEntity that utilizes Entity model for a Message body", + "1.0.0-M39", + ) def httpAppLogBodyText[F[_]: Async, A]( logHeaders: Boolean, logBody: Stream[F, Byte] => Option[F[String]], redactHeadersWhen: CIString => Boolean = Headers.SensitiveHeaders.contains, logAction: Option[String => F[Unit]] = None, )(httpApp: Kleisli[F, A, Response[F]]): Kleisli[F, A, Response[F]] = - impl[F, F, A](logHeaders, Right(logBody), FunctionK.id[F], redactHeadersWhen, logAction)( + httpAppLogEntity[F, A]( + logHeaders, + (entity: Entity[F]) => logBody(entity.body), + redactHeadersWhen, + logAction, + )( httpApp ) @@ -133,17 +152,34 @@ object ResponseLogger { )(httpRoutes: Kleisli[OptionT[F, *], A, Response[F]]): Kleisli[OptionT[F, *], A, Response[F]] = apply(logHeaders, logBody, OptionT.liftK[F], redactHeadersWhen, logAction)(httpRoutes) - def httpRoutesLogBodyText[F[_]: Async, A]( + def httpRoutesLogEntity[F[_]: Async, A]( logHeaders: Boolean, - logBody: Stream[F, Byte] => Option[F[String]], + logEntity: Entity[F] => Option[F[String]], redactHeadersWhen: CIString => Boolean = Headers.SensitiveHeaders.contains, logAction: Option[String => F[Unit]] = None, )(httpRoutes: Kleisli[OptionT[F, *], A, Response[F]]): Kleisli[OptionT[F, *], A, Response[F]] = impl[OptionT[F, *], F, A]( logHeaders, - Right(logBody), + Right(logEntity), OptionT.liftK[F], redactHeadersWhen, logAction, )(httpRoutes) + + @deprecated( + "Use ResponseLogger.httpRoutesLogEntity that utilizes Entity model for a Message body", + "1.0.0-M39", + ) + def httpRoutesLogBodyText[F[_]: Async, A]( + logHeaders: Boolean, + logBody: Stream[F, Byte] => Option[F[String]], + redactHeadersWhen: CIString => Boolean = Headers.SensitiveHeaders.contains, + logAction: Option[String => F[Unit]] = None, + )(httpRoutes: Kleisli[OptionT[F, *], A, Response[F]]): Kleisli[OptionT[F, *], A, Response[F]] = + httpRoutesLogEntity[F, A]( + logHeaders, + (entity: Entity[F]) => logBody(entity.body), + redactHeadersWhen, + logAction, + )(httpRoutes) }