2015-09-09 6 views
20

मुझे अक्का http क्लाइंट अनुरोधों के साथ-साथ उनके प्रतिक्रियाओं को लॉग इन करने की आवश्यकता है। हालांकि इन अनुरोधों को लॉग इन करने के लिए एपीआई का संकेत मिलता है, इस पर कोई स्पष्ट दस्तावेज नहीं है कि इसे कैसे किया जाना चाहिए। मेरे दृष्टिकोण एक लॉग इन अनुरोध जो पारदर्शी रूप से Http().singleRequest(req) लपेटता इस प्रकार बनाने के लिए किया गया है:एक कैसे Akka HTTP क्लाइंट अनुरोध लॉग करता है

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

दुर्भाग्य से, मैं या तो unmarshal के माध्यम से या बस आदेश प्रतिक्रिया के शरीर ठीक करने के लिए में resp.entity.dataBytes अनुरोध करके भविष्य हड़पने के लिए की है। मुझे लॉगिंग मिलती है लेकिन वादा पूरा हो जाता है और मैं वास्तविक डेटा को इकाई को अनधिकृत नहीं कर सकता। एक काम कर समाधान अनुरोध और प्रतिक्रिया लॉग इन करें और "वादा पहले ही पूरा हो" के साथ एक 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 
    } 
} 

विचार स्वागत करते हैं।

+1

मैं ऐसा करने की कोशिश कर रहा हूं। क्या आपको एक समाधान मिला? –

उत्तर

22

समाधान मैंने पाया में से एक का उपयोग करने के लिए है एक:

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]) 

कि समारोह magnet pattern का उपयोग कर लिखा है:

LoggingMagnet[HttpRequest ⇒ RouteResult ⇒ Unit] 

कहाँ:

LoggingMagnet[T](f: LoggingAdapter ⇒ T) 

कि करने के लिए धन्यवाद हम सभी भागों के लिए उपयोग कर सकते है हमें अनुरोध और परिणाम लॉग इन करने की आवश्यकता है। हमारे पास लॉगिंग एडाप्टर, एचटीपीआरक्वेट और रूट रीसेट

मेरे मामले में मैंने एक आंतरिक फ़ंक्शन बनाया है। मैं फिर से सभी मानकों को पारित नहीं करना चाहता हूं।

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

सबसे महत्वपूर्ण हिस्सा अंतिम पंक्ति है, जहां मैं logRequestResult में myLoggingFunction डाल है।

फ़ंक्शन myLoggingFunction नामक फ़ंक्शन, सर्वर गणना के परिणाम से मेल खाता है और इसके आधार पर लॉगइन्रीरी बनाता है।

अंतिम बात एक विधि है जो परिणाम इकाई को स्ट्रीम से डीकोड करने की अनुमति देती है।

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

विधि को आसानी से किसी भी एक्का-http रूट में जोड़ा जा सकता है।

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

कृपया टाइपो सही करें। कृपया इंडेंट सही करें। और हो सकता है कि आपका कोड वास्तव में क्या कर रहा है, इस बारे में थोड़ी अधिक जानकारी जोड़ें। –

+4

सवाल यह है कि सवाल क्लाइंट पर अनुरोध और प्रतिक्रिया लॉगिंग के बारे में था, जबकि यह उत्तर सर्वर पर अनुरोध और प्रतिक्रिया लॉगिंग के बारे में है, है ना? –

+1

लॉगिंग जैसे क्रॉस कटिंग चिंता के लिए यह बहुत अधिक काम है। यह आसान होना चाहिए। इसके अलावा, मैं अर्नाउट से सहमत हूं कि यह ग्राहक अनुरोधों को लॉगिंग के बारे में समाधान प्रदान नहीं करता है। – vijar

4

एक और समाधान के लिए, इस कोड अनुरोध आईपी लॉग करता है और प्रत्येक अनुरोध और प्रतिक्रिया ताकि वे लॉग में संबद्ध किया जा सकता के साथ एक यादृच्छिक संख्या एकत्रित करती है। यह प्रतिक्रिया समय भी रिकॉर्ड करता है।

चूंकि अनुरोध को प्रक्रिया में कुछ समय लग सकता है, और असफल हो सकता है, मैं तुरंत अनुरोध देखना चाहता था, और अगर यह लौटाता है तो प्रतिक्रिया देखें।

RequestFields केवल वह डेटा है जिसे मैं अनुरोध से लेकर ख्याल रखता हूं। डिफ़ॉल्ट रूप से बहुत शोर है।

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 
     } 
     } 
    } 
    } 
} 
संबंधित मुद्दे