如何记录 Akka HTTP 客户端请求 [英] How does one log Akka HTTP client requests

查看:43
本文介绍了如何记录 Akka HTTP 客户端请求的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I need to log akka http client requests as well as their responses. While there seems to be a hint of API for logging these requests, there is no clear documentation on how it should be done. My approach has been to create a logged request which transparently wraps Http().singleRequest(req) as follows:

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 + "
" + s)
    }
    resp
  }
}

不幸的是,我必须通过解组或简单地请求 resp.entity.dataBytes 来获取未来,以便恢复响应的正文.我得到了日志记录,但承诺已完成,我无法再将实体解组为实际数据.一个有效的解决方案会记录请求和响应并通过这个测试用例,而不会抛出承诺已经完成"的 IllegalStateException:

Unfortunately, I have to grab the future either through the unmarshal or by simply requesting resp.entity.dataBytes in order to recover the body of the response. I get the logging but the promise gets completed and I can no longer unmarshal the entity to the actual data. A working solution would log the request and response and pass this test case without an IllegalStateException with "Promise already completed" being thrown:

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
  }
}

欢迎提出想法.

推荐答案

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

One of the solution I've found is to use a:

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

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

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

Which can easily log the request and result in raw(bytes) format. The problem is that those logs are completely unreadable. And here is place where it became complicated.

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

Here is my example that encode the entity of the request/response and write it to the logger.

您可以将函数传递给:

DebuggingDirectives.logRequestResult

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

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

LoggingMagnet[HttpRequest ⇒ RouteResult ⇒ Unit]

地点:

LoggingMagnet[T](f: LoggingAdapter ⇒ T)

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

Thanks to that we have access to all parts that we need to log the request and result. We have LoggingAdapter, HttpRequest and RouteResult

就我而言,我创建了一个内部函数.我不想再次传递所有参数.

In my case I've create an inside function. I don't want to pass all the parameters again.

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} 
 entity: $data", level))
      case other =>
        Future.successful(LogEntry(s"$other", level))
    }
    entry.map(_.logTo(logger))
  }
  DebuggingDirectives.logRequestResult(LoggingMagnet(log => myLoggingFunction(log)))(route)
}

最重要的部分是我将 myLoggingFunction 放入 logRequestResult 的最后一行.

The most important part is the last line where I put myLoggingFunction in to logRequestResult.

名为 myLoggingFunction 的函数,简单匹配服务器计算的结果并基于它创建一个 LogEntry.

The function called myLoggingFunction, simple matched the result of server computation and create a LogEntry based on it.

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

The last thing is a method that allows to decode the result entity from a stream.

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

该方法可以轻松添加到任何 akka-http 路由中.

The method can be easily add to any akka-http route.

val myLoggedRoute = logRequestResult(Logging.InfoLevel, clinetRoute)
Http().bindAndHandle(myLoggedRoute, interface, port)

这篇关于如何记录 Akka HTTP 客户端请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