feat: Add Log class
This commit is contained in:
@@ -75,6 +75,7 @@
|
||||
65E6AC702C2DB3B60073F8FE /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 65E6AC6F2C2DB3B60073F8FE /* FirebaseMessaging */; };
|
||||
65EA58802A60269C0038EE9D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8C10B0BD2281FE7F00125C9F /* Localizable.strings */; };
|
||||
65EA58822A60360D0038EE9D /* EQNRealtimePushNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65EA58812A60360D0038EE9D /* EQNRealtimePushNotification.swift */; };
|
||||
65F9A60C2D70781A008A12B5 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F9A60B2D70781A008A12B5 /* Log.swift */; };
|
||||
65F9B49C2A8CA22800F13260 /* BackgroundTaskManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F9B49B2A8CA22800F13260 /* BackgroundTaskManager.swift */; };
|
||||
65F9B49E2A8CA2AC00F13260 /* EQNBackgroundPosition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F9B49D2A8CA2AC00F13260 /* EQNBackgroundPosition.swift */; };
|
||||
65F9B4A02A8CC58200F13260 /* UpdateUserLocationTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F9B49F2A8CC58200F13260 /* UpdateUserLocationTask.swift */; };
|
||||
@@ -377,6 +378,7 @@
|
||||
65DBFB7225E2BBF20041CBA6 /* GADTTemplateView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GADTTemplateView.m; sourceTree = "<group>"; };
|
||||
65DBFB7325E2BBF20041CBA6 /* GADTMediumTemplateView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GADTMediumTemplateView.h; sourceTree = "<group>"; };
|
||||
65EA58812A60360D0038EE9D /* EQNRealtimePushNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNRealtimePushNotification.swift; sourceTree = "<group>"; };
|
||||
65F9A60B2D70781A008A12B5 /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = "<group>"; };
|
||||
65F9B49B2A8CA22800F13260 /* BackgroundTaskManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskManager.swift; sourceTree = "<group>"; };
|
||||
65F9B49D2A8CA2AC00F13260 /* EQNBackgroundPosition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EQNBackgroundPosition.swift; sourceTree = "<group>"; };
|
||||
65F9B49F2A8CC58200F13260 /* UpdateUserLocationTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateUserLocationTask.swift; sourceTree = "<group>"; };
|
||||
@@ -993,6 +995,7 @@
|
||||
DC10563F251E7EC0002579BB /* Extensions */,
|
||||
8CF66054214C566A009F4314 /* Reachability.h */,
|
||||
8CF66056214C566A009F4314 /* Reachability.m */,
|
||||
65F9A60B2D70781A008A12B5 /* Log.swift */,
|
||||
);
|
||||
path = Libs;
|
||||
sourceTree = "<group>";
|
||||
@@ -1631,6 +1634,7 @@
|
||||
DC7EEE4A252A11C9004B4A2A /* AlertsSmartphoneNetworkTableViewCell.swift in Sources */,
|
||||
DC7EEE4F252A1634004B4A2A /* AlertsPriorityServiceTableViewCell.swift in Sources */,
|
||||
653604E9262348FA00B2B651 /* EQNBaseMapFilter.swift in Sources */,
|
||||
65F9A60C2D70781A008A12B5 /* Log.swift in Sources */,
|
||||
65583A05261B83BE00ECA9F9 /* UIKit+Extensions.swift in Sources */,
|
||||
65DBFB7625E2BBF20041CBA6 /* GADTTemplateView.m in Sources */,
|
||||
8C5EA23D2177B51C002DC156 /* SegnalazioniViewController.m in Sources */,
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
//
|
||||
// Log.swift
|
||||
// Earthquake Network
|
||||
//
|
||||
// Created by Andrea Busi on 27/02/25.
|
||||
// Copyright © 2025 Earthquake Network. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import OSLog
|
||||
|
||||
|
||||
/// Use this protocol to have a base TAG in a Swift class
|
||||
public protocol Loggable {
|
||||
static var TAG: String { get }
|
||||
}
|
||||
|
||||
public extension Loggable {
|
||||
static var TAG: String {
|
||||
String(describing: Self.self)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIViewController: Loggable { }
|
||||
|
||||
|
||||
public class Log {
|
||||
|
||||
private static let dumpDateFormatter: DateFormatter = {
|
||||
// create the default date formatter using ISO8601 date format
|
||||
let formatter = DateFormatter()
|
||||
formatter.locale = Locale(identifier: "en_US_POSIX")
|
||||
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
|
||||
return formatter
|
||||
}()
|
||||
|
||||
private static let shared = Log()
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private let maxNumberOfLogsInDump: Int
|
||||
private let logsLifespanMillis: Int
|
||||
/// Subsystem for OSLog
|
||||
private let subsystem: String
|
||||
/// Logging in everything in a single "APP" category
|
||||
private let appCategory: String = "APP"
|
||||
|
||||
private lazy var logger: os.Logger = {
|
||||
os.Logger(subsystem: subsystem, category: appCategory)
|
||||
}()
|
||||
|
||||
// MARK: - Init
|
||||
|
||||
@objc
|
||||
public init(
|
||||
subsystem: String = Bundle.main.bundleIdentifier!,
|
||||
maxNumberOfLogsInDump: Int = 5000,
|
||||
logsLifespanMillis: Int = 3 * 24 * 3600 * 1000
|
||||
) {
|
||||
self.subsystem = subsystem
|
||||
self.maxNumberOfLogsInDump = maxNumberOfLogsInDump
|
||||
self.logsLifespanMillis = logsLifespanMillis
|
||||
}
|
||||
|
||||
// MARK: - Internal
|
||||
|
||||
public static func error(tag: String?, _ message: String?, _ functionName: String = #function) {
|
||||
shared.log(level: .fault, tag: tag ?? "nil", message: message ?? "nil", functionName: functionName)
|
||||
}
|
||||
|
||||
public static func warning(tag: String?, _ message: String?, _ functionName: String = #function) {
|
||||
shared.log(level: .error, tag: tag ?? "nil", message: message ?? "nil", functionName: functionName)
|
||||
}
|
||||
|
||||
public static func info(tag: String?, _ message: String?, _ functionName: String = #function) {
|
||||
shared.log(level: .info, tag: tag ?? "nil", message: message ?? "nil", functionName: functionName)
|
||||
}
|
||||
|
||||
public static func debug(tag: String?, _ message: String?, _ functionName: String = #function) {
|
||||
shared.log(level: .debug, tag: tag ?? "nil", message: message ?? "nil", functionName: functionName)
|
||||
}
|
||||
|
||||
public static func verbose(tag: String?, _ message: String?, _ functionName: String = #function) {
|
||||
shared.log(level: .debug, tag: tag ?? "nil", message: message ?? "nil", functionName: functionName)
|
||||
}
|
||||
|
||||
@available(iOS 15.0, *)
|
||||
public func dumpLog() async -> String {
|
||||
return (try? await getLogEntries()) ?? ""
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func log(level: OSLogType, tag: String, message: String, functionName: String) {
|
||||
let formattedMessage = "[\(tag)] \(functionName): \(message)"
|
||||
switch level {
|
||||
case .fault: logger.fault("\(formattedMessage, privacy: .public)")
|
||||
case .error: logger.error("\(formattedMessage, privacy: .public)")
|
||||
case .default: logger.notice("\(formattedMessage, privacy: .public)")
|
||||
case .info: logger.info("\(formattedMessage, privacy: .public)")
|
||||
default: logger.debug("\(formattedMessage, privacy: .public)")
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve log entries from a specified time.
|
||||
/// - Returns: String of log entries, newlines separated
|
||||
@available(iOS 15.0, *)
|
||||
private func getLogEntries() async throws -> String {
|
||||
let logTask = Task.init(priority: .utility) { () -> String in
|
||||
let logs = try retrieveLogEntries()
|
||||
let text = logs
|
||||
.compactMap { "\(Self.dumpDateFormatter.string(from: $0.date)) [\($0.level)] \($0.composedMessage)" }
|
||||
.joined(separator: "\n")
|
||||
return text
|
||||
}
|
||||
return try await logTask.value
|
||||
}
|
||||
|
||||
@available(iOS 15.0, *)
|
||||
private func retrieveLogEntries() throws -> [OSLogEntryLog] {
|
||||
// Open the log store.
|
||||
let logStore = try OSLogStore(scope: .currentProcessIdentifier)
|
||||
|
||||
// Fetch log objects from the given time interval
|
||||
let intervalPosition = logStore.position(date: Date().addingTimeInterval(TimeInterval(-logsLifespanMillis / 1000)))
|
||||
let allEntries = try logStore.getEntries(at: intervalPosition)
|
||||
|
||||
// Filter the log to be relevant for our specific subsystem
|
||||
// and remove other elements (signposts, etc).
|
||||
return allEntries
|
||||
.compactMap { $0 as? OSLogEntryLog }
|
||||
.filter { $0.subsystem == subsystem }
|
||||
.suffix(maxNumberOfLogsInDump)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 15.0, *)
|
||||
extension OSLogEntryLog.Level: @retroactive CustomStringConvertible {
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .fault: return "FAULT"
|
||||
case .error: return "ERROR"
|
||||
case .notice: return "WARNING"
|
||||
case .info: return "INFO"
|
||||
case .debug: return "DEBUG"
|
||||
case .undefined: return "UNDEFINED"
|
||||
@unknown default:
|
||||
return "UNKNOWN"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user