mirror of
https://github.com/swift-server/RediStack.git
synced 2026-06-02 07:37:33 +00:00
Motivation: The original implementation of Logging was done in more haste than should have been, without proper attention given to the semantic requirements. As the Swift ecosystem has matured a bit, lessons have been learned on handling metadata and passing of external context into internal subcomponents. A mixture of the "protocol-based context passing" and "explicit context passing" patterns have been adopted. Both patterns are more fully described in the Swift forum discussion: https://forums.swift.org/t/the-context-passing-problem/39162 Modifications: - Add: `RedisLogging` namespace with references to static keys and labels that are used for Logging throughout the library - Add: `Logger` static computed properties to access the Logger prototypes used in connection and connection pools - Add: `RedisClientWithUserContext` protocol and `UserContextRedisClient` types to assist with wrapping client types for custom logger contexts - Remove: `logger` property from `RedisClient` requirements - Change: Many log statements to have higher or lower log levels for their appropriate context - Change: `RedisConnection` and `RedisConnectionPool` to conform to `RedisClientWithUserContext` - Change: `logging(to:)` protocol requirement to return a `RedisClient` existential - Change: ConnectionPool to explicitly pass a logger instance around for pooling methods Result: Logging in RediStack will now have a stronger contract of where and how logs will be generated and which context will be used. Fixes #79 and #74
90 lines
2.8 KiB
Swift
90 lines
2.8 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the RediStack open source project
|
|
//
|
|
// Copyright (c) 2020 RediStack project authors
|
|
// Licensed under Apache License v2.0
|
|
//
|
|
// See LICENSE.txt for license information
|
|
// See CONTRIBUTORS.txt for the list of RediStack project authors
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import Logging
|
|
import RediStack
|
|
import RediStackTestUtils
|
|
import XCTest
|
|
|
|
final class RedisLoggingTests: RediStackIntegrationTestCase {
|
|
func test_connectionUsesCustomLogger() throws {
|
|
let handler = TestLogHandler()
|
|
let logger = Logger(label: #function, factory: { _ in return handler })
|
|
_ = try self.connection
|
|
.logging(to: logger)
|
|
.ping()
|
|
.wait()
|
|
XCTAssertFalse(handler.messages.isEmpty)
|
|
}
|
|
|
|
func test_connectionLoggerMetadata() throws {
|
|
let handler = TestLogHandler()
|
|
let logger = Logger(label: #function, factory: { _ in return handler })
|
|
|
|
_ = try self.connection
|
|
.logging(to: logger)
|
|
.ping()
|
|
.wait()
|
|
XCTAssertEqual(
|
|
handler.metadata[RedisLogging.MetadataKeys.connectionID],
|
|
.string(self.connection.id.description)
|
|
)
|
|
}
|
|
|
|
func test_poolLoggerMetadata() throws {
|
|
let handler = TestLogHandler()
|
|
let logger = Logger(label: #function, factory: { _ in return handler })
|
|
|
|
let pool = RedisConnectionPool(
|
|
serverConnectionAddresses: [try .makeAddressResolvingHost(self.redisHostname, port: self.redisPort)],
|
|
loop: self.connection.eventLoop,
|
|
maximumConnectionCount: .maximumActiveConnections(1),
|
|
connectionPassword: self.redisPassword
|
|
)
|
|
defer { pool.close() }
|
|
pool.activate()
|
|
|
|
_ = try pool
|
|
.logging(to: logger)
|
|
.ping()
|
|
.wait()
|
|
XCTAssertTrue(handler.metadata.keys.contains(RedisLogging.MetadataKeys.connectionID))
|
|
XCTAssertEqual(
|
|
handler.metadata[RedisLogging.MetadataKeys.connectionPoolID],
|
|
.string(pool.id.uuidString)
|
|
)
|
|
}
|
|
}
|
|
|
|
final class TestLogHandler: LogHandler {
|
|
var messages: [Logger.Message]
|
|
var metadata: Logger.Metadata
|
|
var logLevel: Logger.Level
|
|
|
|
init() {
|
|
self.messages = []
|
|
self.metadata = [:]
|
|
self.logLevel = .trace
|
|
}
|
|
|
|
func log(level: Logger.Level, message: Logger.Message, metadata: Logger.Metadata?, file: String, function: String, line: UInt) {
|
|
self.messages.append(message)
|
|
}
|
|
|
|
subscript(metadataKey key: String) -> Logger.Metadata.Value? {
|
|
get { self.metadata[key] }
|
|
set(newValue) { self.metadata[key] = newValue }
|
|
}
|
|
}
|