2
votes

I am using spray framework for REST api and debugging directive logRequestResponse. I would like to create an audit log - dedicated log which contains information about access to this api Request/Reply. For this purpose I am using logRequestResponse but I struggle with separating those log rectords to a dedicated log. I can provide a marker as but not a logger and cannot use a loogger since it is a the main one. trait RestApi extends Directives { this: ServiceActors with Core =>

// we use the enclosing ActorContext's or ActorSystem's dispatcher for our Futures and Scheduler private implicit val _ = system.dispatcher implicit val timeout = Timeout(30.seconds)

  val route: Route = 
    logRequestResponse("REST API", Logging.InfoLevel){
        new DataServiceApi(dataService).route ~
        new AnalyticsServiceApi(analyticsService).route
    }

2015-10-22 12:59:45,599 [INFO ] [BackEndServices-akka.actor.default-dispatcher-16] [akka.actor.ActorSystemImpl]ActorSystem(BackEndServices) - REST API: Response for
  Request : HttpRequest(GET,http://localhost:8080/service/analytics/ ...
  Response: HttpResponse(200 OK,HttpEntity(application/json; charset=UTF-8, ...

Is there a simple way how to achieve that?

1
It's a little unclear what your constraints are. Can you explain further what you mean by "I can provide a marker as but not a logger and cannot use a loogger since it is a the main one"?Eric Woods
There are no constraints. Just wondering what is planned usage from the author of the api. Marker is term from the documentation of this api, in this example it is REST API. My complain was that I cannot provide the name of the logger I want to use instead of marker.jaksky

1 Answers

3
votes

If you dig into the implementation a bit, you'll see that that the logRequestResponse directive ultimately uses the logging facilities provided by Akka. So you should definitely check out the Akka logging documentation if you haven't already.

Akka 2.4.0 Logging

If you want to log your request and response to a separate log file, you're best taking advantage of whatever logging library you're using.

Let's assume you're using Logback, since Akka provides good support for SLF4J, and the team recommends it.

To start logging your requests and responses to a separate log file, you'll need to:

  1. Configure your Spray/Akka app to use Logback
  2. Configure a separate logger and appender in your logback.xml

For step 1, do the following (this is all in the Akka documentation):

  1. Also package the akka-slf4j jar with your project
  2. Add this config to application.conf

application.conf

akka {
  loggers = ["akka.event.slf4j.Slf4jLogger"]
  loglevel = "DEBUG"
}

For step 2, your logback.xml should have 2 appenders -- one for the console where everything is logged, and another where just the logs produced by the logRequestResponse directive are captured. Something like what you see below:

logback.xml

<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    <target>System.out</target>
    <encoder>
        <pattern>%date{ISO8601} %-5level %logger{36} %X{akkaSource} - %msg%n</pattern>
    </encoder>
</appender>

<appender name="ACCESS" class="ch.qos.logback.core.FileAppender">
    <file>access.log</file>
    <encoder>
        <pattern>%date{ISO8601} %-5level %logger{36} %X{akkaSource} - %msg%n</pattern>
    </encoder>
</appender>


<root>
    <appender-ref ref="CONSOLE"/>
</root>

<logger name="akka.actor.RepointableActorRef" level="INFO">
    <appender-ref ref="ACCESS" />
</logger>

<logger name="akka" level="INFO"/>

That should log your request and responses to the access.log file.

The one thing that's not particular great though, is that our logger is always output as "akka.actor.RepointableActorRef". And you'll probably end up getting additional log statements that you don't care about written to that file.

That's a result of the implementation of Spray's LoggingContext, which ends up constructing a LoggingAdapter by passing the ActorRef type to the LoggingAdapter constructor.

Logging(context.system.eventStream, context.self)

We could get around this by mixing into your route a trait that implements our own LoggingMagnet and uses a LoggingAdapter that we construct (instead of the Spray library).

Something like this:

import akka.actor.Actor
import akka.event.Logging
import spray.http.HttpRequest
import spray.routing.directives.LoggingMagnet


trait AccessLogger { this: Actor =>

  val accessLogger: LoggingMagnet[HttpRequest => Any => Unit] = LoggingMagnet {
    request => response =>
      val accessLogger = Logging(context.system.eventStream, "com.my.AccessLogger")
      accessLogger.info("Request: " + request + "\n Response: " + response)
  }

}

For you jaksky, I think you'd probably be mixing AccessLogger into RestApi

Update the logger on logback.xml from this:

<logger name="akka.actor.RepointableActorRef" level="INFO">
    <appender-ref ref="ACCESS" />
</logger>

... to this:

<logger name="com.my.AccessLogger" level="INFO">
    <appender-ref ref="ACCESS" />
</logger>

Then, you could pass accessLogger from the AccessLogger trait into the logRequestResponse directive

logRequestResponse(accessLogger){
  //... rest of route ...
}

After applying those changes, all of your requests and responses should be logged to the com.my.AccessLogger logger, which in turn is configured to write to its own file as configured in the ACCESS appender.

If you're not interested in using Logback or Akka for logging, you can always use a similar approach, but just provide a different implementation of the AccessLogger.