diff --git a/Package.swift b/Package.swift index 92c126b..4f2207f 100644 --- a/Package.swift +++ b/Package.swift @@ -38,6 +38,7 @@ let package = Package( name: "FluentPostgresDriverTests", dependencies: [ .product(name: "FluentBenchmark", package: "fluent-kit"), + .product(name: "InMemoryLogging", package: "swift-log"), .target(name: "FluentPostgresDriver"), ], swiftSettings: swiftSettings diff --git a/Sources/FluentPostgresDriver/FluentPostgresDatabase.swift b/Sources/FluentPostgresDriver/FluentPostgresDatabase.swift index 727fe64..c2a9149 100644 --- a/Sources/FluentPostgresDriver/FluentPostgresDatabase.swift +++ b/Sources/FluentPostgresDriver/FluentPostgresDatabase.swift @@ -41,7 +41,7 @@ extension _FluentPostgresDatabase: Database { self.withConnection { (underlying: any PostgresDatabase) in closure( _FluentPostgresDatabase( - database: underlying.sql( + database: underlying.logging(to: self.logger).sql( encodingContext: self.encodingContext, decodingContext: self.decodingContext, queryLogLevel: self.database.queryLogLevel diff --git a/Tests/FluentPostgresDriverTests/FluentPostgresLoggingTests.swift b/Tests/FluentPostgresDriverTests/FluentPostgresLoggingTests.swift new file mode 100644 index 0000000..4331c12 --- /dev/null +++ b/Tests/FluentPostgresDriverTests/FluentPostgresLoggingTests.swift @@ -0,0 +1,73 @@ +import FluentKit +import InMemoryLogging +import Logging +import SQLKit +import Testing + +extension AllSuites { + @Suite + struct FluentPostgresLoggingTests { + init() { #expect(isLoggingConfigured) } + + @Test(arguments: TestDriver.allCases) + func withConnectionUsesContextLogger(_ driver: TestDriver) async throws { + try await withDbs(driver) { dbs, _ in + let (first, second) = try await Self.warmedUpDatabases(dbs) + + try await second.db.withConnection { db in + try await (db as! any SQLDatabase).raw("SELECT 'inside withConnection'").run() + } + + #expect(second.logs.loggedQueries.contains("SELECT 'inside withConnection'")) + #expect(!first.logs.loggedQueries.contains("SELECT 'inside withConnection'")) + } + } + + @Test(arguments: TestDriver.allCases) + func transactionUsesContextLogger(_ driver: TestDriver) async throws { + try await withDbs(driver) { dbs, _ in + let (first, second) = try await Self.warmedUpDatabases(dbs) + + try await second.db.transaction { db in + try await (db as! any SQLDatabase).raw("SELECT 'inside transaction'").run() + } + + #expect(second.logs.loggedQueries.contains("BEGIN")) + #expect(second.logs.loggedQueries.contains("SELECT 'inside transaction'")) + #expect(second.logs.loggedQueries.contains("COMMIT")) + #expect(!first.logs.loggedQueries.contains("SELECT 'inside transaction'")) + } + } + + private static func warmedUpDatabases( + _ dbs: Databases + ) async throws -> (first: (db: any Database, logs: InMemoryLogHandler), second: (db: any Database, logs: InMemoryLogHandler)) { + let eventLoop = dbs.eventLoopGroup.any() + let firstLogs = InMemoryLogHandler.capturingAllLevels(), secondLogs = InMemoryLogHandler.capturingAllLevels() + let first = dbs.database(.a, logger: Logger(label: "first") { _ in firstLogs }, on: eventLoop)! + let second = dbs.database(.a, logger: Logger(label: "second") { _ in secondLogs }, on: eventLoop)! + + try await first.withConnection { db in + try await (db as! any SQLDatabase).raw("SELECT 'warm up'").run() + } + return ((first, firstLogs), (second, secondLogs)) + } + } +} + +extension InMemoryLogHandler { + /// A handler that captures everything, since SQL queries are logged at `.debug` by default. + static func capturingAllLevels() -> Self { + var handler = Self() + handler.logLevel = .trace + return handler + } + + /// The SQL of every query that was logged to this handler. + var loggedQueries: [String] { + self.entries.compactMap { entry in + guard case .string(let sql) = entry.metadata["sql"] else { return nil } + return sql + } + } +}