Update logging

This commit is contained in:
Ethan Pippin 2021-08-12 11:30:50 -06:00
parent d046f0ab56
commit c3c64c1354
5 changed files with 77 additions and 48 deletions

View File

@ -197,6 +197,9 @@
62EC353426766B03000E9F2D /* DeviceRotationViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62EC353326766B03000E9F2D /* DeviceRotationViewModifier.swift */; };
AE8C3159265D6F90008AA076 /* bitrates.json in Resources */ = {isa = PBXBuildFile; fileRef = AE8C3158265D6F90008AA076 /* bitrates.json */; };
E100720726BDABC100CE3E31 /* MediaPlayButtonRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E100720626BDABC100CE3E31 /* MediaPlayButtonRowView.swift */; };
E131691726C583BC0074BFEE /* LogConstructor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E131691626C583BC0074BFEE /* LogConstructor.swift */; };
E131691826C583BC0074BFEE /* LogConstructor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E131691626C583BC0074BFEE /* LogConstructor.swift */; };
E131691926C583BC0074BFEE /* LogConstructor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E131691626C583BC0074BFEE /* LogConstructor.swift */; };
E1FCD08826C35A0D007C8DCF /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FCD08726C35A0D007C8DCF /* NetworkError.swift */; };
E1FCD08926C35A0D007C8DCF /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FCD08726C35A0D007C8DCF /* NetworkError.swift */; };
E1FCD09626C47118007C8DCF /* ErrorMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1FCD09526C47118007C8DCF /* ErrorMessage.swift */; };
@ -382,6 +385,7 @@
D79953919FED0C4DF72BA578 /* Pods-JellyfinPlayer tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer tvOS.release.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS.release.xcconfig"; sourceTree = "<group>"; };
DE5004F745B19E28744A7DE7 /* Pods-JellyfinPlayer tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JellyfinPlayer tvOS.debug.xcconfig"; path = "Target Support Files/Pods-JellyfinPlayer tvOS/Pods-JellyfinPlayer tvOS.debug.xcconfig"; sourceTree = "<group>"; };
E100720626BDABC100CE3E31 /* MediaPlayButtonRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPlayButtonRowView.swift; sourceTree = "<group>"; };
E131691626C583BC0074BFEE /* LogConstructor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogConstructor.swift; sourceTree = "<group>"; };
E1FCD08726C35A0D007C8DCF /* NetworkError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = "<group>"; };
E1FCD09526C47118007C8DCF /* ErrorMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorMessage.swift; sourceTree = "<group>"; };
EBFE1F64394BCC2EFFF1610D /* Pods_JellyfinPlayer_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_JellyfinPlayer_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -773,6 +777,7 @@
isa = PBXGroup;
children = (
E1FCD08726C35A0D007C8DCF /* NetworkError.swift */,
E131691626C583BC0074BFEE /* LogConstructor.swift */,
E1FCD09526C47118007C8DCF /* ErrorMessage.swift */,
);
path = Errors;
@ -1140,6 +1145,7 @@
53ABFDE4267974EF00886593 /* LibraryListViewModel.swift in Sources */,
5364F456266CA0DC0026ECBA /* APIExtensions.swift in Sources */,
531690FA267AD6EC005D8AB9 /* PlainNavigationLinkButton.swift in Sources */,
E131691826C583BC0074BFEE /* LogConstructor.swift in Sources */,
535870A32669D89F00D05A09 /* Model.xcdatamodeld in Sources */,
09389CC826819B4600AE350E /* VideoPlayerModel.swift in Sources */,
);
@ -1176,6 +1182,7 @@
625CB5682678B6FB00530A6E /* SplashView.swift in Sources */,
535BAEA5264A151C005FA86D /* VideoPlayer.swift in Sources */,
62E632E6267D3F5B0063E547 /* EpisodeItemViewModel.swift in Sources */,
E131691726C583BC0074BFEE /* LogConstructor.swift in Sources */,
5321753B2671BCFC005491E6 /* SettingsViewModel.swift in Sources */,
532E68CF267D9F6B007B9F13 /* VideoPlayerCastDeviceSelector.swift in Sources */,
532175402671EE4F005491E6 /* LibraryFilterView.swift in Sources */,
@ -1232,6 +1239,7 @@
628B95272670CABD0091AF3B /* NextUpWidget.swift in Sources */,
628B95382670CDAB0091AF3B /* Model.xcdatamodeld in Sources */,
E1FCD09926C4F358007C8DCF /* NetworkError.swift in Sources */,
E131691926C583BC0074BFEE /* LogConstructor.swift in Sources */,
62EC353226766849000E9F2D /* SessionManager.swift in Sources */,
536D3D79267BD5D00004248C /* ViewModel.swift in Sources */,
);

View File

