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:
parent
7ccb10febc
commit
efcda495bc
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)}"
|
||||||
}
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
|
@ -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(
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue