add RelayLog class
Signed-off-by: Bryan Montz <bryanmontz@me.com> Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
committed by
William Casarin
parent
13f98659a4
commit
ef4aeb40e0
@@ -295,6 +295,7 @@
|
|||||||
501F8C822A0224EB001AFC1D /* KeychainStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501F8C812A0224EB001AFC1D /* KeychainStorageTests.swift */; };
|
501F8C822A0224EB001AFC1D /* KeychainStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501F8C812A0224EB001AFC1D /* KeychainStorageTests.swift */; };
|
||||||
5053ACA72A56DF3B00851AE3 /* DeveloperSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */; };
|
5053ACA72A56DF3B00851AE3 /* DeveloperSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */; };
|
||||||
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; };
|
50A50A8D29A09E1C00C01BE7 /* RequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */; };
|
||||||
|
50A60D142A28BEEE00186190 /* RelayLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50A60D132A28BEEE00186190 /* RelayLog.swift */; };
|
||||||
50B5685329F97CB400A23243 /* CredentialHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B5685229F97CB400A23243 /* CredentialHandler.swift */; };
|
50B5685329F97CB400A23243 /* CredentialHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50B5685229F97CB400A23243 /* CredentialHandler.swift */; };
|
||||||
50DA11262A16A23F00236234 /* Launch.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50DA11252A16A23F00236234 /* Launch.storyboard */; };
|
50DA11262A16A23F00236234 /* Launch.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 50DA11252A16A23F00236234 /* Launch.storyboard */; };
|
||||||
5C0707D12A1ECB38004E7B51 /* DamusLogoGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C0707D02A1ECB38004E7B51 /* DamusLogoGradient.swift */; };
|
5C0707D12A1ECB38004E7B51 /* DamusLogoGradient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C0707D02A1ECB38004E7B51 /* DamusLogoGradient.swift */; };
|
||||||
@@ -765,6 +766,7 @@
|
|||||||
501F8C812A0224EB001AFC1D /* KeychainStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainStorageTests.swift; sourceTree = "<group>"; };
|
501F8C812A0224EB001AFC1D /* KeychainStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainStorageTests.swift; sourceTree = "<group>"; };
|
||||||
5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperSettingsView.swift; sourceTree = "<group>"; };
|
5053ACA62A56DF3B00851AE3 /* DeveloperSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperSettingsView.swift; sourceTree = "<group>"; };
|
||||||
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; };
|
50A50A8C29A09E1C00C01BE7 /* RequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestTests.swift; sourceTree = "<group>"; };
|
||||||
|
50A60D132A28BEEE00186190 /* RelayLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelayLog.swift; sourceTree = "<group>"; };
|
||||||
50B5685229F97CB400A23243 /* CredentialHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialHandler.swift; sourceTree = "<group>"; };
|
50B5685229F97CB400A23243 /* CredentialHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialHandler.swift; sourceTree = "<group>"; };
|
||||||
50DA11252A16A23F00236234 /* Launch.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Launch.storyboard; sourceTree = "<group>"; };
|
50DA11252A16A23F00236234 /* Launch.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Launch.storyboard; sourceTree = "<group>"; };
|
||||||
5C0707D02A1ECB38004E7B51 /* DamusLogoGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusLogoGradient.swift; sourceTree = "<group>"; };
|
5C0707D02A1ECB38004E7B51 /* DamusLogoGradient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DamusLogoGradient.swift; sourceTree = "<group>"; };
|
||||||
@@ -1132,6 +1134,7 @@
|
|||||||
children = (
|
children = (
|
||||||
501F8C5329FF5EE2001AFC1D /* CoreData */,
|
501F8C5329FF5EE2001AFC1D /* CoreData */,
|
||||||
4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */,
|
4CE6DF1527F8DEBF00C66700 /* RelayConnection.swift */,
|
||||||
|
50A60D132A28BEEE00186190 /* RelayLog.swift */,
|
||||||
4C75EFA527FF87A20006080F /* Nostr.swift */,
|
4C75EFA527FF87A20006080F /* Nostr.swift */,
|
||||||
4C75EFAE28049D340006080F /* NostrFilter.swift */,
|
4C75EFAE28049D340006080F /* NostrFilter.swift */,
|
||||||
4C75EFB028049D510006080F /* NostrResponse.swift */,
|
4C75EFB028049D510006080F /* NostrResponse.swift */,
|
||||||
@@ -1980,6 +1983,7 @@
|
|||||||
4C363A922825FCF2006E126D /* ProfileUpdate.swift in Sources */,
|
4C363A922825FCF2006E126D /* ProfileUpdate.swift in Sources */,
|
||||||
4CB9D4A92992D2F400A9A7E4 /* FollowsYou.swift in Sources */,
|
4CB9D4A92992D2F400A9A7E4 /* FollowsYou.swift in Sources */,
|
||||||
4C3BEFDA281DCA1400B3DE84 /* LikeCounter.swift in Sources */,
|
4C3BEFDA281DCA1400B3DE84 /* LikeCounter.swift in Sources */,
|
||||||
|
50A60D142A28BEEE00186190 /* RelayLog.swift in Sources */,
|
||||||
4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */,
|
4CB88389296AF99A00DC99E7 /* EventDetailBar.swift in Sources */,
|
||||||
4CE8794E2996B16A00F758CC /* RelayToggle.swift in Sources */,
|
4CE8794E2996B16A00F758CC /* RelayToggle.swift in Sources */,
|
||||||
4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */,
|
4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */,
|
||||||
|
|||||||
143
damus/Nostr/RelayLog.swift
Normal file
143
damus/Nostr/RelayLog.swift
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
//
|
||||||
|
// RelayLog.swift
|
||||||
|
// damus
|
||||||
|
//
|
||||||
|
// Created by Bryan Montz on 6/1/23.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
/// Stores a running list of events and state changes related to a relay, so that users
|
||||||
|
/// will have information to help developers debug issues.
|
||||||
|
final class RelayLog: ObservableObject {
|
||||||
|
private static let line_limit = 250
|
||||||
|
private let relay_url: URL?
|
||||||
|
private lazy var formatter: DateFormatter = {
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.dateStyle = .short
|
||||||
|
formatter.timeStyle = .medium
|
||||||
|
return formatter
|
||||||
|
}()
|
||||||
|
|
||||||
|
private(set) var lines = [String]()
|
||||||
|
|
||||||
|
private var notification_token: AnyCancellable?
|
||||||
|
|
||||||
|
/// Creates a RelayLog
|
||||||
|
/// - Parameter relay_url: the relay url the log represents. Pass nil for the url to create
|
||||||
|
/// a RelayLog that does nothing. This is required to allow RelayLog to be used as a StateObject,
|
||||||
|
/// because they cannot be Optional.
|
||||||
|
init(_ relay_url: URL? = nil) {
|
||||||
|
self.relay_url = relay_url
|
||||||
|
|
||||||
|
setUp()
|
||||||
|
}
|
||||||
|
|
||||||
|
private var log_files_directory: URL {
|
||||||
|
FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!.appendingPathComponent("RelayLogs", isDirectory: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private var log_file_url: URL? {
|
||||||
|
guard let file_name = relay_url?.absoluteString.data(using: .utf8) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return log_files_directory.appendingPathComponent(file_name.base64EncodedString())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets up the log file and prepares to listen to app state changes
|
||||||
|
private func setUp() {
|
||||||
|
guard let log_file_url else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try? FileManager.default.createDirectory(at: log_files_directory, withIntermediateDirectories: false)
|
||||||
|
|
||||||
|
if !FileManager.default.fileExists(atPath: log_file_url.path) {
|
||||||
|
// create the log file if it doesn't exist yet
|
||||||
|
FileManager.default.createFile(atPath: log_file_url.path, contents: nil)
|
||||||
|
} else {
|
||||||
|
// otherwise load it into memory
|
||||||
|
readFromDisk()
|
||||||
|
}
|
||||||
|
|
||||||
|
let willResignPublisher = NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)
|
||||||
|
let willTerminatePublisher = NotificationCenter.default.publisher(for: UIApplication.willTerminateNotification)
|
||||||
|
notification_token = Publishers.Merge(willResignPublisher, willTerminatePublisher)
|
||||||
|
.sink { [weak self] _ in
|
||||||
|
self?.writeToDisk()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The current contents of the log
|
||||||
|
var contents: String? {
|
||||||
|
guard !lines.isEmpty else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return lines.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds content to the log
|
||||||
|
/// - Parameter content: what to add to the log. The date and time are prepended to the content.
|
||||||
|
func add(_ content: String) {
|
||||||
|
let line = "\(formatter.string(from: .now)) - \(content)"
|
||||||
|
lines.insert(line, at: 0)
|
||||||
|
truncateLines()
|
||||||
|
|
||||||
|
Task {
|
||||||
|
await publishChanges()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tells views that our log has been updated
|
||||||
|
@MainActor private func publishChanges() {
|
||||||
|
objectWillChange.send()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func truncateLines() {
|
||||||
|
lines = Array(lines.prefix(RelayLog.line_limit))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the contents of the log file from disk into memory
|
||||||
|
private func readFromDisk() {
|
||||||
|
guard let log_file_url else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let handle = try FileHandle(forReadingFrom: log_file_url)
|
||||||
|
let data = try handle.readToEnd()
|
||||||
|
try handle.close()
|
||||||
|
|
||||||
|
guard let data, let content = String(data: data, encoding: .utf8) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lines = content.components(separatedBy: "\n")
|
||||||
|
|
||||||
|
truncateLines()
|
||||||
|
} catch {
|
||||||
|
print("⚠️ Warning: RelayLog failed to read from \(log_file_url)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the contents of the lines in memory to disk
|
||||||
|
private func writeToDisk() {
|
||||||
|
guard let log_file_url, let relay_url,
|
||||||
|
!lines.isEmpty,
|
||||||
|
let content = lines.joined(separator: "\n").data(using: .utf8) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let handle = try FileHandle(forWritingTo: log_file_url)
|
||||||
|
|
||||||
|
try handle.truncate(atOffset: 0)
|
||||||
|
try handle.write(contentsOf: content)
|
||||||
|
try handle.close()
|
||||||
|
} catch {
|
||||||
|
print("⚠️ Warning: RelayLog(\(relay_url)) failed to write to file: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user