From b6ca5e7dd6b5f2cfe8b9e5815235514cde15f3bd Mon Sep 17 00:00:00 2001 From: Thomas Kausch Date: Wed, 22 May 2024 19:20:39 +0200 Subject: [PATCH] Introduced metadata when logging. Refactored logger access to be more DRY --- Sources/SwiftRestRequests/RestApiCaller.swift | 22 ++++++++++++++++++- .../interceptors/LogNetworkInterceptor.swift | 21 ++++++++++-------- .../logging/Logger+Extension.swift | 8 ++++--- .../security/CertificateCAPinning.swift | 12 ++++++---- .../security/PublicKeyServerPinning.swift | 10 +++++---- .../security/URLRequestAuthorizer.swift | 11 ++++++++-- .../AbstractRestApiCallerTests.swift | 6 ++--- 7 files changed, 64 insertions(+), 26 deletions(-) diff --git a/Sources/SwiftRestRequests/RestApiCaller.swift b/Sources/SwiftRestRequests/RestApiCaller.swift index 9cd332a..ce8948a 100644 --- a/Sources/SwiftRestRequests/RestApiCaller.swift +++ b/Sources/SwiftRestRequests/RestApiCaller.swift @@ -24,6 +24,8 @@ import Foundation import FoundationNetworking #endif +import Logging + // MARK: - Protocols used by RestapiCaller @@ -52,6 +54,8 @@ extension URLRequestInterceptor { /// /// **NOTE:** Ensure to configure `App Transport Security` appropriately. open class RestApiCaller : NSObject { + + let logger = Logger.SwiftRestRequests.apiCaller let session: URLSession let baseUrl: URL @@ -110,7 +114,13 @@ open class RestApiCaller : NSObject { registerRequestInterceptor(LogNetworkInterceptor()) } #endif - + + logger.info("Created RestApiCaller", metadata: [ + "baseUrl": "\(baseUrl)", + "errorDeserializer": "\(String(describing: errorDeserializer))", + "headerGenerator": "\(String(describing: headerGenerator))", + "authorizer": "\(String(describing: authorizer))" + ]) } // MARK: Generic request dispatching methods @@ -194,6 +204,13 @@ open class RestApiCaller : NSObject { /// - Returns: The data returned by server and the corresponding `HTTPURLResponse` private func dataTask(relativePath: String?, httpMethod: String, accept: String, payload: Data?, options: RestOptions) async throws -> (Data, HTTPURLResponse) { + logger.debug("Data Task started", metadata: [ + "relativePath": "\(String(describing: relativePath))", + "httpMethod": "\(String(describing: httpMethod))", + "accept": "\(String(describing: accept))", + "requestTimeout": "\(String(describing: options.requestTimeoutSeconds))" + ]) + var restURL: URL; if let relativeURL = relativePath { restURL = baseUrl.appendingPathComponent(relativeURL) @@ -305,6 +322,9 @@ open class RestApiCaller : NSObject { /// Add request interceptor to the api caller. /// - Parameter interceptor: The interceptor to be called public func registerRequestInterceptor(_ interceptor: URLRequestInterceptor) { + logger.info("Registering request interceptor", metadata: [ + "interceptor": "\(interceptor)" + ]) if self.interceptors == nil { self.interceptors = [URLRequestInterceptor]() } diff --git a/Sources/SwiftRestRequests/interceptors/LogNetworkInterceptor.swift b/Sources/SwiftRestRequests/interceptors/LogNetworkInterceptor.swift index 40fc9ce..55b419b 100644 --- a/Sources/SwiftRestRequests/interceptors/LogNetworkInterceptor.swift +++ b/Sources/SwiftRestRequests/interceptors/LogNetworkInterceptor.swift @@ -32,12 +32,14 @@ open class LogNetworkInterceptor: URLRequestInterceptor { static let noBody = "none" + let logger = Logger.SwiftRestRequests.interceptor + public func invokeRequest(request: inout URLRequest, for session: URLSession) { guard let requestHeaders = request.allHTTPHeaderFields, let headerData = try? JSONSerialization.data(withJSONObject: requestHeaders , options: .prettyPrinted), let prettyJsonHeaders = String(data: headerData , encoding: .utf8) else { - Logger.interceptorLogger.warning("Something went wrong while converting headers to JSON data.") + logger.warning("Something went wrong while converting headers to JSON data.") return } @@ -46,10 +48,10 @@ open class LogNetworkInterceptor: URLRequestInterceptor { let url = request.url?.absoluteString ?? "nil" let method = request.httpMethod ?? "nil" - Logger.interceptorLogger.info("Will invoke request: \(method) \(url)") - - Logger.interceptorLogger.trace("HTTP request headers: \(prettyJsonHeaders)") - Logger.interceptorLogger.trace("HTTP request body: \(prettyJsonBody)") + logger.info("Send HTTP \(method) request \(url)", metadata: [ "method": "\(method)", + "url": "\(url)", + "headers": "\(prettyJsonHeaders)", + "body": "\(prettyJsonBody)"]) } public func receiveResponse(data: Data, response: HTTPURLResponse, for session: URLSession) { @@ -65,12 +67,13 @@ open class LogNetworkInterceptor: URLRequestInterceptor { let url = response.url?.absoluteString ?? "nil" let status = response.statusCode - - Logger.interceptorLogger.info("Did receive response: \(url) -> \(status)") + logger.info("Received HTTP response \(url) -> \(status)", metadata: [ + "url": "\(url)", + "status": "\(status)", + "headers": "\(prettyJsonHeaders)", + "body": "\(prettyJsonBody)"]) - Logger.interceptorLogger.trace("HTTP response headers: \(prettyJsonHeaders)") - Logger.interceptorLogger.trace("HTTP response body: \(prettyJsonBody)") } } diff --git a/Sources/SwiftRestRequests/logging/Logger+Extension.swift b/Sources/SwiftRestRequests/logging/Logger+Extension.swift index 63f489b..00eba46 100644 --- a/Sources/SwiftRestRequests/logging/Logger+Extension.swift +++ b/Sources/SwiftRestRequests/logging/Logger+Extension.swift @@ -26,9 +26,11 @@ extension Logger { static let labelPrefix = "com.swisscom.swiftRestRequests." - public static var interceptorLogger = Logger(label: labelPrefix + "Interceptor") - public static var securityLogger = Logger(label: labelPrefix + "Security") - public static var apiCallerLogger = Logger(label: labelPrefix + "ApiCaller") + public struct SwiftRestRequests { + public static var interceptor = Logger(label: labelPrefix + "Interceptor") + public static var security = Logger(label: labelPrefix + "Security") + public static var apiCaller = Logger(label: labelPrefix + "ApiCaller") + } } diff --git a/Sources/SwiftRestRequests/security/CertificateCAPinning.swift b/Sources/SwiftRestRequests/security/CertificateCAPinning.swift index dde2011..65ba435 100644 --- a/Sources/SwiftRestRequests/security/CertificateCAPinning.swift +++ b/Sources/SwiftRestRequests/security/CertificateCAPinning.swift @@ -32,16 +32,20 @@ open class CertificateCAPinning: NSObject, URLSessionDelegate { let pinnedCACertificates: [SecCertificate] + let logger = Logger.SwiftRestRequests.security + public init(pinnedCACertificates: [SecCertificate]) { self.pinnedCACertificates = pinnedCACertificates - Logger.securityLogger.info("Initialized CertificateCAPinning with \(pinnedCACertificates)") + logger.info("Initialized CertificateCAPinning", metadata: [ + "pinnedCACertificates": "\(pinnedCACertificates)" + ]) super.init() } public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) { guard let serverTrust = challenge.protectionSpace.serverTrust else { - Logger.securityLogger.error("Could not get serverTrust! Will cancel authentication.") + logger.error("Could not get serverTrust. Will cancel authentication!!!") return (.cancelAuthenticationChallenge, nil) } @@ -54,10 +58,10 @@ open class CertificateCAPinning: NSObject, URLSessionDelegate { let status = SecTrustEvaluateWithError(serverTrust, &error) if error == nil && status { - Logger.securityLogger.info("ServerTrust evaluation was successful. Will proceed.") + logger.info("ServerTrust evaluation was successful. Will proceed.") return (.useCredential, URLCredential(trust: serverTrust)) } else { - Logger.securityLogger.error("ServerTrustevaluation evaluation failed. Will cancel the request.") + logger.error("ServerTrustevaluation evaluation failed. Will cancel the request!!!") return (.cancelAuthenticationChallenge, nil) } diff --git a/Sources/SwiftRestRequests/security/PublicKeyServerPinning.swift b/Sources/SwiftRestRequests/security/PublicKeyServerPinning.swift index 3f77880..810f8f6 100644 --- a/Sources/SwiftRestRequests/security/PublicKeyServerPinning.swift +++ b/Sources/SwiftRestRequests/security/PublicKeyServerPinning.swift @@ -33,6 +33,8 @@ open class PublicKeyServerPinning: NSObject, URLSessionDelegate { let pinnedPublicKeys: [SecKey] + let logger = Logger.SwiftRestRequests.security + public init(pinnedPublicKeys: [SecKey]) { self.pinnedPublicKeys = pinnedPublicKeys super.init() @@ -41,7 +43,7 @@ open class PublicKeyServerPinning: NSObject, URLSessionDelegate { public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge) async -> (URLSession.AuthChallengeDisposition, URLCredential?) { guard let serverTrust = challenge.protectionSpace.serverTrust else { - Logger.securityLogger.error("Could not get serverTrust! Will cancel authentication.") + logger.error("Could not get serverTrust. Will cancel authentication!!") return(.cancelAuthenticationChallenge, nil) } @@ -51,14 +53,14 @@ open class PublicKeyServerPinning: NSObject, URLSessionDelegate { if pinnedPublicKeys.contains(where: { publicKey in publicKey == serverPublicKey }) { - Logger.securityLogger.info("Trust evaluation was successful. The public key is known (pinned).") + logger.info("Trust evaluation was successful. The public key is known (pinned).") return (.useCredential, URLCredential(trust: serverTrust)) } else { - Logger.securityLogger.error("Trust evaluation failed. The public key is unkown therfore cancel request.") + logger.error("Trust evaluation failed. The public key is unkown therefore cancel request!!!") return (.cancelAuthenticationChallenge, nil) } } else { - Logger.securityLogger.error("Trust evaluation failed. No public key found on server. Cancel request.") + logger.error("Trust evaluation failed. No public key found on server. Cancel request!") return (.cancelAuthenticationChallenge, nil) } } diff --git a/Sources/SwiftRestRequests/security/URLRequestAuthorizer.swift b/Sources/SwiftRestRequests/security/URLRequestAuthorizer.swift index 92b0072..ec128d3 100644 --- a/Sources/SwiftRestRequests/security/URLRequestAuthorizer.swift +++ b/Sources/SwiftRestRequests/security/URLRequestAuthorizer.swift @@ -38,6 +38,8 @@ public protocol URLRequestAuthorizer { /// `AuthorizationDelegate` used to configure HTTP header for basic authorization. public class BasicRequestAuthorizer: URLRequestAuthorizer { + let logger = Logger.SwiftRestRequests.security + public let username: String public let password: String @@ -58,7 +60,9 @@ public class BasicRequestAuthorizer: URLRequestAuthorizer { } public func configureAuthorizationHeader(for urlRequest: inout URLRequest) { - Logger.securityLogger.trace("Set HTTP Authorization header is set to: \(self.headerValue)") + logger.trace("Set HTTP Authorization header", metadata: [ + "urlRequest": "\(String(describing: urlRequest.url?.absoluteString))", + "Authorization": "\(headerValue)"]) urlRequest.setValue(self.headerValue, forHTTPHeaderField: "Authorization") } } @@ -66,6 +70,8 @@ public class BasicRequestAuthorizer: URLRequestAuthorizer { /// `AuthorizationDelegate` used to configure HTTP header for bearer authorization. public class BearerReqeustAuthorizer: URLRequestAuthorizer { + let logger = Logger.SwiftRestRequests.security + // The token value (without `Bearer` prefix) to be used for the HTTP `Authorization` request header. public var token: String @@ -76,7 +82,8 @@ public class BearerReqeustAuthorizer: URLRequestAuthorizer { } public func configureAuthorizationHeader(for urlRequest: inout URLRequest) { - Logger.securityLogger.trace("Set HTTP Authorization header with bearer: \(self.token)") + logger.trace("Set HTTP Authorization header", metadata: [ + "Authorization": "Bearer \(self.token)"]) urlRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") } } diff --git a/Tests/SwiftRestRequestsTests/AbstractRestApiCallerTests.swift b/Tests/SwiftRestRequestsTests/AbstractRestApiCallerTests.swift index 5bcc6be..7f3540b 100644 --- a/Tests/SwiftRestRequestsTests/AbstractRestApiCallerTests.swift +++ b/Tests/SwiftRestRequestsTests/AbstractRestApiCallerTests.swift @@ -22,9 +22,9 @@ class AbstractRestApiCallerTests: XCTestCase { LoggingSystem.bootstrap(OSLogHandler.init) #endif - Logger.securityLogger.logLevel = .trace - Logger.interceptorLogger.logLevel = .trace - Logger.apiCallerLogger.logLevel = .trace + Logger.SwiftRestRequests.security.logLevel = .trace + Logger.SwiftRestRequests.interceptor.logLevel = .trace + Logger.SwiftRestRequests.apiCaller.logLevel = .trace }()