Creating a per-request service (Vapor 3)

Hi.

I'm trying to create a Logger service that is unique per request. The idea is that every log-statement for a given request always prints a request id along with whatever message one wishes to log.

But I'm struggling to make it work. Having read the documentation, I'd think that the new logger would look like the following:

import Vapor
import ULID

final class RequestLogger : Logger, ServiceType {

    let baseLogger : Logger
    let requestId : String

    static var serviceSupports: [Any.Type] {
        return [Logger.self]
    }

    static func makeService(for worker: Container) throws -> RequestLogger {
        let reqId = ULID().ulidString
        return RequestLogger(requestId: reqId)
    }

    init(requestId: String) {
        self.baseLogger = PrintLogger()
        self.requestId = requestId
    }

    func log(_ string: String, at level: LogLevel, file: String, function: String, line: UInt, column: UInt) {
        let formatter = ISO8601DateFormatter()
        let timeString = formatter.string(from: Date())

        let logString = "[ \(timeString) ] [ \(self.requestId) ] \(string)"

        self.baseLogger.log(logString, at: level, file: file, function: function, line: line, column: column)
    }

}

Then registering it in configure.swift:

    services.register(RequestLogger.self)
    config.prefer(RequestLogger.self, for: Logger.self)

However, when using this in a controller function, it seems I get the same instance every time. Here is the console output for three seperate requests:

[INFO] [ 2020-03-15T12:31:26Z ] [ 01E3F2TVT36MYFMSM4FZMZ7503 ] [ GET: / ] Start (/vagrant/Sources/App/Middleware/RequestLoggerMiddleware.swift:respond(to:chainingTo:):20:24)
[INFO] [ 2020-03-15T12:31:26Z ] [ 01E3F2TVT36MYFMSM4FZMZ7503 ] Rendering view for / (/vagrant/Sources/App/routes.swift:routes(_:):7:18)
[INFO] [ 2020-03-15T12:31:26Z ] [ 01E3F2TVT36MYFMSM4FZMZ7503 ] [ Status: ok ] Finished in 5.61ms (/vagrant/Sources/App/Middleware/RequestLoggerMiddleware.swift:respond(to:chainingTo:):27:32)
[INFO] [ 2020-03-15T12:31:27Z ] [ 01E3F2TVT36MYFMSM4FZMZ7503 ] [ GET: / ] Start (/vagrant/Sources/App/Middleware/RequestLoggerMiddleware.swift:respond(to:chainingTo:):20:24)
[INFO] [ 2020-03-15T12:31:27Z ] [ 01E3F2TVT36MYFMSM4FZMZ7503 ] Rendering view for / (/vagrant/Sources/App/routes.swift:routes(_:):7:18)
[INFO] [ 2020-03-15T12:31:27Z ] [ 01E3F2TVT36MYFMSM4FZMZ7503 ] [ Status: ok ] Finished in 2.78ms (/vagrant/Sources/App/Middleware/RequestLoggerMiddleware.swift:respond(to:chainingTo:):27:32)
[INFO] [ 2020-03-15T12:31:27Z ] [ 01E3F2TVT36MYFMSM4FZMZ7503 ] [ GET: / ] Start (/vagrant/Sources/App/Middleware/RequestLoggerMiddleware.swift:respond(to:chainingTo:):20:24)
[INFO] [ 2020-03-15T12:31:27Z ] [ 01E3F2TVT36MYFMSM4FZMZ7503 ] Rendering view for / (/vagrant/Sources/App/routes.swift:routes(_:):7:18)
[INFO] [ 2020-03-15T12:31:27Z ] [ 01E3F2TVT36MYFMSM4FZMZ7503 ] [ Status: ok ] Finished in 2.74ms (/vagrant/Sources/App/Middleware/RequestLoggerMiddleware.swift:respond(to:chainingTo:):27:32)

Does anyone see what I do wrong?

On Vapor 3, you can use request.privateContainer.make(Logger.self) to get a service instance that is unique to that request.

1 Like

That worked beautifully, thanks!