当前位置: 首页 > 知识库问答 >
问题:

如何记录Akka HTTP客户端请求

佴涵蓄
2023-03-14

我需要记录akka http客户端请求及其响应。虽然似乎有记录这些请求的API提示,但没有明确的文档说明应该如何做。我的方法是创建一个记录的请求,它透明地包装Http()。单一请求(req)如下所示:

def loggedRequest(req: HttpRequest)
                  (implicit system: ActorSystem, ctx: ExecutionContext, m: Materializer): Future[HttpResponse] = {

  Http().singleRequest(req).map { resp ⇒
    Unmarshal(resp.entity).to[String].foreach{s ⇒
      system.log.info(req.toString)
      system.log.info(resp.toString + "\n" + s)
    }
    resp
  }
}

不幸的是,我必须通过解组或简单地请求resp来把握未来。实体数据字节,以恢复响应主体。我得到了日志记录,但promise完成了,我无法再将实体解组到实际数据。工作解决方案将记录请求和响应,并通过此测试用例,而不会抛出“Promise ready completed”(promise已完成)的IllegalStateException

describe("Logged rest requests") {

  it("deliver typed responses") {
    val foo = Rest.loggedRequest(Get(s"http://127.0.0.1:9000/some/path"))
    val resp = foo.futureValue(patience)
    resp.status shouldBe StatusCodes.OK
    val res = Unmarshal(resp.entity).to[MyClass].futureValue
  }
}

想法欢迎。

共有3个答案

邹嘉石
2023-03-14

我的完整解决方案,灵感来自@seanmcl

trait TraceDirectives extends LazyLogging {

  private val counter: AtomicLong = new AtomicLong(0)

  def log: Directive0 = count flatMap { requestId =>
    mapInnerRoute(addLoggingToRoute(requestId, _))
  }

  private def count: Directive1[Long] = Directive { innerRouteSupplier =>
    ctx =>
      innerRouteSupplier(Tuple1(counter.incrementAndGet()))(ctx)
  }

  private def addLoggingToRoute(requestId: Long, innerRoute: Route): Route = {
    ctx => {
      val requestStopwatch = Stopwatch.createStarted()
      extractClientIP { ip =>
        logger.info("Http request, id: {}, uri: {}, forwarded ip: {}", requestId, ctx.request.uri, ip)
        mapResponse(httpResponse => {
          logger.info("Http response, id: {}, code: {}, time: {}", requestId, httpResponse.status.intValue(), requestStopwatch.toString)
          httpResponse
        })(innerRoute)
      }(ctx)
    }
  }
}

object TraceDirectives extends TraceDirectives
狄海
2023-03-14

对于另一个解决方案,此代码记录请求IP,并将随机数与每个请求和响应关联,以便它们可以在日志中关联。它还记录响应时间。

由于请求可能需要一段时间才能处理,并且可能会失败,所以我希望立即查看请求,并在请求返回时查看响应。

只是我从请求中关心的数据。默认情况下有很多噪音。

val logRequestResponse: Directive0 =
  extractRequestContext flatMap { ctx =>
    extractClientIP flatMap { ip =>
      val id = scala.math.abs(rand.nextLong).toString
      onSuccess(RequestFields.fromIdIpAndRequest(id, ip, ctx.request)) flatMap { req =>
        logger.info("request", req.asJson)
        val i = Instant.now()
        mapRouteResultWith { result => 
          Result.fromIdStartTimeAndRouteResult(id, i, result) map { res =>
            logger.info("response", res.asJson)
            result
        }
      }
    }
  }
}
虞承泽
2023-03-14

我找到的解决方案之一是使用:

import akka.http.scaladsl.server.directives.DebuggingDirectives

val clientRouteLogged = DebuggingDirectives.logRequestResult("Client ReST", Logging.InfoLevel)(clientRoute)
Http().bindAndHandle(clientRouteLogged, interface, port)

它可以轻松地记录请求并生成原始(字节)格式。问题是这些日志完全不可读。这就是它变得复杂的地方。

下面是我的示例,它对请求/响应的实体进行编码并将其写入记录器。

您可以将函数传递给:

DebuggingDirectives.logRequestResult

def logRequestResult(magnet: LoggingMagnet[HttpRequest ⇒ RouteResult ⇒ Unit])

这是使用磁铁模式编写的函数:

LoggingMagnet[HttpRequest ⇒ RouteResult ⇒ Unit]

在哪里:

LoggingMagnet[T](f: LoggingAdapter ⇒ T)

因此,我们可以访问我们需要记录请求和结果的所有部分。我们有LoggingAdapter, HttpRequest和RouteResult

