Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

akka-http no stack trace or details on error

I got a structure which can basically be summarized as:

  • outside user makes a rest request to akka-http server
  • akka-http makes a request(query?) to a (some)data source using asynchttpclient
  • akka-http transforms the result from asynchttpclient and serves it back to user

At some point I am getting an error from akka which tells me almost nothing. This error happens right after the asynchttpclient returns me some results. (I can infact at this point print the results on the log, they are there parsed from json etc.. but akka had already errored out)

Even in debug logging level I got no decipherable error message from akka or a stacktrace.

only message I got is:

2017-03-24 17:22:55 INFO  CompanyRepository:111 - search company with name:"somecompanyname"
2017-03-24 17:22:55 INFO  CompanyRepository:73 - [QUERY TIME]: 527ms
[ERROR] [03/24/2017 17:22:55.951] [company-api-system-akka.actor.default-dispatcher-3] [akka.actor.ActorSystemImpl(company-api-system)] Error during processing of request: 'requirement failed'. Completing with 500 Internal Server Error response.

This error message is the only thing I get. Relevant parts of my config:

akka {
  loglevel = "DEBUG"
  # edit --  tested with sl4jlogger with no change
  #loggers = ["akka.event.slf4j.Slf4jLogger"]
  #logging-filter = "akka.event.slf4j.Slf4jLoggingFilter"

  parsing {
    max-content-length = 800m
    max-chunk-size             = 100m
  }
  server {
    server-header = akka-http/${akka.http.version}
    idle-timeout = 120 s
    request-timeout = 120 s
    bind-timeout = 10s
    max-connections = 1024
    pipelining-limit = 32

    verbose-error-messages = on
  }

  client {
    user-agent-header = akka-http/${akka.http.version}
  }

  host-connection-pool {
    max-connections = 4
  }
}

akka.http.routing {
  verbose-error-messages = on
}

Anyone knows if I can make akka to spit out more details about what/where the error is occurring?

Edit: I realized I do NOT get this same error on resultsets which are smaller in size. <- ignore

