Address logging issues (#304)

* Make specific message levels a default extension

* Define Loggable protocol

* Keep track of message originator metadata

* Fix tracing of Core Data logs

* Log query on entity not found
This commit is contained in:
Davide De Rosa 2023-05-27 12:14:04 +02:00 committed by GitHub
parent 7ccb10febc
commit efcda495bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 84 additions and 108 deletions

View File

@ -42,15 +42,33 @@ public protocol Logger {
var logLevel: LoggerLevel { get set } var logLevel: LoggerLevel { get set }
func verbose(_ message: Any) func logMessage(_ level: LoggerLevel, _ message: Any, _ file: String, _ function: String, _ line: Int)
}
func debug(_ message: Any) extension Logger {
public func verbose(_ message: Any, _ file: String = #file, _ function: String = #function, _ line: Int = #line) {
logMessage(.verbose, message, file, function, line)
}
func info(_ message: Any) public func debug(_ message: Any, _ file: String = #file, _ function: String = #function, _ line: Int = #line) {
logMessage(.debug, message, file, function, line)
}
func warning(_ message: Any) public func info(_ message: Any, _ file: String = #file, _ function: String = #function, _ line: Int = #line) {
logMessage(.info, message, file, function, line)
}
func error(_ message: Any) public func warning(_ message: Any, _ file: String = #file, _ function: String = #function, _ line: Int = #line) {
logMessage(.warning, message, file, function, line)
}
public func error(_ message: Any, _ file: String = #file, _ function: String = #function, _ line: Int = #line) {
logMessage(.error, message, file, function, line)
}
}
public protocol Loggable {
var logDescription: String { get }
} }
final class DefaultLogger: Logger { final class DefaultLogger: Logger {
@ -58,42 +76,10 @@ final class DefaultLogger: Logger {
var logLevel: LoggerLevel = .debug var logLevel: LoggerLevel = .debug
func verbose(_ message: Any) { func logMessage(_ level: LoggerLevel, _ message: Any, _ file: String, _ function: String, _ line: Int) {
guard logLevel.rawValue >= LoggerLevel.verbose.rawValue else { guard level.rawValue >= logLevel.rawValue else {
return return
} }
logMessage(message)
}
func debug(_ message: Any) {
guard logLevel.rawValue >= LoggerLevel.debug.rawValue else {
return
}
logMessage(message)
}
func info(_ message: Any) {
guard logLevel.rawValue >= LoggerLevel.info.rawValue else {
return
}
logMessage(message)
}
func warning(_ message: Any) {
guard logLevel.rawValue >= LoggerLevel.warning.rawValue else {
return
}
logMessage(message)
}
func error(_ message: Any) {
guard logLevel.rawValue >= LoggerLevel.error.rawValue else {
return
}
logMessage(message)
}
private func logMessage(_ message: Any) {
guard let string = message as? String else { guard let string = message as? String else {
return return
} }

View File

@ -25,23 +25,32 @@
import Foundation import Foundation
// XXX: these should be aliases like #define to print original caller line
extension Utils { extension Utils {
public static func assertCoreDataDecodingFailed( public static func assertCoreDataDecodingFailed(
_ file: String, _ message: String? = nil,
_ function: String, _ file: String = #file,
_ line: Int, _ function: String = #function,
_ message: String? = nil _ line: Int = #line
) { ) {
// assertionFailure(message ?? "Cannot decode entity required fields - \(file):\(function):\(line)") // assertionFailure(message ?? "Cannot decode entity required fields", file, function, line)
pp_log.warning(message ?? "Cannot decode entity required fields - \(file):\(function):\(line)") pp_log.warning(message ?? "Cannot decode entity required fields", file, function, line)
} }
public static func logFetchError(_ file: String, _ function: String, _ line: Int, _ error: Error) { public static func logFetchError(
pp_log.error("Unable to fetch: \(error) - \(file):\(function):\(line)") _ error: Error,
_ file: String = #file,
_ function: String = #function,
_ line: Int = #line
) {
pp_log.error("Unable to fetch: \(error)", file, function, line)
} }
public static func logFetchNotFound(_ file: String, _ function: String, _ line: Int) { public static func logFetchNotFound(
pp_log.debug("Not found - \(file):\(function):\(line)") _ query: String,
_ file: String = #file,
_ function: String = #function,
_ line: Int = #line
) {
pp_log.debug("Entity not found: (\(query))", file, function, line)
} }
} }

View File

@ -1,5 +1,5 @@
// //
// Domain+Logging.swift // Domain+Loggable.swift
// Passepartout // Passepartout
// //
// Created by Davide De Rosa on 4/7/22. // Created by Davide De Rosa on 4/7/22.
@ -24,8 +24,9 @@
// //
import Foundation import Foundation
import PassepartoutCore
extension ProviderServer { extension ProviderServer: Loggable {
public var logDescription: String { public var logDescription: String {
"{'\(categoryName)', \(countryCode), '\(apiId)', \(id)}" "{'\(categoryName)', \(countryCode), '\(apiId)', \(id)}"
} }

View File

@ -39,12 +39,12 @@ extension CDLocalProvidersRepository: InfrastructureRepository {
] ]
do { do {
guard let infrastructureDTO = try context.fetch(request).first else { guard let infrastructureDTO = try context.fetch(request).first else {
Utils.logFetchNotFound(#file, #function, #line) Utils.logFetchNotFound("\(name), \(vpnProtocol)")
return nil return nil
} }
return infrastructureDTO.defaults?.usernamePlaceholder return infrastructureDTO.defaults?.usernamePlaceholder
} catch { } catch {
Utils.logFetchError(#file, #function, #line, error) Utils.logFetchError(error)
return nil return nil
} }
} }
@ -61,14 +61,14 @@ extension CDLocalProvidersRepository: InfrastructureRepository {
do { do {
let infrastructures = try context.fetch(request) let infrastructures = try context.fetch(request)
guard !infrastructures.isEmpty else { guard !infrastructures.isEmpty else {
Utils.logFetchNotFound(#file, #function, #line) Utils.logFetchNotFound("\(name), \(vpnProtocol)")
return nil return nil
} }
let recent = infrastructures.first! let recent = infrastructures.first!
return recent.lastUpdate return recent.lastUpdate
} catch { } catch {
context.rollback() context.rollback()
Utils.logFetchError(#file, #function, #line, error) Utils.logFetchError(error)
return nil return nil
} }
} }

View File

@ -45,7 +45,7 @@ extension CDLocalProvidersRepository: ProviderRepository {
} }
return providers.compactMap(ProviderMapper.toModel) return providers.compactMap(ProviderMapper.toModel)
} catch { } catch {
Utils.logFetchError(#file, #function, #line, error) Utils.logFetchError(error)
return [] return []
} }
} }
@ -62,13 +62,13 @@ extension CDLocalProvidersRepository: ProviderRepository {
do { do {
let providers = try context.fetch(request) let providers = try context.fetch(request)
guard !providers.isEmpty else { guard !providers.isEmpty else {
Utils.logFetchNotFound(#file, #function, #line) Utils.logFetchNotFound(name)
return nil return nil
} }
let recent = providers.first! let recent = providers.first!
return ProviderMapper.toModel(recent) return ProviderMapper.toModel(recent)
} catch { } catch {
Utils.logFetchError(#file, #function, #line, error) Utils.logFetchError(error)
return nil return nil
} }
} }

View File

@ -47,7 +47,7 @@ extension CDLocalProvidersRepository: ServerRepository {
let categoryDTOs = try context.fetch(request) let categoryDTOs = try context.fetch(request)
return categoryDTOs.compactMap(CategoryMapper.toModel) return categoryDTOs.compactMap(CategoryMapper.toModel)
} catch { } catch {
Utils.logFetchError(#file, #function, #line, error) Utils.logFetchError(error)
return [] return []
} }
} }
@ -73,7 +73,7 @@ extension CDLocalProvidersRepository: ServerRepository {
// just preset ids, no full ProviderServer.Preset // just preset ids, no full ProviderServer.Preset
return serverDTOs.compactMap(ServerMapper.toModel) return serverDTOs.compactMap(ServerMapper.toModel)
} catch { } catch {
Utils.logFetchError(#file, #function, #line, error) Utils.logFetchError(error)
return [] return []
} }
} }
@ -94,12 +94,12 @@ extension CDLocalProvidersRepository: ServerRepository {
] ]
do { do {
guard let serverDTO = try context.fetch(request).first else { guard let serverDTO = try context.fetch(request).first else {
Utils.logFetchNotFound(#file, #function, #line) Utils.logFetchNotFound("\(providerName), \(vpnProtocol), \(apiId)")
return nil return nil
} }
return ServerMapper.toModelWithPresets(serverDTO) return ServerMapper.toModelWithPresets(serverDTO)
} catch { } catch {
Utils.logFetchError(#file, #function, #line, error) Utils.logFetchError(error)
return nil return nil
} }
} }
@ -121,12 +121,12 @@ extension CDLocalProvidersRepository: ServerRepository {
do { do {
try Utils.randomizeFetchResults(request, in: context) try Utils.randomizeFetchResults(request, in: context)
guard let serverDTO = try context.fetch(request).first else { guard let serverDTO = try context.fetch(request).first else {
Utils.logFetchNotFound(#file, #function, #line) Utils.logFetchNotFound("\(providerName), \(vpnProtocol), \(countryCode)")
return nil return nil
} }
return ServerMapper.toModelWithPresets(serverDTO) return ServerMapper.toModelWithPresets(serverDTO)
} catch { } catch {
Utils.logFetchError(#file, #function, #line, error) Utils.logFetchError(error)
return nil return nil
} }
} }
@ -148,12 +148,12 @@ extension CDLocalProvidersRepository: ServerRepository {
do { do {
try Utils.randomizeFetchResults(request, in: context) try Utils.randomizeFetchResults(request, in: context)
guard let serverDTO = try context.fetch(request).first else { guard let serverDTO = try context.fetch(request).first else {
Utils.logFetchNotFound(#file, #function, #line) Utils.logFetchNotFound("\(providerName), \(vpnProtocol)")
return nil return nil
} }
return ServerMapper.toModelWithPresets(serverDTO) return ServerMapper.toModelWithPresets(serverDTO)
} catch { } catch {
Utils.logFetchError(#file, #function, #line, error) Utils.logFetchError(error)
return nil return nil
} }
} }
@ -171,12 +171,12 @@ extension CDLocalProvidersRepository: ServerRepository {
] ]
do { do {
guard let serverDTO = try context.fetch(request).first else { guard let serverDTO = try context.fetch(request).first else {
Utils.logFetchNotFound(#file, #function, #line) Utils.logFetchNotFound(id)
return nil return nil
} }
return ServerMapper.toModelWithPresets(serverDTO) return ServerMapper.toModelWithPresets(serverDTO)
} catch { } catch {
Utils.logFetchError(#file, #function, #line, error) Utils.logFetchError(error)
return nil return nil
} }
} }

View File

@ -64,7 +64,7 @@ struct CategoryMapper: DTOMapper, ModelMapper {
let name = dto.name, let name = dto.name,
let locations = dto.locations else { let locations = dto.locations else {
Utils.assertCoreDataDecodingFailed(#file, #function, #line) Utils.assertCoreDataDecodingFailed()
return nil return nil
} }

View File

@ -100,7 +100,7 @@ struct InfrastructureMapper: DTOMapper {
vpnProtocol: vpnProtocol, vpnProtocol: vpnProtocol,
apiId: apiId apiId: apiId
) else { ) else {
Utils.assertCoreDataDecodingFailed(#file, #function, #line) Utils.assertCoreDataDecodingFailed()
return return
} }
server.uniqueId = uniqueId server.uniqueId = uniqueId

View File

@ -58,7 +58,7 @@ struct LocationMapper: DTOMapper, ModelMapper {
let categoryName = dto.category?.name, let categoryName = dto.category?.name,
let countryCode = dto.countryCode else { let countryCode = dto.countryCode else {
Utils.assertCoreDataDecodingFailed(#file, #function, #line) Utils.assertCoreDataDecodingFailed()
return nil return nil
} }

View File

@ -60,7 +60,7 @@ struct PresetMapper: DTOMapper, ModelMapper {
let vpnProtocol = VPNProtocolType(rawValue: vpnProtocolString), let vpnProtocol = VPNProtocolType(rawValue: vpnProtocolString),
let vpnConfiguration = dto.decodedConfiguration else { let vpnConfiguration = dto.decodedConfiguration else {
Utils.assertCoreDataDecodingFailed(#file, #function, #line) Utils.assertCoreDataDecodingFailed()
return nil return nil
} }

View File

@ -57,7 +57,7 @@ struct ProviderMapper: DTOMapper, ModelMapper {
guard let name = dto.name, guard let name = dto.name,
let fullName = dto.fullName else { let fullName = dto.fullName else {
Utils.assertCoreDataDecodingFailed(#file, #function, #line) Utils.assertCoreDataDecodingFailed()
return nil return nil
} }

View File

@ -73,14 +73,11 @@ struct ServerMapper: DTOMapper, ModelMapper {
let providerMetadata = ProviderMapper.toModel(providerDTO), let providerMetadata = ProviderMapper.toModel(providerDTO),
let countryCode = dto.countryCode else { let countryCode = dto.countryCode else {
Utils.assertCoreDataDecodingFailed(#file, #function, #line) Utils.assertCoreDataDecodingFailed()
return nil return nil
} }
guard let presetDTOs = categoryDTO.presets?.allObjects as? [CDInfrastructurePreset], !presetDTOs.isEmpty else { guard let presetDTOs = categoryDTO.presets?.allObjects as? [CDInfrastructurePreset], !presetDTOs.isEmpty else {
Utils.assertCoreDataDecodingFailed( Utils.assertCoreDataDecodingFailed("Category '\(categoryName)' of server \(apiId) has no presets")
#file, #function, #line,
"Category '\(categoryName)' of server \(apiId) has no presets"
)
return nil return nil
} }
@ -109,14 +106,11 @@ struct ServerMapper: DTOMapper, ModelMapper {
let categoryDTO = dto.category, let categoryDTO = dto.category,
let categoryName = categoryDTO.name else { let categoryName = categoryDTO.name else {
Utils.assertCoreDataDecodingFailed(#file, #function, #line) Utils.assertCoreDataDecodingFailed()
return nil return nil
} }
guard let presetDTOs = dto.category?.presets?.allObjects as? [CDInfrastructurePreset], !presetDTOs.isEmpty else { guard let presetDTOs = dto.category?.presets?.allObjects as? [CDInfrastructurePreset], !presetDTOs.isEmpty else {
Utils.assertCoreDataDecodingFailed( Utils.assertCoreDataDecodingFailed("Category '\(categoryName)' has no presets")
#file, #function, #line,
"Category '\(categoryName)' has no presets"
)
return nil return nil
} }

View File

@ -1,5 +1,5 @@
// //
// Domain+Logging.swift // Domain+Loggable.swift
// Passepartout // Passepartout
// //
// Created by Davide De Rosa on 4/7/22. // Created by Davide De Rosa on 4/7/22.
@ -24,14 +24,15 @@
// //
import Foundation import Foundation
import PassepartoutCore
extension Profile.Header { extension Profile.Header: Loggable {
public var logDescription: String { public var logDescription: String {
"{\(id), '\(name)'}" "{\(id), '\(name)'}"
} }
} }
extension Profile { extension Profile: Loggable {
public var logDescription: String { public var logDescription: String {
header.logDescription header.logDescription
} }

View File

@ -48,7 +48,7 @@ struct ProfileMapper: DTOMapper, ModelMapper {
static func toModel(_ dto: CDProfile) throws -> Profile? { static func toModel(_ dto: CDProfile) throws -> Profile? {
guard let json = dto.json else { guard let json = dto.json else {
Utils.assertCoreDataDecodingFailed(#file, #function, #line) Utils.assertCoreDataDecodingFailed()
return nil return nil
} }
do { do {
@ -80,7 +80,7 @@ struct ProfileHeaderMapper: DTOMapper, ModelMapper {
guard let uuid = dto.uuid, guard let uuid = dto.uuid,
let name = dto.name else { let name = dto.name else {
Utils.assertCoreDataDecodingFailed(#file, #function, #line) Utils.assertCoreDataDecodingFailed()
return nil return nil
} }
return Profile.Header( return Profile.Header(

View File

@ -32,8 +32,9 @@ public final class SwiftyBeaverLogger: Logger {
public var logLevel: LoggerLevel { public var logLevel: LoggerLevel {
didSet { didSet {
let nativeLevel = logLevel.toSwiftyBeaver
SwiftyBeaver.destinations.forEach { SwiftyBeaver.destinations.forEach {
$0.minLevel = logLevel.toSwiftyBeaver $0.minLevel = nativeLevel
} }
} }
} }
@ -62,24 +63,8 @@ public final class SwiftyBeaverLogger: Logger {
} }
} }
public func verbose(_ message: Any) { public func logMessage(_ level: LoggerLevel, _ message: Any, _ file: String, _ function: String, _ line: Int) {
SwiftyBeaver.verbose(message) SwiftyBeaver.custom(level: level.toSwiftyBeaver, message: message, file: file, function: function, line: line)
}
public func debug(_ message: Any) {
SwiftyBeaver.debug(message)
}
public func info(_ message: Any) {
SwiftyBeaver.info(message)
}
public func warning(_ message: Any) {
SwiftyBeaver.warning(message)
}
public func error(_ message: Any) {
SwiftyBeaver.error(message)
} }
} }