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:
- Configure your Spray/Akka app to use Logback
- Configure a separate logger and appender in your logback.xml
For step 1, do the following (this is all in the Akka documentation):
- Also package the akka-slf4j jar with your project
- 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.