Edit 2:

  • Added akka.loglevel = DEBUG, spits out a lot more noise but still not detail about the actual error.
  • Converted asynchttpclient to akka quickly to rule out AHC
  • I already had a wrapper around my query to time it, added some logging there trying to pinpoint when exactly the error is happening.

    def queryTimer[ R <: Future[ Any ] ]( block: => R ): R = {
      val t0 = System.currentTimeMillis()
      val result = block
      result.onComplete { maybeResult =>
        val t1 = System.currentTimeMillis()
        logger.info( "[QUERY TIME]: " + ( t1 - t0 ) + "ms" )
    
        maybeResult match {
          case Success(some) =>
            logger.info( "successful feature:")
            logger.info( FormattedString.prettyPrint(some))
          case Failure(someFailure) =>
            logger.info( "failed feature:")
            logger.debug( FormattedString.prettyPrint(someFailure))
        }
      }
      result
    }
    
  • resulting log:

    2017-03-28 13:19:10 INFO  CompanyRepository:111 - search company with name:"some company"
    [DEBUG] [03/28/2017 13:19:10.497] [company-api-system-akka.actor.default-dispatcher-2] [EventStream(akka://xca-api-actor-system)] logger log1-Logging$DefaultLogger started
    [DEBUG] [03/28/2017 13:19:10.497] [company-api-system-akka.actor.default-dispatcher-2] [EventStream(akka://xca-api-actor-system)] Default Loggers started
    [DEBUG] [03/28/2017 13:19:10.613] [company-api-system-akka.actor.default-dispatcher-2] [AkkaSSLConfig(akka://xca-api-actor-system)] Initializing AkkaSSLConfig extension...
    [DEBUG] [03/28/2017 13:19:10.613] [company-api-system-akka.actor.default-dispatcher-2] [AkkaSSLConfig(akka://xca-api-actor-system)] buildHostnameVerifier: created hostname verifier: com.typesafe.sslconfig.ssl.DefaultHostnameVerifier@779e2339
    [DEBUG] [03/28/2017 13:19:10.633] [xca-api-actor-system-akka.actor.default-dispatcher-3] [akka://xca-api-actor-system/user/pool-master/PoolInterfaceActor-0] (Re-)starting host connection pool to localhost:27474
    [DEBUG] [03/28/2017 13:19:10.727] [xca-api-actor-system-akka.actor.default-dispatcher-3] [akka://xca-api-actor-system/system/IO-TCP/selectors/$a/0] Resolving localhost before connecting
    [DEBUG] [03/28/2017 13:19:10.740] [xca-api-actor-system-akka.actor.default-dispatcher-4] [akka://xca-api-actor-system/system/IO-DNS] Resolution request for localhost from Actor[akka://xca-api-actor-system/system/IO-TCP/selectors/$a/0#-815754478]
    [DEBUG] [03/28/2017 13:19:10.749] [xca-api-actor-system-akka.actor.default-dispatcher-4] [akka://xca-api-actor-system/system/IO-TCP/selectors/$a/0] Attempting connection to [localhost/127.0.0.1:27474]
    [DEBUG] [03/28/2017 13:19:10.751] [xca-api-actor-system-akka.actor.default-dispatcher-4] [akka://xca-api-actor-system/system/IO-TCP/selectors/$a/0] Connection established to [localhost:27474]
    2017-03-28 13:19:10 INFO  CompanyRepository:73 - [QUERY TIME]: 376ms
    2017-03-28 13:19:10 INFO  CompanyRepository:77 - successful feature:
    [ERROR] [03/28/2017 13:19:10.896] [company-api-system-akka.actor.default-dispatcher-7] [akka.actor.ActorSystemImpl(company-api-system)] Error during processing of request: 'requirement failed'. Completing with 500 Internal Server Error response.
    2017-03-28 13:19:10 INFO  CompanyRepository:78 - SearchResult(List(
    ( prettyprint output here!!! lots and lots of legit result, json parsed succcesfully into a bunch of case classes)
    

    as you can see my logging format and akkas' are different, the ERROR is coming from akka with do details, while everything looks like working.

Edit 3: logs with sleep in between calls

  • new query timer function with sleeps

    def queryTimer[ R <: Future[ Any ] ]( block: => R ): R = {
      val t0 = System.currentTimeMillis()
      val result = block
      result.onComplete { maybeResult =>
        val t1 = System.currentTimeMillis()
        logger.info( "[QUERY TIME]: " + ( t1 - t0 ) + "ms" )
    
        maybeResult match {
          case Success(some) =>
            Thread.sleep(500)
            logger.info( "successful feature:")
            Thread.sleep(500)
            logger.info( FormattedString.prettyPrint(some))
            Thread.sleep(500)
            logger.info("we are there!")
          case Failure(someFailure) =>
            logger.info( "failed feature:")
            logger.debug( FormattedString.prettyPrint(someFailure))
        }
      }
      result
    }
    
  • logs with sleeps

    [DEBUG] [03/30/2017 11:11:58.629] [xca-api-actor-system-akka.actor.default-dispatcher-7] [akka://xca-api-actor-system/system/IO-TCP/selectors/$a/0] Attempting connection to [localhost/127.0.0.1:27474]
    [DEBUG] [03/30/2017 11:11:58.631] [xca-api-actor-system-akka.actor.default-dispatcher-7] [akka://xca-api-actor-system/system/IO-TCP/selectors/$a/0] Connection established to [localhost:27474]
    11:11:59.442 [pool-2-thread-1] DEBUG o.a.netty.channel.DefaultChannelPool - Closed 0 connections out of 0 in 0 ms
    11:11:59.496 [pool-1-thread-1] DEBUG o.a.netty.channel.DefaultChannelPool - Closed 0 connections out of 0 in 0 ms
    11:12:00.250 [ForkJoinPool-2-worker-15] INFO  c.s.s.r.neo4j.CompanyRepository - [QUERY TIME]: 1880ms
    [ERROR] [03/30/2017 11:12:00.265] [company-api-system-akka.actor.default-dispatcher-3] [akka.actor.ActorSystemImpl(company-api-system)] Error during processing of request: 'requirement failed'. Completing with 500 Internal Server Error response.
    11:12:00.543 [pool-2-thread-1] DEBUG o.a.netty.channel.DefaultChannelPool - Closed 0 connections out of 0 in 0 ms
    11:12:00.597 [pool-1-thread-1] DEBUG o.a.netty.channel.DefaultChannelPool - Closed 0 connections out of 0 in 0 ms
    11:12:00.752 [ForkJoinPool-2-worker-15] INFO  c.s.s.r.neo4j.CompanyRepository - successful feature:
    11:12:01.645 [pool-2-thread-1] DEBUG o.a.netty.channel.DefaultChannelPool - Closed 0 connections out of 0 in 0 ms
    11:12:01.697 [pool-1-thread-1] DEBUG o.a.netty.channel.DefaultChannelPool - Closed 0 connections out of 0 in 0 ms
    11:12:01.750 [ForkJoinPool-2-worker-15] INFO  c.s.s.r.neo4j.CompanyRepository - SearchResult(List( "lots of legit result here"
    11:12:02.281 [ForkJoinPool-2-worker-15] INFO  c.s.s.r.neo4j.CompanyRepository - we are there!
    

Edit 4 and solution!

  • Apparently the default exception handler does not print a stack trace! overriding the exception handler with a very basic catch all:

    implicit def myExceptionHandler: ExceptionHandler =
      ExceptionHandler {
        case e: Exception => {
          logger.info("---------------- exception log start")
          logger.error(e.getMessage, e)
          logger.error("cause" , e.getCause)
          logger.error("cause" , e.getStackTraceString )
          logger.info( FormattedString.prettyPrint(e))
          logger.info("---------------- exception log end")
          Directives.complete("server made a boo boo")
          }
      }
    
  • results in a stack trace that befuddles the sh*t out of me!!

    11:42:04.634 [company-api-system-akka.actor.default-dispatcher-2] INFO  c.stepweb.scarifgate.CompanyApiApp$ - ---------------- exception log start
    11:42:04.640 [company-api-system-akka.actor.default-dispatcher-2] ERROR c.stepweb.scarifgate.CompanyApiApp$ - requirement failed
    java.lang.IllegalArgumentException: requirement failed
        at scala.Predef$.require(Predef.scala:212) ~[scala-library-2.11.8.jar:na]
        at spray.json.BasicFormats$StringJsonFormat$.write(BasicFormats.scala:121) ~[spray-json_2.11-1.3.2.jar:na]
        at spray.json.BasicFormats$StringJsonFormat$.write(BasicFormats.scala:119) ~[spray-json_2.11-1.3.2.jar:na]
        at spray.json.ProductFormats$class.productElement2Field(ProductFormats.scala:46) ~[spray-json_2.11-1.3.2.jar:na]
        at com.stepweb.scarifgate.services.CompanyService.productElement2Field(CompanyService.scala:14) ~[classes/:na]
        at spray.json.ProductFormatsInstances$$anon$3.write(ProductFormatsInstances.scala:73) ~[spray-json_2.11-1.3.2.jar:na]
        at spray.json.ProductFormatsInstances$$anon$3.write(ProductFormatsInstances.scala:68) ~[spray-json_2.11-1.3.2.jar:na]
        at spray.json.PimpedAny.toJson(package.scala:39) ~[spray-json_2.11-1.3.2.jar:na]
        at spray.json.CollectionFormats$$anon$1$$anonfun$write$1.apply(CollectionFormats.scala:26) ~[spray-json_2.11-1.3.2.jar:na]
        at spray.json.CollectionFormats$$anon$1$$anonfun$write$1.apply(CollectionFormats.scala:26) ~[spray-json_2.11-1.3.2.jar:na]
        at scala.collection.immutable.List.map(List.scala:273) ~[scala-library-2.11.8.jar:na]
        at spray.json.CollectionFormats$$anon$1.write(CollectionFormats.scala:26) ~[spray-json_2.11-1.3.2.jar:na]
        at spray.json.CollectionFormats$$anon$1.write(CollectionFormats.scala:25) ~[spray-json_2.11-1.3.2.jar:na]
        at spray.json.ProductFormats$class.productElement2Field(ProductFormats.scala:46) ~[spray-json_2.11-1.3.2.jar:na]
        at com.stepweb.scarifgate.services.CompanyService.productElement2Field(CompanyService.scala:14) ~[classes/:na]
        at spray.json.ProductFormatsInstances$$anon$1.write(ProductFormatsInstances.scala:30) ~[spray-json_2.11-1.3.2.jar:na]
        at spray.json.ProductFormatsInstances$$anon$1.write(ProductFormatsInstances.scala:26) ~[spray-json_2.11-1.3.2.jar:na]
        at akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport$$anonfun$sprayJsonMarshaller$1.apply(SprayJsonSupport.scala:62) ~[akka-http-spray-json_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport$$anonfun$sprayJsonMarshaller$1.apply(SprayJsonSupport.scala:62) ~[akka-http-spray-json_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.marshalling.Marshaller$$anonfun$compose$1$$anonfun$apply$15.apply(Marshaller.scala:73) ~[akka-http_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.marshalling.Marshaller$$anonfun$compose$1$$anonfun$apply$15.apply(Marshaller.scala:73) ~[akka-http_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.marshalling.Marshaller$$anon$1.apply(Marshaller.scala:92) ~[akka-http_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.marshalling.GenericMarshallers$$anonfun$optionMarshaller$1$$anonfun$apply$1.apply(GenericMarshallers.scala:19) ~[akka-http_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.marshalling.GenericMarshallers$$anonfun$optionMarshaller$1$$anonfun$apply$1.apply(GenericMarshallers.scala:18) ~[akka-http_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.marshalling.Marshaller$$anon$1.apply(Marshaller.scala:92) ~[akka-http_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.marshalling.PredefinedToResponseMarshallers$$anonfun$fromStatusCodeAndHeadersAndValue$1$$anonfun$apply$5.apply(PredefinedToResponseMarshallers.scala:58) ~[akka-http_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.marshalling.PredefinedToResponseMarshallers$$anonfun$fromStatusCodeAndHeadersAndValue$1$$anonfun$apply$5.apply(PredefinedToResponseMarshallers.scala:57) ~[akka-http_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.marshalling.Marshaller$$anon$1.apply(Marshaller.scala:92) ~[akka-http_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.marshalling.Marshaller$$anonfun$compose$1$$anonfun$apply$15.apply(Marshaller.scala:73) ~[akka-http_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.marshalling.Marshaller$$anonfun$compose$1$$anonfun$apply$15.apply(Marshaller.scala:73) ~[akka-http_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.marshalling.Marshaller$$anon$1.apply(Marshaller.scala:92) ~[akka-http_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.marshalling.ToResponseMarshallable$$anonfun$1$$anonfun$apply$1.apply(ToResponseMarshallable.scala:29) ~[akka-http_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.marshalling.ToResponseMarshallable$$anonfun$1$$anonfun$apply$1.apply(ToResponseMarshallable.scala:29) ~[akka-http_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.marshalling.Marshaller$$anon$1.apply(Marshaller.scala:92) ~[akka-http_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.marshalling.GenericMarshallers$$anonfun$futureMarshaller$1$$anonfun$apply$3$$anonfun$apply$4.apply(GenericMarshallers.scala:33) ~[akka-http_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.marshalling.GenericMarshallers$$anonfun$futureMarshaller$1$$anonfun$apply$3$$anonfun$apply$4.apply(GenericMarshallers.scala:33) ~[akka-http_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.util.FastFuture$.akka$http$scaladsl$util$FastFuture$$strictTransform$1(FastFuture.scala:41) ~[akka-http-core_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.util.FastFuture$$anonfun$transformWith$extension1$1.apply(FastFuture.scala:51) [akka-http-core_2.11-10.0.0.jar:10.0.0]
        at akka.http.scaladsl.util.FastFuture$$anonfun$transformWith$extension1$1.apply(FastFuture.scala:50) [akka-http-core_2.11-10.0.0.jar:10.0.0]
        at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:32) [scala-library-2.11.8.jar:na]
        at akka.dispatch.BatchingExecutor$AbstractBatch.processBatch(BatchingExecutor.scala:55) [akka-actor_2.11-2.4.16.jar:na]
        at akka.dispatch.BatchingExecutor$BlockableBatch$$anonfun$run$1.apply$mcV$sp(BatchingExecutor.scala:91) [akka-actor_2.11-2.4.16.jar:na]
        at akka.dispatch.BatchingExecutor$BlockableBatch$$anonfun$run$1.apply(BatchingExecutor.scala:91) [akka-actor_2.11-2.4.16.jar:na]
        at akka.dispatch.BatchingExecutor$BlockableBatch$$anonfun$run$1.apply(BatchingExecutor.scala:91) [akka-actor_2.11-2.4.16.jar:na]
        at scala.concurrent.BlockContext$.withBlockContext(BlockContext.scala:72) [scala-library-2.11.8.jar:na]
        at akka.dispatch.BatchingExecutor$BlockableBatch.run(BatchingExecutor.scala:90) [akka-actor_2.11-2.4.16.jar:na]
        at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:39) [akka-actor_2.11-2.4.16.jar:na]
        at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:415) [akka-actor_2.11-2.4.16.jar:na]
        at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260) [scala-library-2.11.8.jar:na]
        at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339) [scala-library-2.11.8.jar:na]
        at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979) [scala-library-2.11.8.jar:na]
        at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107) [scala-library-2.11.8.jar:na]
    11:42:04.640 [company-api-system-akka.actor.default-dispatcher-2] ERROR c.stepweb.scarifgate.CompanyApiApp$ - cause
    11:42:04.641 [company-api-system-akka.actor.default-dispatcher-2] ERROR c.stepweb.scarifgate.CompanyApiApp$ - cause
    11:42:04.644 [company-api-system-akka.actor.default-dispatcher-2] INFO  c.stepweb.scarifgate.CompanyApiApp$ - java.lang.IllegalArgumentException: requirement failed
    11:42:04.644 [company-api-system-akka.actor.default-dispatcher-2] INFO  c.stepweb.scarifgate.CompanyApiApp$ - ---------------- exception log end 
    
  • so... the exception is caused here in spray.json.BasicFormats

    implicit object StringJsonFormat extends JsonFormat[String] {
      def write(x: String) = {
        require(x ne null)    // <-----------------------------------
        JsString(x)
      }
      def read(value: JsValue) = value match {
        case JsString(x) => x
        case x => deserializationError("Expected String as JsString, but got " + x)
      }
    }
    

which sort of means one of the strings in this thousands of lines of response is null. Special thanks goes to the laziness of using that "require" without a message. Debugging which string is empty where will be a nightmare but I still think akka should fail in a better way.

like image 202
kali Avatar asked Mar 24 '17 17:03

kali


1 Answers

akka-http no stack trace or details on error

Well, default akka-http ExceptionHandler doesn't print stack trace and prints only error message or its class name if the message is empty but you can provide custom exception handler that will print anything you want (i.e. stack trace in your example).

Some examples of how to make a custom exception handler are provided at GitHub ExceptionHandlerExamplesSpec.spec

The simplest way in your case seems to be to define your own custom implicit exception handler

  import akka.http.scaladsl.model._
  import akka.http.scaladsl.server._
  import StatusCodes._
  import Directives._

  implicit def myExceptionHandler: ExceptionHandler =
    ExceptionHandler {
      case NonFatal(e) =>
          logger.error(s"Exception $e at\n${e.getStackTraceString}")
          complete(HttpResponse(InternalServerError, entity = "Internal Server Error"))
        }
    }
like image 116
SergGr Avatar answered Sep 20 '22 10:09

SergGr