All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sttp.client3.logging.LoggingBackend.scala Maven / Gradle / Ivy

The newest version!
package sttp.client3.logging

import java.util.concurrent.TimeUnit
import sttp.capabilities.Effect
import sttp.client3._
import sttp.client3.listener.{ListenerBackend, RequestListener}
import sttp.model.{HeaderNames, StatusCode}
import sttp.monad.MonadError
import sttp.monad.syntax._

import scala.concurrent.duration.Duration

object LoggingBackend {
  def apply[F[_], S](
      delegate: SttpBackend[F, S],
      logger: Logger[F],
      includeTiming: Boolean = true,
      beforeCurlInsteadOfShow: Boolean = false,
      logRequestBody: Boolean = false,
      logRequestHeaders: Boolean = true,
      logResponseBody: Boolean = false,
      logResponseHeaders: Boolean = true,
      sensitiveHeaders: Set[String] = HeaderNames.SensitiveHeaders,
      beforeRequestSendLogLevel: LogLevel = LogLevel.Debug,
      responseLogLevel: StatusCode => LogLevel = DefaultLog.defaultResponseLogLevel,
      responseExceptionLogLevel: LogLevel = LogLevel.Error
  ): SttpBackend[F, S] = {
    val log = new DefaultLog(
      logger,
      beforeCurlInsteadOfShow,
      logRequestBody,
      logRequestHeaders,
      logResponseHeaders,
      sensitiveHeaders,
      beforeRequestSendLogLevel,
      responseLogLevel,
      responseExceptionLogLevel
    )
    apply(delegate, log, includeTiming, logResponseBody)
  }

  def apply[F[_], S](delegate: SttpBackend[F, S], log: Log[F]): SttpBackend[F, S] =
    apply(delegate, log, includeTiming = true, logResponseBody = false)

  def apply[F[_], S](
      delegate: SttpBackend[F, S],
      log: Log[F],
      includeTiming: Boolean,
      logResponseBody: Boolean
  ): SttpBackend[F, S] = {
    implicit val m: MonadError[F] = delegate.responseMonad
    if (logResponseBody) new LoggingWithResponseBodyBackend(delegate, log, includeTiming)
    else
      new ListenerBackend(delegate, new LoggingListener(log, includeTiming))
  }
}

class LoggingListener[F[_]](log: Log[F], includeTiming: Boolean)(implicit m: MonadError[F])
    extends RequestListener[F, Option[Long]] {
  private def now(): Long = System.currentTimeMillis()
  private def elapsed(from: Option[Long]): Option[Duration] = from.map(f => Duration(now() - f, TimeUnit.MILLISECONDS))

  override def beforeRequest(request: Request[_, _]): F[Option[Long]] = {
    log.beforeRequestSend(request).map(_ => if (includeTiming) Some(now()) else None)
  }

  override def requestException(request: Request[_, _], tag: Option[Long], e: Exception): F[Unit] = {
    log.requestException(request, elapsed(tag), e)
  }

  override def requestSuccessful(request: Request[_, _], response: Response[_], tag: Option[Long]): F[Unit] = {
    log.response(request, response, None, elapsed(tag))
  }
}

class LoggingWithResponseBodyBackend[F[_], S](
    delegate: SttpBackend[F, S],
    log: Log[F],
    includeTiming: Boolean
) extends DelegateSttpBackend[F, S](delegate) {
  private def now(): Long = System.currentTimeMillis()
  private def elapsed(from: Option[Long]): Option[Duration] = from.map(f => Duration(now() - f, TimeUnit.MILLISECONDS))

  override def send[T, R >: S with Effect[F]](request: Request[T, R]): F[Response[T]] = {
    log.beforeRequestSend(request).flatMap { _ =>
      val start = if (includeTiming) Some(now()) else None
      if (request.isWebSocket) {
        for {
          response <- request.send(delegate)
          _ <- log.response(request, response, None, elapsed(start))
        } yield response
      } else
        {
          for {
            response <- request.response(asBothOption(request.response, asStringAlways)).send(delegate)
            _ <- log.response(request, response, response.body._2, elapsed(start))
          } yield response.copy(body = response.body._1)
        }.handleError { case e: Exception =>
          log
            .requestException(request, elapsed(start), e)
            .flatMap(_ => responseMonad.error(e))
        }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy