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 }
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 {
@ -58,42 +76,10 @@ final class DefaultLogger: Logger {
var logLevel: LoggerLevel = .debug
func verbose(_ message: Any) {
guard logLevel.rawValue >= LoggerLevel.verbose.rawValue else {
func logMessage(_ level: LoggerLevel, _ message: Any, _ file: String, _ function: String, _ line: Int) {
guard level.rawValue >= logLevel.rawValue else {
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 {
return
}

View File

@ -25,23 +25,32 @@
import Foundation
// XXX: these should be aliases like #define to print original caller line
extension Utils {
public static func assertCoreDataDecodingFailed(
_ file: String,
_ function: String,
_ line: Int,
_ message: String? = nil
_ message: String? = nil,
_ file: String = #file,
_ function: String = #function,
_ line: Int = #line
) {
// assertionFailure(message ?? "Cannot decode entity required fields - \(file):\(function):\(line)")
pp_log.warning(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)
}
public static func logFetchError(_ file: String, _ function: String, _ line: Int, _ error: Error) {
pp_log.error("Unable to fetch: \(error) - \(file):\(function):\(line)")
public static func logFetchError(
_ 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) {
pp_log.debug("Not found - \(file):\(function):\(line)")
public static func logFetchNotFound(
_ 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
//
// Created by Davide De Rosa on 4/7/22.
@ -24,8 +24,9 @@
//
import Foundation
import PassepartoutCore
extension ProviderServer {
extension ProviderServer: Loggable {
public var logDescription: String {
"{'\(categoryName)', \(countryCode), '\(apiId)', \(id)}"
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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