diff --git a/Sources/Vapor/Logging/Logger+LogError.swift b/Sources/Vapor/Logging/Logger+LogError.swift index a82cf62684..565efd3374 100644 --- a/Sources/Vapor/Logging/Logger+LogError.swift +++ b/Sources/Vapor/Logging/Logger+LogError.swift @@ -9,6 +9,7 @@ extension Logger { /// Defaults to `true`. public func report( error e: Error, + request: Request? = nil, verbose: Bool = true, file: String = #file, function: String = #function, @@ -17,7 +18,15 @@ extension Logger { ) { switch e { case let debuggable as Debuggable: - let errorString = "\(debuggable.fullIdentifier): \(debuggable.reason)" + + let requestString: String + if let request = request { + requestString = " \(request.http.method) \(request.http.urlString)" + } else { + requestString = "" + } + + let errorString = "\(debuggable.fullIdentifier):\(requestString) \(debuggable.reason)" if let source = debuggable.sourceLocation { error(errorString, file: source.file.lastPart, function: source.function, line: source.line, column: source.column) } else { diff --git a/Sources/Vapor/Middleware/ErrorMiddleware.swift b/Sources/Vapor/Middleware/ErrorMiddleware.swift index 3113f31af6..853f495f7c 100644 --- a/Sources/Vapor/Middleware/ErrorMiddleware.swift +++ b/Sources/Vapor/Middleware/ErrorMiddleware.swift @@ -23,8 +23,12 @@ public final class ErrorMiddleware: Middleware, ServiceType { public static func `default`(environment: Environment, log: Logger) -> ErrorMiddleware { return .init { req, error in // log the error - log.report(error: error, verbose: !environment.isRelease) - + log.report( + error: error, + request: req, + verbose: !environment.isRelease + ) + // variables to determine let status: HTTPResponseStatus let reason: String diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 61dfb65638..d7fa53c781 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -7,6 +7,7 @@ XCTMain([ // Vapor testCase(ApplicationTests.allTests), testCase(MiddlewareTests.allTests), + testCase(LoggingTests.allTests) ]) #endif diff --git a/Tests/VaporTests/LoggingTests.swift b/Tests/VaporTests/LoggingTests.swift new file mode 100644 index 0000000000..5c6a92024a --- /dev/null +++ b/Tests/VaporTests/LoggingTests.swift @@ -0,0 +1,64 @@ +import Vapor +import XCTest + +class LoggingTests : XCTestCase { + + func testNotFoundLogging() throws { + + var services = Services.default() + var config = Config.default() + let loggerProvider = TestLoggerProvider() + + try services.register(loggerProvider) + config.prefer(TestLogger.self, for: Logger.self) + + let app = try Application(config: config, services: services) + + let req = Request( + http: HTTPRequest(method: .GET, url: "/hello/vapor"), + using: app + ) + + do { + _ = try app.make(Responder.self).respond(to: req).wait() + } catch {} + + XCTAssert(loggerProvider.logger.didLog(string: "Abort.404: GET /hello/vapor Not Found")) + } + + func testInternalServerErrorLogging() throws { + + var services = Services.default() + var config = Config.default() + let loggerProvider = TestLoggerProvider() + + try services.register(loggerProvider) + config.prefer(TestLogger.self, for: Logger.self) + + let router = EngineRouter.default() + + router.post("fail/me") { (_) -> String in + throw Abort(.internalServerError) + } + + services.register(router, as: Router.self) + + let app = try Application(config: config, services: services) + + let req = Request( + http: HTTPRequest(method: .POST, url: "/fail/me"), + using: app + ) + + do { + _ = try app.make(Responder.self).respond(to: req).wait() + } catch {} + + XCTAssert(loggerProvider.logger.didLog(string: "Abort.500: POST /fail/me Internal Server Error")) + } + + static let allTests = [ + ("testNotFoundLogging", testNotFoundLogging), + ("testInternalServerErrorLogging", testInternalServerErrorLogging) + ] +} diff --git a/Tests/VaporTests/LoggingTestsMocks.swift b/Tests/VaporTests/LoggingTestsMocks.swift new file mode 100644 index 0000000000..f6dbb89b7d --- /dev/null +++ b/Tests/VaporTests/LoggingTestsMocks.swift @@ -0,0 +1,33 @@ +import Foundation +import Vapor + +final class TestLogger: Logger { + + var logs = [String]() + + public func log(_ string: String, at level: LogLevel, file: String, function: String, line: UInt, column: UInt) { + + logs += [string] + } + + func didLog(string: String) -> Bool { + return logs.contains(string) + } +} + +extension TestLogger: Service {} + +final class TestLoggerProvider: Provider { + + let logger = TestLogger() + + func register(_ services: inout Services) throws { + services.register(Logger.self) { container -> TestLogger in + return self.logger + } + } + + func didBoot(_ container: Container) throws -> EventLoopFuture { + return .done(on: container) + } +}