在我的例子中,我创建了一个内部函数。我不想再次传递所有参数。

def logRequestResult(level: LogLevel, route: Route)
                      (implicit m: Materializer, ex: ExecutionContext) = {
  def myLoggingFunction(logger: LoggingAdapter)(req: HttpRequest)(res: Any): Unit = {
    val entry = res match {
      case Complete(resp) =>
        entityAsString(resp.entity).map(data ⇒ LogEntry(s"${req.method} ${req.uri}: ${resp.status} \n entity: $data", level))
      case other =>
        Future.successful(LogEntry(s"$other", level))
    }
    entry.map(_.logTo(logger))
  }
  DebuggingDirectives.logRequestResult(LoggingMagnet(log => myLoggingFunction(log)))(route)
}

最重要的部分是我将MyLogging函数放入logRequestResult的最后一行。

名为myLoggingFunction的函数,简单地匹配服务器计算的结果,并基于它创建一个日志条目。

最后一件事是允许从流中解码结果实体的方法。

def entityAsString(entity: HttpEntity)
                   (implicit m: Materializer, ex: ExecutionContext): Future[String] = {
entity.dataBytes
  .map(_.decodeString(entity.contentType().charset().value))
  .runWith(Sink.head)
}

方法可以轻松地添加到任何akka http路由。

val myLoggedRoute = logRequestResult(Logging.InfoLevel, clinetRoute)
Http().bindAndHandle(myLoggedRoute, interface, port)
 类似资料:
  • 问题内容: 我想记录一个axis2客户端发出的所有请求/响应。我试图在http://code.google.com/support/bin/answer.py?hl=zh_CN&answer=15137中创建一个称为describer 的文件,但没有成功(我没有日志文件)。 请求是通过https发出的,我不确定是否重要。我试过了 和 没有成功。 问题答案: 对于SOAP消息的Axis2客户端日志记

  • 问题内容: 我正在使用php,mysql进行搜索,过滤操作。 我的分页课程是 我已经使用以下复选框执行了过滤器: 过滤器的Javascript / ajax代码 process.php文件 我返回分页的结果,但是当我单击页码时,将我带到process.php,因为分页类使用 $ _SERVER [PHP_SELF] 如何在不更改页面url的情况下对结果进行分页,即使用ajax实施。我不能做太多更改

  • 0927,美团二面,1h 1. 自我介绍 2. 深挖项目 1. SwiftUI 怎么样,和 UIKit 比如何,你是怎么学的 Swift,有没有做方案的选型 2. 动画如何实现(自己瞎掰头,他说差不多是这个意思) 3. 序列化怎么做的 4. 网络库用的什么 5. iOS 这边 MVVM,APNs 怎么做的 3. 有没有了解过一些其他的移动端框架,Flutter 画点粒度做 UI 4. 实验室项目

  • 客户端的HTTP/HTTPS请求。 进程:主进程​ ClientRequest是由EventEmitter来实现Writable Stream​ new ClientRequest(options) 作用:发起新的HTTP/HTTPS请求 options(Object | String) - options是String时即请求URL。 options 是Object时则按以下属性请求: meth

  • httplib 库主要用来模拟客户端发送 HTTP 请求,类似于 Curl 工具,支持 JQuery 类似的链式操作。使用起来相当的方便;通过如下方式进行安装: go get github.com/astaxie/beego/httplib 如何使用 首先导入包 import ( "github.com/astaxie/beego/httplib" ) 然后初始化请求方法,返回对象 r

  • 当浏览器请求一个网页时,它会向网络服务器发送一系列不能被直接读取的信息,因为这些信息是作为HTTP信息头的一部分来传送的。您可以查阅HTTP协议来获得更多的信息。 下表列出了浏览器端信息头的一些重要内容,在以后的网络编程中将会经常见到这些信息: 信息 描述 Accept 指定浏览器或其他客户端可以处理的MIME类型。它的值通常为 image/png 或 image/jpeg Accept-Char

  • 我们的应用程序使用ActiveMQ 5.9。0客户端。当我们加载应用程序时,控制台上会记录一条消息,说明: log4j:WARN找不到记录器(org.apache.activemq.thread.TaskRunnerFactory)的追加器 log4j:警告请正确初始化log4j系统 log4j:警告请参阅http://logging.apache.org/log4j/1.2/faq.html#n

  • 我正在使用一个假客户机成功地调用一个RESTendpoint,并将日志记录打开到FULL。这有助于向我显示发送的请求和收到的响应。但是,我看不到请求被发送到哪个服务器。它只告诉我它被发布到,这是服务的名称,而不是服务器的名称。 如何记录此请求发送到的服务器名称?