const mixinafBedSheet::RequestLogger


Implement to create HTTP request / response loggers.

logIncoming() is called once per request before any request processing, and logOutgoing() is called after all processing has finished. Here's an example basic logger:

using afIoc
using afConcurrent

const class BasicRequestLogger : RequestLogger {
    @Inject private const HttpRequest  httpReq
    @Inject private const HttpResponse httpRes
    @Inject private const LocalRef     startTimeRef
    @Inject private const Log          log
    new make(|This|in) { in(this) }
    override Void logIncoming() {
        startTimeRef.val =

    override Void logOutgoing() {
        timeTaken :=
        msg := "${httpReq.httpMethod} ${httpReq.url.encode} ${httpRes.statusCode} in ${timeTaken}"

Middleware could be used to log HTTP requests, but Middleware is wrapped in an error handling mechanism. So if an error handler changes response, this may not be seen by the logger.

Requestloggers are invoked outside of error handling, so the response seen by the logger IS the response sent to the browser. The caveat to this, is that ALL errors raised by RequestLoggers are simply logged and swallowed. So unless you're monitoring the server logs, you're unlikely to see any logger problems.

IoC Configuration

Instances of RequestLogger should be contributed to the RequestLoggers service. Example:

@Contribute { serviceType=RequestLoggers# }
Void contributeRequestLoggers(Configuration config) {

A config key is not required, but it's polite to provide one so others may remove it, or order their loggers before or after yours. You can also use IoC to autobuild your logger should it have any dependencies:

@Contribute { serviceType=RequestLoggers# }
Void contributeRequestLoggers(Configuration config) {
    config["myLogger"] =


virtual Void logIncoming()

Called before all request processing.



virtual Void logOutgoing()

Called after all request processing.