@ -15,21 +15,17 @@ struct ErrorMessage: Identifiable {
let code: Int
let title: String
let displayMessage: String
let logMessage: String
let logLevel: LogLevel
let logTag: String
let logConstructor: LogConstructor
var id: String {
return "\(code)\(title)\(logMessage)"
return "\(code)\(title)\(logConstructor.message)"
}
/// If the given displayMessage is `nil`, it will be set to the given logMessage
init(code: Int, title: String, displayMessage: String?, logMessage: String, logLevel: LogLevel, logTag: String?) {
/// If the custom displayMessage is `nil`, it will be set to the given logConstructor's message
init(code: Int, title: String, displayMessage: String?, logConstructor: LogConstructor) {
self.code = code
self.title = title
self.displayMessage = displayMessage ?? logMessage
self.logMessage = logMessage
self.logLevel = logLevel
self.logTag = logTag ?? ""
self.displayMessage = displayMessage ?? logConstructor.message
self.logConstructor = logConstructor
}
}

View File

@ -0,0 +1,20 @@
//
/*
* SwiftFin is subject to the terms of the Mozilla Public
* License, v2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
*/
import Foundation
import JellyfinAPI
struct LogConstructor {
var message: String
let tag: String
let level: LogLevel
let function: String
let file: String
let line: UInt
}

View File

@ -10,33 +10,40 @@
import Foundation
import JellyfinAPI
/**
The implementation of the network errors here are a temporary measure.
It is very repetitive, messy, and doesn't fulfill the entire specification of "error reporting".
The specific kind of errors here should be created and surfaced from within JellyfinAPI on API calls.
*/
enum NetworkError: Error {
/// For the case that the ErrorResponse object has a code of -1
case URLError(response: ErrorResponse, displayMessage: String?, logLevel: LogLevel, tag: String?)
case URLError(response: ErrorResponse, displayMessage: String?, logConstructor: LogConstructor)
/// For the case that the ErrorRespones object has a code of -2
case HTTPURLError(response: ErrorResponse, displayMessage: String?, logLevel: LogLevel, tag: String?)
case HTTPURLError(response: ErrorResponse, displayMessage: String?, logConstructor: LogConstructor)
/// For the case that the ErrorResponse object has a positive code
case JellyfinError(response: ErrorResponse, displayMessage: String?, logLevel: LogLevel, tag: String?)
case JellyfinError(response: ErrorResponse, displayMessage: String?, logConstructor: LogConstructor)
var errorMessage: ErrorMessage {
switch self {
case .URLError(response: let response, displayMessage: let displayMessage, let logLevel, let tag):
return NetworkError.parseURLError(from: response, displayMessage: displayMessage, logLevel: logLevel, logTag: tag)
case .HTTPURLError(response: let response, displayMessage: let displayMessage, let logLevel, let tag):
return NetworkError.parseHTTPURLError(from: response, displayMessage: displayMessage, logLevel: logLevel, logTag: tag)
case .JellyfinError(response: let response, displayMessage: let displayMessage, let logLevel, let tag):
return NetworkError.parseJellyfinError(from: response, displayMessage: displayMessage, logLevel: logLevel, logTag: tag)
case .URLError(let response, let displayMessage, let logConstructor):
return NetworkError.parseURLError(from: response, displayMessage: displayMessage, logConstructor: logConstructor)
case .HTTPURLError(let response, let displayMessage, let logConstructor):
return NetworkError.parseHTTPURLError(from: response, displayMessage: displayMessage, logConstructor: logConstructor)
case .JellyfinError(let response, let displayMessage, let logConstructor):
return NetworkError.parseJellyfinError(from: response, displayMessage: displayMessage, logConstructor: logConstructor)
}
}
func logMessage() {
let errorMessage = self.errorMessage
let logConstructor = errorMessage.logConstructor
let logFunction: (@autoclosure () -> String, String, String, String, UInt) -> Void
switch errorMessage.logLevel {
switch logConstructor.level {
case .trace:
logFunction = LogManager.shared.log.trace
case .debug:
@ -53,13 +60,14 @@ enum NetworkError: Error {
logFunction = LogManager.shared.log.debug
}
logFunction(errorMessage.logMessage, "", "", "", 0)
logFunction(logConstructor.message, logConstructor.tag, logConstructor.function, logConstructor.file, logConstructor.line)
}
private static func parseURLError(from response: ErrorResponse, displayMessage: String?, logLevel: LogLevel, logTag: String?) -> ErrorMessage {
private static func parseURLError(from response: ErrorResponse, displayMessage: String?, logConstructor: LogConstructor) -> ErrorMessage {
let errorMessage: ErrorMessage
var logMessage = "An error has occurred."
var logConstructor = logConstructor
switch response {
case .error(_, _, _, let err):
@ -69,56 +77,54 @@ enum NetworkError: Error {
switch err._code {
case -1001:
logMessage = "Network timed out."
logConstructor.message = logMessage
errorMessage = ErrorMessage(code: err._code,
title: "Timed Out",
displayMessage: displayMessage,
logMessage: logMessage,
logLevel: logLevel,
logTag: logTag)
logConstructor: logConstructor)
case -1004:
logMessage = "Cannot connect to host."
logConstructor.message = logMessage
errorMessage = ErrorMessage(code: err._code,
title: "Error",
displayMessage: displayMessage,
logMessage: logMessage,
logLevel: logLevel,
logTag: logTag)
logConstructor: logConstructor)
default:
logConstructor.message = logMessage
errorMessage = ErrorMessage(code: err._code,
title: "Error",
displayMessage: displayMessage,
logMessage: logMessage,
logLevel: logLevel,
logTag: logTag)
logConstructor: logConstructor)
}
}
return errorMessage
}
private static func parseHTTPURLError(from response: ErrorResponse, displayMessage: String?, logLevel: LogLevel, logTag: String?) -> ErrorMessage {
private static func parseHTTPURLError(from response: ErrorResponse, displayMessage: String?, logConstructor: LogConstructor) -> ErrorMessage {
let errorMessage: ErrorMessage
let logMessage = "An HTTP URL error has occurred"
var logConstructor = logConstructor
// Not implemented as has not run into one of these errors as time of writing
switch response {
case .error(_, _, _, _):
logConstructor.message = logMessage
errorMessage = ErrorMessage(code: 0,
title: "Error",
displayMessage: displayMessage,
logMessage: logMessage,
logLevel: logLevel,
logTag: logTag)
logConstructor: logConstructor)
}
return errorMessage
}
private static func parseJellyfinError(from response: ErrorResponse, displayMessage: String?, logLevel: LogLevel, logTag: String?) -> ErrorMessage {
private static func parseJellyfinError(from response: ErrorResponse, displayMessage: String?, logConstructor: LogConstructor) -> ErrorMessage {
let errorMessage: ErrorMessage
var logMessage = "An error has occurred."
var logConstructor = logConstructor
switch response {
case .error(let code, _, _, _):
@ -127,19 +133,17 @@ enum NetworkError: Error {
switch code {
case 401:
logMessage = "User is unauthorized."
logConstructor.message = logMessage
errorMessage = ErrorMessage(code: code,
title: "Unauthorized",
displayMessage: displayMessage,
logMessage: logMessage,
logLevel: logLevel,
logTag: logTag)
logConstructor: logConstructor)
default:
logConstructor.message = logMessage
errorMessage = ErrorMessage(code: code,
title: "Error",
displayMessage: displayMessage,
logMessage: logMessage,
logLevel: logLevel,
logTag: logTag)
logConstructor: logConstructor)
}
}

View File

@ -24,7 +24,7 @@ class ViewModel: ObservableObject {
loading.loading.assign(to: \.isLoading, on: self).store(in: &cancellables)
}
func handleAPIRequestError(displayMessage: String? = nil, logLevel: LogLevel = .error, tag: String = "", completion: Subscribers.Completion<Error>) {
func handleAPIRequestError(displayMessage: String? = nil, logLevel: LogLevel = .error, tag: String = "", function: String = #function, file: String = #file, line: UInt = #line, completion: Subscribers.Completion<Error>) {
switch completion {
case .finished:
break
@ -32,19 +32,20 @@ class ViewModel: ObservableObject {
if let errorResponse = error as? ErrorResponse {
let networkError: NetworkError
let logConstructor = LogConstructor(message: "__NOTHING__", tag: tag, level: logLevel, function: function, file: file, line: line)
switch errorResponse {
case .error(-1, _, _, _):
networkError = .URLError(response: errorResponse, displayMessage: displayMessage, logLevel: logLevel, tag: tag)
networkError = .URLError(response: errorResponse, displayMessage: displayMessage, logConstructor: logConstructor)
// Use the errorResponse description for debugging, rather than the user-facing friendly description which may not be implemented
LogManager.shared.log.error("Request failed: URL request failed with error \(networkError.errorMessage.code): \(errorResponse.localizedDescription)")
case .error(-2, _, _, _):
networkError = .HTTPURLError(response: errorResponse, displayMessage: displayMessage, logLevel: logLevel, tag: tag)
networkError = .HTTPURLError(response: errorResponse, displayMessage: displayMessage, logConstructor: logConstructor)
LogManager.shared.log.error("Request failed: HTTP URL request failed with description: \(errorResponse.localizedDescription)")
default:
networkError = .JellyfinError(response: errorResponse, displayMessage: displayMessage, logLevel: logLevel, tag: tag)
networkError = .JellyfinError(response: errorResponse, displayMessage: displayMessage, logConstructor: logConstructor)
// Able to use user-facing friendly description here since just HTTP status codes
LogManager.shared.log.error("Request failed: \(networkError.errorMessage.code) - \(networkError.errorMessage.title): \(networkError.errorMessage.logMessage)\n\(error.localizedDescription)")
LogManager.shared.log.error("Request failed: \(networkError.errorMessage.code) - \(networkError.errorMessage.title): \(networkError.errorMessage.logConstructor.message)\n\(error.localizedDescription)")
}
self.errorMessage = networkError.errorMessage